Add an API method for clearing compat overrides on release builds
This will be useful when we want to clear overrides for apps that are no longer needed regardless of the version that is installed on device, e.g., when an app is being uninstalled, when we want to rollback an override, or when we deprecate a change-id.
In addition, rename CompatChanges#setPackageOverride to
addPackageOverrides.
Bug: 183183372
Test: atest FrameworksServicesTests:CompatConfigTest
Test: atest FrameworksServicesTests:PlatformCompatTest
Change-Id: Iff416c8b4c88b5eddc9e21c73219198d23266fa1
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7e729e5..f7e28fc 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1124,10 +1124,11 @@
package android.app.compat {
public final class CompatChanges {
+ method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void addPackageOverrides(@NonNull String, @NonNull java.util.Map<java.lang.Long,android.app.compat.PackageOverride>);
method public static boolean isChangeEnabled(long);
method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, int);
- method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void setPackageOverride(@NonNull String, @NonNull java.util.Map<java.lang.Long,android.app.compat.PackageOverride>);
+ method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void removePackageOverrides(@NonNull String, @NonNull java.util.Set<java.lang.Long>);
}
public final class PackageOverride {
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index 74e1ece..8ca43c4 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -26,9 +26,11 @@
import android.os.UserHandle;
import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import com.android.internal.compat.IPlatformCompat;
import java.util.Map;
+import java.util.Set;
/**
* CompatChanges APIs - to be used by platform code only (including mainline
@@ -98,15 +100,19 @@
}
/**
- * Set an app compat override for a given package. This will check whether the caller is allowed
+ * Adds app compat overrides for a given package. This will check whether the caller is allowed
* to perform this operation on the given apk and build. Only the installer package is allowed
* to set overrides on a non-debuggable final build and a non-test apk.
*
+ * <p>Note that calling this method doesn't remove previously added overrides for the given
+ * package if their change ID isn't in the given map, only replaces those that have the same
+ * change ID.
+ *
* @param packageName The package name of the app in question.
- * @param overrides A map from changeId to the override applied for this change id.
+ * @param overrides A map from change ID to the override applied for this change ID.
*/
@RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
- public static void setPackageOverride(@NonNull String packageName,
+ public static void addPackageOverrides(@NonNull String packageName,
@NonNull Map<Long, PackageOverride> overrides) {
IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
@@ -117,4 +123,29 @@
e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Removes app compat overrides for a given package. This will check whether the caller is
+ * allowed to perform this operation on the given apk and build. Only the installer package is
+ * allowed to clear overrides on a non-debuggable final build and a non-test apk.
+ *
+ * <p>Note that calling this method with an empty set is a no-op and no overrides will be
+ * removed for the given package.
+ *
+ * @param packageName The package name of the app in question.
+ * @param overridesToRemove A set of change IDs for which to remove overrides.
+ */
+ @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+ public static void removePackageOverrides(@NonNull String packageName,
+ @NonNull Set<Long> overridesToRemove) {
+ IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ CompatibilityOverridesToRemoveConfig config = new CompatibilityOverridesToRemoveConfig(
+ overridesToRemove);
+ try {
+ platformCompat.removeOverridesOnReleaseBuilds(config, packageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.aidl b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.aidl
new file mode 100644
index 0000000..441e553
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 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.internal.compat;
+
+parcelable CompatibilityOverridesToRemoveConfig;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
new file mode 100644
index 0000000..642f79c
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 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.internal.compat;
+
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Parcelable containing compat config change IDs for which to remove overrides for a given
+ * application.
+ * @hide
+ */
+public final class CompatibilityOverridesToRemoveConfig implements Parcelable {
+ public final Set<Long> changeIds;
+
+ public CompatibilityOverridesToRemoveConfig(Set<Long> changeIds) {
+ this.changeIds = changeIds;
+ }
+
+ private CompatibilityOverridesToRemoveConfig(Parcel in) {
+ int keyCount = in.readInt();
+ changeIds = new HashSet<>();
+ for (int i = 0; i < keyCount; i++) {
+ changeIds.add(in.readLong());
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(changeIds.size());
+ for (Long changeId : changeIds) {
+ dest.writeLong(changeId);
+ }
+ }
+
+ public static final Creator<CompatibilityOverridesToRemoveConfig> CREATOR =
+ new Creator<CompatibilityOverridesToRemoveConfig>() {
+
+ @Override
+ public CompatibilityOverridesToRemoveConfig createFromParcel(Parcel in) {
+ return new CompatibilityOverridesToRemoveConfig(in);
+ }
+
+ @Override
+ public CompatibilityOverridesToRemoveConfig[] newArray(int size) {
+ return new CompatibilityOverridesToRemoveConfig[size];
+ }
+ };
+}
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 78d1d22..c9f1704 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -22,6 +22,7 @@
parcelable CompatibilityChangeConfig;
parcelable CompatibilityOverrideConfig;
+parcelable CompatibilityOverridesToRemoveConfig;
parcelable CompatibilityChangeInfo;
/**
* Platform private API for talking with the PlatformCompat service.
@@ -204,6 +205,27 @@
void clearOverrideForTest(long changeId, String packageName);
/**
+ * Restores the default behaviour for compatibility changes on release builds.
+ *
+ * <p>The caller to this API needs to hold
+ * {@code android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD} and all change ids
+ * in {@code overridesToRemove} need to annotated with
+ * {@link android.compat.annotation.Overridable}.
+ *
+ * A release build in this definition means that {@link android.os.Build#IS_DEBUGGABLE} needs to
+ * be {@code false}.
+ *
+ * <p>Note that this does not kill the app, and therefore overrides read from the app process
+ * will not be updated. Overrides read from the system process do take effect.
+ *
+ * @param overridesToRemove parcelable containing the compat change overrides to be removed
+ * @param packageName the package name of the app whose changes will be restored to the
+ * default behaviour
+ * @throws SecurityException if overriding changes is not permitted
+ */
+ void removeOverridesOnReleaseBuilds(in CompatibilityOverridesToRemoveConfig overridesToRemove, in String packageName);
+
+ /**
* Enables all compatibility changes that have enabledSinceTargetSdk ==
* {@param targetSdkVersion} for an app, subject to the policy.
*
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 55e2696..9247568 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -33,6 +33,7 @@
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
@@ -370,6 +371,27 @@
}
}
+ /**
+ * Removes overrides whose change ID is specified in {@code overridesToRemove} that were
+ * previously added via {@link #addOverride(long, String, boolean)} or
+ * {@link #addOverrides(CompatibilityOverrideConfig, String)} for a certain package.
+ *
+ * <p>This restores the default behaviour for the given change IDs and app.
+ *
+ * @param overridesToRemove list of change IDs for which to restore the default behaviour.
+ * @param packageName the package for which the overrides should be purged
+ */
+ void removePackageOverrides(CompatibilityOverridesToRemoveConfig overridesToRemove,
+ String packageName) {
+ synchronized (mChanges) {
+ for (Long changeId : overridesToRemove.changeIds) {
+ removeOverrideUnsafe(changeId, packageName);
+ }
+ saveOverrides();
+ invalidateCache();
+ }
+ }
+
private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName,
int targetSdkVersion) {
LongArray allowed = new LongArray();
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 20469a2..2591f38 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -46,6 +46,7 @@
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.util.DumpUtils;
@@ -54,6 +55,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -187,7 +189,7 @@
String packageName) {
// TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods.
checkCompatChangeOverrideOverridablePermission();
- checkAllCompatOverridesAreOverridable(overrides);
+ checkAllCompatOverridesAreOverridable(overrides.overrides.keySet());
mCompatConfig.addOverrides(overrides, packageName);
}
@@ -251,6 +253,16 @@
}
@Override
+ public void removeOverridesOnReleaseBuilds(
+ CompatibilityOverridesToRemoveConfig overridesToRemove,
+ String packageName) {
+ // TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods.
+ checkCompatChangeOverrideOverridablePermission();
+ checkAllCompatOverridesAreOverridable(overridesToRemove.changeIds);
+ mCompatConfig.removePackageOverrides(overridesToRemove, packageName);
+ }
+
+ @Override
public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
checkCompatChangeReadAndLogPermission();
return mCompatConfig.getAppConfig(appInfo);
@@ -396,8 +408,8 @@
}
}
- private void checkAllCompatOverridesAreOverridable(CompatibilityOverrideConfig overrides) {
- for (Long changeId : overrides.overrides.keySet()) {
+ private void checkAllCompatOverridesAreOverridable(Collection<Long> changeIds) {
+ for (Long changeId : changeIds) {
if (!mCompatConfig.isOverridable(changeId)) {
throw new SecurityException("Only change ids marked as Overridable can be "
+ "overridden.");
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 7bdc87e..6b9aa18 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -115,7 +115,12 @@
return this;
}
- CompatConfigBuilder addOverridableChangeWithId(long id) {
+ CompatConfigBuilder addEnabledOverridableChangeWithId(long id) {
+ mChanges.add(new CompatChange(id, "", -1, -1, false, false, "", true));
+ return this;
+ }
+
+ CompatConfigBuilder addDisabledOverridableChangeWithId(long id) {
mChanges.add(new CompatChange(id, "", -1, -1, true, false, "", true));
return this;
}
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 a866363..0248b9b 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -36,6 +36,7 @@
import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import org.junit.Before;
import org.junit.Test;
@@ -50,6 +51,10 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@@ -249,7 +254,7 @@
when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
.thenReturn(applicationInfo);
- // Force the validator to prevent overriding the change by using a user build.
+ // Force the validator to prevent overriding non-overridable changes by using a user build.
when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
when(mBuildClassifier.isFinalBuild()).thenReturn(true);
@@ -261,10 +266,12 @@
@Test
public void testInstallerCanSetOverrides() throws Exception {
- final long changeId = 1234L;
- final int installerUid = 23;
+ final long disabledChangeId1 = 1234L;
+ final long disabledChangeId2 = 1235L;
+ // We make disabledChangeId2 non-overridable to make sure it is ignored.
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
- .addOverridableChangeWithId(1234L)
+ .addDisabledOverridableChangeWithId(disabledChangeId1)
+ .addDisabledChangeWithId(disabledChangeId2)
.build();
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
.withPackageName("com.some.package")
@@ -274,19 +281,56 @@
when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
.thenReturn(applicationInfo);
- // Force the validator to prevent overriding the change by using a user build.
+ // Force the validator to prevent overriding non-overridable changes by using a user build.
when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
when(mBuildClassifier.isFinalBuild()).thenReturn(true);
CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(
- Collections.singletonMap(1234L,
+ Collections.singletonMap(disabledChangeId1,
new PackageOverride.Builder()
.setMaxVersionCode(99L)
.setEnabled(true)
.build()));
compatConfig.addOverrides(config, "com.some.package");
- assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo)).isFalse();
+ }
+
+ @Test
+ public void testPreventInstallerSetNonOverridable() throws Exception {
+ final long disabledChangeId1 = 1234L;
+ final long disabledChangeId2 = 1235L;
+ final long disabledChangeId3 = 1236L;
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledOverridableChangeWithId(disabledChangeId1)
+ .addDisabledChangeWithId(disabledChangeId2)
+ .addDisabledOverridableChangeWithId(disabledChangeId3)
+ .build();
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package")
+ .build();
+ PackageManager packageManager = mock(PackageManager.class);
+ when(mContext.getPackageManager()).thenReturn(packageManager);
+ when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+ .thenReturn(applicationInfo);
+
+ // Force the validator to prevent overriding non-overridable changes by using a user build.
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+ Map<Long, PackageOverride> overrides = new HashMap<>();
+ overrides.put(disabledChangeId1, new PackageOverride.Builder().setEnabled(true).build());
+ overrides.put(disabledChangeId2, new PackageOverride.Builder().setEnabled(true).build());
+ overrides.put(disabledChangeId3, new PackageOverride.Builder().setEnabled(true).build());
+ CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides);
+
+ assertThrows(SecurityException.class,
+ () -> compatConfig.addOverrides(config, "com.some.package")
+ );
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo)).isFalse();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId3, applicationInfo)).isFalse();
}
@Test
@@ -459,7 +503,7 @@
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
// Reject all override attempts.
- // Force the validator to prevent overriding the change by using a user build.
+ // Force the validator to prevent overriding non-overridable changes by using a user build.
when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
// Try to turn off change, but validator prevents it.
@@ -481,7 +525,7 @@
.thenReturn(applicationInfo);
// Reject all override attempts.
- // Force the validator to prevent overriding the change by using a user build.
+ // Force the validator to prevent overriding non-overridable changes by using a user build.
when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
when(mBuildClassifier.isFinalBuild()).thenReturn(true);
// Try to remove a non existing override, and it doesn't fail.
@@ -509,6 +553,90 @@
}
@Test
+ public void testInstallerCanRemoveOverrides() throws Exception {
+ final long disabledChangeId1 = 1234L;
+ final long disabledChangeId2 = 1235L;
+ final long enabledChangeId = 1236L;
+ // We make disabledChangeId2 non-overridable to make sure it is ignored.
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledOverridableChangeWithId(disabledChangeId1)
+ .addDisabledChangeWithId(disabledChangeId2)
+ .addEnabledOverridableChangeWithId(enabledChangeId)
+ .build();
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package")
+ .build();
+ when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+ .thenReturn(applicationInfo);
+
+ assertThat(compatConfig.addOverride(disabledChangeId1, "com.some.package", true)).isTrue();
+ assertThat(compatConfig.addOverride(disabledChangeId2, "com.some.package", true)).isTrue();
+ assertThat(compatConfig.addOverride(enabledChangeId, "com.some.package", false)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(enabledChangeId, applicationInfo)).isFalse();
+
+ // Force the validator to prevent overriding non-overridable changes by using a user build.
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+ Set<Long> overridesToRemove = new HashSet<>();
+ overridesToRemove.add(disabledChangeId1);
+ overridesToRemove.add(enabledChangeId);
+ CompatibilityOverridesToRemoveConfig config = new CompatibilityOverridesToRemoveConfig(
+ overridesToRemove);
+
+ compatConfig.removePackageOverrides(config, "com.some.package");
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo)).isFalse();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(enabledChangeId, applicationInfo)).isTrue();
+ }
+
+ @Test
+ public void testPreventInstallerRemoveNonOverridable() throws Exception {
+ final long disabledChangeId1 = 1234L;
+ final long disabledChangeId2 = 1235L;
+ final long disabledChangeId3 = 1236L;
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledOverridableChangeWithId(disabledChangeId1)
+ .addDisabledChangeWithId(disabledChangeId2)
+ .addDisabledOverridableChangeWithId(disabledChangeId3)
+ .build();
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("com.some.package")
+ .build();
+ PackageManager packageManager = mock(PackageManager.class);
+ when(mContext.getPackageManager()).thenReturn(packageManager);
+ when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+ .thenReturn(applicationInfo);
+
+ assertThat(compatConfig.addOverride(disabledChangeId1, "com.some.package", true)).isTrue();
+ assertThat(compatConfig.addOverride(disabledChangeId2, "com.some.package", true)).isTrue();
+ assertThat(compatConfig.addOverride(disabledChangeId3, "com.some.package", true)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId3, applicationInfo)).isTrue();
+
+ // Force the validator to prevent overriding non-overridable changes by using a user build.
+ when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+ when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+ Set<Long> overridesToRemove = new HashSet<>();
+ overridesToRemove.add(disabledChangeId1);
+ overridesToRemove.add(disabledChangeId2);
+ overridesToRemove.add(disabledChangeId3);
+ CompatibilityOverridesToRemoveConfig config = new CompatibilityOverridesToRemoveConfig(
+ overridesToRemove);
+
+ assertThrows(SecurityException.class,
+ () -> compatConfig.removePackageOverrides(config, "com.some.package")
+ );
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo)).isFalse();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId3, applicationInfo)).isTrue();
+ }
+
+ @Test
public void testEnableTargetSdkChangesForPackage() throws Exception {
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
.addEnabledChangeWithId(1L)
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 a2664e5..9accd49 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -98,7 +98,7 @@
.addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
.addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L)
.addLoggingOnlyChangeWithId(7L)
- .addOverridableChangeWithId(8L)
+ .addDisabledOverridableChangeWithId(8L)
.build();
mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(