From 224f8c01d7096a57de8d8da6f51a79134553ee74 Mon Sep 17 00:00:00 2001 From: Andrei Onea Date: Tue, 24 Nov 2020 23:54:04 +0000 Subject: [PATCH 1/2] Allow overriding compat config prior to install time. If the user tries to apply an override for a package that is not yet installed, the override is put into a 'deferred' bucket, to be validated upon installation. Overrides are rechecked every time an app is installed, updated or deleted. If a deferred override is valid at install or update time, it gets promoted to a regular override. If a deferred override cannot be applied upon app installation or update, it remains deferred. If an app is deleted, all its overrides become deferred. When any of the reset commands are called for a given package, that removes both regular and deferred overrides. Bug: 158696818 Test: atest CompatConfigTest Test: atest OverrideValidatorImplTest Change-Id: I50eab62214d1325ec6185ddcfe9e8f425e11c98b --- .../internal/compat/OverrideAllowedState.java | 18 ++-- .../android/server/compat/CompatChange.java | 66 +++++++++++++++ .../android/server/compat/CompatConfig.java | 83 ++++++++++++------- .../server/compat/OverrideValidatorImpl.java | 4 +- .../android/server/compat/PlatformCompat.java | 35 ++++++++ .../java/com/android/server/SystemServer.java | 3 + .../server/compat/CompatConfigTest.java | 80 +++++++++++++++++- .../compat/OverrideValidatorImplTest.java | 43 ++++++++++ 8 files changed, 286 insertions(+), 46 deletions(-) diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java index 9a78ad2011cf4..c0bbe5082131b 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/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index a8aa9aada607a..c4ff99bae6947 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 mPackageOverrides; + private Map mDeferredOverrides; public CompatChange(long changeId) { this(changeId, null, -1, -1, false, false, null); @@ -120,6 +121,56 @@ public final class CompatChange extends CompatibilityChangeInfo { notifyListener(pname); } + /** + * 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. + * + *

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. + * + *

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.

+ * + * @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. * @@ -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 8511118cc840a..1cbaa1e3451ea 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -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; @@ -248,22 +258,22 @@ final class CompatConfig { 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. * - *

This restores the default behaviour for the given change and app, once any app - * processes have been restarted. + *

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 { 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(); } @@ -340,7 +336,7 @@ final class CompatConfig { } } catch (RemoteException e) { // Should never occur, since validator is in the same process. - throw new RuntimeException("Unable to call override validator!", e); + throw new AssertionError("Unable to call override validator!", e); } } } @@ -511,4 +507,27 @@ 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); + try { + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(c.getId(), packageName); + boolean allowedOverride = (allowedState.state == OverrideAllowedState.ALLOWED); + shouldInvalidateCache |= c.recheckOverride(packageName, allowedOverride); + } catch (RemoteException e) { + // Should never occur, since validator is in the same process. + throw new AssertionError("Unable to call override validator!", e); + } + } + if (shouldInvalidateCache) { + invalidateCache(); + } + } + } } diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java index 79a13ca242c17..58250407b8643 100644 --- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java +++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java @@ -17,11 +17,11 @@ 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; @@ -73,7 +73,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. diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index aa85f7f0f55f9..fada37bce82c1 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,34 @@ 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); + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b5c5bb52d0501..59b24f887f2c2 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -910,6 +910,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/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index 8c63bfcf1407d..6cb2dbc7cbff8 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; @@ -229,6 +230,83 @@ public class CompatConfigTest { assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse(); } + @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) @@ -259,7 +337,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")); 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 c53b29a08a4a8..019c615fed381 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,8 @@ 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 +412,44 @@ 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)); + } } From 46111c8ea0b39e6f0ace60681169cc83c4bc93e2 Mon Sep 17 00:00:00 2001 From: Andrei Onea Date: Fri, 4 Dec 2020 17:42:34 +0000 Subject: [PATCH 2/2] Add force_non_debuggable_final_build_for_compat This flag forces the compat framework to assume we're on a non-debuggable build, and use stricter rules for enforcing the change id override policy. On a non-debuggable build, this flag has no effect. Test: atest OverrideValidatorImplTest Bug: 158696818 Change-Id: I3b13f1898e7ff00580140debe2ced9f8074502ea --- core/java/android/provider/Settings.java | 10 + .../android/provider/SettingsBackupTest.java | 1 + .../server/am/ActivityManagerService.java | 1 + .../android/server/compat/CompatConfig.java | 53 +++--- .../server/compat/OverrideValidatorImpl.java | 37 +++- .../android/server/compat/PlatformCompat.java | 8 + .../server/compat/CompatConfigBuilder.java | 1 + .../server/compat/CompatConfigTest.java | 10 + .../compat/OverrideValidatorImplTest.java | 173 +++++++++++++++++- .../server/compat/PlatformCompatTest.java | 1 + 10 files changed, 265 insertions(+), 30 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 300bb7603722a..1e14e3a4a701d 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/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index e90bb36214a82..5ecb171024388 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 e4cdfe92ec8a0..8cf9f6fa5afb2 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/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index 1cbaa1e3451ea..f03a608232a23 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 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); @@ -254,7 +254,7 @@ 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); @@ -307,7 +307,7 @@ final class CompatConfig { * * @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) { CompatChange change = mChanges.valueAt(i); @@ -323,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 AssertionError("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()); } } } @@ -396,6 +391,11 @@ final class CompatConfig { return c.defaultValue(); } + @VisibleForTesting + void forceNonDebuggableFinalForTest(boolean value) { + mOverrideValidator.forceNonDebuggableFinalForTest(value); + } + @VisibleForTesting void clearChanges() { synchronized (mChanges) { @@ -515,19 +515,18 @@ final class CompatConfig { boolean shouldInvalidateCache = false; for (int idx = 0; idx < mChanges.size(); ++idx) { CompatChange c = mChanges.valueAt(idx); - try { - OverrideAllowedState allowedState = - mOverrideValidator.getOverrideAllowedState(c.getId(), packageName); - boolean allowedOverride = (allowedState.state == OverrideAllowedState.ALLOWED); - shouldInvalidateCache |= c.recheckOverride(packageName, allowedOverride); - } catch (RemoteException e) { - // Should never occur, since validator is in the same process. - throw new AssertionError("Unable to call override validator!", e); - } + 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 58250407b8643..fe5b4a98797dd 100644 --- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java +++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java @@ -27,6 +27,9 @@ 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); @@ -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 fada37bce82c1..283dba7aff8ad 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -424,4 +424,12 @@ public class PlatformCompat extends IPlatformCompat.Stub { 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/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java index 870fe4a0837ed..4f4aa3f16f09b 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 6cb2dbc7cbff8..ac8dc341999ac 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -82,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(); } @@ -181,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); @@ -438,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); } @@ -452,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, @@ -478,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 019c615fed381..0fd6445fbeebe 100644 --- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java @@ -34,7 +34,6 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; - import androidx.test.runner.AndroidJUnit4; import com.android.internal.compat.AndroidBuildClassifier; @@ -452,4 +451,176 @@ public class OverrideValidatorImplTest { 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 1d3b643ba83fc..3f65a4621778e 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);