summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrei-Valentin Onea <andreionea@google.com>2021-01-07 14:04:12 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2021-01-07 14:04:12 +0000
commit29ed176b46bd5d754e582c2c506193f1993a33fa (patch)
tree2b46673a945fc691bc1c9fa249ff490332967948
parent169c132627440174123c5fafed6c6e5fb97c584e (diff)
parent46111c8ea0b39e6f0ace60681169cc83c4bc93e2 (diff)
Merge changes from topic "preinstall-overrides"
* changes: Add force_non_debuggable_final_build_for_compat Allow overriding compat config prior to install time.
-rwxr-xr-xcore/java/android/provider/Settings.java10
-rw-r--r--core/java/com/android/internal/compat/OverrideAllowedState.java18
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java1
-rw-r--r--services/core/java/com/android/server/compat/CompatChange.java66
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java116
-rw-r--r--services/core/java/com/android/server/compat/OverrideValidatorImpl.java41
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java43
-rw-r--r--services/java/com/android/server/SystemServer.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java214
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java1
13 files changed, 540 insertions, 65 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 300bb7603722..1e14e3a4a701 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13283,6 +13283,16 @@ public final class Settings {
@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/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index 9a78ad2011cf..c0bbe5082131 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -33,7 +33,7 @@ public final class OverrideAllowedState implements Parcelable {
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 @@ public final class OverrideAllowedState implements Parcelable {
* 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 @@ public final class OverrideAllowedState implements Parcelable {
throws SecurityException {
switch (state) {
case ALLOWED:
+ case DEFERRED_VERIFICATION:
return;
case DISABLED_NOT_DEBUGGABLE:
throw new SecurityException(
@@ -118,11 +119,6 @@ public final class OverrideAllowedState implements Parcelable {
"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 @@ public final class OverrideAllowedState implements Parcelable {
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/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index e90bb36214a8..5ecb17102438 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -300,6 +300,7 @@ public class SettingsBackupTest {
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/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7e67f9758dfe..928ddab9dca7 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 @@ public class ActivityManagerService extends IActivityManager.Stub
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 a8aa9aada607..c4ff99bae694 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 @@ public final class CompatChange extends CompatibilityChangeInfo {
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 @@ public final class CompatChange extends CompatibilityChangeInfo {
}
/**
+ * 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 @@ public final class CompatChange extends CompatibilityChangeInfo {
notifyListener(pname);
}
}
+ if (mDeferredOverrides != null) {
+ mDeferredOverrides.remove(pname);
+ }
}
/**
@@ -176,6 +230,15 @@ public final class CompatChange extends CompatibilityChangeInfo {
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 @@ public final class CompatChange extends CompatibilityChangeInfo {
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 8511118cc840..f03a608232a2 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 @@ final class CompatConfig {
@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 @@ final class CompatConfig {
* @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 @@ final class CompatConfig {
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 @@ final class CompatConfig {
* @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 @@ final class CompatConfig {
* 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 @@ final class CompatConfig {
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 @@ final class CompatConfig {
}
@VisibleForTesting
+ void forceNonDebuggableFinalForTest(boolean value) {
+ mOverrideValidator.forceNonDebuggableFinalForTest(value);
+ }
+
+ @VisibleForTesting
void clearChanges() {
synchronized (mChanges) {
mChanges.clear();
@@ -511,4 +507,26 @@ final class CompatConfig {
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 79a13ca242c1..fe5b4a98797d 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 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub {
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 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub {
mAndroidBuildClassifier = androidBuildClassifier;
mContext = context;
mCompatConfig = config;
+ mForceNonDebuggableFinalBuild = false;
}
@Override
@@ -56,8 +74,10 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub {
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 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub {
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 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub {
}
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 aa85f7f0f55f..283dba7aff8a 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 static android.os.Process.SYSTEM_UID;
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 @@ public class PlatformCompat extends IPlatformCompat.Stub {
mChangeReporter = new ChangeReporter(
ChangeReporter.SOURCE_SYSTEM_SERVER);
mCompatConfig = compatConfig;
+ registerPackageReceiver(context);
}
@Override
@@ -389,4 +394,42 @@ public class PlatformCompat extends IPlatformCompat.Stub {
}
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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d55a5b7792d4..d533848c6f75 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -912,6 +912,9 @@ public final class SystemServer {
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 870fe4a0837e..4f4aa3f16f09 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 @@ class CompatConfigBuilder {
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 8c63bfcf1407..ac8dc341999a 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.app.compat.ChangeIdStateCache;
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 @@ public class CompatConfigTest {
@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 @@ public class CompatConfigTest {
@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 @@ public class CompatConfigTest {
}
@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 @@ public class CompatConfigTest {
// 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 @@ public class CompatConfigTest {
@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 @@ public class CompatConfigTest {
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 @@ public class CompatConfigTest {
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 c53b29a08a4a..0fd6445fbeeb 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 static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import androidx.test.runner.AndroidJUnit4;
@@ -409,4 +411,216 @@ public class OverrideValidatorImplTest {
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 1d3b643ba83f..3f65a4621778 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 @@ public class PlatformCompatTest {
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);