summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author tomnatan <tomnatan@google.com> 2021-12-16 17:22:32 +0000
committer tomnatan <tomnatan@google.com> 2021-12-22 10:27:41 +0000
commit6162b703490f73047f6c7a04723668ced64c50b5 (patch)
treec1bbb4e792faf5b0ec1288643ecbd18e7544f318
parent4e59b1968c504f59ba6047405c54d782f00f9b34 (diff)
Add API methods for adding/clearing compat overrides for multiple apps on release builds
This is important in cases where we want to add/clear overrides for multiple apps at the same time, without having to make a binder call for each package and have the compat config save the changes to file and invalidate the cache each time. Bug: 199730202 Test: atest FrameworksServicesTests:CompatConfigTest Test: atest FrameworksServicesTests:PlatformCompatTest Change-Id: I3b4a3d9db12fdf99eb78c486bdd8e248fff92e50
-rw-r--r--core/api/system-current.txt2
-rw-r--r--core/java/android/app/compat/CompatChanges.java55
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.aidl19
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java74
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.aidl19
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java77
-rw-r--r--core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java2
-rw-r--r--core/java/com/android/internal/compat/IPlatformCompat.aidl42
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java87
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java114
11 files changed, 508 insertions, 9 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6668dd5ef7b6..bf7f591c72c5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1336,7 +1336,9 @@ package android.app.compat {
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 putAllPackageOverrides(@NonNull java.util.Map<java.lang.String,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 putPackageOverrides(@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 removeAllPackageOverrides(@NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.Long>>);
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>);
}
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index 0d85fb9488be..d7b2ab4351a4 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -22,8 +22,11 @@ import android.annotation.SystemApi;
import android.compat.Compatibility;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.ArrayMap;
import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesByPackageConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveByPackageConfig;
import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import java.util.Map;
@@ -98,6 +101,31 @@ public final class CompatChanges {
}
/**
+ * Equivalent to calling {@link #putPackageOverrides(String, Map)} on each entry in {@code
+ * packageNameToOverrides}, but the state of the compat config will be updated only once
+ * instead of for each package.
+ *
+ * @param packageNameToOverrides A map from package name to a map from change ID to the
+ * override applied for that package name and change ID.
+ */
+ @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+ public static void putAllPackageOverrides(
+ @NonNull Map<String, Map<Long, PackageOverride>> packageNameToOverrides) {
+ ArrayMap<String, CompatibilityOverrideConfig> packageNameToConfig = new ArrayMap<>();
+ for (String packageName : packageNameToOverrides.keySet()) {
+ packageNameToConfig.put(packageName,
+ new CompatibilityOverrideConfig(packageNameToOverrides.get(packageName)));
+ }
+ CompatibilityOverridesByPackageConfig config = new CompatibilityOverridesByPackageConfig(
+ packageNameToConfig);
+ try {
+ QUERY_CACHE.getPlatformCompatService().putAllOverridesOnReleaseBuilds(config);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Associates app compat overrides with the given package and their respective change IDs.
* 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
@@ -123,6 +151,33 @@ public final class CompatChanges {
}
/**
+ * Equivalent to calling {@link #removePackageOverrides(String, Set)} on each entry in {@code
+ * packageNameToOverridesToRemove}, but the state of the compat config will be updated only once
+ * instead of for each package.
+ *
+ * @param packageNameToOverridesToRemove A map from package name to a set of change IDs for
+ * which to remove overrides for that package name.
+ */
+ @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
+ public static void removeAllPackageOverrides(
+ @NonNull Map<String, Set<Long>> packageNameToOverridesToRemove) {
+ ArrayMap<String, CompatibilityOverridesToRemoveConfig> packageNameToConfig =
+ new ArrayMap<>();
+ for (String packageName : packageNameToOverridesToRemove.keySet()) {
+ packageNameToConfig.put(packageName,
+ new CompatibilityOverridesToRemoveConfig(
+ packageNameToOverridesToRemove.get(packageName)));
+ }
+ CompatibilityOverridesToRemoveByPackageConfig config =
+ new CompatibilityOverridesToRemoveByPackageConfig(packageNameToConfig);
+ try {
+ QUERY_CACHE.getPlatformCompatService().removeAllOverridesOnReleaseBuilds(config);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Removes app compat overrides for the 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.
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.aidl b/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.aidl
new file mode 100644
index 000000000000..eed401559e37
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.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 CompatibilityOverridesByPackageConfig;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java
new file mode 100644
index 000000000000..8652bb6d05e4
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesByPackageConfig.java
@@ -0,0 +1,74 @@
+/*
+ * 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.HashMap;
+import java.util.Map;
+
+/**
+ * Parcelable containing compat config overrides by application.
+ * @hide
+ */
+public final class CompatibilityOverridesByPackageConfig implements Parcelable {
+ public final Map<String, CompatibilityOverrideConfig> packageNameToOverrides;
+
+ public CompatibilityOverridesByPackageConfig(
+ Map<String, CompatibilityOverrideConfig> packageNameToOverrides) {
+ this.packageNameToOverrides = packageNameToOverrides;
+ }
+
+ private CompatibilityOverridesByPackageConfig(Parcel in) {
+ int keyCount = in.readInt();
+ packageNameToOverrides = new HashMap<>();
+ for (int i = 0; i < keyCount; i++) {
+ String key = in.readString();
+ packageNameToOverrides.put(key,
+ CompatibilityOverrideConfig.CREATOR.createFromParcel(in));
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(packageNameToOverrides.size());
+ for (String key : packageNameToOverrides.keySet()) {
+ dest.writeString(key);
+ packageNameToOverrides.get(key).writeToParcel(dest, /* flags= */ 0);
+ }
+ }
+
+ public static final Parcelable.Creator<CompatibilityOverridesByPackageConfig> CREATOR =
+ new Parcelable.Creator<CompatibilityOverridesByPackageConfig>() {
+
+ @Override
+ public CompatibilityOverridesByPackageConfig createFromParcel(Parcel in) {
+ return new CompatibilityOverridesByPackageConfig(in);
+ }
+
+ @Override
+ public CompatibilityOverridesByPackageConfig[] newArray(int size) {
+ return new CompatibilityOverridesByPackageConfig[size];
+ }
+ };
+}
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.aidl b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.aidl
new file mode 100644
index 000000000000..b9d0cef325dd
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.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 CompatibilityOverridesToRemoveByPackageConfig;
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java
new file mode 100644
index 000000000000..b408d6440070
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveByPackageConfig.java
@@ -0,0 +1,77 @@
+/*
+ * 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.HashMap;
+import java.util.Map;
+
+/**
+ * Parcelable containing compat config change IDs for which to remove overrides by application.
+ *
+ * <p>This class is separate from CompatibilityOverridesByPackageConfig since we only need change
+ * IDs.
+ * @hide
+ */
+public final class CompatibilityOverridesToRemoveByPackageConfig implements Parcelable {
+ public final Map<String, CompatibilityOverridesToRemoveConfig> packageNameToOverridesToRemove;
+
+ public CompatibilityOverridesToRemoveByPackageConfig(
+ Map<String, CompatibilityOverridesToRemoveConfig> packageNameToOverridesToRemove) {
+ this.packageNameToOverridesToRemove = packageNameToOverridesToRemove;
+ }
+
+ private CompatibilityOverridesToRemoveByPackageConfig(Parcel in) {
+ int keyCount = in.readInt();
+ packageNameToOverridesToRemove = new HashMap<>();
+ for (int i = 0; i < keyCount; i++) {
+ String key = in.readString();
+ packageNameToOverridesToRemove.put(key,
+ CompatibilityOverridesToRemoveConfig.CREATOR.createFromParcel(in));
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(packageNameToOverridesToRemove.size());
+ for (String key : packageNameToOverridesToRemove.keySet()) {
+ dest.writeString(key);
+ packageNameToOverridesToRemove.get(key).writeToParcel(dest, /* flags= */ 0);
+ }
+ }
+
+ public static final Parcelable.Creator<CompatibilityOverridesToRemoveByPackageConfig> CREATOR =
+ new Parcelable.Creator<CompatibilityOverridesToRemoveByPackageConfig>() {
+
+ @Override
+ public CompatibilityOverridesToRemoveByPackageConfig createFromParcel(Parcel in) {
+ return new CompatibilityOverridesToRemoveByPackageConfig(in);
+ }
+
+ @Override
+ public CompatibilityOverridesToRemoveByPackageConfig[] newArray(int size) {
+ return new CompatibilityOverridesToRemoveByPackageConfig[size];
+ }
+ };
+}
diff --git a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
index 642f79ca7afa..e85afefdc39a 100644
--- a/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityOverridesToRemoveConfig.java
@@ -26,6 +26,8 @@ import java.util.Set;
/**
* Parcelable containing compat config change IDs for which to remove overrides for a given
* application.
+ *
+ * <p>This class is separate from CompatibilityOverrideConfig since we only need change IDs.
* @hide
*/
public final class CompatibilityOverridesToRemoveConfig implements Parcelable {
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 418d16e07a75..8847a490e39c 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -22,6 +22,8 @@ import java.util.Map;
parcelable CompatibilityChangeConfig;
parcelable CompatibilityOverrideConfig;
+parcelable CompatibilityOverridesByPackageConfig;
+parcelable CompatibilityOverridesToRemoveByPackageConfig;
parcelable CompatibilityOverridesToRemoveConfig;
parcelable CompatibilityChangeInfo;
/**
@@ -152,6 +154,26 @@ interface IPlatformCompat {
void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
/**
+ * Adds overrides to compatibility changes on release builds for multiple apps.
+ *
+ * <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 overridesByPackage} 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 overridesByPackage parcelable containing the compat change overrides to be applied
+ * on specific apps by their package name
+ * @throws SecurityException if overriding changes is not permitted
+ */
+ void putAllOverridesOnReleaseBuilds(in CompatibilityOverridesByPackageConfig overridesByPackage);
+
+ /**
* Adds overrides to compatibility changes on release builds.
*
* <p>The caller to this API needs to hold
@@ -206,6 +228,26 @@ interface IPlatformCompat {
boolean clearOverrideForTest(long changeId, String packageName);
/**
+ * Restores the default behaviour for compatibility changes on release builds for multiple apps.
+ *
+ * <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 overridesToRemoveByPackage} 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 overridesToRemoveByPackage parcelable containing the compat change overrides to be
+ * removed for specific apps by their package name
+ * @throws SecurityException if overriding changes is not permitted
+ */
+ void removeAllOverridesOnReleaseBuilds(in CompatibilityOverridesToRemoveByPackageConfig overridesToRemoveByPackage);
+
+ /**
* Restores the default behaviour for compatibility changes on release builds.
*
* <p>The caller to this API needs to hold
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index e2e56ae8e620..9dd7daf1a1cc 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -36,6 +36,8 @@ import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesByPackageConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveByPackageConfig;
import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
@@ -220,15 +222,47 @@ final class CompatConfig {
}
/**
- * Overrides the enabled state for a given change and app.
+ * Adds compat config overrides for multiple packages.
+ *
+ * <p>Equivalent to calling
+ * {@link #addPackageOverrides(CompatibilityOverrideConfig, String, boolean)} on each entry
+ * in {@code overridesByPackage}, but the state of the compat config will be updated only
+ * once instead of for each package.
+ *
+ * @param overridesByPackage map from package name to compat config overrides to add for that
+ * package.
+ * @param skipUnknownChangeIds whether to skip unknown change IDs in {@code overridesByPackage}.
+ */
+ synchronized void addAllPackageOverrides(
+ CompatibilityOverridesByPackageConfig overridesByPackage,
+ boolean skipUnknownChangeIds) {
+ for (String packageName : overridesByPackage.packageNameToOverrides.keySet()) {
+ addPackageOverridesWithoutSaving(
+ overridesByPackage.packageNameToOverrides.get(packageName), packageName,
+ skipUnknownChangeIds);
+ }
+ saveOverrides();
+ invalidateCache();
+ }
+
+ /**
+ * Adds compat config overrides for a given package.
*
+ * <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
*
- * @param overrides list of overrides to default changes config.
- * @param packageName app for which the overrides will be applied.
+ * @param overrides list of compat config overrides to add for the given package.
+ * @param packageName app for which the overrides will be applied.
* @param skipUnknownChangeIds whether to skip unknown change IDs in {@code overrides}.
*/
synchronized void addPackageOverrides(CompatibilityOverrideConfig overrides,
String packageName, boolean skipUnknownChangeIds) {
+ addPackageOverridesWithoutSaving(overrides, packageName, skipUnknownChangeIds);
+ saveOverrides();
+ invalidateCache();
+ }
+
+ private void addPackageOverridesWithoutSaving(CompatibilityOverrideConfig overrides,
+ String packageName, boolean skipUnknownChangeIds) {
for (Long changeId : overrides.overrides.keySet()) {
if (skipUnknownChangeIds && !isKnownChangeId(changeId)) {
Slog.w(TAG, "Trying to add overrides for unknown Change ID " + changeId + ". "
@@ -237,8 +271,6 @@ final class CompatConfig {
}
addOverrideUnsafe(changeId, packageName, overrides.overrides.get(changeId));
}
- saveOverrides();
- invalidateCache();
}
private boolean addOverrideUnsafe(long changeId, String packageName,
@@ -344,6 +376,36 @@ final class CompatConfig {
}
/**
+ * Removes overrides with a specified change ID that were previously added via
+ * {@link #addOverride(long, String, boolean)} or
+ * {@link #addPackageOverrides(CompatibilityOverrideConfig, String, boolean)} for multiple
+ * packages.
+ *
+ * <p>Equivalent to calling
+ * {@link #removePackageOverrides(CompatibilityOverridesToRemoveConfig, String)} on each entry
+ * in {@code overridesToRemoveByPackage}, but the state of the compat config will be updated
+ * only once instead of for each package.
+ *
+ * @param overridesToRemoveByPackage map from package name to a list of change IDs for
+ * which to restore the default behaviour for that
+ * package.
+ */
+ synchronized void removeAllPackageOverrides(
+ CompatibilityOverridesToRemoveByPackageConfig overridesToRemoveByPackage) {
+ boolean shouldInvalidateCache = false;
+ for (String packageName :
+ overridesToRemoveByPackage.packageNameToOverridesToRemove.keySet()) {
+ shouldInvalidateCache |= removePackageOverridesWithoutSaving(
+ overridesToRemoveByPackage.packageNameToOverridesToRemove.get(packageName),
+ packageName);
+ }
+ if (shouldInvalidateCache) {
+ saveOverrides();
+ invalidateCache();
+ }
+ }
+
+ /**
* Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or
* {@link #addPackageOverrides(CompatibilityOverrideConfig, String, boolean)} for a certain
* package.
@@ -377,6 +439,16 @@ final class CompatConfig {
*/
synchronized void removePackageOverrides(CompatibilityOverridesToRemoveConfig overridesToRemove,
String packageName) {
+ boolean shouldInvalidateCache = removePackageOverridesWithoutSaving(overridesToRemove,
+ packageName);
+ if (shouldInvalidateCache) {
+ saveOverrides();
+ invalidateCache();
+ }
+ }
+
+ private boolean removePackageOverridesWithoutSaving(
+ CompatibilityOverridesToRemoveConfig overridesToRemove, String packageName) {
boolean shouldInvalidateCache = false;
for (Long changeId : overridesToRemove.changeIds) {
if (!isKnownChangeId(changeId)) {
@@ -386,10 +458,7 @@ final class CompatConfig {
}
shouldInvalidateCache |= removeOverrideUnsafe(changeId, packageName);
}
- if (shouldInvalidateCache) {
- saveOverrides();
- invalidateCache();
- }
+ return shouldInvalidateCache;
}
private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName,
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 6ea89d445402..fec9f40e051a 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -47,6 +47,8 @@ import com.android.internal.compat.ChangeReporter;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesByPackageConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveByPackageConfig;
import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.IPlatformCompat;
@@ -226,6 +228,18 @@ public class PlatformCompat extends IPlatformCompat.Stub {
}
@Override
+ public void putAllOverridesOnReleaseBuilds(
+ CompatibilityOverridesByPackageConfig overridesByPackage) {
+ // TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods.
+ checkCompatChangeOverrideOverridablePermission();
+ for (CompatibilityOverrideConfig overrides :
+ overridesByPackage.packageNameToOverrides.values()) {
+ checkAllCompatOverridesAreOverridable(overrides.overrides.keySet());
+ }
+ mCompatConfig.addAllPackageOverrides(overridesByPackage, /* skipUnknownChangeIds= */ true);
+ }
+
+ @Override
public void putOverridesOnReleaseBuilds(CompatibilityOverrideConfig overrides,
String packageName) {
// TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods.
@@ -280,6 +294,18 @@ public class PlatformCompat extends IPlatformCompat.Stub {
}
@Override
+ public void removeAllOverridesOnReleaseBuilds(
+ CompatibilityOverridesToRemoveByPackageConfig overridesToRemoveByPackage) {
+ // TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods.
+ checkCompatChangeOverrideOverridablePermission();
+ for (CompatibilityOverridesToRemoveConfig overridesToRemove :
+ overridesToRemoveByPackage.packageNameToOverridesToRemove.values()) {
+ checkAllCompatOverridesAreOverridable(overridesToRemove.changeIds);
+ }
+ mCompatConfig.removeAllPackageOverrides(overridesToRemoveByPackage);
+ }
+
+ @Override
public void removeOverridesOnReleaseBuilds(
CompatibilityOverridesToRemoveConfig overridesToRemove,
String packageName) {
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 d926dcba54a3..b2854ceb1017 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,8 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesByPackageConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveByPackageConfig;
import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import org.junit.Before;
@@ -303,6 +305,51 @@ public class CompatConfigTest {
assertThat(compatConfig.isChangeEnabled(unknownChangeId, applicationInfo)).isTrue();
}
+ @Test
+ public void testInstallerCanAddOverridesForMultiplePackages() throws Exception {
+ final String packageName1 = "com.some.package1";
+ final String packageName2 = "com.some.package2";
+ final long disabledChangeId1 = 1234L;
+ final long disabledChangeId2 = 1235L;
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledOverridableChangeWithId(disabledChangeId1)
+ .addDisabledOverridableChangeWithId(disabledChangeId2)
+ .build();
+ ApplicationInfo applicationInfo1 = ApplicationInfoBuilder.create()
+ .withPackageName(packageName1)
+ .build();
+ ApplicationInfo applicationInfo2 = ApplicationInfoBuilder.create()
+ .withPackageName(packageName2)
+ .build();
+ PackageManager packageManager = mock(PackageManager.class);
+ when(mContext.getPackageManager()).thenReturn(packageManager);
+ when(packageManager.getApplicationInfo(eq(packageName1), anyInt()))
+ .thenReturn(applicationInfo1);
+ when(packageManager.getApplicationInfo(eq(packageName2), anyInt()))
+ .thenReturn(applicationInfo2);
+
+ // 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> overrides1 = new HashMap<>();
+ overrides1.put(disabledChangeId1, new PackageOverride.Builder().setEnabled(true).build());
+ Map<Long, PackageOverride> overrides2 = new HashMap<>();
+ overrides2.put(disabledChangeId1, new PackageOverride.Builder().setEnabled(true).build());
+ overrides2.put(disabledChangeId2, new PackageOverride.Builder().setEnabled(true).build());
+ Map<String, CompatibilityOverrideConfig> packageNameToOverrides = new HashMap<>();
+ packageNameToOverrides.put(packageName1, new CompatibilityOverrideConfig(overrides1));
+ packageNameToOverrides.put(packageName2, new CompatibilityOverrideConfig(overrides2));
+ CompatibilityOverridesByPackageConfig config = new CompatibilityOverridesByPackageConfig(
+ packageNameToOverrides);
+
+ compatConfig.addAllPackageOverrides(config, /* skipUnknownChangeIds */ true);
+
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo1)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo1)).isFalse();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo2)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo2)).isTrue();
+ }
+
@Test
public void testPreventInstallerSetNonOverridable() throws Exception {
@@ -641,6 +688,73 @@ public class CompatConfigTest {
}
@Test
+ public void testInstallerCanRemoveOverridesForMultiplePackages() throws Exception {
+ final String packageName1 = "com.some.package1";
+ final String packageName2 = "com.some.package2";
+ final long disabledChangeId1 = 1234L;
+ final long disabledChangeId2 = 1235L;
+ final long enabledChangeId = 1236L;
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledOverridableChangeWithId(disabledChangeId1)
+ .addDisabledOverridableChangeWithId(disabledChangeId2)
+ .addEnabledOverridableChangeWithId(enabledChangeId)
+ .build();
+ ApplicationInfo applicationInfo1 = ApplicationInfoBuilder.create()
+ .withPackageName(packageName1)
+ .build();
+ ApplicationInfo applicationInfo2 = ApplicationInfoBuilder.create()
+ .withPackageName(packageName2)
+ .build();
+ PackageManager packageManager = mock(PackageManager.class);
+ when(mContext.getPackageManager()).thenReturn(packageManager);
+ when(packageManager.getApplicationInfo(eq(packageName1), anyInt()))
+ .thenReturn(applicationInfo1);
+ when(packageManager.getApplicationInfo(eq(packageName2), anyInt()))
+ .thenReturn(applicationInfo2);
+
+ assertThat(compatConfig.addOverride(disabledChangeId1, packageName1, true)).isTrue();
+ assertThat(compatConfig.addOverride(disabledChangeId2, packageName1, true)).isTrue();
+ assertThat(compatConfig.addOverride(enabledChangeId, packageName1, false)).isTrue();
+ assertThat(compatConfig.addOverride(disabledChangeId1, packageName2, true)).isTrue();
+ assertThat(compatConfig.addOverride(disabledChangeId2, packageName2, true)).isTrue();
+ assertThat(compatConfig.addOverride(enabledChangeId, packageName2, false)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo1)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo1)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(enabledChangeId, applicationInfo1)).isFalse();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo1)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo1)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(enabledChangeId, applicationInfo1)).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> overridesToRemove1 = new HashSet<>();
+ overridesToRemove1.add(disabledChangeId1);
+ overridesToRemove1.add(enabledChangeId);
+ Set<Long> overridesToRemove2 = new HashSet<>();
+ overridesToRemove2.add(disabledChangeId1);
+ overridesToRemove2.add(disabledChangeId2);
+ Map<String, CompatibilityOverridesToRemoveConfig> packageNameToOverridesToRemove =
+ new HashMap<>();
+ packageNameToOverridesToRemove.put(packageName1,
+ new CompatibilityOverridesToRemoveConfig(overridesToRemove1));
+ packageNameToOverridesToRemove.put(packageName2,
+ new CompatibilityOverridesToRemoveConfig(overridesToRemove2));
+ CompatibilityOverridesToRemoveByPackageConfig config =
+ new CompatibilityOverridesToRemoveByPackageConfig(packageNameToOverridesToRemove);
+
+ compatConfig.removeAllPackageOverrides(config);
+
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo1)).isFalse();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo1)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(enabledChangeId, applicationInfo1)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo2)).isFalse();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo2)).isFalse();
+ assertThat(compatConfig.isChangeEnabled(enabledChangeId, applicationInfo2)).isFalse();
+ }
+
+ @Test
public void testPreventInstallerRemoveNonOverridable() throws Exception {
final long disabledChangeId1 = 1234L;
final long disabledChangeId2 = 1235L;