From 5b1e2eb495cc8fc3ee48db406536f75ff578dc6c Mon Sep 17 00:00:00 2001 From: Andrei Onea Date: Thu, 27 Feb 2020 15:35:38 +0000 Subject: [PATCH] Add option to toggle all changes This change adds the ability of enabling and disabling all allowed changes for a given package and target sdk through adb. Bug: 149980515 Test: adb shell am enable-all 28 foo.bar && \ adb shell dumpsys platform_compat Test: adb shell am disable-all 28 foo.bar && \ adb shell dumpsys platform_compat Test: atest CompatConfigTest Merged-In: Ia84d8e1162ea0b3f4c6afe87e63db56256236940 Change-Id: I4696e13882b1b2c930731e6e7787924e20da1a46 --- .../internal/compat/IPlatformCompat.aidl | 24 +++++ .../am/ActivityManagerShellCommand.java | 97 +++++++++++++------ .../android/server/compat/CompatConfig.java | 57 +++++++++++ .../android/server/compat/PlatformCompat.java | 20 ++++ .../server/compat/CompatConfigTest.java | 43 ++++++++ 5 files changed, 212 insertions(+), 29 deletions(-) diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 4c203d3947595..523ed6fa9a4f0 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -163,6 +163,30 @@ interface IPlatformCompat */ boolean clearOverride(long changeId, String packageName); + /** + * Enable all compatibility changes which have enabledAfterTargetSdk == + * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the + * changes to take effect. + * + * @param packageName The package name of the app whose compatibility changes will be enabled. + * @param targetSdkVersion The targetSdkVersion for filtering the changes to be enabled. + * + * @return The number of changes that were enabled. + */ + int enableTargetSdkChanges(in String packageName, int targetSdkVersion); + + /** + * Disable all compatibility changes which have enabledAfterTargetSdk == + * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the + * changes to take effect. + * + * @param packageName The package name of the app whose compatibility changes will be disabled. + * @param targetSdkVersion The targetSdkVersion for filtering the changes to be disabled. + * + * @return The number of changes that were disabled. + */ + int disableTargetSdkChanges(in String packageName, int targetSdkVersion); + /** * Revert overrides to compatibility changes. Kills the app to allow the changes to take effect. * diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 59f64ac8c6893..8f5fbf7431e11 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2931,25 +2931,35 @@ final class ActivityManagerShellCommand extends ShellCommand { final PlatformCompat platformCompat = (PlatformCompat) ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); String toggleValue = getNextArgRequired(); - if (toggleValue.equals("reset-all")) { - final String packageName = getNextArgRequired(); - pw.println("Reset all changes for " + packageName + " to default value."); - platformCompat.clearOverrides(packageName); - return 0; - } - long changeId; - String changeIdString = getNextArgRequired(); - try { - changeId = Long.parseLong(changeIdString); - } catch (NumberFormatException e) { - changeId = platformCompat.lookupChangeId(changeIdString); - } - if (changeId == -1) { - pw.println("Unknown or invalid change: '" + changeIdString + "'."); - return -1; + boolean toggleAll = false; + int targetSdkVersion = -1; + long changeId = -1; + + if (toggleValue.endsWith("-all")) { + toggleValue = toggleValue.substring(0, toggleValue.lastIndexOf("-all")); + toggleAll = true; + if (!toggleValue.equals("reset")) { + try { + targetSdkVersion = Integer.parseInt(getNextArgRequired()); + } catch (NumberFormatException e) { + pw.println("Invalid targetSdkVersion!"); + return -1; + } + } + } else { + String changeIdString = getNextArgRequired(); + try { + changeId = Long.parseLong(changeIdString); + } catch (NumberFormatException e) { + changeId = platformCompat.lookupChangeId(changeIdString); + } + if (changeId == -1) { + pw.println("Unknown or invalid change: '" + changeIdString + "'."); + return -1; + } } String packageName = getNextArgRequired(); - if (!platformCompat.isKnownChangeId(changeId)) { + if (!toggleAll && !platformCompat.isKnownChangeId(changeId)) { pw.println("Warning! Change " + changeId + " is not known yet. Enabling/disabling it" + " could have no effect."); } @@ -2958,22 +2968,49 @@ final class ActivityManagerShellCommand extends ShellCommand { try { switch (toggleValue) { case "enable": - enabled.add(changeId); - CompatibilityChangeConfig overrides = - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); - pw.println("Enabled change " + changeId + " for " + packageName + "."); + if (toggleAll) { + int numChanges = platformCompat.enableTargetSdkChanges(packageName, + targetSdkVersion); + if (numChanges == 0) { + pw.println("No changes were enabled."); + return -1; + } + pw.println("Enabled " + numChanges + " changes gated by targetSdkVersion " + + targetSdkVersion + " for " + packageName + "."); + } else { + enabled.add(changeId); + CompatibilityChangeConfig overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); + pw.println("Enabled change " + changeId + " for " + packageName + "."); + } return 0; case "disable": - disabled.add(changeId); - overrides = - new CompatibilityChangeConfig( - new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); - pw.println("Disabled change " + changeId + " for " + packageName + "."); + if (toggleAll) { + int numChanges = platformCompat.disableTargetSdkChanges(packageName, + targetSdkVersion); + if (numChanges == 0) { + pw.println("No changes were disabled."); + return -1; + } + pw.println("Disabled " + numChanges + " changes gated by targetSdkVersion " + + targetSdkVersion + " for " + packageName + "."); + } else { + disabled.add(changeId); + CompatibilityChangeConfig overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); + pw.println("Disabled change " + changeId + " for " + packageName + "."); + } return 0; case "reset": + if (toggleAll) { + platformCompat.clearOverrides(packageName); + pw.println("Reset all changes for " + packageName + " to default value."); + return 0; + } if (platformCompat.clearOverride(changeId, packageName)) { pw.println("Reset change " + changeId + " for " + packageName + " to default value."); @@ -3304,6 +3341,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" enable|disable|reset "); pw.println(" Toggles a change either by id or by name for ."); pw.println(" It kills (to allow the toggle to take effect)."); + pw.println(" enable-all|disable-all ."); pw.println(" reset-all "); pw.println(" Removes all existing overrides for all changes for "); pw.println(" (back to default behaviour)."); diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index 441d9d9f380ef..41813bf4746a6 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -288,6 +288,63 @@ final class CompatConfig { } } + private long[] getAllowedChangesAfterTargetSdkForPackage(String packageName, + int targetSdkVersion) + throws RemoteException { + LongArray allowed = new LongArray(); + synchronized (mChanges) { + for (int i = 0; i < mChanges.size(); ++i) { + try { + CompatChange change = mChanges.valueAt(i); + if (change.getEnableAfterTargetSdk() != targetSdkVersion) { + continue; + } + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(change.getId(), + packageName); + if (allowedState.state == OverrideAllowedState.ALLOWED) { + allowed.add(change.getId()); + } + } catch (RemoteException e) { + // Should never occur, since validator is in the same process. + throw new RuntimeException("Unable to call override validator!", e); + } + } + } + return allowed.toArray(); + } + + /** + * Enables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for + * {@param packageName}. + * + * @return The number of changes that were toggled. + */ + int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) + throws RemoteException { + long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion); + for (long changeId : changes) { + addOverride(changeId, packageName, true); + } + return changes.length; + } + + + /** + * Disables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for + * {@param packageName}. + * + * @return The number of changes that were toggled. + */ + int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) + throws RemoteException { + long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion); + for (long changeId : changes) { + addOverride(changeId, packageName, false); + } + return changes.length; + } + boolean registerListener(long changeId, CompatChange.ChangeListener listener) { boolean alreadyKnown = true; synchronized (mChanges) { diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 821653af301d3..8519b00237aa4 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -165,6 +165,26 @@ public class PlatformCompat extends IPlatformCompat.Stub { mCompatConfig.addOverrides(overrides, packageName); } + @Override + public int enableTargetSdkChanges(String packageName, int targetSdkVersion) + throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); + int numChanges = mCompatConfig.enableTargetSdkChangesForPackage(packageName, + targetSdkVersion); + killPackage(packageName); + return numChanges; + } + + @Override + public int disableTargetSdkChanges(String packageName, int targetSdkVersion) + throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); + int numChanges = mCompatConfig.disableTargetSdkChangesForPackage(packageName, + targetSdkVersion); + killPackage(packageName); + return numChanges; + } + @Override public void clearOverrides(String packageName) throws RemoteException, SecurityException { checkCompatChangeOverridePermission(); 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 44f4ccf69cdd3..7a824304247d2 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -255,6 +255,49 @@ public class CompatConfigTest { assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue(); } + @Test + public void testEnableTargetSdkChangesForPackage() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithId(1L) + .addDisabledChangeWithId(2L) + .addTargetSdkChangeWithId(3, 3L) + .addTargetSdkChangeWithId(4, 4L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("foo.bar") + .withTargetSdk(2) + .build(); + + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isFalse(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + + assertThat(compatConfig.enableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1); + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isTrue(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + } + + @Test + public void testDisableTargetSdkChangesForPackage() throws Exception { + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithId(1L) + .addDisabledChangeWithId(2L) + .addTargetSdkChangeWithId(3, 3L) + .addTargetSdkChangeWithId(4, 4L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("foo.bar") + .withTargetSdk(2) + .build(); + + assertThat(compatConfig.enableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1); + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isTrue(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + + assertThat(compatConfig.disableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1); + assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isFalse(); + assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse(); + } + @Test public void testLookupChangeId() throws Exception { CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)