diff options
| author | 2021-12-16 17:22:32 +0000 | |
|---|---|---|
| committer | 2021-12-22 10:27:41 +0000 | |
| commit | 6162b703490f73047f6c7a04723668ced64c50b5 (patch) | |
| tree | c1bbb4e792faf5b0ec1288643ecbd18e7544f318 | |
| parent | 4e59b1968c504f59ba6047405c54d782f00f9b34 (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
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; |