diff options
5 files changed, 180 insertions, 68 deletions
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 5857642cbd4e..045a6800bb13 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -133,7 +133,7 @@ interface IPlatformCompat boolean isChangeEnabledByUid(long changeId, int uid); /** - * Add overrides to compatibility changes. + * Add overrides to compatibility changes. Kills the app to allow the changes to take effect. * * @param overrides Parcelable containing the compat change overrides to be applied. * @param packageName The package name of the app whose changes will be overridden. @@ -142,7 +142,28 @@ interface IPlatformCompat void setOverrides(in CompatibilityChangeConfig overrides, in String packageName); /** - * Revert overrides to compatibility changes. + * Add overrides to compatibility changes. Doesn't kill the app, to be only used in tests. + * + * @param overrides Parcelable containing the compat change overrides to be applied. + * @param packageName The package name of the app whose changes will be overridden. + * + */ + void setOverridesForTest(in CompatibilityChangeConfig overrides, in String packageName); + + /** + * Removes an override previously added via {@link #setOverrides(CompatibilityChangeConfig, + * String)}. This restores the default behaviour for the given change and app, once any app + * processes have been restarted. + * Kills the app to allow the changes to take effect. + * + * @param changeId The ID of the change that was overridden. + * @param packageName The app package name that was overridden. + * @return {@code true} if an override existed; + */ + boolean clearOverride(long changeId, String packageName); + + /** + * Revert overrides to compatibility changes. Kills the app to allow the changes to take effect. * * @param packageName The package name of the app whose overrides will be cleared. * diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e5c8ba515446..5af1480af94b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -349,7 +349,6 @@ import com.android.server.ThreadPriorityBooster; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto; import com.android.server.appop.AppOpsService; -import com.android.server.compat.CompatConfig; import com.android.server.compat.PlatformCompat; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.firewall.IntentFirewall; @@ -5049,8 +5048,9 @@ public class ActivityManagerService extends IActivityManager.Stub bindApplicationTimeMillis = SystemClock.elapsedRealtime(); mAtmInternal.preBindApplication(app.getWindowProcessController()); final ActiveInstrumentation instr2 = app.getActiveInstrumentation(); - long[] disabledCompatChanges = CompatConfig.get().getDisabledChanges(app.info); + long[] disabledCompatChanges = {}; if (mPlatformCompat != null) { + disabledCompatChanges = mPlatformCompat.getDisabledChanges(app.info); mPlatformCompat.resetReporting(app.info); } if (app.isolatedEntryPoint != null) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 6e92e80424fa..d9e4844209d0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -41,6 +41,7 @@ import android.app.usage.AppStandbyInfo; import android.app.usage.ConfigurationStats; import android.app.usage.IUsageStatsManager; import android.app.usage.UsageStatsManager; +import android.compat.Compatibility; import android.content.ComponentCallbacks2; import android.content.ComponentName; import android.content.Context; @@ -80,15 +81,17 @@ import android.os.UserManager; import android.text.TextUtils; import android.text.format.Time; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.DebugUtils; import android.util.DisplayMetrics; import android.util.proto.ProtoOutputStream; import android.view.Display; +import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.util.HexDump; import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; -import com.android.server.compat.CompatConfig; +import com.android.server.compat.PlatformCompat; import java.io.BufferedReader; import java.io.File; @@ -2868,56 +2871,49 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } - private void killPackage(String packageName, PrintWriter pw) throws RemoteException { - int uid = mPm.getPackageUid(packageName, 0, mUserId); - if (uid < 0) { - // uid is negative if the package wasn't found. - pw.println("Didn't find package " + packageName + " on device."); - } else { - pw.println("Killing package " + packageName + " (UID " + uid + ")."); - final long origId = Binder.clearCallingIdentity(); - mInterface.killUid(UserHandle.getAppId(uid), - UserHandle.USER_ALL, "killPackage"); - Binder.restoreCallingIdentity(origId); - } - } - private int runCompat(PrintWriter pw) throws RemoteException { - final CompatConfig config = CompatConfig.get(); + final PlatformCompat platformCompat = (PlatformCompat) + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); String toggleValue = getNextArgRequired(); long changeId; String changeIdString = getNextArgRequired(); try { changeId = Long.parseLong(changeIdString); } catch (NumberFormatException e) { - changeId = config.lookupChangeId(changeIdString); + changeId = platformCompat.lookupChangeId(changeIdString); } if (changeId == -1) { pw.println("Unknown or invalid change: '" + changeIdString + "'."); + return -1; } String packageName = getNextArgRequired(); + if (!platformCompat.isKnownChangeId(changeId)) { + pw.println("Warning! Change " + changeId + " is not known yet. Enabling/disabling it" + + " could have no effect."); + } + ArraySet<Long> enabled = new ArraySet<>(); + ArraySet<Long> disabled = new ArraySet<>(); switch (toggleValue) { case "enable": - if (!config.addOverride(changeId, packageName, true)) { - pw.println("Warning! Change " + changeId + " is not known yet. Enabling it" - + " could have no effect."); - } + enabled.add(changeId); pw.println("Enabled change " + changeId + " for " + packageName + "."); - killPackage(packageName, pw); + CompatibilityChangeConfig overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); return 0; case "disable": - if (!config.addOverride(changeId, packageName, false)) { - pw.println("Warning! Change " + changeId + " is not known yet. Disabling it" - + " could have no effect."); - } + disabled.add(changeId); pw.println("Disabled change " + changeId + " for " + packageName + "."); - killPackage(packageName, pw); + overrides = + new CompatibilityChangeConfig( + new Compatibility.ChangeConfig(enabled, disabled)); + platformCompat.setOverrides(overrides, packageName); return 0; case "reset": - if (config.removeOverride(changeId, packageName)) { + if (platformCompat.clearOverride(changeId, packageName)) { pw.println("Reset change " + changeId + " for " + packageName + " to default value."); - killPackage(packageName, pw); } else { pw.println("No override exists for changeId " + changeId + "."); } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index d6ec22b078ea..490cce347188 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -43,13 +43,14 @@ import java.util.HashSet; import java.util.Set; import javax.xml.datatype.DatatypeConfigurationException; + /** * This class maintains state relating to platform compatibility changes. * * <p>It stores the default configuration for each change, and any per-package overrides that have * been configured. */ -public final class CompatConfig { +final class CompatConfig { private static final String TAG = "CompatConfig"; @@ -61,13 +62,13 @@ public final class CompatConfig { private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>(); @VisibleForTesting - public CompatConfig() { + CompatConfig() { } /** * @return The static instance of this class to be used within the system server. */ - public static CompatConfig get() { + static CompatConfig get() { return sInstance; } @@ -77,7 +78,7 @@ public final class CompatConfig { * * @param change The change to add. Any change with the same ID will be overwritten. */ - public void addChange(CompatChange change) { + void addChange(CompatChange change) { synchronized (mChanges) { mChanges.put(change.getId(), change); } @@ -89,10 +90,10 @@ public final class CompatConfig { * * @param app The app in question * @return A sorted long array of change IDs. We use a primitive array to minimize memory - * footprint: Every app process will store this array statically so we aim to reduce - * overhead as much as possible. + * footprint: Every app process will store this array statically so we aim to reduce + * overhead as much as possible. */ - public long[] getDisabledChanges(ApplicationInfo app) { + long[] getDisabledChanges(ApplicationInfo app) { LongArray disabled = new LongArray(); synchronized (mChanges) { for (int i = 0; i < mChanges.size(); ++i) { @@ -113,7 +114,7 @@ public final class CompatConfig { * @param name Name of the change to look up * @return The change ID, or {@code -1} if no change with that name exists. */ - public long lookupChangeId(String name) { + long lookupChangeId(String name) { synchronized (mChanges) { for (int i = 0; i < mChanges.size(); ++i) { if (TextUtils.equals(mChanges.valueAt(i).getName(), name)) { @@ -128,11 +129,11 @@ public final class CompatConfig { * Find if a given change is enabled for a given application. * * @param changeId The ID of the change in question - * @param app App to check for + * @param app App to check for * @return {@code true} if the change is enabled for this app. Also returns {@code true} if the - * change ID is not known, as unknown changes are enabled by default. + * change ID is not known, as unknown changes are enabled by default. */ - public boolean isChangeEnabled(long changeId, ApplicationInfo app) { + boolean isChangeEnabled(long changeId, ApplicationInfo app) { synchronized (mChanges) { CompatChange c = mChanges.get(changeId); if (c == null) { @@ -150,14 +151,15 @@ public final class CompatConfig { * * <p>Note, package overrides are not persistent and will be lost on system or runtime restart. * - * @param changeId The ID of the change to be overridden. Note, this call will succeed even if - * this change is not known; it will only have any effect if any code in the - * platform is gated on the ID given. + * @param changeId The ID of the change to be overridden. Note, this call will succeed even + * if + * this change is not known; it will only have any effect if any code in the + * platform is gated on the ID given. * @param packageName The app package name to override the change for. - * @param enabled If the change should be enabled or disabled. + * @param enabled If the change should be enabled or disabled. * @return {@code true} if the change existed before adding the override. */ - public boolean addOverride(long changeId, String packageName, boolean enabled) { + boolean addOverride(long changeId, String packageName, boolean enabled) { boolean alreadyKnown = true; synchronized (mChanges) { CompatChange c = mChanges.get(changeId); @@ -172,15 +174,27 @@ public final class CompatConfig { } /** + * Check whether the change is known to the compat config. + * + * @return {@code true} if the change is known. + */ + boolean isKnownChangeId(long changeId) { + synchronized (mChanges) { + CompatChange c = mChanges.get(changeId); + return c != null; + } + } + + /** * Removes an override previously added via {@link #addOverride(long, String, boolean)}. This * restores the default behaviour for the given change and app, once any app processes have been * restarted. * - * @param changeId The ID of the change that was overridden. + * @param changeId The ID of the change that was overridden. * @param packageName The app package name that was overridden. * @return {@code true} if an override existed; */ - public boolean removeOverride(long changeId, String packageName) { + boolean removeOverride(long changeId, String packageName) { boolean overrideExists = false; synchronized (mChanges) { CompatChange c = mChanges.get(changeId); @@ -191,22 +205,22 @@ public final class CompatConfig { } return overrideExists; } + /** * Overrides the enabled state for a given change and app. This method is intended to be used * *only* for debugging purposes. * * <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 overrides list of overrides to default changes config. * @param packageName app for which the overrides will be applied. */ - public void addOverrides( - CompatibilityChangeConfig overrides, String packageName) { + void addOverrides(CompatibilityChangeConfig overrides, String packageName) { synchronized (mChanges) { - for (Long changeId: overrides.enabledChanges()) { + for (Long changeId : overrides.enabledChanges()) { addOverride(changeId, packageName, true); } - for (Long changeId: overrides.disabledChanges()) { + for (Long changeId : overrides.disabledChanges()) { addOverride(changeId, packageName, false); } } @@ -221,7 +235,7 @@ public final class CompatConfig { * * @param packageName The package for which the overrides should be purged. */ - public void removePackageOverrides(String packageName) { + void removePackageOverrides(String packageName) { synchronized (mChanges) { for (int i = 0; i < mChanges.size(); ++i) { mChanges.valueAt(i).removePackageOverride(packageName); @@ -230,11 +244,11 @@ public final class CompatConfig { } /** - * Dumps the current list of compatibility config information. - * - * @param pw The {@link PrintWriter} instance to which the information will be dumped. - */ - public void dumpConfig(PrintWriter pw) { + * Dumps the current list of compatibility config information. + * + * @param pw The {@link PrintWriter} instance to which the information will be dumped. + */ + void dumpConfig(PrintWriter pw) { synchronized (mChanges) { if (mChanges.size() == 0) { pw.println("No compat overrides."); @@ -252,10 +266,10 @@ public final class CompatConfig { * * @param applicationInfo the {@link ApplicationInfo} for which the info should be dumped. * @return A {@link CompatibilityChangeConfig} which contains the compat config info for the - * given app. + * given app. */ - public CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) { + CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) { Set<Long> enabled = new HashSet<>(); Set<Long> disabled = new HashSet<>(); synchronized (mChanges) { @@ -276,15 +290,15 @@ public final class CompatConfig { * * @return An array of {@link CompatibilityChangeInfo} with the current changes. */ - public CompatibilityChangeInfo[] dumpChanges() { + CompatibilityChangeInfo[] dumpChanges() { synchronized (mChanges) { CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()]; for (int i = 0; i < mChanges.size(); ++i) { CompatChange change = mChanges.valueAt(i); changeInfos[i] = new CompatibilityChangeInfo(change.getId(), - change.getName(), - change.getEnableAfterTargetSdk(), - change.getDisabled()); + change.getName(), + change.getEnableAfterTargetSdk(), + change.getDisabled()); } return changeInfos; } diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 75e2d220898d..709f3f82d4c5 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -16,9 +16,13 @@ package com.android.server.compat; +import android.app.ActivityManager; +import android.app.IActivityManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; import android.util.StatsLog; @@ -106,12 +110,26 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void setOverrides(CompatibilityChangeConfig overrides, String packageName) { CompatConfig.get().addOverrides(overrides, packageName); + killPackage(packageName); + } + + @Override + public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) { + CompatConfig.get().addOverrides(overrides, packageName); } @Override public void clearOverrides(String packageName) { CompatConfig config = CompatConfig.get(); config.removePackageOverrides(packageName); + killPackage(packageName); + } + + @Override + public boolean clearOverride(long changeId, String packageName) { + boolean existed = CompatConfig.get().removeOverride(changeId, packageName); + killPackage(packageName); + return existed; } @Override @@ -124,6 +142,39 @@ public class PlatformCompat extends IPlatformCompat.Stub { return CompatConfig.get().dumpChanges(); } + /** + * Check whether the change is known to the compat config. + * @param changeId + * @return {@code true} if the change is known. + */ + public boolean isKnownChangeId(long changeId) { + return CompatConfig.get().isKnownChangeId(changeId); + + } + + /** + * Retrieves the set of disabled changes for a given app. Any change ID not in the returned + * array is by default enabled for the app. + * + * @param appInfo The app in question + * @return A sorted long array of change IDs. We use a primitive array to minimize memory + * footprint: Every app process will store this array statically so we aim to reduce + * overhead as much as possible. + */ + public long[] getDisabledChanges(ApplicationInfo appInfo) { + return CompatConfig.get().getDisabledChanges(appInfo); + } + + /** + * Look up a change ID by name. + * + * @param name Name of the change to look up + * @return The change ID, or {@code -1} if no change with that name exists. + */ + public long lookupChangeId(String name) { + return CompatConfig.get().lookupChangeId(name); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return; @@ -151,4 +202,34 @@ public class PlatformCompat extends IPlatformCompat.Stub { private void reportChange(long changeId, int uid, int state) { mChangeReporter.reportChange(uid, changeId, state); } + + private void killPackage(String packageName) { + int uid = -1; + try { + uid = mContext.getPackageManager().getPackageUid(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Didn't find package " + packageName + " on device.", e); + return; + } + + Slog.d(TAG, "Killing package " + packageName + " (UID " + uid + ")."); + killUid(UserHandle.getAppId(uid), + UserHandle.USER_ALL, "PlatformCompat overrides"); + } + + private void killUid(int appId, int userId, String reason) { + final long identity = Binder.clearCallingIdentity(); + try { + IActivityManager am = ActivityManager.getService(); + if (am != null) { + try { + am.killUid(appId, userId, reason); + } catch (RemoteException e) { + /* ignore - same process */ + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } } |