Merge "Add top-level OWNERS coverage for APAC."
diff --git a/core/api/current.txt b/core/api/current.txt
index 2e821b3..252c33c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8909,6 +8909,8 @@
method public int getConnectionState(android.bluetooth.BluetoothDevice);
method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
method public boolean isAudioConnected(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isNoiseReductionSupported(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isVoiceRecognitionSupported(@NonNull android.bluetooth.BluetoothDevice);
method public boolean sendVendorSpecificResultCode(android.bluetooth.BluetoothDevice, String, String);
method public boolean startVoiceRecognition(android.bluetooth.BluetoothDevice);
method public boolean stopVoiceRecognition(android.bluetooth.BluetoothDevice);
@@ -41015,6 +41017,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot();
method public int getActiveModemCount();
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCallComposerStatus();
method public int getCallState();
method public int getCardIdForDefaultEuicc();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
@@ -41109,6 +41112,7 @@
method @Deprecated public String sendEnvelopeWithStatus(String);
method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallComposerStatus(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledForReason(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>);
@@ -41143,6 +41147,8 @@
field public static final int APPTYPE_USIM = 2; // 0x2
field public static final int AUTHTYPE_EAP_AKA = 129; // 0x81
field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80
+ field public static final int CALL_COMPOSER_STATUS_OFF = 0; // 0x0
+ field public static final int CALL_COMPOSER_STATUS_ON = 1; // 0x1
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index adb7e2f7..f59ae33 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -682,6 +682,48 @@
}
/**
+ * Checks whether the headset supports some form of noise reduction
+ *
+ * @param device Bluetooth device
+ * @return true if echo cancellation and/or noise reduction is supported, false otherwise
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) {
+ if (DBG) log("isNoiseReductionSupported()");
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return service.isNoiseReductionSupported(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
+ * Checks whether the headset supports voice recognition
+ *
+ * @param device Bluetooth device
+ * @return true if voice recognition is supported, false otherwise
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) {
+ if (DBG) log("isVoiceRecognitionSupported()");
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return service.isVoiceRecognitionSupported(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
* Start Bluetooth voice recognition. This methods sends the voice
* recognition AT command to the headset and establishes the
* audio connection.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3d724f0..5bdd521 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -943,7 +943,7 @@
/** @hide */
@Override
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 175981568)
public Context createApplicationContext(ApplicationInfo application,
int flags) throws PackageManager.NameNotFoundException {
return mBase.createApplicationContext(application, flags);
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 44b5c44..0b950b4 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -17,6 +17,7 @@
package android.content.om;
import android.content.om.OverlayInfo;
+import android.content.om.OverlayManagerTransaction;
/**
* Api for getting information about overlay packages.
@@ -163,4 +164,18 @@
* @param packageName The name of the overlay package whose idmap should be deleted.
*/
void invalidateCachesForOverlay(in String packageName, in int userIs);
+
+ /**
+ * Perform a series of requests related to overlay packages. This is an
+ * atomic operation: either all requests were performed successfully and
+ * the changes were propagated to the rest of the system, or at least one
+ * request could not be performed successfully and nothing is changed and
+ * nothing is propagated to the rest of the system.
+ *
+ * @see OverlayManagerTransaction
+ *
+ * @param transaction the series of overlay related requests to perform
+ * @throws SecurityException if the transaction failed
+ */
+ void commit(in OverlayManagerTransaction transaction);
}
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 217f637c..7c14c28 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -254,6 +254,29 @@
}
/**
+ * Perform a series of requests related to overlay packages. This is an
+ * atomic operation: either all requests were performed successfully and
+ * the changes were propagated to the rest of the system, or at least one
+ * request could not be performed successfully and nothing is changed and
+ * nothing is propagated to the rest of the system.
+ *
+ * @see OverlayManagerTransaction
+ *
+ * @param transaction the series of overlay related requests to perform
+ * @throws Exception if not all the requests could be successfully and
+ * atomically executed
+ *
+ * @hide
+ */
+ public void commit(@NonNull final OverlayManagerTransaction transaction) {
+ try {
+ mService.commit(transaction);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Starting on R, actor enforcement and app visibility changes introduce additional failure
* cases, but the SecurityException thrown with these checks is unexpected for existing
* consumers of the API.
diff --git a/core/java/android/content/om/OverlayManagerTransaction.aidl b/core/java/android/content/om/OverlayManagerTransaction.aidl
new file mode 100644
index 0000000..6715c82
--- /dev/null
+++ b/core/java/android/content/om/OverlayManagerTransaction.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.om;
+
+parcelable OverlayManagerTransaction;
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
new file mode 100644
index 0000000..1fa8973
--- /dev/null
+++ b/core/java/android/content/om/OverlayManagerTransaction.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.om;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Container for a batch of requests to the OverlayManagerService.
+ *
+ * Transactions are created using a builder interface. Example usage:
+ *
+ * final OverlayManager om = ctx.getSystemService(OverlayManager.class);
+ * final OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+ * .setEnabled(...)
+ * .setEnabled(...)
+ * .build();
+ * om.commit(t);
+ *
+ * @hide
+ */
+public class OverlayManagerTransaction
+ implements Iterable<OverlayManagerTransaction.Request>, Parcelable {
+ // TODO: remove @hide from this class when OverlayManager is added to the
+ // SDK, but keep OverlayManagerTransaction.Request @hidden
+ private final List<Request> mRequests;
+
+ OverlayManagerTransaction(@NonNull final List<Request> requests) {
+ checkNotNull(requests);
+ if (requests.contains(null)) {
+ throw new IllegalArgumentException("null request");
+ }
+ mRequests = requests;
+ }
+
+ private OverlayManagerTransaction(@NonNull final Parcel source) {
+ final int size = source.readInt();
+ mRequests = new ArrayList<Request>(size);
+ for (int i = 0; i < size; i++) {
+ final int request = source.readInt();
+ final String packageName = source.readString();
+ final int userId = source.readInt();
+ mRequests.add(new Request(request, packageName, userId));
+ }
+ }
+
+ @Override
+ public Iterator<Request> iterator() {
+ return mRequests.iterator();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("OverlayManagerTransaction { mRequests = %s }", mRequests);
+ }
+
+ /**
+ * A single unit of the transaction, such as a request to enable an
+ * overlay, or to disable an overlay.
+ *
+ * @hide
+ */
+ public static class Request {
+ @IntDef(prefix = "TYPE_", value = {
+ TYPE_SET_ENABLED,
+ TYPE_SET_DISABLED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface RequestType {}
+
+ public static final int TYPE_SET_ENABLED = 0;
+ public static final int TYPE_SET_DISABLED = 1;
+
+ @RequestType public final int type;
+ public final String packageName;
+ public final int userId;
+
+ public Request(@RequestType final int type, @NonNull final String packageName,
+ final int userId) {
+ this.type = type;
+ this.packageName = packageName;
+ this.userId = userId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}",
+ type, typeToString(), packageName, userId);
+ }
+
+ /**
+ * Translate the request type into a human readable string. Only
+ * intended for debugging.
+ *
+ * @hide
+ */
+ public String typeToString() {
+ switch (type) {
+ case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED";
+ case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED";
+ default: return String.format("TYPE_UNKNOWN (0x%02x)", type);
+ }
+ }
+ }
+
+ /**
+ * Builder class for OverlayManagerTransaction objects.
+ *
+ * @hide
+ */
+ public static class Builder {
+ private final List<Request> mRequests = new ArrayList<>();
+
+ /**
+ * Request that an overlay package be enabled and change its loading
+ * order to the last package to be loaded, or disabled
+ *
+ * If the caller has the correct permissions, it is always possible to
+ * disable an overlay. Due to technical and security reasons it may not
+ * always be possible to enable an overlay, for instance if the overlay
+ * does not successfully overlay any target resources due to
+ * overlayable policy restrictions.
+ *
+ * An enabled overlay is a part of target package's resources, i.e. it will
+ * be part of any lookups performed via {@link android.content.res.Resources}
+ * and {@link android.content.res.AssetManager}. A disabled overlay will no
+ * longer affect the resources of the target package. If the target is
+ * currently running, its outdated resources will be replaced by new ones.
+ *
+ * @param packageName The name of the overlay package.
+ * @param enable true to enable the overlay, false to disable it.
+ * @return this Builder object, so you can chain additional requests
+ */
+ public Builder setEnabled(@NonNull String packageName, boolean enable) {
+ return setEnabled(packageName, enable, UserHandle.myUserId());
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) {
+ checkNotNull(packageName);
+ @Request.RequestType final int type =
+ enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED;
+ mRequests.add(new Request(type, packageName, userId));
+ return this;
+ }
+
+ /**
+ * Create a new transaction out of the requests added so far. Execute
+ * the transaction by calling OverlayManager#commit.
+ *
+ * @see OverlayManager#commit
+ * @return a new transaction
+ */
+ public OverlayManagerTransaction build() {
+ return new OverlayManagerTransaction(mRequests);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ final int size = mRequests.size();
+ dest.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ final Request req = mRequests.get(i);
+ dest.writeInt(req.type);
+ dest.writeString(req.packageName);
+ dest.writeInt(req.userId);
+ }
+ }
+
+ public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR =
+ new Parcelable.Creator<OverlayManagerTransaction>() {
+
+ @Override
+ public OverlayManagerTransaction createFromParcel(Parcel source) {
+ return new OverlayManagerTransaction(source);
+ }
+
+ @Override
+ public OverlayManagerTransaction[] newArray(int size) {
+ return new OverlayManagerTransaction[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
index 24872e8..f88df95 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -5,4 +5,5 @@
patb@google.com
per-file PackageParser.java = chiuwinson@google.com
-per-file *Shortcut* = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
+per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 6c911f6..63c58d2 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2057,7 +2057,7 @@
}
/** @hide */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 176190631)
public DisplayAdjustments getDisplayAdjustments() {
final DisplayAdjustments overrideDisplayAdjustments = mOverrideDisplayAdjustments;
if (overrideDisplayAdjustments != null) {
diff --git a/core/java/android/hardware/biometrics/OWNERS b/core/java/android/hardware/biometrics/OWNERS
index 33527f8..2065ffa 100644
--- a/core/java/android/hardware/biometrics/OWNERS
+++ b/core/java/android/hardware/biometrics/OWNERS
@@ -1,3 +1,8 @@
# Bug component: 879035
+curtislb@google.com
+ilyamaty@google.com
jaggies@google.com
+joshmccloskey@google.com
+kchyn@google.com
+
diff --git a/core/java/android/hardware/fingerprint/OWNERS b/core/java/android/hardware/fingerprint/OWNERS
index dcead40..e55b8c56 100644
--- a/core/java/android/hardware/fingerprint/OWNERS
+++ b/core/java/android/hardware/fingerprint/OWNERS
@@ -1,3 +1,8 @@
# Bug component: 114777
+curtislb@google.com
+ilyamaty@google.com
jaggies@google.com
+joshmccloskey@google.com
+kchyn@google.com
+
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 704f31d..5234494 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -623,32 +623,41 @@
/** @hide */
@VisibleForTesting
public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
- Binder.withCleanCallingIdentity(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
mExecutor.execute(() -> {
mCb.onConnectivityReportAvailable(report);
});
- });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/** @hide */
@VisibleForTesting
public void onDataStallSuspected(@NonNull DataStallReport report) {
- Binder.withCleanCallingIdentity(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
mExecutor.execute(() -> {
mCb.onDataStallSuspected(report);
});
- });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/** @hide */
@VisibleForTesting
public void onNetworkConnectivityReported(
@NonNull Network network, boolean hasConnectivity) {
- Binder.withCleanCallingIdentity(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
mExecutor.execute(() -> {
mCb.onNetworkConnectivityReported(network, hasConnectivity);
});
- });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 6cef73d..06c15980 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -59,6 +59,7 @@
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Range;
import android.util.SparseIntArray;
import com.android.connectivity.aidl.INetworkAgent;
@@ -73,10 +74,12 @@
import java.io.UncheckedIOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -1163,6 +1166,55 @@
}
/**
+ * Adds or removes a requirement for given UID ranges to use the VPN.
+ *
+ * If set to {@code true}, informs the system that the UIDs in the specified ranges must not
+ * have any connectivity except if a VPN is connected and applies to the UIDs, or if the UIDs
+ * otherwise have permission to bypass the VPN (e.g., because they have the
+ * {@link android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS} permission, or when
+ * using a socket protected by a method such as {@link VpnService#protect(DatagramSocket)}. If
+ * set to {@code false}, a previously-added restriction is removed.
+ * <p>
+ * Each of the UID ranges specified by this method is added and removed as is, and no processing
+ * is performed on the ranges to de-duplicate, merge, split, or intersect them. In order to
+ * remove a previously-added range, the exact range must be removed as is.
+ * <p>
+ * The changes are applied asynchronously and may not have been applied by the time the method
+ * returns. Apps will be notified about any changes that apply to them via
+ * {@link NetworkCallback#onBlockedStatusChanged} callbacks called after the changes take
+ * effect.
+ * <p>
+ * This method should be called only by the VPN code.
+ *
+ * @param ranges the UID ranges to restrict
+ * @param requireVpn whether the specified UID ranges must use a VPN
+ *
+ * TODO: expose as @SystemApi.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public void setRequireVpnForUids(boolean requireVpn,
+ @NonNull Collection<Range<Integer>> ranges) {
+ Objects.requireNonNull(ranges);
+ // The Range class is not parcelable. Convert to UidRange, which is what is used internally.
+ // This method is not necessarily expected to be used outside the system server, so
+ // parceling may not be necessary, but it could be used out-of-process, e.g., by the network
+ // stack process, or by tests.
+ UidRange[] rangesArray = new UidRange[ranges.size()];
+ int index = 0;
+ for (Range<Integer> range : ranges) {
+ rangesArray[index++] = new UidRange(range.getLower(), range.getUpper());
+ }
+ try {
+ mService.setRequireVpnForUids(requireVpn, rangesArray);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns details about the currently active default data network
* for a given uid. This is for internal use only to avoid spying
* other apps.
@@ -1833,30 +1885,42 @@
mCallback = new ISocketKeepaliveCallback.Stub() {
@Override
public void onStarted(int slot) {
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> {
- mSlot = slot;
- callback.onStarted();
- }));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ mSlot = slot;
+ callback.onStarted();
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public void onStopped() {
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> {
- mSlot = null;
- callback.onStopped();
- }));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ mSlot = null;
+ callback.onStopped();
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
mExecutor.shutdown();
}
@Override
public void onError(int error) {
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> {
- mSlot = null;
- callback.onError(error);
- }));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ mSlot = null;
+ callback.onError(error);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
mExecutor.shutdown();
}
@@ -3120,39 +3184,6 @@
}
/**
- * Check mobile provisioning.
- *
- * @param suggestedTimeOutMs, timeout in milliseconds
- *
- * @return time out that will be used, maybe less that suggestedTimeOutMs
- * -1 if an error.
- *
- * {@hide}
- */
- public int checkMobileProvisioning(int suggestedTimeOutMs) {
- int timeOutMs = -1;
- try {
- timeOutMs = mService.checkMobileProvisioning(suggestedTimeOutMs);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- return timeOutMs;
- }
-
- /**
- * Get the mobile provisioning url.
- * {@hide}
- */
- @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public String getMobileProvisioningUrl() {
- try {
- return mService.getMobileProvisioningUrl();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Set sign in error notification to visible or invisible
*
* @hide
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index cbb1197..b32c98b 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -29,6 +29,7 @@
import android.net.NetworkState;
import android.net.ISocketKeepaliveCallback;
import android.net.ProxyInfo;
+import android.net.UidRange;
import android.os.Bundle;
import android.os.IBinder;
import android.os.INetworkActivityListener;
@@ -146,10 +147,7 @@
String getAlwaysOnVpnPackage(int userId);
boolean isVpnLockdownEnabled(int userId);
List<String> getVpnLockdownWhitelist(int userId);
-
- int checkMobileProvisioning(int suggestedTimeOutMs);
-
- String getMobileProvisioningUrl();
+ void setRequireVpnForUids(boolean requireVpn, in UidRange[] ranges);
void setProvisioningNotificationVisible(boolean visible, int networkType, in String action);
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index bf25602..f413063 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -45,8 +45,8 @@
import libcore.util.EmptyArray;
import java.io.CharArrayWriter;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ProtocolException;
@@ -162,7 +162,7 @@
out.writeLong(totalBytes);
}
- public NetworkStatsHistory(DataInputStream in) throws IOException {
+ public NetworkStatsHistory(DataInput in) throws IOException {
final int version = in.readInt();
switch (version) {
case VERSION_INIT: {
@@ -204,7 +204,7 @@
}
}
- public void writeToStream(DataOutputStream out) throws IOException {
+ public void writeToStream(DataOutput out) throws IOException {
out.writeInt(VERSION_ADD_ACTIVE);
out.writeLong(bucketDuration);
writeVarLongArray(out, bucketStart, bucketCount);
@@ -768,7 +768,7 @@
*/
public static class DataStreamUtils {
@Deprecated
- public static long[] readFullLongArray(DataInputStream in) throws IOException {
+ public static long[] readFullLongArray(DataInput in) throws IOException {
final int size = in.readInt();
if (size < 0) throw new ProtocolException("negative array size");
final long[] values = new long[size];
@@ -781,7 +781,7 @@
/**
* Read variable-length {@link Long} using protobuf-style approach.
*/
- public static long readVarLong(DataInputStream in) throws IOException {
+ public static long readVarLong(DataInput in) throws IOException {
int shift = 0;
long result = 0;
while (shift < 64) {
@@ -797,7 +797,7 @@
/**
* Write variable-length {@link Long} using protobuf-style approach.
*/
- public static void writeVarLong(DataOutputStream out, long value) throws IOException {
+ public static void writeVarLong(DataOutput out, long value) throws IOException {
while (true) {
if ((value & ~0x7FL) == 0) {
out.writeByte((int) value);
@@ -809,7 +809,7 @@
}
}
- public static long[] readVarLongArray(DataInputStream in) throws IOException {
+ public static long[] readVarLongArray(DataInput in) throws IOException {
final int size = in.readInt();
if (size == -1) return null;
if (size < 0) throw new ProtocolException("negative array size");
@@ -820,7 +820,7 @@
return values;
}
- public static void writeVarLongArray(DataOutputStream out, long[] values, int size)
+ public static void writeVarLongArray(DataOutput out, long[] values, int size)
throws IOException {
if (values == null) {
out.writeInt(-1);
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index a7dce18..d007a95 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -187,38 +187,54 @@
mCallback = new ISocketKeepaliveCallback.Stub() {
@Override
public void onStarted(int slot) {
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> {
- mSlot = slot;
- callback.onStarted();
- }));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ mSlot = slot;
+ callback.onStarted();
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public void onStopped() {
- Binder.withCleanCallingIdentity(() ->
- executor.execute(() -> {
- mSlot = null;
- callback.onStopped();
- }));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ mSlot = null;
+ callback.onStopped();
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public void onError(int error) {
- Binder.withCleanCallingIdentity(() ->
- executor.execute(() -> {
- mSlot = null;
- callback.onError(error);
- }));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ mSlot = null;
+ callback.onError(error);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public void onDataReceived() {
- Binder.withCleanCallingIdentity(() ->
- executor.execute(() -> {
- mSlot = null;
- callback.onDataReceived();
- }));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ mSlot = null;
+ callback.onDataReceived();
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
};
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 300bb760..1e14e3a 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13283,6 +13283,16 @@
@TestApi
public static final String HIDDEN_API_POLICY = "hidden_api_policy";
+ /**
+ * Flag for forcing {@link com.android.server.compat.OverrideValidatorImpl}
+ * to consider this a non-debuggable build.
+ *
+ * @hide
+ */
+ public static final String FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT =
+ "force_non_debuggable_final_build_for_compat";
+
+
/**
* Current version of signed configuration applied.
*
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index cf0e0d1..5e74381 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -73,7 +73,7 @@
/**
* <p>Displays a vertically-scrollable collection of views, where each view is positioned
* immediatelybelow the previous view in the list. For a more modern, flexible, and performant
- * approach to displaying lists, use {@link android.support.v7.widget.RecyclerView}.</p>
+ * approach to displaying lists, use {@link androidx.recyclerview.widget.RecyclerView}.</p>
*
* <p>To display a list, you can include a list view in your layout XML file:</p>
*
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index 9a78ad2..c0bbe50 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -33,7 +33,7 @@
DISABLED_NOT_DEBUGGABLE,
DISABLED_NON_TARGET_SDK,
DISABLED_TARGET_SDK_TOO_HIGH,
- PACKAGE_DOES_NOT_EXIST,
+ DEFERRED_VERIFICATION,
LOGGING_ONLY_CHANGE
})
@Retention(RetentionPolicy.SOURCE)
@@ -57,10 +57,10 @@
* Change cannot be overridden, due to the app's targetSdk being above the change's targetSdk.
*/
public static final int DISABLED_TARGET_SDK_TOO_HIGH = 3;
- /**
- * Package does not exist.
+ /**
+ * Change override decision is currently being deferred, due to the app not being installed yet.
*/
- public static final int PACKAGE_DOES_NOT_EXIST = 4;
+ public static final int DEFERRED_VERIFICATION = 4;
/**
* Change is marked as logging only, and cannot be toggled.
*/
@@ -106,6 +106,7 @@
throws SecurityException {
switch (state) {
case ALLOWED:
+ case DEFERRED_VERIFICATION:
return;
case DISABLED_NOT_DEBUGGABLE:
throw new SecurityException(
@@ -118,11 +119,6 @@
"Cannot override %1$d for %2$s because the app's targetSdk (%3$d) is "
+ "above the change's targetSdk threshold (%4$d)",
changeId, packageName, appTargetSdk, changeIdTargetSdk));
- case PACKAGE_DOES_NOT_EXIST:
- throw new SecurityException(String.format(
- "Cannot override %1$d for %2$s because the package does not exist, and "
- + "the change is targetSdk gated.",
- changeId, packageName));
case LOGGING_ONLY_CHANGE:
throw new SecurityException(String.format(
"Cannot override %1$d because it is marked as a logging-only change.",
@@ -170,8 +166,8 @@
return "DISABLED_NON_TARGET_SDK";
case DISABLED_TARGET_SDK_TOO_HIGH:
return "DISABLED_TARGET_SDK_TOO_HIGH";
- case PACKAGE_DOES_NOT_EXIST:
- return "PACKAGE_DOES_NOT_EXIST";
+ case DEFERRED_VERIFICATION:
+ return "DEFERRED_VERIFICATION";
case LOGGING_ONLY_CHANGE:
return "LOGGING_ONLY_CHANGE";
}
diff --git a/core/proto/android/app/OWNERS b/core/proto/android/app/OWNERS
new file mode 100644
index 0000000..296abd1
--- /dev/null
+++ b/core/proto/android/app/OWNERS
@@ -0,0 +1 @@
+per-file location_time_zone_manager.proto = nfuller@google.com, mingaleev@google.com
diff --git a/core/proto/android/app/location_time_zone_manager.proto b/core/proto/android/app/location_time_zone_manager.proto
new file mode 100644
index 0000000..f44d549
--- /dev/null
+++ b/core/proto/android/app/location_time_zone_manager.proto
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.app.time;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "LocationTimeZoneManagerProto";
+
+// Represents the state of the LocationTimeZoneManagerService for use in tests.
+message LocationTimeZoneManagerServiceStateProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional GeolocationTimeZoneSuggestionProto last_suggestion = 1;
+ repeated TimeZoneProviderStateProto primary_provider_states = 2;
+ repeated TimeZoneProviderStateProto secondary_provider_states = 3;
+}
+
+// Represents a GeolocationTimeZoneSuggestion that can be / has been passed to the time zone
+// detector.
+message GeolocationTimeZoneSuggestionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ repeated string zone_ids = 1;
+ repeated string debug_info = 2;
+}
+
+// The state tracked for a LocationTimeZoneProvider.
+message TimeZoneProviderStateProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional TimeZoneProviderStateEnum state = 1;
+}
+
+// The state enum for LocationTimeZoneProviders.
+enum TimeZoneProviderStateEnum {
+ TIME_ZONE_PROVIDER_STATE_UNKNOWN = 0;
+ TIME_ZONE_PROVIDER_STATE_INITIALIZING = 1;
+ TIME_ZONE_PROVIDER_STATE_CERTAIN = 2;
+ TIME_ZONE_PROVIDER_STATE_UNCERTAIN = 3;
+ TIME_ZONE_PROVIDER_STATE_DISABLED = 4;
+ TIME_ZONE_PROVIDER_STATE_PERM_FAILED = 5;
+ TIME_ZONE_PROVIDER_STATE_DESTROYED = 6;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a9fe5d5..714a09d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2206,7 +2206,9 @@
<!-- Allows to query ongoing call details and manage ongoing calls
<p>Protection level: signature|appop -->
<permission android:name="android.permission.MANAGE_ONGOING_CALLS"
- android:protectionLevel="signature|appop" />
+ android:protectionLevel="signature|appop"
+ android:label="@string/permlab_manageOngoingCalls"
+ android:description="@string/permdesc_manageOngoingCalls" />
<!-- Allows the app to request network scans from telephony.
<p>Not for use by third-party applications.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c1f3028..1143467 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -914,6 +914,15 @@
interfere with the performance or operation of your device when an
emergency cell broadcast is received.</string>
+ <!-- Title for an application which grants an app the ability to see and manage calls on
+ the user's device. Usually reserved for apps associated with wearable devices that
+ can show information about calls. -->
+ <string name="permlab_manageOngoingCalls">Manage ongoing calls</string>
+ <!-- Description of an application permission, listed so the user can choose whether they
+ want to allow the application to do this. -->
+ <string name="permdesc_manageOngoingCalls">Allows an app to see details about ongoing calls
+ on your device and to control these calls.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readCellBroadcasts">read cell broadcast messages</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
index 911efb2..912db1e 100644
--- a/core/tests/coretests/src/android/content/OWNERS
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -1 +1,3 @@
per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
+per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
diff --git a/core/tests/coretests/src/android/view/inputmethod/OWNERS b/core/tests/coretests/src/android/view/inputmethod/OWNERS
new file mode 100644
index 0000000..eb06b78
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 34867
+
+include /core/java/android/view/inputmethod/OWNERS
diff --git a/core/tests/overlaytests/device/Android.bp b/core/tests/overlaytests/device/Android.bp
index 12a2b08..f86ac9c 100644
--- a/core/tests/overlaytests/device/Android.bp
+++ b/core/tests/overlaytests/device/Android.bp
@@ -16,7 +16,11 @@
name: "OverlayDeviceTests",
srcs: ["src/**/*.java"],
platform_apis: true,
- static_libs: ["androidx.test.rules"],
+ certificate: "platform",
+ static_libs: [
+ "androidx.test.rules",
+ "testng",
+ ],
test_suites: ["device-tests"],
data: [
":OverlayDeviceTests_AppOverlayOne",
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index 4881636..a69911f 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -19,6 +19,8 @@
<uses-sdk android:minSdkVersion="21" />
+ <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
+
<application>
<uses-library android:name="android.test.runner"/>
</application>
diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml
index 6507839..db45750 100644
--- a/core/tests/overlaytests/device/AndroidTest.xml
+++ b/core/tests/overlaytests/device/AndroidTest.xml
@@ -19,9 +19,20 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="remount-system" value="true" />
+ <option name="push" value="OverlayDeviceTests.apk->/system/app/OverlayDeviceTests.apk" />
+ </target_preparer>
+
+ <!-- Reboot to have the test APK scanned by PM and reboot after to remove the test APK. -->
+ <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
+ <option name="pre-reboot" value="true" />
+ <option name="post-reboot" value="true" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="OverlayDeviceTests.apk" />
<option name="test-file-name" value="OverlayDeviceTests_AppOverlayOne.apk" />
<option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" />
<option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.apk" />
diff --git a/core/tests/overlaytests/device/TEST_MAPPING b/core/tests/overlaytests/device/TEST_MAPPING
new file mode 100644
index 0000000..43ee00f
--- /dev/null
+++ b/core/tests/overlaytests/device/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name" : "OverlayDeviceTests"
+ }
+ ]
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
index 390bb76..76c01a7 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
@@ -18,60 +18,76 @@
import static java.util.concurrent.TimeUnit.SECONDS;
-import android.app.UiAutomation;
-import android.content.res.Resources;
-import android.os.ParcelFileDescriptor;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.os.UserHandle;
import androidx.test.InstrumentationRegistry;
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
class LocalOverlayManager {
private static final long TIMEOUT = 30;
- public static void setEnabledAndWait(Executor executor, final String packageName,
- boolean enable) throws Exception {
- final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName;
- if (executeShellCommand("cmd overlay list").contains(pattern)) {
- // nothing to do, overlay already in the requested state
- return;
+ public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable,
+ @NonNull final String[] overlaysToDisable) throws Exception {
+ final int userId = UserHandle.myUserId();
+ OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
+ for (String pkg : overlaysToEnable) {
+ builder.setEnabled(pkg, true, userId);
}
+ for (String pkg : overlaysToDisable) {
+ builder.setEnabled(pkg, false, userId);
+ }
+ OverlayManagerTransaction transaction = builder.build();
- final Resources res = InstrumentationRegistry.getContext().getResources();
- final String[] oldApkPaths = res.getAssets().getApkPaths();
+ final Context ctx = InstrumentationRegistry.getTargetContext();
FutureTask<Boolean> task = new FutureTask<>(() -> {
while (true) {
- if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) {
+ final String[] paths = ctx.getResources().getAssets().getApkPaths();
+ if (arrayTailContains(paths, overlaysToEnable)
+ && arrayDoesNotContain(paths, overlaysToDisable)) {
return true;
}
Thread.sleep(10);
}
});
+
+ OverlayManager om = ctx.getSystemService(OverlayManager.class);
+ om.commit(transaction);
+
+ Executor executor = (cmd) -> new Thread(cmd).start();
executor.execute(task);
- executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName);
task.get(TIMEOUT, SECONDS);
}
- private static String executeShellCommand(final String command)
- throws Exception {
- final UiAutomation uiAutomation =
- InstrumentationRegistry.getInstrumentation().getUiAutomation();
- final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command);
- try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
- final BufferedReader reader = new BufferedReader(
- new InputStreamReader(in, StandardCharsets.UTF_8));
- StringBuilder str = new StringBuilder();
- String line;
- while ((line = reader.readLine()) != null) {
- str.append(line);
- }
- return str.toString();
+ private static boolean arrayTailContains(@NonNull final String[] array,
+ @NonNull final String[] substrings) {
+ if (array.length < substrings.length) {
+ return false;
}
+ for (int i = 0; i < substrings.length; i++) {
+ String a = array[array.length - substrings.length + i];
+ String s = substrings[i];
+ if (!a.contains(s)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean arrayDoesNotContain(@NonNull final String[] array,
+ @NonNull final String[] substrings) {
+ for (String s : substrings) {
+ for (String a : array) {
+ if (a.contains(s)) {
+ return false;
+ }
+ }
+ }
+ return true;
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
new file mode 100644
index 0000000..0b4f5e2
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.overlaytest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.om.OverlayInfo;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.content.res.Resources;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+@MediumTest
+public class TransactionTest {
+ static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
+ static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
+
+ private Context mContext;
+ private Resources mResources;
+ private OverlayManager mOverlayManager;
+ private int mUserId;
+ private UserHandle mUserHandle;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mResources = mContext.getResources();
+ mOverlayManager = mContext.getSystemService(OverlayManager.class);
+ mUserId = UserHandle.myUserId();
+ mUserHandle = UserHandle.of(mUserId);
+
+ LocalOverlayManager.toggleOverlaysAndWait(
+ new String[]{},
+ new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
+ }
+
+ @Test
+ public void testValidTransaction() throws Exception {
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+
+ OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+ .setEnabled(APP_OVERLAY_ONE_PKG, true)
+ .setEnabled(APP_OVERLAY_TWO_PKG, true)
+ .build();
+ mOverlayManager.commit(t);
+
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
+ List<OverlayInfo> ois =
+ mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+ assertEquals(ois.size(), 2);
+ assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG);
+ assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG);
+
+ OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder()
+ .setEnabled(APP_OVERLAY_TWO_PKG, true)
+ .setEnabled(APP_OVERLAY_ONE_PKG, true)
+ .build();
+ mOverlayManager.commit(t2);
+
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
+ List<OverlayInfo> ois2 =
+ mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+ assertEquals(ois2.size(), 2);
+ assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG);
+ assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG);
+
+ OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder()
+ .setEnabled(APP_OVERLAY_TWO_PKG, false)
+ .build();
+ mOverlayManager.commit(t3);
+
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+ List<OverlayInfo> ois3 =
+ mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+ assertEquals(ois3.size(), 2);
+ assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG);
+ assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG);
+ }
+
+ @Test
+ public void testInvalidRequestHasNoEffect() {
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+
+ OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+ .setEnabled(APP_OVERLAY_ONE_PKG, true)
+ .setEnabled("does-not-exist", true)
+ .setEnabled(APP_OVERLAY_TWO_PKG, true)
+ .build();
+ assertThrows(SecurityException.class, () -> mOverlayManager.commit(t));
+
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+ }
+
+ private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) {
+ final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId));
+ assertNotNull(oi);
+ assertEquals(oi.isEnabled(), enabled);
+ }
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index d28c47d..420f755 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -22,8 +22,6 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.util.concurrent.Executor;
-
@RunWith(JUnit4.class)
@MediumTest
public class WithMultipleOverlaysTest extends OverlayBaseTest {
@@ -33,9 +31,8 @@
@BeforeClass
public static void enableOverlay() throws Exception {
- Executor executor = (cmd) -> new Thread(cmd).start();
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true);
- LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
+ LocalOverlayManager.toggleOverlaysAndWait(
+ new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG},
+ new String[]{});
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index 6566ad3..a86255e 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -22,8 +22,6 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.util.concurrent.Executor;
-
@RunWith(JUnit4.class)
@MediumTest
public class WithOverlayTest extends OverlayBaseTest {
@@ -32,10 +30,9 @@
}
@BeforeClass
- public static void enableOverlay() throws Exception {
- Executor executor = (cmd) -> new Thread(cmd).start();
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
- LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
+ public static void enableOverlays() throws Exception {
+ LocalOverlayManager.toggleOverlaysAndWait(
+ new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
+ new String[]{APP_OVERLAY_TWO_PKG});
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index 48cfeab..51c4118 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -22,8 +22,6 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.util.concurrent.Executor;
-
@RunWith(JUnit4.class)
@MediumTest
public class WithoutOverlayTest extends OverlayBaseTest {
@@ -33,9 +31,8 @@
@BeforeClass
public static void disableOverlays() throws Exception {
- Executor executor = (cmd) -> new Thread(cmd).start();
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false);
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
- LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false);
+ LocalOverlayManager.toggleOverlaysAndWait(
+ new String[]{},
+ new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
}
}
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
index da3aa00..847b491 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
@@ -15,6 +15,6 @@
android_test {
name: "OverlayDeviceTests_AppOverlayOne",
sdk_version: "current",
-
+ certificate: "platform",
aaptflags: ["--no-resource-removal"],
}
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
index 215b66da3..7d5f82a 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
@@ -15,6 +15,6 @@
android_test {
name: "OverlayDeviceTests_AppOverlayTwo",
sdk_version: "current",
-
+ certificate: "platform",
aaptflags: ["--no-resource-removal"],
}
diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt
index d8af726..52ee63a 100644
--- a/framework-jarjar-rules.txt
+++ b/framework-jarjar-rules.txt
@@ -1,2 +1,3 @@
rule android.hidl.** android.internal.hidl.@1
rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1
+rule com.android.server.vcn.util.** com.android.server.vcn.repackaged.util.@1
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 7a2e584..9b2effc 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -76,9 +76,9 @@
/**
* These fields are used by native code, do not access or modify.
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 176388660)
private long mSurfaceTexture;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 176388660)
private long mProducer;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private long mFrameAvailableListener;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 635f6c6..b3c3355 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -122,7 +122,7 @@
private final Rect mDirtyBounds = new Rect();
/** Mirrors mLayerState with some extra information. */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 175939224)
private RippleState mState;
/** The masking layer, e.g. the layer with id R.id.mask. */
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
index 334900b..766d603 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
@@ -43,6 +43,10 @@
FrontendCapabilities frontendCap) {
mId = id;
mType = type;
+ // if max Frequency is negative, we set it as max value of the Integer.
+ if (maxFrequency < 0) {
+ maxFrequency = Integer.MAX_VALUE;
+ }
mFrequencyRange = new Range<>(minFrequency, maxFrequency);
mSymbolRateRange = new Range<>(minSymbolRate, maxSymbolRate);
mAcquireRange = acquireRange;
diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java
index c0eb5e8..0bede0d 100644
--- a/media/java/android/mtp/MtpStorageManager.java
+++ b/media/java/android/mtp/MtpStorageManager.java
@@ -958,7 +958,7 @@
MtpObject parent = obj.getParent();
MtpObject oldObj = parent.getChild(oldName);
if (!success) {
- // If the rename failed, we want oldObj to be the original and obj to be the dummy.
+ // If the rename failed, we want oldObj to be the original and obj to be the stand-in.
// Switch the objects, except for their name and state.
MtpObject temp = oldObj;
MtpObjectState oldState = oldObj.getState();
@@ -1034,7 +1034,7 @@
return generalBeginRemoveObject(obj, MtpOperation.RENAME)
&& generalBeginCopyObject(newObj, false);
}
- // Move obj to new parent, create a dummy object in the old parent.
+ // Move obj to new parent, create a fake object in the old parent.
MtpObject oldObj = obj.copy(false);
obj.setParent(newParent);
oldObj.getParent().addChild(oldObj);
@@ -1063,7 +1063,7 @@
return generalEndCopyObject(newObj, success, true) && ret;
}
if (!success) {
- // If the rename failed, we want oldObj to be the original and obj to be the dummy.
+ // If the rename failed, we want oldObj to be the original and obj to be the stand-in.
// Switch the objects, except for their parent and state.
MtpObject temp = oldObj;
MtpObjectState oldState = oldObj.getState();
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index e90bb36..5ecb1710 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -300,6 +300,7 @@
Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
Settings.Global.HIDDEN_API_POLICY,
+ Settings.Global.FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT,
Settings.Global.HIDE_ERROR_DIALOGS,
Settings.Global.HTTP_PROXY,
HYBRID_SYSUI_BATTERY_WARNING_FLAGS,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 88eef6a..741a680 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -120,6 +120,7 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.CREATE_USERS" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
+ <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
<uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/>
<uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE"/>
<uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL"/>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 8b73c5e..7b4cf0f 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -2146,8 +2146,10 @@
name = in.readString();
initialName = in.readString();
title = in.readString();
+ shareTitle = in.readString();
description = in.readString();
progress.set(in.readInt());
+ lastProgress.set(in.readInt());
lastUpdate.set(in.readLong());
formattedLastUpdate = in.readString();
bugreportFile = readFile(in);
@@ -2158,9 +2160,10 @@
}
finished.set(in.readInt() == 1);
+ addingDetailsToZip = in.readBoolean();
+ addedDetailsToZip = in.readBoolean();
screenshotCounter = in.readInt();
shareDescription = in.readString();
- shareTitle = in.readString();
type = in.readInt();
}
@@ -2171,8 +2174,10 @@
dest.writeString(name);
dest.writeString(initialName);
dest.writeString(title);
+ dest.writeString(shareTitle);
dest.writeString(description);
dest.writeInt(progress.intValue());
+ dest.writeInt(lastProgress.intValue());
dest.writeLong(lastUpdate.longValue());
dest.writeString(getFormattedLastUpdate());
writeFile(dest, bugreportFile);
@@ -2183,9 +2188,10 @@
}
dest.writeInt(finished.get() ? 1 : 0);
+ dest.writeBoolean(addingDetailsToZip);
+ dest.writeBoolean(addedDetailsToZip);
dest.writeInt(screenshotCounter);
dest.writeString(shareDescription);
- dest.writeString(shareTitle);
dest.writeInt(type);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1378776..397eeb2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -74,7 +74,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.content.res.Configuration;
import android.database.ContentObserver;
import android.net.CaptivePortal;
import android.net.CaptivePortalData;
@@ -90,12 +89,10 @@
import android.net.IDnsResolver;
import android.net.IIpConnectivityMetrics;
import android.net.INetd;
-import android.net.INetdEventCallback;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
-import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.ISocketKeepaliveCallback;
import android.net.InetAddresses;
@@ -133,6 +130,7 @@
import android.net.Uri;
import android.net.VpnManager;
import android.net.VpnService;
+import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
@@ -173,7 +171,6 @@
import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import android.util.Xml;
import com.android.connectivity.aidl.INetworkAgent;
import com.android.internal.R;
@@ -190,7 +187,6 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
-import com.android.internal.util.XmlUtils;
import com.android.modules.utils.BasicShellCommandHandler;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
@@ -211,7 +207,6 @@
import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.Vpn;
-import com.android.server.net.BaseNetdEventCallback;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -221,14 +216,7 @@
import libcore.io.IoUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
@@ -337,7 +325,7 @@
@VisibleForTesting
protected INetd mNetd;
private INetworkStatsService mStatsService;
- private INetworkPolicyManager mPolicyManager;
+ private NetworkPolicyManager mPolicyManager;
private NetworkPolicyManagerInternal mPolicyManagerInternal;
/**
@@ -558,6 +546,13 @@
private static final int EVENT_CAPPORT_DATA_CHANGED = 46;
/**
+ * Used by setRequireVpnForUids.
+ * arg1 = whether the specified UID ranges are required to use a VPN.
+ * obj = Array of UidRange objects.
+ */
+ private static final int EVENT_SET_REQUIRE_VPN_FOR_UIDS = 47;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
@@ -946,15 +941,15 @@
}
public ConnectivityService(Context context, INetworkManagementService netManager,
- INetworkStatsService statsService, INetworkPolicyManager policyManager) {
- this(context, netManager, statsService, policyManager, getDnsResolver(context),
- new IpConnectivityLog(), NetdService.getInstance(), new Dependencies());
+ INetworkStatsService statsService) {
+ this(context, netManager, statsService, getDnsResolver(context), new IpConnectivityLog(),
+ NetdService.getInstance(), new Dependencies());
}
@VisibleForTesting
protected ConnectivityService(Context context, INetworkManagementService netManager,
- INetworkStatsService statsService, INetworkPolicyManager policyManager,
- IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd, Dependencies deps) {
+ INetworkStatsService statsService, IDnsResolver dnsresolver, IpConnectivityLog logger,
+ INetd netd, Dependencies deps) {
if (DBG) log("ConnectivityService starting up");
mDeps = Objects.requireNonNull(deps, "missing Dependencies");
@@ -992,7 +987,7 @@
mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
- mPolicyManager = Objects.requireNonNull(policyManager, "missing INetworkPolicyManager");
+ mPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
mPolicyManagerInternal = Objects.requireNonNull(
LocalServices.getService(NetworkPolicyManagerInternal.class),
"missing NetworkPolicyManagerInternal");
@@ -1008,12 +1003,7 @@
// To ensure uid rules are synchronized with Network Policy, register for
// NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
// reading existing policy from disk.
- try {
- mPolicyManager.registerListener(mPolicyListener);
- } catch (RemoteException e) {
- // ouch, no rules updates means some processes may never get network
- loge("unable to register INetworkPolicyListener" + e);
- }
+ mPolicyManager.registerListener(mPolicyListener);
final PowerManager powerManager = (PowerManager) context.getSystemService(
Context.POWER_SERVICE);
@@ -1290,19 +1280,28 @@
}
}
- private Network[] getVpnUnderlyingNetworks(int uid) {
- synchronized (mVpns) {
- if (!mLockdownEnabled) {
- int user = UserHandle.getUserId(uid);
- Vpn vpn = mVpns.get(user);
- if (vpn != null && vpn.appliesToUid(uid)) {
- return vpn.getUnderlyingNetworks();
+ // TODO: determine what to do when more than one VPN applies to |uid|.
+ private NetworkAgentInfo getVpnForUid(int uid) {
+ synchronized (mNetworkForNetId) {
+ for (int i = 0; i < mNetworkForNetId.size(); i++) {
+ final NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
+ if (nai.isVPN() && nai.everConnected && nai.networkCapabilities.appliesToUid(uid)) {
+ return nai;
}
}
}
return null;
}
+ private Network[] getVpnUnderlyingNetworks(int uid) {
+ synchronized (mVpns) {
+ if (mLockdownEnabled) return null;
+ }
+ final NetworkAgentInfo nai = getVpnForUid(uid);
+ if (nai != null) return nai.declaredUnderlyingNetworks;
+ return null;
+ }
+
private NetworkState getUnfilteredActiveNetworkState(int uid) {
NetworkAgentInfo nai = getDefaultNetwork();
@@ -1328,7 +1327,7 @@
}
/**
- * Check if UID should be blocked from using the network with the given LinkProperties.
+ * Check if UID should be blocked from using the specified network.
*/
private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid,
boolean ignoreBlocked) {
@@ -1336,12 +1335,7 @@
if (ignoreBlocked) {
return false;
}
- synchronized (mVpns) {
- final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
- if (vpn != null && vpn.getLockdown() && vpn.isBlockingUid(uid)) {
- return true;
- }
- }
+ if (isUidBlockedByVpn(uid, mVpnBlockedUidRanges)) return true;
final String iface = (lp == null ? "" : lp.getInterfaceName());
return mPolicyManagerInternal.isUidNetworkingBlocked(uid, iface);
}
@@ -1567,22 +1561,14 @@
nc, mDeps.getCallingUid(), callingPackageName));
}
- synchronized (mVpns) {
- if (!mLockdownEnabled) {
- Vpn vpn = mVpns.get(userId);
- if (vpn != null) {
- Network[] networks = vpn.getUnderlyingNetworks();
- if (networks != null) {
- for (Network network : networks) {
- nc = getNetworkCapabilitiesInternal(network);
- if (nc != null) {
- result.put(
- network,
- maybeSanitizeLocationInfoForCaller(
- nc, mDeps.getCallingUid(), callingPackageName));
- }
- }
- }
+ // No need to check mLockdownEnabled. If it's true, getVpnUnderlyingNetworks returns null.
+ final Network[] networks = getVpnUnderlyingNetworks(Binder.getCallingUid());
+ if (networks != null) {
+ for (Network network : networks) {
+ nc = getNetworkCapabilitiesInternal(network);
+ if (nc != null) {
+ result.put(network, maybeSanitizeLocationInfoForCaller(
+ nc, mDeps.getCallingUid(), callingPackageName));
}
}
}
@@ -1920,8 +1906,7 @@
return true;
}
- @VisibleForTesting
- protected final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
+ private class NetdEventCallback extends INetdEventListener.Stub {
@Override
public void onPrivateDnsValidationEvent(int netId, String ipAddress,
String hostname, boolean validated) {
@@ -1937,8 +1922,8 @@
}
@Override
- public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
- String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
+ public void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
+ String hostname, String[] ipAddresses, int ipAddressesCount, int uid) {
NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
// Netd event only allow registrants from system. Each NetworkMonitor thread is under
// the caller thread of registerNetworkAgent. Thus, it's not allowed to register netd
@@ -1957,21 +1942,42 @@
String prefixString, int prefixLength) {
mHandler.post(() -> handleNat64PrefixEvent(netId, added, prefixString, prefixLength));
}
- };
- private void registerNetdEventCallback() {
- final IIpConnectivityMetrics ipConnectivityMetrics = mDeps.getIpConnectivityMetrics();
- if (ipConnectivityMetrics == null) {
- Log.wtf(TAG, "Missing IIpConnectivityMetrics");
- return;
+ @Override
+ public void onConnectEvent(int netId, int error, int latencyMs, String ipAddr, int port,
+ int uid) {
}
+ @Override
+ public void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader,
+ byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort,
+ long timestampNs) {
+ }
+
+ @Override
+ public void onTcpSocketStatsEvent(int[] networkIds, int[] sentPackets, int[] lostPackets,
+ int[] rttsUs, int[] sentAckDiffsMs) {
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return this.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
+ };
+
+ @VisibleForTesting
+ protected final INetdEventListener mNetdEventCallback = new NetdEventCallback();
+
+ private void registerNetdEventCallback() {
try {
- ipConnectivityMetrics.addNetdEventCallback(
- INetdEventCallback.CALLBACK_CALLER_CONNECTIVITY_SERVICE,
- mNetdEventCallback);
+ mDnsResolver.registerEventListener(mNetdEventCallback);
} catch (Exception e) {
- loge("Error registering netd callback: " + e);
+ loge("Error registering DnsResolver callback: " + e);
}
}
@@ -2008,29 +2014,18 @@
void handleRestrictBackgroundChanged(boolean restrictBackground) {
if (mRestrictBackground == restrictBackground) return;
+ final List<UidRange> blockedRanges = mVpnBlockedUidRanges;
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
final boolean curMetered = nai.networkCapabilities.isMetered();
maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
- restrictBackground);
+ restrictBackground, blockedRanges, blockedRanges);
}
mRestrictBackground = restrictBackground;
}
- private boolean isUidNetworkingWithVpnBlocked(int uid, int uidRules, boolean isNetworkMetered,
+ private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered,
boolean isBackgroundRestricted) {
- synchronized (mVpns) {
- final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
- // Because the return value of this function depends on the list of UIDs the
- // always-on VPN blocks when in lockdown mode, when the always-on VPN changes that
- // list all state depending on the return value of this function has to be recomputed.
- // TODO: add a trigger when the always-on VPN sets its blocked UIDs to reevaluate and
- // send the necessary onBlockedStatusChanged callbacks.
- if (vpn != null && vpn.getLockdown() && vpn.isBlockingUid(uid)) {
- return true;
- }
- }
-
return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
isNetworkMetered, isBackgroundRestricted);
}
@@ -4305,6 +4300,9 @@
case EVENT_DATA_SAVER_CHANGED:
handleRestrictBackgroundChanged(toBool(msg.arg1));
break;
+ case EVENT_SET_REQUIRE_VPN_FOR_UIDS:
+ handleSetRequireVpnForUids(toBool(msg.arg1), (UidRange[]) msg.obj);
+ break;
}
}
}
@@ -4473,8 +4471,7 @@
if (!nai.everConnected) {
return;
}
- LinkProperties lp = getLinkProperties(nai);
- if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) {
+ if (isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, false)) {
return;
}
nai.networkMonitor().forceReevaluation(uid);
@@ -4901,6 +4898,56 @@
}
}
+ private boolean isUidBlockedByVpn(int uid, List<UidRange> blockedUidRanges) {
+ // Determine whether this UID is blocked because of always-on VPN lockdown. If a VPN applies
+ // to the UID, then the UID is not blocked because always-on VPN lockdown applies only when
+ // a VPN is not up.
+ final NetworkAgentInfo vpnNai = getVpnForUid(uid);
+ if (vpnNai != null && !vpnNai.networkAgentConfig.allowBypass) return false;
+ for (UidRange range : blockedUidRanges) {
+ if (range.contains(uid)) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void setRequireVpnForUids(boolean requireVpn, UidRange[] ranges) {
+ NetworkStack.checkNetworkStackPermission(mContext);
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_REQUIRE_VPN_FOR_UIDS,
+ encodeBool(requireVpn), 0 /* arg2 */, ranges));
+ }
+
+ private void handleSetRequireVpnForUids(boolean requireVpn, UidRange[] ranges) {
+ if (DBG) {
+ Log.d(TAG, "Setting VPN " + (requireVpn ? "" : "not ") + "required for UIDs: "
+ + Arrays.toString(ranges));
+ }
+ // Cannot use a Set since the list of UID ranges might contain duplicates.
+ final List<UidRange> newVpnBlockedUidRanges = new ArrayList(mVpnBlockedUidRanges);
+ for (int i = 0; i < ranges.length; i++) {
+ if (requireVpn) {
+ newVpnBlockedUidRanges.add(ranges[i]);
+ } else {
+ newVpnBlockedUidRanges.remove(ranges[i]);
+ }
+ }
+
+ try {
+ mNetd.networkRejectNonSecureVpn(requireVpn, toUidRangeStableParcels(ranges));
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.e(TAG, "setRequireVpnForUids(" + requireVpn + ", "
+ + Arrays.toString(ranges) + "): netd command failed: " + e);
+ }
+
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
+ final boolean curMetered = nai.networkCapabilities.isMetered();
+ maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
+ mRestrictBackground, mVpnBlockedUidRanges, newVpnBlockedUidRanges);
+ }
+
+ mVpnBlockedUidRanges = newVpnBlockedUidRanges;
+ }
+
@Override
public boolean updateLockdownVpn() {
if (mDeps.getCallingUid() != Process.SYSTEM_UID) {
@@ -5085,101 +5132,6 @@
}
@Override
- public int checkMobileProvisioning(int suggestedTimeOutMs) {
- // TODO: Remove? Any reason to trigger a provisioning check?
- return -1;
- }
-
- /** Location to an updatable file listing carrier provisioning urls.
- * An example:
- *
- * <?xml version="1.0" encoding="utf-8"?>
- * <provisioningUrls>
- * <provisioningUrl mcc="310" mnc="4">http://myserver.com/foo?mdn=%3$s&iccid=%1$s&imei=%2$s</provisioningUrl>
- * </provisioningUrls>
- */
- private static final String PROVISIONING_URL_PATH =
- "/data/misc/radio/provisioning_urls.xml";
- private final File mProvisioningUrlFile = new File(PROVISIONING_URL_PATH);
-
- /** XML tag for root element. */
- private static final String TAG_PROVISIONING_URLS = "provisioningUrls";
- /** XML tag for individual url */
- private static final String TAG_PROVISIONING_URL = "provisioningUrl";
- /** XML attribute for mcc */
- private static final String ATTR_MCC = "mcc";
- /** XML attribute for mnc */
- private static final String ATTR_MNC = "mnc";
-
- private String getProvisioningUrlBaseFromFile() {
- XmlPullParser parser;
- Configuration config = mContext.getResources().getConfiguration();
-
- try (FileReader fileReader = new FileReader(mProvisioningUrlFile)) {
- parser = Xml.newPullParser();
- parser.setInput(fileReader);
- XmlUtils.beginDocument(parser, TAG_PROVISIONING_URLS);
-
- while (true) {
- XmlUtils.nextElement(parser);
-
- String element = parser.getName();
- if (element == null) break;
-
- if (element.equals(TAG_PROVISIONING_URL)) {
- String mcc = parser.getAttributeValue(null, ATTR_MCC);
- try {
- if (mcc != null && Integer.parseInt(mcc) == config.mcc) {
- String mnc = parser.getAttributeValue(null, ATTR_MNC);
- if (mnc != null && Integer.parseInt(mnc) == config.mnc) {
- parser.next();
- if (parser.getEventType() == XmlPullParser.TEXT) {
- return parser.getText();
- }
- }
- }
- } catch (NumberFormatException e) {
- loge("NumberFormatException in getProvisioningUrlBaseFromFile: " + e);
- }
- }
- }
- return null;
- } catch (FileNotFoundException e) {
- loge("Carrier Provisioning Urls file not found");
- } catch (XmlPullParserException e) {
- loge("Xml parser exception reading Carrier Provisioning Urls file: " + e);
- } catch (IOException e) {
- loge("I/O exception reading Carrier Provisioning Urls file: " + e);
- }
- return null;
- }
-
- @Override
- public String getMobileProvisioningUrl() {
- enforceSettingsPermission();
- String url = getProvisioningUrlBaseFromFile();
- if (TextUtils.isEmpty(url)) {
- url = mContext.getResources().getString(R.string.mobile_provisioning_url);
- log("getMobileProvisioningUrl: mobile_provisioining_url from resource =" + url);
- } else {
- log("getMobileProvisioningUrl: mobile_provisioning_url from File =" + url);
- }
- // populate the iccid, imei and phone number in the provisioning url.
- if (!TextUtils.isEmpty(url)) {
- String phoneNumber = mTelephonyManager.getLine1Number();
- if (TextUtils.isEmpty(phoneNumber)) {
- phoneNumber = "0000000000";
- }
- url = String.format(url,
- mTelephonyManager.getSimSerialNumber() /* ICCID */,
- mTelephonyManager.getDeviceId() /* IMEI */,
- phoneNumber /* Phone number */);
- }
-
- return url;
- }
-
- @Override
public void setProvisioningNotificationVisible(boolean visible, int networkType,
String action) {
enforceSettingsPermission();
@@ -5981,6 +5933,12 @@
// NOTE: Only should be accessed on ConnectivityServiceThread, except dump().
private final ArraySet<NetworkAgentInfo> mNetworkAgentInfos = new ArraySet<>();
+ // UID ranges for users that are currently blocked by VPNs.
+ // This array is accessed and iterated on multiple threads without holding locks, so its
+ // contents must never be mutated. When the ranges change, the array is replaced with a new one
+ // (on the handler thread).
+ private volatile List<UidRange> mVpnBlockedUidRanges = new ArrayList<>();
+
@GuardedBy("mBlockedAppUids")
private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
@@ -6635,7 +6593,7 @@
if (meteredChanged) {
maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground,
- mRestrictBackground);
+ mRestrictBackground, mVpnBlockedUidRanges, mVpnBlockedUidRanges);
}
final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
@@ -6700,6 +6658,15 @@
return stableRanges;
}
+ private static UidRangeParcel[] toUidRangeStableParcels(UidRange[] ranges) {
+ final UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.length];
+ for (int i = 0; i < ranges.length; i++) {
+ stableRanges[i] = new UidRangeParcel(ranges[i].start, ranges[i].stop);
+ }
+ return stableRanges;
+ }
+
+
private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
NetworkCapabilities newNc) {
Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUids();
@@ -7527,7 +7494,9 @@
}
final boolean metered = nai.networkCapabilities.isMetered();
- final boolean blocked = isUidNetworkingWithVpnBlocked(nri.mUid, mUidRules.get(nri.mUid),
+ boolean blocked;
+ blocked = isUidBlockedByVpn(nri.mUid, mVpnBlockedUidRanges);
+ blocked |= isUidBlockedByRules(nri.mUid, mUidRules.get(nri.mUid),
metered, mRestrictBackground);
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
}
@@ -7549,21 +7518,25 @@
* @param newRestrictBackground True if data saver is enabled.
*/
private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
- boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground) {
+ boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground,
+ List<UidRange> oldBlockedUidRanges, List<UidRange> newBlockedUidRanges) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
final int uidRules = mUidRules.get(nri.mUid);
- final boolean oldBlocked, newBlocked;
- // mVpns lock needs to be hold here to ensure that the active VPN cannot be changed
- // between these two calls.
- synchronized (mVpns) {
- oldBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, oldMetered,
- oldRestrictBackground);
- newBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, newMetered,
- newRestrictBackground);
- }
+ final boolean oldBlocked, newBlocked, oldVpnBlocked, newVpnBlocked;
+
+ oldVpnBlocked = isUidBlockedByVpn(nri.mUid, oldBlockedUidRanges);
+ newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges)
+ ? isUidBlockedByVpn(nri.mUid, newBlockedUidRanges)
+ : oldVpnBlocked;
+
+ oldBlocked = oldVpnBlocked || isUidBlockedByRules(nri.mUid, uidRules, oldMetered,
+ oldRestrictBackground);
+ newBlocked = newVpnBlocked || isUidBlockedByRules(nri.mUid, uidRules, newMetered,
+ newRestrictBackground);
+
if (oldBlocked != newBlocked) {
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
encodeBool(newBlocked));
@@ -7579,17 +7552,12 @@
private void maybeNotifyNetworkBlockedForNewUidRules(int uid, int newRules) {
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
final boolean metered = nai.networkCapabilities.isMetered();
+ final boolean vpnBlocked = isUidBlockedByVpn(uid, mVpnBlockedUidRanges);
final boolean oldBlocked, newBlocked;
- // TODO: Consider that doze mode or turn on/off battery saver would deliver lots of uid
- // rules changed event. And this function actually loop through all connected nai and
- // its requests. It seems that mVpns lock will be grabbed frequently in this case.
- // Reduce the number of locking or optimize the use of lock are likely needed in future.
- synchronized (mVpns) {
- oldBlocked = isUidNetworkingWithVpnBlocked(
- uid, mUidRules.get(uid), metered, mRestrictBackground);
- newBlocked = isUidNetworkingWithVpnBlocked(
- uid, newRules, metered, mRestrictBackground);
- }
+ oldBlocked = vpnBlocked || isUidBlockedByRules(
+ uid, mUidRules.get(uid), metered, mRestrictBackground);
+ newBlocked = vpnBlocked || isUidBlockedByRules(
+ uid, newRules, metered, mRestrictBackground);
if (oldBlocked == newBlocked) {
continue;
}
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
index 2bc8925..f701688 100644
--- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -20,7 +20,6 @@
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import android.content.Context;
-import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.os.INetworkManagementService;
import android.os.ServiceManager;
@@ -38,7 +37,7 @@
super(context);
// TODO: Define formal APIs to get the needed services.
mConnectivity = new ConnectivityService(context, getNetworkManagementService(),
- getNetworkStatsService(), getNetworkPolicyManager());
+ getNetworkStatsService());
}
@Override
@@ -57,10 +56,4 @@
return INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
}
-
- private INetworkPolicyManager getNetworkPolicyManager() {
- return INetworkPolicyManager.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
- }
-
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index bf53f28..f6b72d6 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,5 +1,5 @@
# Connectivity / Networking
-per-file ConnectivityService.java,NetworkManagementService.java,NsdService.java = codewiz@google.com, ek@google.com, jchalard@google.com, junyulai@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
+per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java = file:/services/core/java/com/android/server/net/OWNERS
# Vibrator / Threads
per-file VibratorService.java, DisplayThread.java = michaelwr@google.com
@@ -25,7 +25,6 @@
per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
per-file *Storage* = file:/core/java/android/os/storage/OWNERS
per-file *TimeUpdate* = file:/core/java/android/app/timezone/OWNERS
-per-file ConnectivityService.java = file:/services/core/java/com/android/server/net/OWNERS
per-file DynamicSystemService.java = file:/packages/DynamicSystemInstallationService/OWNERS
per-file GestureLauncherService.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
per-file IpSecService.java = file:/services/core/java/com/android/server/net/OWNERS
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index eb1123e..5f6e8df 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -304,7 +304,7 @@
private final LocalLog mLocalLog = new LocalLog(200);
- private final LocalLog mListenLog = new LocalLog(00);
+ private final LocalLog mListenLog = new LocalLog(200);
/**
* Per-phone map of precise data connection state. The key of the map is the pair of transport
@@ -2316,7 +2316,9 @@
pw.println("local logs:");
pw.increaseIndent();
mLocalLog.dump(fd, pw, args);
+ pw.decreaseIndent();
pw.println("listen logs:");
+ pw.increaseIndent();
mListenLog.dump(fd, pw, args);
pw.decreaseIndent();
pw.println("registrations: count=" + recordCount);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7e67f97..928ddab 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9481,6 +9481,7 @@
final long waitForNetworkTimeoutMs = Settings.Global.getLong(resolver,
NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
mHiddenApiBlacklist.registerObserver();
+ mPlatformCompat.registerContentObserver();
final long pssDeferralMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
ACTIVITY_START_PSS_DEFER_CONFIG, 0L);
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index a8aa9aa..c4ff99b 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -61,6 +61,7 @@
ChangeListener mListener = null;
private Map<String, Boolean> mPackageOverrides;
+ private Map<String, Boolean> mDeferredOverrides;
public CompatChange(long changeId) {
this(changeId, null, -1, -1, false, false, null);
@@ -121,6 +122,56 @@
}
/**
+ * Tentatively set the state of this change for a given package name.
+ * The override will only take effect after that package is installed, if applicable.
+ *
+ * <p>Note, this method is not thread safe so callers must ensure thread safety.
+ *
+ * @param packageName Package name to tentatively enable the change for.
+ * @param enabled Whether or not to enable the change.
+ */
+ void addPackageDeferredOverride(String packageName, boolean enabled) {
+ if (getLoggingOnly()) {
+ throw new IllegalArgumentException(
+ "Can't add overrides for a logging only change " + toString());
+ }
+ if (mDeferredOverrides == null) {
+ mDeferredOverrides = new HashMap<>();
+ }
+ mDeferredOverrides.put(packageName, enabled);
+ }
+
+ /**
+ * Rechecks an existing (and possibly deferred) override.
+ *
+ * <p>For deferred overrides, check if they can be promoted to a regular override. For regular
+ * overrides, check if they need to be demoted to deferred.</p>
+ *
+ * @param packageName Package name to apply deferred overrides for.
+ * @param allowed Whether the override is allowed.
+ *
+ * @return {@code true} if the recheck yielded a result that requires invalidating caches
+ * (a deferred override was consolidated or a regular override was removed).
+ */
+ boolean recheckOverride(String packageName, boolean allowed) {
+ // A deferred override now is allowed by the policy, so promote it to a regular override.
+ if (hasDeferredOverride(packageName) && allowed) {
+ boolean overrideValue = mDeferredOverrides.remove(packageName);
+ addPackageOverride(packageName, overrideValue);
+ return true;
+ }
+ // A previously set override is no longer allowed by the policy, so make it deferred.
+ if (hasOverride(packageName) && !allowed) {
+ boolean overrideValue = mPackageOverrides.remove(packageName);
+ addPackageDeferredOverride(packageName, overrideValue);
+ // Notify because the override was removed.
+ notifyListener(packageName);
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Remove any package override for the given package name, restoring the default behaviour.
*
* <p>Note, this method is not thread safe so callers must ensure thread safety.
@@ -133,6 +184,9 @@
notifyListener(pname);
}
}
+ if (mDeferredOverrides != null) {
+ mDeferredOverrides.remove(pname);
+ }
}
/**
@@ -176,6 +230,15 @@
return mPackageOverrides != null && mPackageOverrides.containsKey(packageName);
}
+ /**
+ * Checks whether a change has a deferred override for a package.
+ * @param packageName name of the package
+ * @return true if there is such a deferred override
+ */
+ boolean hasDeferredOverride(String packageName) {
+ return mDeferredOverrides != null && mDeferredOverrides.containsKey(packageName);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ChangeId(")
@@ -195,6 +258,9 @@
if (mPackageOverrides != null && mPackageOverrides.size() > 0) {
sb.append("; packageOverrides=").append(mPackageOverrides);
}
+ if (mDeferredOverrides != null && mDeferredOverrides.size() > 0) {
+ sb.append("; deferredOverrides=").append(mDeferredOverrides);
+ }
return sb.append(")").toString();
}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 8511118..f03a608 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -65,7 +65,7 @@
@GuardedBy("mChanges")
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
- private IOverrideValidator mOverrideValidator;
+ private OverrideValidatorImpl mOverrideValidator;
@VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
@@ -161,7 +161,7 @@
* @return {@code true} if the change existed before adding the override.
*/
boolean addOverride(long changeId, String packageName, boolean enabled)
- throws RemoteException, SecurityException {
+ throws SecurityException {
boolean alreadyKnown = true;
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
@@ -173,7 +173,17 @@
c = new CompatChange(changeId);
addChange(c);
}
- c.addPackageOverride(packageName, enabled);
+ switch (allowedState.state) {
+ case OverrideAllowedState.ALLOWED:
+ c.addPackageOverride(packageName, enabled);
+ break;
+ case OverrideAllowedState.DEFERRED_VERIFICATION:
+ c.addPackageDeferredOverride(packageName, enabled);
+ break;
+ default:
+ throw new IllegalStateException("Should only be able to override changes that "
+ + "are allowed or can be deferred.");
+ }
invalidateCache();
}
return alreadyKnown;
@@ -244,26 +254,26 @@
* @return {@code true} if an override existed;
*/
boolean removeOverride(long changeId, String packageName)
- throws RemoteException, SecurityException {
+ throws SecurityException {
boolean overrideExists = false;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
- try {
- if (c != null) {
- overrideExists = c.hasOverride(packageName);
- if (overrideExists) {
- OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedState(changeId, packageName);
- allowedState.enforce(changeId, packageName);
- c.removePackageOverride(packageName);
- }
+ if (c != null) {
+ // Always allow removing a deferred override.
+ if (c.hasDeferredOverride(packageName)) {
+ c.removePackageOverride(packageName);
+ overrideExists = true;
+ } else if (c.hasOverride(packageName)) {
+ // Regular overrides need to pass the policy.
+ overrideExists = true;
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedState(changeId, packageName);
+ allowedState.enforce(changeId, packageName);
+ c.removePackageOverride(packageName);
}
- } catch (RemoteException e) {
- // Should never occur, since validator is in the same process.
- throw new RuntimeException("Unable to call override validator!", e);
}
- invalidateCache();
}
+ invalidateCache();
return overrideExists;
}
@@ -293,29 +303,15 @@
* Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or
* {@link #addOverrides(CompatibilityChangeConfig, String)} for a certain package.
*
- * <p>This restores the default behaviour for the given change and app, once any app
- * processes have been restarted.
+ * <p>This restores the default behaviour for the given app.
*
* @param packageName The package for which the overrides should be purged.
*/
- void removePackageOverrides(String packageName) throws RemoteException, SecurityException {
+ void removePackageOverrides(String packageName) throws SecurityException {
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
- try {
- CompatChange change = mChanges.valueAt(i);
- if (change.hasOverride(packageName)) {
- OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedState(change.getId(),
- packageName);
- allowedState.enforce(change.getId(), packageName);
- if (change != null) {
- mChanges.valueAt(i).removePackageOverride(packageName);
- }
- }
- } catch (RemoteException e) {
- // Should never occur, since validator is in the same process.
- throw new RuntimeException("Unable to call override validator!", e);
- }
+ CompatChange change = mChanges.valueAt(i);
+ removeOverride(change.getId(), packageName);
}
invalidateCache();
}
@@ -327,20 +323,15 @@
LongArray allowed = new LongArray();
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
- try {
- CompatChange change = mChanges.valueAt(i);
- if (change.getEnableSinceTargetSdk() != targetSdkVersion) {
- continue;
- }
- OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedState(change.getId(),
- packageName);
- if (allowedState.state == OverrideAllowedState.ALLOWED) {
- allowed.add(change.getId());
- }
- } catch (RemoteException e) {
- // Should never occur, since validator is in the same process.
- throw new RuntimeException("Unable to call override validator!", e);
+ CompatChange change = mChanges.valueAt(i);
+ if (change.getEnableSinceTargetSdk() != targetSdkVersion) {
+ continue;
+ }
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedState(change.getId(),
+ packageName);
+ if (allowedState.state == OverrideAllowedState.ALLOWED) {
+ allowed.add(change.getId());
}
}
}
@@ -401,6 +392,11 @@
}
@VisibleForTesting
+ void forceNonDebuggableFinalForTest(boolean value) {
+ mOverrideValidator.forceNonDebuggableFinalForTest(value);
+ }
+
+ @VisibleForTesting
void clearChanges() {
synchronized (mChanges) {
mChanges.clear();
@@ -511,4 +507,26 @@
private void invalidateCache() {
ChangeIdStateCache.invalidate();
}
+ /**
+ * Rechecks all the existing overrides for a package.
+ */
+ void recheckOverrides(String packageName) {
+ synchronized (mChanges) {
+ boolean shouldInvalidateCache = false;
+ for (int idx = 0; idx < mChanges.size(); ++idx) {
+ CompatChange c = mChanges.valueAt(idx);
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedState(c.getId(), packageName);
+ boolean allowedOverride = (allowedState.state == OverrideAllowedState.ALLOWED);
+ shouldInvalidateCache |= c.recheckOverride(packageName, allowedOverride);
+ }
+ if (shouldInvalidateCache) {
+ invalidateCache();
+ }
+ }
+ }
+
+ void registerContentObserver() {
+ mOverrideValidator.registerContentObserver();
+ }
}
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index 79a13ca..fe5b4a9 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -17,16 +17,19 @@
package com.android.server.compat;
import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
+import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
-import static com.android.internal.compat.OverrideAllowedState.PACKAGE_DOES_NOT_EXIST;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.AndroidBuildClassifier;
@@ -41,6 +44,20 @@
private AndroidBuildClassifier mAndroidBuildClassifier;
private Context mContext;
private CompatConfig mCompatConfig;
+ private boolean mForceNonDebuggableFinalBuild;
+
+ private class SettingsObserver extends ContentObserver {
+ SettingsObserver() {
+ super(new Handler());
+ }
+ @Override
+ public void onChange(boolean selfChange) {
+ mForceNonDebuggableFinalBuild = Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT,
+ 0) == 1;
+ }
+ }
@VisibleForTesting
OverrideValidatorImpl(AndroidBuildClassifier androidBuildClassifier,
@@ -48,6 +65,7 @@
mAndroidBuildClassifier = androidBuildClassifier;
mContext = context;
mCompatConfig = config;
+ mForceNonDebuggableFinalBuild = false;
}
@Override
@@ -56,8 +74,10 @@
return new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1);
}
- boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild();
- boolean finalBuild = mAndroidBuildClassifier.isFinalBuild();
+ boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild()
+ && !mForceNonDebuggableFinalBuild;
+ boolean finalBuild = mAndroidBuildClassifier.isFinalBuild()
+ || mForceNonDebuggableFinalBuild;
int maxTargetSdk = mCompatConfig.maxTargetSdkForChangeIdOptIn(changeId);
boolean disabled = mCompatConfig.isDisabled(changeId);
@@ -73,7 +93,7 @@
try {
applicationInfo = packageManager.getApplicationInfo(packageName, 0);
} catch (NameNotFoundException e) {
- return new OverrideAllowedState(PACKAGE_DOES_NOT_EXIST, -1, -1);
+ return new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1);
}
int appTargetSdk = applicationInfo.targetSdkVersion;
// Only allow overriding debuggable apps.
@@ -94,4 +114,17 @@
}
return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, maxTargetSdk);
}
+
+ void registerContentObserver() {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(
+ Settings.Global.FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT),
+ false,
+ new SettingsObserver());
+ }
+
+ void forceNonDebuggableFinalForTest(boolean value) {
+ mForceNonDebuggableFinalBuild = value;
+ }
+
}
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index aa85f7f..283dba7 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -25,9 +25,13 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
@@ -74,6 +78,7 @@
mChangeReporter = new ChangeReporter(
ChangeReporter.SOURCE_SYSTEM_SERVER);
mCompatConfig = compatConfig;
+ registerPackageReceiver(context);
}
@Override
@@ -389,4 +394,42 @@
}
return true;
}
+
+ /**
+ * Registers a broadcast receiver that listens for package install, replace or remove.
+ * @param context the context where the receiver should be registered.
+ */
+ public void registerPackageReceiver(Context context) {
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent == null) {
+ return;
+ }
+ final Uri packageData = intent.getData();
+ if (packageData == null) {
+ return;
+ }
+ final String packageName = packageData.getSchemeSpecificPart();
+ if (packageName == null) {
+ return;
+ }
+ mCompatConfig.recheckOverrides(packageName);
+ }
+ };
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ context.registerReceiver(receiver, filter);
+ }
+
+ /**
+ * Register the observer for
+ * {@link android.provider.Settings.Global#FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT}
+ */
+ public void registerContentObserver() {
+ mCompatConfig.registerContentObserver();
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 841c970..7bde4d5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -146,7 +146,11 @@
// Underlying networks declared by the agent. Only set if supportsUnderlyingNetworks is true.
// The networks in this list might be declared by a VPN app using setUnderlyingNetworks and are
// not guaranteed to be current or correct, or even to exist.
- public @Nullable Network[] declaredUnderlyingNetworks;
+ //
+ // This array is read and iterated on multiple threads with no locking so its contents must
+ // never be modified. When the list of networks changes, replace with a new array, on the
+ // handler thread.
+ public @Nullable volatile Network[] declaredUnderlyingNetworks;
// The capabilities originally announced by the NetworkAgent, regardless of any capabilities
// that were added or removed due to this network's underlying networks.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 228ad588..07a4b89 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -101,6 +101,7 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Range;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -223,7 +224,7 @@
private @NonNull List<String> mLockdownAllowlist = Collections.emptyList();
/**
- * A memory of what UIDs this class told netd to block for the lockdown feature.
+ * A memory of what UIDs this class told ConnectivityService to block for the lockdown feature.
*
* Netd maintains ranges of UIDs for which network should be restricted to using only the VPN
* for the lockdown feature. This class manages these UIDs and sends this information to netd.
@@ -237,7 +238,7 @@
* @see mLockdown
*/
@GuardedBy("this")
- private final Set<UidRangeParcel> mBlockedUidsAsToldToNetd = new ArraySet<>();
+ private final Set<UidRangeParcel> mBlockedUidsAsToldToConnectivity = new ArraySet<>();
// The user id of initiating VPN.
private final int mUserId;
@@ -1588,7 +1589,7 @@
* {@link Vpn} goes through a VPN connection or is blocked until one is
* available, {@code false} to lift the requirement.
*
- * @see #mBlockedUidsAsToldToNetd
+ * @see #mBlockedUidsAsToldToConnectivity
*/
@GuardedBy("this")
private void setVpnForcedLocked(boolean enforce) {
@@ -1599,10 +1600,8 @@
exemptedPackages = new ArrayList<>(mLockdownAllowlist);
exemptedPackages.add(mPackage);
}
- final Set<UidRangeParcel> rangesToTellNetdToRemove =
- new ArraySet<>(mBlockedUidsAsToldToNetd);
-
- final Set<UidRangeParcel> rangesToTellNetdToAdd;
+ final Set<UidRangeParcel> rangesToRemove = new ArraySet<>(mBlockedUidsAsToldToConnectivity);
+ final Set<UidRangeParcel> rangesToAdd;
if (enforce) {
final Set<UidRange> restrictedProfilesRanges =
createUserAndRestrictedProfilesRanges(mUserId,
@@ -1621,26 +1620,27 @@
}
}
- rangesToTellNetdToRemove.removeAll(rangesThatShouldBeBlocked);
- rangesToTellNetdToAdd = rangesThatShouldBeBlocked;
- // The ranges to tell netd to add are the ones that should be blocked minus the
- // ones it already knows to block. Note that this will change the contents of
+ rangesToRemove.removeAll(rangesThatShouldBeBlocked);
+ rangesToAdd = rangesThatShouldBeBlocked;
+ // The ranges to tell ConnectivityService to add are the ones that should be blocked
+ // minus the ones it already knows to block. Note that this will change the contents of
// rangesThatShouldBeBlocked, but the list of ranges that should be blocked is
// not used after this so it's fine to destroy it.
- rangesToTellNetdToAdd.removeAll(mBlockedUidsAsToldToNetd);
+ rangesToAdd.removeAll(mBlockedUidsAsToldToConnectivity);
} else {
- rangesToTellNetdToAdd = Collections.emptySet();
+ rangesToAdd = Collections.emptySet();
}
// If mBlockedUidsAsToldToNetd used to be empty, this will always be a no-op.
- setAllowOnlyVpnForUids(false, rangesToTellNetdToRemove);
+ setAllowOnlyVpnForUids(false, rangesToRemove);
// If nothing should be blocked now, this will now be a no-op.
- setAllowOnlyVpnForUids(true, rangesToTellNetdToAdd);
+ setAllowOnlyVpnForUids(true, rangesToAdd);
}
/**
- * Tell netd to add or remove a list of {@link UidRange}s to the list of UIDs that are only
- * allowed to make connections through sockets that have had {@code protect()} called on them.
+ * Tell ConnectivityService to add or remove a list of {@link UidRange}s to the list of UIDs
+ * that are only allowed to make connections through sockets that have had {@code protect()}
+ * called on them.
*
* @param enforce {@code true} to add to the denylist, {@code false} to remove.
* @param ranges {@link Collection} of {@link UidRange}s to add (if {@param enforce} is
@@ -1653,18 +1653,22 @@
if (ranges.size() == 0) {
return true;
}
- final UidRangeParcel[] stableRanges = ranges.toArray(new UidRangeParcel[ranges.size()]);
+ // Convert to Collection<Range> which is what the ConnectivityManager API takes.
+ ArrayList<Range<Integer>> integerRanges = new ArrayList<>(ranges.size());
+ for (UidRangeParcel uidRange : ranges) {
+ integerRanges.add(new Range<>(uidRange.start, uidRange.stop));
+ }
try {
- mNetd.networkRejectNonSecureVpn(enforce, stableRanges);
- } catch (RemoteException | RuntimeException e) {
+ mConnectivityManager.setRequireVpnForUids(enforce, integerRanges);
+ } catch (RuntimeException e) {
Log.e(TAG, "Updating blocked=" + enforce
+ " for UIDs " + Arrays.toString(ranges.toArray()) + " failed", e);
return false;
}
if (enforce) {
- mBlockedUidsAsToldToNetd.addAll(ranges);
+ mBlockedUidsAsToldToConnectivity.addAll(ranges);
} else {
- mBlockedUidsAsToldToNetd.removeAll(ranges);
+ mBlockedUidsAsToldToConnectivity.removeAll(ranges);
}
return true;
}
@@ -1783,9 +1787,6 @@
/**
* Updates underlying network set.
- *
- * <p>Note: Does not updates capabilities. Call {@link #updateCapabilities} from
- * ConnectivityService thread to get updated capabilities.
*/
public synchronized boolean setUnderlyingNetworks(Network[] networks) {
if (!isCallerEstablishedOwnerLocked()) {
@@ -1808,13 +1809,6 @@
return true;
}
- public synchronized Network[] getUnderlyingNetworks() {
- if (!isRunningLocked()) {
- return null;
- }
- return mConfig.underlyingNetworks;
- }
-
/**
* This method should only be called by ConnectivityService because it doesn't
* have enough data to fill VpnInfo.primaryUnderlyingIface field.
@@ -1864,13 +1858,13 @@
* the {@code uid}.
*
* @apiNote This method don't check VPN lockdown status.
- * @see #mBlockedUidsAsToldToNetd
+ * @see #mBlockedUidsAsToldToConnectivity
*/
public synchronized boolean isBlockingUid(int uid) {
if (mNetworkInfo.isConnected()) {
return !appliesToUid(uid);
} else {
- return containsUid(mBlockedUidsAsToldToNetd, uid);
+ return containsUid(mBlockedUidsAsToldToConnectivity, uid);
}
}
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java
index 2326ad3..bce8069 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java
@@ -20,8 +20,8 @@
import android.service.NetworkIdentitySetProto;
import android.util.proto.ProtoOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
import java.io.IOException;
import java.util.HashSet;
@@ -44,7 +44,7 @@
public NetworkIdentitySet() {
}
- public NetworkIdentitySet(DataInputStream in) throws IOException {
+ public NetworkIdentitySet(DataInput in) throws IOException {
final int version = in.readInt();
final int size = in.readInt();
for (int i = 0; i < size; i++) {
@@ -89,7 +89,7 @@
}
}
- public void writeToStream(DataOutputStream out) throws IOException {
+ public void writeToStream(DataOutput out) throws IOException {
out.writeInt(VERSION_ADD_DEFAULT_NETWORK);
out.writeInt(size());
for (NetworkIdentity ident : this) {
@@ -143,7 +143,7 @@
return true;
}
- private static void writeOptionalString(DataOutputStream out, String value) throws IOException {
+ private static void writeOptionalString(DataOutput out, String value) throws IOException {
if (value != null) {
out.writeByte(1);
out.writeUTF(value);
@@ -152,7 +152,7 @@
}
}
- private static String readOptionalString(DataInputStream in) throws IOException {
+ private static String readOptionalString(DataInput in) throws IOException {
if (in.readByte() != 0) {
return in.readUTF();
} else {
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index c4beddd4..6aefe41 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -63,12 +63,15 @@
import com.google.android.collect.Maps;
import java.io.BufferedInputStream;
+import java.io.DataInput;
import java.io.DataInputStream;
+import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ProtocolException;
import java.time.ZonedDateTime;
@@ -82,7 +85,7 @@
* Collection of {@link NetworkStatsHistory}, stored based on combined key of
* {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
*/
-public class NetworkStatsCollection implements FileRotator.Reader {
+public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer {
/** File header magic number: "ANET" */
private static final int FILE_MAGIC = 0x414E4554;
@@ -431,10 +434,10 @@
@Override
public void read(InputStream in) throws IOException {
- read(new DataInputStream(in));
+ read((DataInput) new DataInputStream(in));
}
- public void read(DataInputStream in) throws IOException {
+ private void read(DataInput in) throws IOException {
// verify file magic header intact
final int magic = in.readInt();
if (magic != FILE_MAGIC) {
@@ -468,7 +471,13 @@
}
}
- public void write(DataOutputStream out) throws IOException {
+ @Override
+ public void write(OutputStream out) throws IOException {
+ write((DataOutput) new DataOutputStream(out));
+ out.flush();
+ }
+
+ private void write(DataOutput out) throws IOException {
// cluster key lists grouped by ident
final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
for (Key key : mStats.keySet()) {
@@ -497,8 +506,6 @@
history.writeToStream(out);
}
}
-
- out.flush();
}
@Deprecated
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index ce74169..978ae87 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -42,7 +42,6 @@
import libcore.io.IoUtils;
import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -375,7 +374,7 @@
@Override
public void write(OutputStream out) throws IOException {
- mCollection.write(new DataOutputStream(out));
+ mCollection.write(out);
mCollection.reset();
}
}
@@ -412,7 +411,7 @@
@Override
public void write(OutputStream out) throws IOException {
- mTemp.write(new DataOutputStream(out));
+ mTemp.write(out);
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 0f8c9c7..7a6792c 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -24,11 +24,15 @@
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_REASON;
+import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED;
+import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
import static android.os.Trace.TRACE_TAG_RRO;
import static android.os.Trace.traceBegin;
import static android.os.Trace.traceEnd;
+import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -39,6 +43,7 @@
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
+import android.content.om.OverlayManagerTransaction;
import android.content.om.OverlayableInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
@@ -48,6 +53,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -64,7 +70,6 @@
import com.android.internal.content.om.OverlayConfig;
import com.android.server.FgThread;
-import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
@@ -82,12 +87,15 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
/**
* Service to manage asset overlays.
@@ -236,7 +244,14 @@
private final OverlayActorEnforcer mActorEnforcer;
- private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
+ private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> {
+ persistSettings();
+ FgThread.getHandler().post(() -> {
+ List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId);
+ updateActivityManager(affectedTargets, pair.userId);
+ broadcastActionOverlayChanged(affectedTargets, pair.userId);
+ });
+ };
public OverlayManagerService(@NonNull final Context context) {
super(context);
@@ -249,17 +264,19 @@
IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager);
mSettings = new OverlayManagerSettings();
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
- OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
- new OverlayChangeListener());
+ OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
+ HandlerThread packageReceiverThread = new HandlerThread(TAG);
+ packageReceiverThread.start();
+
final IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(ACTION_PACKAGE_ADDED);
packageFilter.addAction(ACTION_PACKAGE_CHANGED);
packageFilter.addAction(ACTION_PACKAGE_REMOVED);
packageFilter.addDataScheme("package");
getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
- packageFilter, null, null);
+ packageFilter, null, packageReceiverThread.getThreadHandler());
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(ACTION_USER_ADDED);
@@ -292,11 +309,11 @@
for (int i = 0; i < userCount; i++) {
final UserInfo userInfo = users.get(i);
if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
- // Initialize any users that can't be switched to, as there state would
+ // Initialize any users that can't be switched to, as their state would
// never be setup in onSwitchUser(). We will switch to the system user right
// after this, and its state will be setup there.
final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
- updateOverlayPaths(users.get(i).id, targets);
+ updatePackageManager(targets, users.get(i).id);
}
}
}
@@ -310,9 +327,10 @@
// any asset changes to the rest of the system
synchronized (mLock) {
final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
- updateAssets(newUserId, targets);
+ final List<String> affectedTargets = updatePackageManager(targets, newUserId);
+ updateActivityManager(affectedTargets, newUserId);
}
- schedulePersistSettings();
+ persistSettings();
} finally {
traceEnd(TRACE_TAG_RRO);
}
@@ -396,10 +414,17 @@
false);
if (pi != null && !pi.applicationInfo.isInstantApp()) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
- if (pi.isOverlayPackage()) {
- mImpl.onOverlayPackageAdded(packageName, userId);
- } else {
- mImpl.onTargetPackageAdded(packageName, userId);
+
+ try {
+ if (pi.isOverlayPackage()) {
+ mImpl.onOverlayPackageAdded(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ } else {
+ mImpl.onTargetPackageAdded(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ }
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageAdded internal error", e);
}
}
}
@@ -419,10 +444,17 @@
false);
if (pi != null && pi.applicationInfo.isInstantApp()) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
- if (pi.isOverlayPackage()) {
- mImpl.onOverlayPackageChanged(packageName, userId);
- } else {
- mImpl.onTargetPackageChanged(packageName, userId);
+
+ try {
+ if (pi.isOverlayPackage()) {
+ mImpl.onOverlayPackageChanged(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ } else {
+ mImpl.onTargetPackageChanged(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ }
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageChanged internal error", e);
}
}
}
@@ -441,7 +473,12 @@
mPackageManager.forgetPackageInfo(packageName, userId);
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
if (oi != null) {
- mImpl.onOverlayPackageReplacing(packageName, userId);
+ try {
+ mImpl.onOverlayPackageReplacing(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplacing internal error", e);
+ }
}
}
}
@@ -460,10 +497,16 @@
false);
if (pi != null && !pi.applicationInfo.isInstantApp()) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
- if (pi.isOverlayPackage()) {
- mImpl.onOverlayPackageReplaced(packageName, userId);
- } else {
- mImpl.onTargetPackageReplaced(packageName, userId);
+ try {
+ if (pi.isOverlayPackage()) {
+ mImpl.onOverlayPackageReplaced(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ } else {
+ mImpl.onTargetPackageReplaced(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ }
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplaced internal error", e);
}
}
}
@@ -481,10 +524,17 @@
synchronized (mLock) {
mPackageManager.forgetPackageInfo(packageName, userId);
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
- if (oi != null) {
- mImpl.onOverlayPackageRemoved(packageName, userId);
- } else {
- mImpl.onTargetPackageRemoved(packageName, userId);
+
+ try {
+ if (oi != null) {
+ mImpl.onOverlayPackageRemoved(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ } else {
+ mImpl.onTargetPackageRemoved(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ }
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageRemoved internal error", e);
}
}
}
@@ -507,7 +557,7 @@
synchronized (mLock) {
targets = mImpl.updateOverlaysForUser(userId);
}
- updateOverlayPaths(userId, targets);
+ updatePackageManager(targets, userId);
} finally {
traceEnd(TRACE_TAG_RRO);
}
@@ -602,7 +652,13 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setEnabled(packageName, enable, realUserId);
+ try {
+ mImpl.setEnabled(packageName, enable, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -627,8 +683,14 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setEnabledExclusive(packageName, false /* withinCategory */,
- realUserId);
+ try {
+ mImpl.setEnabledExclusive(packageName,
+ false /* withinCategory */, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -654,8 +716,14 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setEnabledExclusive(packageName, true /* withinCategory */,
- realUserId);
+ try {
+ mImpl.setEnabledExclusive(packageName,
+ true /* withinCategory */, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -681,7 +749,13 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setPriority(packageName, parentPackageName, realUserId);
+ try {
+ mImpl.setPriority(packageName, parentPackageName, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -705,7 +779,13 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setHighestPriority(packageName, realUserId);
+ try {
+ mImpl.setHighestPriority(packageName, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -729,7 +809,13 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setLowestPriority(packageName, realUserId);
+ try {
+ mImpl.setLowestPriority(packageName, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -778,6 +864,120 @@
}
@Override
+ public void commit(@NonNull final OverlayManagerTransaction transaction)
+ throws RemoteException {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#commit " + transaction);
+ try {
+ executeAllRequests(transaction);
+ } catch (Exception e) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ restoreSettings();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ Slog.d(TAG, "commit failed: " + e.getMessage(), e);
+ throw new SecurityException("commit failed"
+ + (DEBUG ? ": " + e.getMessage() : ""));
+ }
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
+ }
+ }
+
+ private Optional<PackageAndUser> executeRequest(
+ @NonNull final OverlayManagerTransaction.Request request) throws Exception {
+ final int realUserId = handleIncomingUser(request.userId, request.typeToString());
+ enforceActor(request.packageName, request.typeToString(), realUserId);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ switch (request.type) {
+ case TYPE_SET_ENABLED:
+ Optional<PackageAndUser> opt1 =
+ mImpl.setEnabled(request.packageName, true, request.userId);
+ Optional<PackageAndUser> opt2 =
+ mImpl.setHighestPriority(request.packageName, request.userId);
+ // Both setEnabled and setHighestPriority affected the same
+ // target package and user: if both return non-empty
+ // Optionals, they are identical
+ return opt1.isPresent() ? opt1 : opt2;
+ case TYPE_SET_DISABLED:
+ return mImpl.setEnabled(request.packageName, false, request.userId);
+ default:
+ throw new IllegalArgumentException("unsupported request: " + request);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction)
+ throws Exception {
+ if (DEBUG) {
+ Slog.d(TAG, "commit " + transaction);
+ }
+ if (transaction == null) {
+ throw new IllegalArgumentException("null transaction");
+ }
+
+ // map: userId -> list<targetPackageName>
+ SparseArray<List<String>> affectedTargetsToUpdate = new SparseArray<>();
+
+ synchronized (mLock) {
+ // map: userId -> set<targetPackageName>
+ SparseArray<Set<String>> targetsToUpdate = new SparseArray<>();
+
+ // execute the requests (as calling user)
+ for (final OverlayManagerTransaction.Request request : transaction) {
+ executeRequest(request).ifPresent(target -> {
+ Set<String> userTargets = targetsToUpdate.get(target.userId);
+ if (userTargets == null) {
+ userTargets = new ArraySet<String>();
+ targetsToUpdate.put(target.userId, userTargets);
+ }
+ userTargets.add(target.packageName);
+ });
+ }
+
+ // past the point of no return: the entire transaction has been
+ // processed successfully, we can no longer fail: continue as
+ // system_server
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ persistSettings();
+
+ // inform the package manager about the new paths
+ for (int index = 0; index < targetsToUpdate.size(); index++) {
+ final int userId = targetsToUpdate.keyAt(index);
+ final List<String> affectedTargets =
+ updatePackageManager(targetsToUpdate.valueAt(index), userId);
+ affectedTargetsToUpdate.put(userId, affectedTargets);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } // synchronized (mLock)
+
+ FgThread.getHandler().post(() -> {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ // schedule apps to refresh + broadcast the ACTION_OVERLAY_CHANGED intents
+ for (int index = 0; index < affectedTargetsToUpdate.size(); index++) {
+ final int userId = affectedTargetsToUpdate.keyAt(index);
+ final List<String> packageNames = affectedTargetsToUpdate.valueAt(index);
+
+ updateActivityManager(packageNames, userId);
+ broadcastActionOverlayChanged(packageNames, userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ });
+ }
+
+ @Override
public void onShellCommand(@NonNull final FileDescriptor in,
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
@NonNull final String[] args, @NonNull final ShellCallback callback,
@@ -898,162 +1098,7 @@
}
};
- private final class OverlayChangeListener
- implements OverlayManagerServiceImpl.OverlayChangeListener {
- @Override
- public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
- schedulePersistSettings();
- FgThread.getHandler().post(() -> {
- updateAssets(userId, targetPackageName);
-
- final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
- Uri.fromParts("package", targetPackageName, null));
- intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-
- if (DEBUG) {
- Slog.d(TAG, "send broadcast " + intent);
- }
-
- try {
- ActivityManager.getService().broadcastIntentWithFeature(null, null, intent,
- null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE,
- null, false, false, userId);
- } catch (RemoteException e) {
- // Intentionally left empty.
- }
- });
- }
- }
-
- /**
- * Updates the target packages' set of enabled overlays in PackageManager.
- */
- private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
- if (DEBUG) {
- Slog.d(TAG, "Updating overlay assets");
- }
- final PackageManagerInternal pm =
- LocalServices.getService(PackageManagerInternal.class);
- final boolean updateFrameworkRes = targetPackageNames.contains("android");
- if (updateFrameworkRes) {
- targetPackageNames = pm.getTargetPackageNames(userId);
- }
-
- final Map<String, List<String>> pendingChanges =
- new ArrayMap<>(targetPackageNames.size());
- synchronized (mLock) {
- final List<String> frameworkOverlays =
- mImpl.getEnabledOverlayPackageNames("android", userId);
- final int n = targetPackageNames.size();
- for (int i = 0; i < n; i++) {
- final String targetPackageName = targetPackageNames.get(i);
- List<String> list = new ArrayList<>();
- if (!"android".equals(targetPackageName)) {
- list.addAll(frameworkOverlays);
- }
- list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
- pendingChanges.put(targetPackageName, list);
- }
- }
-
- final HashSet<String> updatedPackages = new HashSet<>();
- final int n = targetPackageNames.size();
- for (int i = 0; i < n; i++) {
- final String targetPackageName = targetPackageNames.get(i);
- if (DEBUG) {
- Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
- + TextUtils.join(",", pendingChanges.get(targetPackageName))
- + "] userId=" + userId);
- }
-
- if (!pm.setEnabledOverlayPackages(
- userId, targetPackageName, pendingChanges.get(targetPackageName),
- updatedPackages)) {
- Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
- targetPackageName, userId));
- }
- }
- return new ArrayList<>(updatedPackages);
- } finally {
- traceEnd(TRACE_TAG_RRO);
- }
- }
-
- private void updateAssets(final int userId, final String targetPackageName) {
- updateAssets(userId, Collections.singletonList(targetPackageName));
- }
-
- private void updateAssets(final int userId, List<String> targetPackageNames) {
- final IActivityManager am = ActivityManager.getService();
- try {
- final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames);
- am.scheduleApplicationInfoChanged(updatedPaths, userId);
- } catch (RemoteException e) {
- // Intentionally left empty.
- }
- }
-
- private void schedulePersistSettings() {
- if (mPersistSettingsScheduled.getAndSet(true)) {
- return;
- }
- IoThread.getHandler().post(() -> {
- mPersistSettingsScheduled.set(false);
- if (DEBUG) {
- Slog.d(TAG, "Writing overlay settings");
- }
- synchronized (mLock) {
- FileOutputStream stream = null;
- try {
- stream = mSettingsFile.startWrite();
- mSettings.persist(stream);
- mSettingsFile.finishWrite(stream);
- } catch (IOException | XmlPullParserException e) {
- mSettingsFile.failWrite(stream);
- Slog.e(TAG, "failed to persist overlay state", e);
- }
- }
- });
- }
-
- private void restoreSettings() {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
- synchronized (mLock) {
- if (!mSettingsFile.getBaseFile().exists()) {
- return;
- }
- try (FileInputStream stream = mSettingsFile.openRead()) {
- mSettings.restore(stream);
-
- // We might have data for dying users if the device was
- // restarted before we received USER_REMOVED. Remove data for
- // users that will not exist after the system is ready.
-
- final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
- final int[] liveUserIds = new int[liveUsers.size()];
- for (int i = 0; i < liveUsers.size(); i++) {
- liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
- }
- Arrays.sort(liveUserIds);
-
- for (int userId : mSettings.getUsers()) {
- if (Arrays.binarySearch(liveUserIds, userId) < 0) {
- mSettings.removeUser(userId);
- }
- }
- } catch (IOException | XmlPullParserException e) {
- Slog.e(TAG, "failed to restore overlay state", e);
- }
- }
- } finally {
- traceEnd(TRACE_TAG_RRO);
- }
- }
-
- private static final class PackageManagerHelperImpl implements PackageManagerHelper {
+ private static final class PackageManagerHelperImpl implements PackageManagerHelper {
private final Context mContext;
private final IPackageManager mPackageManager;
@@ -1263,4 +1308,151 @@
}
}
}
+
+ // Helper methods to update other parts of the system or read/write
+ // settings: these methods should never call into each other!
+
+ private void broadcastActionOverlayChanged(@NonNull final Collection<String> packageNames,
+ final int userId) {
+ for (final String packageName : packageNames) {
+ broadcastActionOverlayChanged(packageName, userId);
+ }
+ }
+
+ private void broadcastActionOverlayChanged(@NonNull final String targetPackageName,
+ final int userId) {
+ final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
+ Uri.fromParts("package", targetPackageName, null));
+ intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ try {
+ ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null,
+ null, android.app.AppOpsManager.OP_NONE, null, false, false, userId);
+ } catch (RemoteException e) {
+ // Intentionally left empty.
+ }
+ }
+
+ /**
+ * Tell the activity manager to tell a set of packages to reload their
+ * resources.
+ */
+ private void updateActivityManager(List<String> targetPackageNames, final int userId) {
+ final IActivityManager am = ActivityManager.getService();
+ try {
+ am.scheduleApplicationInfoChanged(targetPackageNames, userId);
+ } catch (RemoteException e) {
+ // Intentionally left empty.
+ }
+ }
+
+ private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) {
+ return updatePackageManager(Collections.singletonList(targetPackageNames), userId);
+ }
+
+ /**
+ * Updates the target packages' set of enabled overlays in PackageManager.
+ * @return the package names of affected targets (a superset of
+ * targetPackageNames: the target themserlves and shared libraries)
+ */
+ private ArrayList<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
+ final int userId) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames);
+ if (DEBUG) {
+ Slog.d(TAG, "Update package manager about changed overlays");
+ }
+ final PackageManagerInternal pm =
+ LocalServices.getService(PackageManagerInternal.class);
+ final boolean updateFrameworkRes = targetPackageNames.contains("android");
+ if (updateFrameworkRes) {
+ targetPackageNames = pm.getTargetPackageNames(userId);
+ }
+
+ final Map<String, List<String>> pendingChanges =
+ new ArrayMap<>(targetPackageNames.size());
+ synchronized (mLock) {
+ final List<String> frameworkOverlays =
+ mImpl.getEnabledOverlayPackageNames("android", userId);
+ for (final String targetPackageName : targetPackageNames) {
+ List<String> list = new ArrayList<>();
+ if (!"android".equals(targetPackageName)) {
+ list.addAll(frameworkOverlays);
+ }
+ list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
+ pendingChanges.put(targetPackageName, list);
+ }
+ }
+
+ final HashSet<String> updatedPackages = new HashSet<>();
+ for (final String targetPackageName : targetPackageNames) {
+ if (DEBUG) {
+ Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
+ + TextUtils.join(",", pendingChanges.get(targetPackageName))
+ + "] userId=" + userId);
+ }
+
+ if (!pm.setEnabledOverlayPackages(
+ userId, targetPackageName, pendingChanges.get(targetPackageName),
+ updatedPackages)) {
+ Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
+ targetPackageName, userId));
+ }
+ }
+ return new ArrayList<>(updatedPackages);
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
+ }
+ }
+
+ private void persistSettings() {
+ if (DEBUG) {
+ Slog.d(TAG, "Writing overlay settings");
+ }
+ synchronized (mLock) {
+ FileOutputStream stream = null;
+ try {
+ stream = mSettingsFile.startWrite();
+ mSettings.persist(stream);
+ mSettingsFile.finishWrite(stream);
+ } catch (IOException | XmlPullParserException e) {
+ mSettingsFile.failWrite(stream);
+ Slog.e(TAG, "failed to persist overlay state", e);
+ }
+ }
+ }
+
+ private void restoreSettings() {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
+ synchronized (mLock) {
+ if (!mSettingsFile.getBaseFile().exists()) {
+ return;
+ }
+ try (FileInputStream stream = mSettingsFile.openRead()) {
+ mSettings.restore(stream);
+
+ // We might have data for dying users if the device was
+ // restarted before we received USER_REMOVED. Remove data for
+ // users that will not exist after the system is ready.
+
+ final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
+ final int[] liveUserIds = new int[liveUsers.size()];
+ for (int i = 0; i < liveUsers.size(); i++) {
+ liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
+ }
+ Arrays.sort(liveUserIds);
+
+ for (int userId : mSettings.getUsers()) {
+ if (Arrays.binarySearch(liveUserIds, userId) < 0) {
+ mSettings.removeUser(userId);
+ }
+ }
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "failed to restore overlay state", e);
+ }
+ }
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 05a4a38..e60411bb 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -45,6 +45,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
/**
@@ -71,7 +72,6 @@
private final OverlayManagerSettings mSettings;
private final OverlayConfig mOverlayConfig;
private final String[] mDefaultOverlays;
- private final OverlayChangeListener mListener;
/**
* Helper method to merge the overlay manager's (as read from overlays.xml)
@@ -114,14 +114,12 @@
@NonNull final IdmapManager idmapManager,
@NonNull final OverlayManagerSettings settings,
@NonNull final OverlayConfig overlayConfig,
- @NonNull final String[] defaultOverlays,
- @NonNull final OverlayChangeListener listener) {
+ @NonNull final String[] defaultOverlays) {
mPackageManager = packageManager;
mIdmapManager = idmapManager;
mSettings = settings;
mOverlayConfig = overlayConfig;
mDefaultOverlays = defaultOverlays;
- mListener = listener;
}
/**
@@ -259,52 +257,58 @@
mSettings.removeUser(userId);
}
- void onTargetPackageAdded(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onTargetPackageAdded(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
}
- updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
- void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onTargetPackageChanged(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
}
- updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
- void onTargetPackageReplacing(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onTargetPackageReplacing(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId="
+ userId);
}
- updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
- void onTargetPackageReplaced(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onTargetPackageReplaced(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId);
}
- updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
- void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onTargetPackageRemoved(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
}
- updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
/**
* Update the state of any overlays for this target.
*/
- private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName,
- final int userId, final int flags) {
+ private Optional<PackageAndUser> updateAndRefreshOverlaysForTarget(
+ @NonNull final String targetPackageName, final int userId, final int flags)
+ throws OperationFailedException {
final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName,
userId);
@@ -364,11 +368,13 @@
}
if (modified) {
- mListener.onOverlaysChanged(targetPackageName, userId);
+ return Optional.of(new PackageAndUser(targetPackageName, userId));
}
+ return Optional.empty();
}
- void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onOverlayPackageAdded(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
}
@@ -376,8 +382,7 @@
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
- onOverlayPackageRemoved(packageName, userId);
- return;
+ return onOverlayPackageRemoved(packageName, userId);
}
mSettings.init(packageName, userId, overlayPackage.overlayTarget,
@@ -389,15 +394,17 @@
overlayPackage.overlayCategory);
try {
if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
- mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
}
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to update settings", e);
mSettings.remove(packageName, userId);
+ throw new OperationFailedException("failed to update settings", e);
}
}
- void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onOverlayPackageChanged(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
}
@@ -405,14 +412,16 @@
try {
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
if (updateState(oi.targetPackageName, packageName, userId, 0)) {
- mListener.onOverlaysChanged(oi.targetPackageName, userId);
+ return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
}
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to update settings", e);
+ throw new OperationFailedException("failed to update settings", e);
}
}
- void onOverlayPackageReplacing(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onOverlayPackageReplacing(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId="
+ userId);
@@ -423,14 +432,16 @@
if (updateState(oi.targetPackageName, packageName, userId,
FLAG_OVERLAY_IS_BEING_REPLACED)) {
removeIdmapIfPossible(oi);
- mListener.onOverlaysChanged(oi.targetPackageName, userId);
+ return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
}
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to update settings", e);
+ throw new OperationFailedException("failed to update settings", e);
}
}
- void onOverlayPackageReplaced(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onOverlayPackageReplaced(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId="
+ userId);
@@ -439,16 +450,12 @@
final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
if (pkg == null) {
Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found");
- onOverlayPackageRemoved(packageName, userId);
- return;
+ return onOverlayPackageRemoved(packageName, userId);
}
try {
final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
if (mustReinitializeOverlay(pkg, oldOi)) {
- if (oldOi != null && !oldOi.targetPackageName.equals(pkg.overlayTarget)) {
- mListener.onOverlaysChanged(pkg.overlayTarget, userId);
- }
mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName,
pkg.applicationInfo.getBaseCodePath(),
isPackageConfiguredMutable(pkg.packageName),
@@ -457,22 +464,25 @@
}
if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
- mListener.onOverlaysChanged(pkg.overlayTarget, userId);
+ return Optional.of(new PackageAndUser(pkg.overlayTarget, userId));
}
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to update settings", e);
+ throw new OperationFailedException("failed to update settings", e);
}
}
- void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onOverlayPackageRemoved(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
try {
final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId);
if (mSettings.remove(packageName, userId)) {
removeIdmapIfPossible(overlayInfo);
- mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId);
+ return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
}
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to remove overlay", e);
+ throw new OperationFailedException("failed to remove overlay", e);
}
}
@@ -493,8 +503,8 @@
return mSettings.getOverlaysForUser(userId);
}
- boolean setEnabled(@NonNull final String packageName, final boolean enable,
- final int userId) {
+ Optional<PackageAndUser> setEnabled(@NonNull final String packageName, final boolean enable,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
packageName, enable, userId));
@@ -502,30 +512,33 @@
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
- return false;
+ throw new OperationFailedException(
+ String.format("failed to find overlay package %s for user %d",
+ packageName, userId));
}
try {
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
if (!oi.isMutable) {
// Ignore immutable overlays.
- return false;
+ throw new OperationFailedException(
+ "cannot enable immutable overlay packages in runtime");
}
boolean modified = mSettings.setEnabled(packageName, userId, enable);
modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
if (modified) {
- mListener.onOverlaysChanged(oi.targetPackageName, userId);
+ return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
}
- return true;
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- return false;
+ throw new OperationFailedException("failed to update settings", e);
}
}
- boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory,
- final int userId) {
+ Optional<PackageAndUser> setEnabledExclusive(@NonNull final String packageName,
+ boolean withinCategory, final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, String.format("setEnabledExclusive packageName=%s"
+ " withinCategory=%s userId=%d", packageName, withinCategory, userId));
@@ -533,7 +546,8 @@
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
- return false;
+ throw new OperationFailedException(String.format(
+ "failed to find overlay package %s for user %d", packageName, userId));
}
try {
@@ -576,11 +590,11 @@
modified |= updateState(targetPackageName, packageName, userId, 0);
if (modified) {
- mListener.onOverlaysChanged(targetPackageName, userId);
+ return Optional.of(new PackageAndUser(targetPackageName, userId));
}
- return true;
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- return false;
+ throw new OperationFailedException("failed to update settings", e);
}
}
@@ -596,66 +610,75 @@
return mOverlayConfig.isEnabled(packageName);
}
- boolean setPriority(@NonNull final String packageName,
- @NonNull final String newParentPackageName, final int userId) {
+ Optional<PackageAndUser> setPriority(@NonNull final String packageName,
+ @NonNull final String newParentPackageName, final int userId)
+ throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
+ newParentPackageName + " userId=" + userId);
}
if (!isPackageConfiguredMutable(packageName)) {
- return false;
+ throw new OperationFailedException(String.format(
+ "overlay package %s user %d is not updatable", packageName, userId));
}
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
- return false;
+ throw new OperationFailedException(String.format(
+ "failed to find overlay package %s for user %d", packageName, userId));
}
if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
- mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
}
- return true;
+ return Optional.empty();
}
- boolean setHighestPriority(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> setHighestPriority(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
}
if (!isPackageConfiguredMutable(packageName)) {
- return false;
+ throw new OperationFailedException(String.format(
+ "overlay package %s user %d is not updatable", packageName, userId));
}
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
- return false;
+ throw new OperationFailedException(String.format(
+ "failed to find overlay package %s for user %d", packageName, userId));
}
if (mSettings.setHighestPriority(packageName, userId)) {
- mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
}
- return true;
+ return Optional.empty();
}
- boolean setLowestPriority(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> setLowestPriority(@NonNull final String packageName, final int userId)
+ throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
}
if (!isPackageConfiguredMutable(packageName)) {
- return false;
+ throw new OperationFailedException(String.format(
+ "overlay package %s user %d is not updatable", packageName, userId));
}
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
- return false;
+ throw new OperationFailedException(String.format(
+ "failed to find overlay package %s for user %d", packageName, userId));
}
if (mSettings.setLowestPriority(packageName, userId)) {
- mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
}
- return true;
+ return Optional.empty();
}
void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
@@ -797,12 +820,13 @@
mIdmapManager.removeIdmap(oi, oi.userId);
}
- interface OverlayChangeListener {
+ static final class OperationFailedException extends Exception {
+ OperationFailedException(@NonNull final String message) {
+ super(message);
+ }
- /**
- * An event triggered by changes made to overlay state or settings as well as changes that
- * add or remove target packages of overlays.
- **/
- void onOverlaysChanged(@NonNull String targetPackage, int userId);
+ OperationFailedException(@NonNull final String message, @NonNull Throwable cause) {
+ super(message, cause);
+ }
}
}
diff --git a/services/core/java/com/android/server/om/PackageAndUser.java b/services/core/java/com/android/server/om/PackageAndUser.java
new file mode 100644
index 0000000..5c38ba7
--- /dev/null
+++ b/services/core/java/com/android/server/om/PackageAndUser.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+
+final class PackageAndUser {
+ public final @NonNull String packageName;
+ public final @UserIdInt int userId;
+
+ PackageAndUser(@NonNull String packageName, @UserIdInt int userId) {
+ this.packageName = packageName;
+ this.userId = userId;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof PackageAndUser)) {
+ return false;
+ }
+ PackageAndUser other = (PackageAndUser) obj;
+ return packageName.equals(other.packageName) && userId == other.userId;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + packageName.hashCode();
+ result = prime * result + userId;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("PackageAndUser{packageName=%s, userId=%d}", packageName, userId);
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b5c5bb5..59b24f8 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -910,6 +910,9 @@
mActivityManagerService.setSystemProcess();
t.traceEnd();
+ // The package receiver depends on the activity service in order to get registered.
+ platformCompat.registerPackageReceiver(mSystemContext);
+
// Complete the watchdog setup with an ActivityManager instance and listen for reboots
// Do this only after the ActivityManagerService is properly started as a system process
t.traceBegin("InitWatchdog");
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index 870fe4a..4f4aa3f 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -117,6 +117,7 @@
CompatConfig build() {
CompatConfig config = new CompatConfig(mBuildClassifier, mContext);
+ config.forceNonDebuggableFinalForTest(false);
for (CompatChange change : mChanges) {
config.addChange(change);
}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 8c63bfc..ac8dc34 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -28,6 +28,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import androidx.test.runner.AndroidJUnit4;
@@ -81,6 +82,8 @@
@Test
public void testUnknownChangeEnabled() throws Exception {
CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+ compatConfig.forceNonDebuggableFinalForTest(false);
+
assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create().build()))
.isTrue();
}
@@ -180,6 +183,8 @@
@Test
public void testPackageOverrideUnknownPackage() throws Exception {
CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+ compatConfig.forceNonDebuggableFinalForTest(false);
+
compatConfig.addOverride(1234L, "com.some.package", false);
@@ -230,6 +235,83 @@
}
@Test
+ public void testApplyDeferredOverridesAfterInstallingApp() throws Exception {
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.notinstalled.foo")
+ .debuggable().build();
+ when(mPackageManager.getApplicationInfo(eq("com.notinstalled.foo"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L).build();
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+ // Add override before the app is available.
+ compatConfig.addOverride(1234L, "com.notinstalled.foo", true);
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
+
+ // Pretend the app is now installed.
+ when(mPackageManager.getApplicationInfo(eq("com.notinstalled.foo"), anyInt()))
+ .thenReturn(applicationInfo);
+
+ compatConfig.recheckOverrides("com.notinstalled.foo");
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+ }
+
+ @Test
+ public void testApplyDeferredOverrideClearsOverrideAfterUninstall() throws Exception {
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.installedapp.foo")
+ .debuggable().build();
+ when(mPackageManager.getApplicationInfo(eq("com.installedapp.foo"), anyInt()))
+ .thenReturn(applicationInfo);
+
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L).build();
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+ // Add override when app is installed.
+ compatConfig.addOverride(1234L, "com.installedapp.foo", true);
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+
+ // Pretend the app is now uninstalled.
+ when(mPackageManager.getApplicationInfo(eq("com.installedapp.foo"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ compatConfig.recheckOverrides("com.installedapp.foo");
+ assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
+ }
+
+ @Test
+ public void testApplyDeferredOverrideClearsOverrideAfterChange() throws Exception {
+ ApplicationInfo debuggableApp = ApplicationInfoBuilder.create()
+ .withPackageName("com.installedapp.foo")
+ .debuggable().build();
+ ApplicationInfo releaseApp = ApplicationInfoBuilder.create()
+ .withPackageName("com.installedapp.foo")
+ .build();
+ when(mPackageManager.getApplicationInfo(eq("com.installedapp.foo"), anyInt()))
+ .thenReturn(debuggableApp);
+
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1234L).build();
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+ // Add override for debuggable app.
+ compatConfig.addOverride(1234L, "com.installedapp.foo", true);
+ assertThat(compatConfig.isChangeEnabled(1234L, debuggableApp)).isTrue();
+
+ // Pretend the app now is no longer debuggable, but has the same package.
+ when(mPackageManager.getApplicationInfo(eq("com.installedapp.foo"), anyInt()))
+ .thenReturn(releaseApp);
+
+ compatConfig.recheckOverrides("com.installedapp.foo");
+ assertThat(compatConfig.isChangeEnabled(1234L, releaseApp)).isFalse();
+ }
+
+ @Test
public void testLoggingOnlyChangePreventAddOverride() throws Exception {
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
.addLoggingOnlyChangeWithId(1234L)
@@ -259,7 +341,7 @@
// Reject all override attempts.
// Force the validator to prevent overriding the change by using a user build.
when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
- when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(false);
// Try to turn off change, but validator prevents it.
assertThrows(SecurityException.class,
() -> compatConfig.removeOverride(1234L, "com.some.package"));
@@ -360,6 +442,8 @@
@Test
public void testLookupChangeIdNotPresent() throws Exception {
CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+ compatConfig.forceNonDebuggableFinalForTest(false);
+
assertThat(compatConfig.lookupChangeId("MY_CHANGE")).isEqualTo(-1L);
}
@@ -374,6 +458,8 @@
File dir = createTempDir();
writeToFile(dir, "platform_compat_config.xml", configXml);
CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+ compatConfig.forceNonDebuggableFinalForTest(false);
+
compatConfig.initConfigFromLib(dir);
assertThat(compatConfig.isChangeEnabled(1234L,
@@ -400,6 +486,8 @@
writeToFile(dir, "libcore_platform_compat_config.xml", configXml1);
writeToFile(dir, "frameworks_platform_compat_config.xml", configXml2);
CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+ compatConfig.forceNonDebuggableFinalForTest(false);
+
compatConfig.initConfigFromLib(dir);
assertThat(compatConfig.isChangeEnabled(1234L,
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
index c53b29a..0fd6445 100644
--- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -17,6 +17,7 @@
package com.android.server.compat;
import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
+import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
@@ -31,6 +32,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import androidx.test.runner.AndroidJUnit4;
@@ -409,4 +411,216 @@
assertThat(stateDLoggingOnlyChange)
.isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1));
}
+ @Test
+ public void getOverrideAllowedState_finalBuildAnyChangeNotInstalledApp_deferOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
+ .addEnabledChangeWithId(4)
+ .addDisabledChangeWithId(5)
+ .addLoggingOnlyChangeWithId(6).build();
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkEqualChange =
+ overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkAfterChange =
+ overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+ OverrideAllowedState stateEnabledChange =
+ overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+ OverrideAllowedState stateDisabledChange =
+ overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+ OverrideAllowedState stateDLoggingOnlyChange =
+ overrideValidator.getOverrideAllowedState(6, PACKAGE_NAME);
+
+ assertThat(stateTargetSdkLessChange)
+ .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+ assertThat(stateTargetSdkEqualChange)
+ .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+ assertThat(stateTargetSdkAfterChange)
+ .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+ assertThat(stateEnabledChange)
+ .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+ assertThat(stateDisabledChange)
+ .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+ assertThat(stateDLoggingOnlyChange)
+ .isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1));
+ }
+
+ @Test
+ public void getOverrideAllowedState_forceFinalBuildTargetSdkChangeDebugAppOptin_allowOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 1)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK, 2).build();
+ config.forceNonDebuggableFinalForTest(true);
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .debuggable()
+ .withTargetSdk(TARGET_SDK)
+ .withPackageName(PACKAGE_NAME).build());
+
+ OverrideAllowedState stateTargetSdkGreaterChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkEqualChange =
+ overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+
+ assertThat(stateTargetSdkGreaterChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER));
+
+ assertThat(stateTargetSdkEqualChange)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK));
+ }
+
+ @Test
+ public void getOverrideAllowedState_forceFinalBldTargetSdkChangeDebugAppOptout_rejectOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1).build();
+ config.forceNonDebuggableFinalForTest(true);
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .withTargetSdk(TARGET_SDK)
+ .debuggable()
+ .build());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+ assertThat(stateTargetSdkLessChange).isEqualTo(
+ new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK,
+ TARGET_SDK_BEFORE));
+ }
+
+ @Test
+ public void getOverrideAllowedState_forceFinalBuildEnabledChangeDebugApp_rejectOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+ .addEnabledChangeWithId(1).build();
+ config.forceNonDebuggableFinalForTest(true);
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .debuggable().build());
+
+ OverrideAllowedState allowedState =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+ assertThat(allowedState)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1));
+ }
+
+ @Test
+ public void getOverrideAllowedState_forceFinalBuildDisabledChangeDebugApp_allowOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+ .addDisabledChangeWithId(1).build();
+ config.forceNonDebuggableFinalForTest(true);
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .withTargetSdk(TARGET_SDK)
+ .debuggable().build());
+
+ OverrideAllowedState allowedState =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+ assertThat(allowedState)
+ .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, -1));
+ }
+
+ @Test
+ public void getOverrideAllowedState_forceFinalBuildAnyChangeReleaseApp_rejectOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
+ .addEnabledChangeWithId(4)
+ .addDisabledChangeWithId(5)
+ .addLoggingOnlyChangeWithId(6).build();
+ config.forceNonDebuggableFinalForTest(true);
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .withTargetSdk(TARGET_SDK).build());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkEqualChange =
+ overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkAfterChange =
+ overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+ OverrideAllowedState stateEnabledChange =
+ overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+ OverrideAllowedState stateDisabledChange =
+ overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+ OverrideAllowedState stateDLoggingOnlyChange =
+ overrideValidator.getOverrideAllowedState(6, PACKAGE_NAME);
+
+ assertThat(stateTargetSdkLessChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateTargetSdkEqualChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateTargetSdkAfterChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateEnabledChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateDisabledChange)
+ .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+ assertThat(stateDLoggingOnlyChange)
+ .isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1));
+ }
+ @Test
+ public void getOverrideAllowedState_forceFinalBuildAnyChangeNotInstalledApp_deferOverride()
+ throws Exception {
+ CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+ .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
+ .addEnabledChangeWithId(4)
+ .addDisabledChangeWithId(5)
+ .addLoggingOnlyChangeWithId(6).build();
+ config.forceNonDebuggableFinalForTest(true);
+ IOverrideValidator overrideValidator = config.getOverrideValidator();
+ when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ OverrideAllowedState stateTargetSdkLessChange =
+ overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkEqualChange =
+ overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+ OverrideAllowedState stateTargetSdkAfterChange =
+ overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+ OverrideAllowedState stateEnabledChange =
+ overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+ OverrideAllowedState stateDisabledChange =
+ overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+ OverrideAllowedState stateDLoggingOnlyChange =
+ overrideValidator.getOverrideAllowedState(6, PACKAGE_NAME);
+
+ assertThat(stateTargetSdkLessChange)
+ .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+ assertThat(stateTargetSdkEqualChange)
+ .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+ assertThat(stateTargetSdkAfterChange)
+ .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+ assertThat(stateEnabledChange)
+ .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+ assertThat(stateDisabledChange)
+ .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+ assertThat(stateDLoggingOnlyChange)
+ .isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 1d3b643..3f65a46 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -73,6 +73,7 @@
mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
// Assume userdebug/eng non-final build
+ mCompatConfig.forceNonDebuggableFinalForTest(false);
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
index 391611b..5468fba 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
@@ -78,7 +78,7 @@
}
@Test
- public void testImmutableEnabledChange() {
+ public void testImmutableEnabledChange() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -106,7 +106,7 @@
}
@Test
- public void testMutableEnabledChangeHasNoEffect() {
+ public void testMutableEnabledChangeHasNoEffect() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -134,7 +134,7 @@
}
@Test
- public void testMutableEnabledToImmutableEnabled() {
+ public void testMutableEnabledToImmutableEnabled() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -178,7 +178,7 @@
}
@Test
- public void testMutablePriorityChange() {
+ public void testMutablePriorityChange() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -218,7 +218,7 @@
}
@Test
- public void testImmutablePriorityChange() {
+ public void testImmutablePriorityChange() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index 4f882ce..33dbcc0 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -22,11 +22,14 @@
import static android.os.OverlayablePolicy.CONFIG_SIGNATURE;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
import android.content.om.OverlayInfo;
+import android.util.Pair;
import androidx.test.runner.AndroidJUnit4;
@@ -35,6 +38,7 @@
import java.util.List;
import java.util.Map;
+import java.util.Optional;
@RunWith(AndroidJUnit4.class)
public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
@@ -55,7 +59,7 @@
private static final String CERT_CONFIG_NOK = "config_certificate_nok";
@Test
- public void testGetOverlayInfo() {
+ public void testGetOverlayInfo() throws Exception {
installNewPackage(overlay(OVERLAY, TARGET), USER);
final OverlayManagerServiceImpl impl = getImpl();
@@ -67,7 +71,7 @@
}
@Test
- public void testGetOverlayInfosForTarget() {
+ public void testGetOverlayInfosForTarget() throws Exception {
installNewPackage(overlay(OVERLAY, TARGET), USER);
installNewPackage(overlay(OVERLAY2, TARGET), USER);
installNewPackage(overlay(OVERLAY3, TARGET), USER2);
@@ -92,7 +96,7 @@
}
@Test
- public void testGetOverlayInfosForUser() {
+ public void testGetOverlayInfosForUser() throws Exception {
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
installNewPackage(overlay(OVERLAY2, TARGET), USER);
@@ -119,7 +123,7 @@
}
@Test
- public void testPriority() {
+ public void testPriority() throws Exception {
installNewPackage(overlay(OVERLAY, TARGET), USER);
installNewPackage(overlay(OVERLAY2, TARGET), USER);
installNewPackage(overlay(OVERLAY3, TARGET), USER);
@@ -131,18 +135,21 @@
assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
- assertTrue(impl.setLowestPriority(OVERLAY3, USER));
+ assertEquals(impl.setLowestPriority(OVERLAY3, USER),
+ Optional.of(new PackageAndUser(TARGET, USER)));
assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2);
- assertTrue(impl.setHighestPriority(OVERLAY3, USER));
+ assertEquals(impl.setHighestPriority(OVERLAY3, USER),
+ Optional.of(new PackageAndUser(TARGET, USER)));
assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
- assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER));
+ assertEquals(impl.setPriority(OVERLAY, OVERLAY2, USER),
+ Optional.of(new PackageAndUser(TARGET, USER)));
assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3);
}
@Test
- public void testOverlayInfoStateTransitions() {
+ public void testOverlayInfoStateTransitions() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
assertNull(impl.getOverlayInfo(OVERLAY, USER));
@@ -153,7 +160,8 @@
installNewPackage(target, USER);
assertState(STATE_DISABLED, OVERLAY, USER);
- impl.setEnabled(OVERLAY, true, USER);
+ assertEquals(impl.setEnabled(OVERLAY, true, USER),
+ Optional.of(new PackageAndUser(TARGET, USER)));
assertState(STATE_ENABLED, OVERLAY, USER);
// target upgrades do not change the state of the overlay
@@ -168,50 +176,40 @@
}
@Test
- public void testOnOverlayPackageUpgraded() {
- final FakeListener listener = getListener();
+ public void testOnOverlayPackageUpgraded() throws Exception {
final FakeDeviceState.PackageBuilder target = target(TARGET);
final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
installNewPackage(target, USER);
installNewPackage(overlay, USER);
- listener.count = 0;
upgradePackage(overlay, USER);
- assertEquals(2, listener.count);
// upgrade to a version where the overlay has changed its target
- // expect once for the old target package, once for the new target package
- listener.count = 0;
final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
- upgradePackage(overlay2, USER);
- assertEquals(3, listener.count);
-
- listener.count = 0;
- upgradePackage(overlay2, USER);
- assertEquals(2, listener.count);
+ final Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> pair =
+ upgradePackage(overlay2, USER);
+ assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER)));
+ assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER)));
}
@Test
- public void testListener() {
+ public void testSetEnabledAtVariousConditions() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
- final FakeListener listener = getListener();
- installNewPackage(overlay(OVERLAY, TARGET), USER);
- assertEquals(1, listener.count);
- listener.count = 0;
+ assertThrows(OverlayManagerServiceImpl.OperationFailedException.class,
+ () -> impl.setEnabled(OVERLAY, true, USER));
+ // request succeeded, and there was a change that needs to be
+ // propagated to the rest of the system
installNewPackage(target(TARGET), USER);
- assertEquals(1, listener.count);
- listener.count = 0;
+ installNewPackage(overlay(OVERLAY, TARGET), USER);
+ assertEquals(impl.setEnabled(OVERLAY, true, USER),
+ Optional.of(new PackageAndUser(TARGET, USER)));
- impl.setEnabled(OVERLAY, true, USER);
- assertEquals(1, listener.count);
- listener.count = 0;
-
- impl.setEnabled(OVERLAY, true, USER);
- assertEquals(0, listener.count);
+ // request succeeded, but nothing changed
+ assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent());
}
@Test
- public void testConfigSignaturePolicyOk() {
+ public void testConfigSignaturePolicyOk() throws Exception {
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
reinitializeImpl();
@@ -229,7 +227,7 @@
}
@Test
- public void testConfigSignaturePolicyCertNok() {
+ public void testConfigSignaturePolicyCertNok() throws Exception {
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
reinitializeImpl();
@@ -247,7 +245,7 @@
}
@Test
- public void testConfigSignaturePolicyNoConfig() {
+ public void testConfigSignaturePolicyNoConfig() throws Exception {
addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
@@ -262,7 +260,7 @@
}
@Test
- public void testConfigSignaturePolicyNoRefPkg() {
+ public void testConfigSignaturePolicyNoRefPkg() throws Exception {
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
@@ -276,7 +274,7 @@
}
@Test
- public void testConfigSignaturePolicyRefPkgNotSystem() {
+ public void testConfigSignaturePolicyRefPkgNotSystem() throws Exception {
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
reinitializeImpl();
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index 006dda0..2c477c8 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -16,6 +16,8 @@
package com.android.server.om;
+import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
@@ -30,6 +32,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Pair;
import androidx.annotation.Nullable;
@@ -43,13 +46,13 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.stream.Collectors;
/** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
class OverlayManagerServiceImplTestsBase {
private OverlayManagerServiceImpl mImpl;
private FakeDeviceState mState;
- private FakeListener mListener;
private FakePackageManagerHelper mPackageManager;
private FakeIdmapDaemon mIdmapDaemon;
private OverlayConfig mOverlayConfig;
@@ -58,7 +61,6 @@
@Before
public void setUp() {
mState = new FakeDeviceState();
- mListener = new FakeListener();
mPackageManager = new FakePackageManagerHelper(mState);
mIdmapDaemon = new FakeIdmapDaemon(mState);
mOverlayConfig = mock(OverlayConfig.class);
@@ -73,18 +75,13 @@
new IdmapManager(mIdmapDaemon, mPackageManager),
new OverlayManagerSettings(),
mOverlayConfig,
- new String[0],
- mListener);
+ new String[0]);
}
OverlayManagerServiceImpl getImpl() {
return mImpl;
}
- FakeListener getListener() {
- return mListener;
- }
-
FakeIdmapDaemon getIdmapd() {
return mIdmapDaemon;
}
@@ -155,7 +152,8 @@
*
* @throws IllegalStateException if the package is currently installed
*/
- void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
+ void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId)
+ throws OperationFailedException {
if (mState.select(pkg.packageName, userId) != null) {
throw new IllegalStateException("package " + pkg.packageName + " already installed");
}
@@ -176,23 +174,30 @@
* {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the
* {@link android.content.Intent#EXTRA_REPLACING} extra.
*
+ * @return the two Optional<PackageAndUser> objects from starting and finishing the upgrade
+ *
* @throws IllegalStateException if the package is not currently installed
*/
- void upgradePackage(FakeDeviceState.PackageBuilder pkg, int userId) {
+ Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> upgradePackage(
+ FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException {
final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
if (replacedPackage == null) {
throw new IllegalStateException("package " + pkg.packageName + " not installed");
}
+ Optional<PackageAndUser> opt1 = Optional.empty();
if (replacedPackage.targetPackageName != null) {
- mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
+ opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
}
mState.add(pkg, userId);
+ Optional<PackageAndUser> opt2;
if (pkg.targetPackage == null) {
- mImpl.onTargetPackageReplaced(pkg.packageName, userId);
+ opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId);
} else {
- mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
+ opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
}
+
+ return Pair.create(opt1, opt2);
}
/**
@@ -203,7 +208,7 @@
*
* @throws IllegalStateException if the package is not currently installed
*/
- void uninstallPackage(String packageName, int userId) {
+ void uninstallPackage(String packageName, int userId) throws OperationFailedException {
final FakeDeviceState.Package pkg = mState.select(packageName, userId);
if (pkg == null) {
throw new IllegalStateException("package " + packageName+ " not installed");
@@ -485,12 +490,4 @@
}
}
}
-
- static class FakeListener implements OverlayManagerServiceImpl.OverlayChangeListener {
- public int count;
-
- public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
- count++;
- }
- }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7d3cef5..c05e90b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8679,6 +8679,77 @@
return Collections.EMPTY_LIST;
}
+ /**
+ * Call composer status OFF from user setting.
+ */
+ public static final int CALL_COMPOSER_STATUS_OFF = 0;
+
+ /**
+ * Call composer status ON from user setting.
+ */
+ public static final int CALL_COMPOSER_STATUS_ON = 1;
+
+ /** @hide */
+ @IntDef(prefix = {"CALL_COMPOSER_STATUS_"},
+ value = {
+ CALL_COMPOSER_STATUS_ON,
+ CALL_COMPOSER_STATUS_OFF,
+ })
+ public @interface CallComposerStatus {}
+
+ /**
+ * Set the user-set status for enriched calling with call composer.
+ *
+ * @param status user-set status for enriched calling with call composer;
+ * it must be a value of either {@link #CALL_COMPOSER_STATUS_ON}
+ * or {@link #CALL_COMPOSER_STATUS_OFF}.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * @throws IllegalArgumentException if requested state is invalid.
+ * @throws SecurityException if the caller does not have the permission.
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setCallComposerStatus(@CallComposerStatus int status) {
+ if (status != CALL_COMPOSER_STATUS_ON && status != CALL_COMPOSER_STATUS_OFF) {
+ throw new IllegalArgumentException("requested status is invalid");
+ }
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setCallComposerStatus(getSubId(), status);
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Error calling ITelephony#setCallComposerStatus", ex);
+ ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the user-set status for enriched calling with call composer.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * @throws SecurityException if the caller does not have the permission.
+ *
+ * @return the user-set status for enriched calling with call composer either
+ * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @CallComposerStatus int getCallComposerStatus() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getCallComposerStatus(getSubId());
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Error calling ITelephony#getCallComposerStatus", ex);
+ ex.rethrowFromSystemServer();
+ }
+ return CALL_COMPOSER_STATUS_OFF;
+ }
/** @hide */
@SystemApi
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index b0aaa92..e4d20e9 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -39,6 +39,10 @@
/** The service id of the MMTEL */
public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
+ /** The service id of the Call Composer */
+ public static final String SERVICE_ID_CALL_COMPOSER =
+ "org.3gpp.urn:urn-7:3gppservice.ims.icsi.gsma.callcomposer";
+
/** The service capabilities is available. */
public static final String TUPLE_BASIC_STATUS_OPEN = "open";
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 33acc15..c60a44c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -128,6 +128,15 @@
*/
boolean isRadioOnForSubscriberWithFeature(int subId, String callingPackage, String callingFeatureId);
+ /**
+ * Set the user-set status for enriched calling with call composer.
+ */
+ void setCallComposerStatus(int subId, int status);
+
+ /**
+ * Get the user-set status for enriched calling with call composer.
+ */
+ int getCallComposerStatus(int subId);
/**
* Supply a pin to unlock the SIM for particular subId.
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 70f6386..8e18751 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -25,7 +25,6 @@
import android.net.ConnectivityManager
import android.net.IDnsResolver
import android.net.INetd
-import android.net.INetworkPolicyManager
import android.net.INetworkStatsService
import android.net.LinkProperties
import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
@@ -88,8 +87,6 @@
@Mock
private lateinit var statsService: INetworkStatsService
@Mock
- private lateinit var policyManager: INetworkPolicyManager
- @Mock
private lateinit var log: IpConnectivityLog
@Mock
private lateinit var netd: INetd
@@ -171,7 +168,7 @@
}
private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService(
- context, netManager, statsService, policyManager, dnsResolver, log, netd, deps)
+ context, netManager, statsService, dnsResolver, log, netd, deps)
private fun makeDependencies(): ConnectivityService.Dependencies {
val deps = spy(ConnectivityService.Dependencies())
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 5b6f637..9421acd 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -166,7 +166,6 @@
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
-import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
@@ -183,6 +182,7 @@
import android.net.NetworkFactory;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkPolicyManager;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
@@ -299,6 +299,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import kotlin.reflect.KClass;
@@ -365,7 +366,6 @@
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
@Mock IBatteryStats mBatteryStatsService;
- @Mock INetworkPolicyManager mNpm;
@Mock IDnsResolver mMockDnsResolver;
@Mock INetd mMockNetd;
@Mock NetworkStackClient mNetworkStack;
@@ -380,6 +380,7 @@
@Mock TelephonyManager mTelephonyManager;
@Mock MockableSystemProperties mSystemProperties;
@Mock EthernetManager mEthernetManager;
+ @Mock NetworkPolicyManager mNetworkPolicyManager;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -412,6 +413,7 @@
@Spy private Resources mResources;
private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
+
// Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
@@ -477,6 +479,7 @@
if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager;
+ if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager;
return super.getSystemService(name);
}
@@ -1326,7 +1329,6 @@
mService = new ConnectivityService(mServiceContext,
mNetworkManagementService,
mStatsService,
- mNpm,
mMockDnsResolver,
mock(IpConnectivityLog.class),
mMockNetd,
@@ -1336,7 +1338,7 @@
final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
ArgumentCaptor.forClass(INetworkPolicyListener.class);
- verify(mNpm).registerListener(policyListenerCaptor.capture());
+ verify(mNetworkPolicyManager).registerListener(policyListenerCaptor.capture());
mPolicyListener = policyListenerCaptor.getValue();
// Create local CM before sending system ready so that we can answer
@@ -6509,6 +6511,26 @@
checkNetworkInfo(mCm.getNetworkInfo(type), type, state);
}
+ // Checks that each of the |agents| receive a blocked status change callback with the specified
+ // |blocked| value, in any order. This is needed because when an event affects multiple
+ // networks, ConnectivityService does not guarantee the order in which callbacks are fired.
+ private void assertBlockedCallbackInAnyOrder(TestNetworkCallback callback, boolean blocked,
+ TestNetworkAgentWrapper... agents) {
+ final List<Network> expectedNetworks = Arrays.asList(agents).stream()
+ .map((agent) -> agent.getNetwork())
+ .collect(Collectors.toList());
+
+ // Expect exactly one blocked callback for each agent.
+ for (int i = 0; i < agents.length; i++) {
+ CallbackEntry e = callback.expectCallbackThat(TIMEOUT_MS, (c) ->
+ c instanceof CallbackEntry.BlockedStatus
+ && ((CallbackEntry.BlockedStatus) c).getBlocked() == blocked);
+ Network network = e.getNetwork();
+ assertTrue("Received unexpected blocked callback for network " + network,
+ expectedNetworks.remove(network));
+ }
+ }
+
@Test
public void testNetworkBlockedStatusAlwaysOnVpn() throws Exception {
mServiceContext.setPermission(
@@ -6555,9 +6577,10 @@
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
// Disable lockdown, expect to see the network unblocked.
- // There are no callbacks because they are not implemented yet.
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
+ callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
+ defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
vpnUidCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -6605,6 +6628,8 @@
allowList.clear();
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
+ defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
+ assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
vpnUidCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertNull(mCm.getActiveNetwork());
@@ -6614,6 +6639,8 @@
// Disable lockdown. Everything is unblocked.
mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
+ assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent);
vpnUidCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -6647,6 +6674,8 @@
// Enable lockdown and connect a VPN. The VPN is not blocked.
mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
+ assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
vpnUidCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertNull(mCm.getActiveNetwork());
@@ -6658,7 +6687,7 @@
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
vpnUidCallback.assertNoCallback(); // vpnUidCallback has NOT_VPN capability.
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
- assertEquals(null, mCm.getActiveNetworkForUid(VPN_UID)); // BUG?
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index cc47317..3648c4d 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -27,7 +27,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -89,6 +88,7 @@
import android.security.KeyStore;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Range;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -350,7 +350,7 @@
// Set always-on with lockdown.
assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore));
- verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
}));
@@ -361,12 +361,11 @@
// Switch to another app.
assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
- verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
}));
-
- verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
}));
@@ -383,7 +382,7 @@
// Set always-on with lockdown and allow app PKGS[2] from lockdown.
assertTrue(vpn.setAlwaysOnPackage(
PKGS[1], true, Collections.singletonList(PKGS[2]), mKeyStore));
- verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
}));
@@ -392,10 +391,10 @@
// Change allowed app list to PKGS[3].
assertTrue(vpn.setAlwaysOnPackage(
PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore));
- verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
}));
- verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
}));
@@ -405,11 +404,11 @@
// Change the VPN app.
assertTrue(vpn.setAlwaysOnPackage(
PKGS[0], true, Collections.singletonList(PKGS[3]), mKeyStore));
- verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
}));
- verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start, user.start + PKG_UIDS[0] - 1),
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
}));
@@ -418,11 +417,11 @@
// Remove the list of allowed packages.
assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore));
- verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
}));
- verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop),
}));
assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2],
@@ -432,10 +431,10 @@
// Add the list of allowed packages.
assertTrue(vpn.setAlwaysOnPackage(
PKGS[0], true, Collections.singletonList(PKGS[1]), mKeyStore));
- verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop)
}));
- verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
}));
@@ -450,11 +449,11 @@
// allowed package should change from PGKS[1] to PKGS[2].
assertTrue(vpn.setAlwaysOnPackage(
PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"), mKeyStore));
- verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[]{
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
}));
- verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[]{
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1),
new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
}));
@@ -475,7 +474,7 @@
// Set lockdown.
assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
- verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
}));
@@ -485,7 +484,7 @@
// Add the restricted user.
setMockedUsers(primaryUser, tempProfile);
vpn.onUserAdded(tempProfile.id);
- verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
}));
@@ -493,7 +492,7 @@
// Remove the restricted user.
tempProfile.partial = true;
vpn.onUserRemoved(tempProfile.id);
- verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+ verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
}));
@@ -506,22 +505,29 @@
new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
// Given legacy lockdown is already enabled,
vpn.setLockdown(true);
-
- verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(primaryUserRangeParcel));
+ verify(mConnectivityManager, times(1)).setRequireVpnForUids(true,
+ toRanges(primaryUserRangeParcel));
// Enabling legacy lockdown twice should do nothing.
vpn.setLockdown(true);
- verify(mNetd, times(1))
- .networkRejectNonSecureVpn(anyBoolean(), any(UidRangeParcel[].class));
+ verify(mConnectivityManager, times(1)).setRequireVpnForUids(anyBoolean(), any());
// And disabling should remove the rules exactly once.
vpn.setLockdown(false);
- verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(primaryUserRangeParcel));
+ verify(mConnectivityManager, times(1)).setRequireVpnForUids(false,
+ toRanges(primaryUserRangeParcel));
// Removing the lockdown again should have no effect.
vpn.setLockdown(false);
- verify(mNetd, times(2)).networkRejectNonSecureVpn(
- anyBoolean(), any(UidRangeParcel[].class));
+ verify(mConnectivityManager, times(2)).setRequireVpnForUids(anyBoolean(), any());
+ }
+
+ private ArrayList<Range<Integer>> toRanges(UidRangeParcel[] ranges) {
+ ArrayList<Range<Integer>> rangesArray = new ArrayList<>(ranges.length);
+ for (int i = 0; i < ranges.length; i++) {
+ rangesArray.add(new Range<>(ranges[i].start, ranges[i].stop));
+ }
+ return rangesArray;
}
@Test
@@ -535,21 +541,21 @@
new UidRangeParcel(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop)
};
- final InOrder order = inOrder(mNetd);
+ final InOrder order = inOrder(mConnectivityManager);
// Given lockdown is enabled with no package (legacy VPN),
vpn.setLockdown(true);
- order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(entireUser));
+ order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(entireUser));
// When a new VPN package is set the rules should change to cover that package.
vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE);
- order.verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(entireUser));
- order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(exceptPkg0));
+ order.verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(entireUser));
+ order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(exceptPkg0));
// When that VPN package is unset, everything should be undone again in reverse.
vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE);
- order.verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(exceptPkg0));
- order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(entireUser));
+ order.verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(exceptPkg0));
+ order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(entireUser));
}
@Test
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index 89146f9..435c3c0 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -64,7 +64,6 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
@@ -124,7 +123,7 @@
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- collection.write(new DataOutputStream(bos));
+ collection.write(bos);
// clear structure completely
collection.reset();
@@ -152,7 +151,7 @@
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- collection.write(new DataOutputStream(bos));
+ collection.write(bos);
// clear structure completely
collection.reset();
@@ -180,7 +179,7 @@
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- collection.write(new DataOutputStream(bos));
+ collection.write(bos);
// clear structure completely
collection.reset();