Merge "Refactor PlatformCompat and related files."

This commit is contained in:
satayev
2021-01-20 14:13:45 +00:00
committed by Gerrit Code Review
3 changed files with 254 additions and 269 deletions

View File

@@ -22,17 +22,16 @@ import java.util.Map;
parcelable CompatibilityChangeConfig; parcelable CompatibilityChangeConfig;
parcelable CompatibilityChangeInfo; parcelable CompatibilityChangeInfo;
/** /**
* Platform private API for talking with the PlatformCompat service. * Platform private API for talking with the PlatformCompat service.
* *
* <p> Should be used for gating and logging from non-app processes. * <p>Should be used for gating and logging from non-app processes.
* For app processes please use android.compat.Compatibility API. *
* <p>Note: for app processes please use {@code android.compat.Compatibility} API.
* *
* {@hide} * {@hide}
*/ */
interface IPlatformCompat interface IPlatformCompat {
{
/** /**
* Reports that a compatibility change is affecting an app process now. * Reports that a compatibility change is affecting an app process now.
@@ -40,8 +39,9 @@ interface IPlatformCompat
* <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)}, * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)},
* you do not need to call this API directly. The change will be reported for you. * you do not need to call this API directly. The change will be reported for you.
* *
* @param changeId The ID of the compatibility change taking effect. * @param changeId the ID of the compatibility change taking effect
* @param appInfo Representing the affected app. * @param appInfo representing the affected app
* @throws SecurityException if logging is not allowed
*/ */
void reportChange(long changeId, in ApplicationInfo appInfo); void reportChange(long changeId, in ApplicationInfo appInfo);
@@ -51,11 +51,12 @@ interface IPlatformCompat
* <p>Note: for changes that are gated using {@link #isChangeEnabled(long, String)}, * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, String)},
* you do not need to call this API directly. The change will be reported for you. * you do not need to call this API directly. The change will be reported for you.
* *
* @param changeId The ID of the compatibility change taking effect. * @param changeId the ID of the compatibility change taking effect
* @param userId The ID of the user that the operation is done for. * @param userId the ID of the user that the operation is done for
* @param packageName The package name of the app in question. * @param packageName the package name of the app in question
* @throws SecurityException if logging is not allowed
*/ */
void reportChangeByPackageName(long changeId, in String packageName, int userId); void reportChangeByPackageName(long changeId, in String packageName, int userId);
/** /**
* Reports that a compatibility change is affecting an app process now. * Reports that a compatibility change is affecting an app process now.
@@ -63,13 +64,14 @@ interface IPlatformCompat
* <p>Note: for changes that are gated using {@link #isChangeEnabled(long, int)}, * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, int)},
* you do not need to call this API directly. The change will be reported for you. * you do not need to call this API directly. The change will be reported for you.
* *
* @param changeId The ID of the compatibility change taking effect. * @param changeId the ID of the compatibility change taking effect
* @param uid The UID of the app in question. * @param uid the UID of the app in question
* @throws SecurityException if logging is not allowed
*/ */
void reportChangeByUid(long changeId, int uid); void reportChangeByUid(long changeId, int uid);
/** /**
* Query if a given compatibility change is enabled for an app process. This method should * Queries if a given compatibility change is enabled for an app process. This method should
* be called when implementing functionality on behalf of the affected app. * be called when implementing functionality on behalf of the affected app.
* *
* <p>If this method returns {@code true}, the calling code should implement the compatibility * <p>If this method returns {@code true}, the calling code should implement the compatibility
@@ -79,14 +81,15 @@ interface IPlatformCompat
* <p>It will also report the change as {@link #reportChange(long, ApplicationInfo)} would, so * <p>It will also report the change as {@link #reportChange(long, ApplicationInfo)} would, so
* there is no need to call that method directly. * there is no need to call that method directly.
* *
* @param changeId The ID of the compatibility change in question. * @param changeId the ID of the compatibility change in question
* @param appInfo Representing the app in question. * @param appInfo representing the app in question
* @return {@code true} if the change is enabled for the current app. * @return {@code true} if the change is enabled for the current app
* @throws SecurityException if logging or reading compat confis is not allowed
*/ */
boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo); boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo);
/** /**
* Query if a given compatibility change is enabled for an app process. This method should * Queries if a given compatibility change is enabled for an app process. This method should
* be called when implementing functionality on behalf of the affected app. * be called when implementing functionality on behalf of the affected app.
* *
* <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a package name * <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a package name
@@ -102,15 +105,16 @@ interface IPlatformCompat
* <p>It will also report the change as {@link #reportChange(long, String)} would, so there is * <p>It will also report the change as {@link #reportChange(long, String)} would, so there is
* no need to call that method directly. * no need to call that method directly.
* *
* @param changeId The ID of the compatibility change in question. * @param changeId the ID of the compatibility change in question
* @param packageName The package name of the app in question. * @param packageName the package name of the app in question
* @param userId The ID of the user that the operation is done for. * @param userId the ID of the user that the operation is done for
* @return {@code true} if the change is enabled for the current app. * @return {@code true} if the change is enabled for the current app
* @throws SecurityException if logging or reading compat confis is not allowed
*/ */
boolean isChangeEnabledByPackageName(long changeId, in String packageName, int userId); boolean isChangeEnabledByPackageName(long changeId, in String packageName, int userId);
/** /**
* Query if a given compatibility change is enabled for an app process. This method should * Queries if a given compatibility change is enabled for an app process. This method should
* be called when implementing functionality on behalf of the affected app. * be called when implementing functionality on behalf of the affected app.
* *
* <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a uid * <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a uid
@@ -127,110 +131,121 @@ interface IPlatformCompat
* <p>It will also report the change as {@link #reportChange(long, int)} would, so there is * <p>It will also report the change as {@link #reportChange(long, int)} would, so there is
* no need to call that method directly. * no need to call that method directly.
* *
* @param changeId The ID of the compatibility change in question. * @param changeId the ID of the compatibility change in question
* @param uid The UID of the app in question. * @param uid the UID of the app in question
* @return {@code true} if the change is enabled for the current app. * @return {@code true} if the change is enabled for the current app
* @throws SecurityException if logging or reading compat confis is not allowed
*/ */
boolean isChangeEnabledByUid(long changeId, int uid); boolean isChangeEnabledByUid(long changeId, int uid);
/** /**
* Add overrides to compatibility changes. Kills the app to allow the changes to take effect. * Adds overrides to compatibility changes.
* *
* @param overrides Parcelable containing the compat change overrides to be applied. * <p>Kills the app to allow the changes to take effect.
* @param packageName The package name of the app whose changes will be overridden.
* *
* @param overrides parcelable containing the compat change overrides to be applied
* @param packageName the package name of the app whose changes will be overridden
* @throws SecurityException if overriding changes is not permitted
*/ */
void setOverrides(in CompatibilityChangeConfig overrides, in String packageName); void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
/** /**
* Add overrides to compatibility changes. Doesn't kill the app, to be only used in tests. * Adds overrides to compatibility changes.
* *
* @param overrides Parcelable containing the compat change overrides to be applied. * <p>Does not kill the app, to be only used in tests.
* @param packageName The package name of the app whose changes will be overridden.
* *
* @param overrides parcelable containing the compat change overrides to be applied
* @param packageName the package name of the app whose changes will be overridden
* @throws SecurityException if overriding changes is not permitted.
*/ */
void setOverridesForTest(in CompatibilityChangeConfig overrides, in String packageName); void setOverridesForTest(in CompatibilityChangeConfig overrides, in String packageName);
/** /**
* Removes an override previously added via {@link #setOverrides(CompatibilityChangeConfig, * Restores the default behaviour for the given change and app.
* 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. * <p>Kills the app to allow the changes to take effect.
* @param packageName The app package name that was overridden. *
* @return {@code true} if an override existed; * @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
* @throws SecurityException if overriding changes is not permitted
*/ */
boolean clearOverride(long changeId, String packageName); boolean clearOverride(long changeId, String packageName);
/** /**
* Enable all compatibility changes which have enabledSinceTargetSdk == * Enables all compatibility changes that have enabledSinceTargetSdk ==
* {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the * {@param targetSdkVersion} for an app, subject to the policy.
* changes to take effect.
* *
* @param packageName The package name of the app whose compatibility changes will be enabled. * <p>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. * @param targetSdkVersion The targetSdkVersion for filtering the changes to be enabled.
*
* @return The number of changes that were enabled. * @return The number of changes that were enabled.
* @throws SecurityException if overriding changes is not permitted.
*/ */
int enableTargetSdkChanges(in String packageName, int targetSdkVersion); int enableTargetSdkChanges(in String packageName, int targetSdkVersion);
/** /**
* Disable all compatibility changes which have enabledAfterTargetSdk == * Disables all compatibility changes that have enabledAfterTargetSdk ==
* {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the * {@param targetSdkVersion} for an app, subject to the policy.
* changes to take effect.
* *
* @param packageName The package name of the app whose compatibility changes will be disabled. * <p>Kills the app to allow the changes to take effect.
* @param targetSdkVersion The targetSdkVersion for filtering the changes to be disabled.
* *
* @return The number of changes that were disabled. * @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
* @throws SecurityException if overriding changes is not permitted.
*/ */
int disableTargetSdkChanges(in String packageName, int targetSdkVersion); int disableTargetSdkChanges(in String packageName, int targetSdkVersion);
/** /**
* Revert overrides to compatibility changes. Kills the app to allow the changes to take effect. * Restores the default behaviour for the given app.
* *
* @param packageName The package name of the app whose overrides will be cleared. * <p>Kills the app to allow the changes to take effect.
* *
* @param packageName the package name of the app whose overrides will be cleared
* @throws SecurityException if overriding changes is not permitted
*/ */
void clearOverrides(in String packageName); void clearOverrides(in String packageName);
/** /**
* Revert overrides to compatibility changes. Doesn't kill the app, to be only used in tests. * Restores the default behaviour for the given app.
* *
* @param packageName The package name of the app whose overrides will be cleared. * <p>Does not kill the app; to be only used in tests.
* *
* @param packageName the package name of the app whose overrides will be cleared
* @throws SecurityException if overriding changes is not permitted
*/ */
void clearOverridesForTest(in String packageName); void clearOverridesForTest(in String packageName);
/** /**
* Get configs for an application. * Get configs for an application.
* *
* @param appInfo The application whose config will be returned. * @param appInfo the application whose config will be returned
* * @return a {@link CompatibilityChangeConfig}, representing whether a change is enabled for
* @return A {@link CompatibilityChangeConfig}, representing whether a change is enabled for * the given app or not
* the given app or not.
*/ */
CompatibilityChangeConfig getAppConfig(in ApplicationInfo appInfo); CompatibilityChangeConfig getAppConfig(in ApplicationInfo appInfo);
/** /**
* List all compatibility changes. * List all compatibility changes.
* *
* @return An array of {@link CompatChangeInfo} known to the service. * @return an array of {@link CompatibilityChangeInfo} known to the service
*/ */
CompatibilityChangeInfo[] listAllChanges(); CompatibilityChangeInfo[] listAllChanges();
/** /**
* List the compatibility changes that should be present in the UI. * List the compatibility changes that should be present in the UI.
* Filters out certain changes like e.g. logging only. * Filters out certain changes like e.g. logging only.
* *
* @return An array of {@link CompatChangeInfo}. * @return an array of {@link CompatibilityChangeInfo}
*/ */
CompatibilityChangeInfo[] listUIChanges(); CompatibilityChangeInfo[] listUIChanges();
/** /**
* Get an instance that can determine whether a changeid can be overridden for a package name. * Gets an instance that can determine whether a changeid can be overridden for a package name.
*/ */
IOverrideValidator getOverrideValidator(); IOverrideValidator getOverrideValidator();
} }

View File

@@ -21,7 +21,6 @@ import android.compat.Compatibility.ChangeConfig;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.os.Environment; import android.os.Environment;
import android.os.RemoteException;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.LongArray; import android.util.LongArray;
import android.util.LongSparseArray; import android.util.LongSparseArray;
@@ -53,7 +52,7 @@ import java.util.Set;
import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConfigurationException;
/** /**
* This class maintains state relating to platform compatibility changes. * CompatConfig maintains state related to the platform compatibility changes.
* *
* <p>It stores the default configuration for each change, and any per-package overrides that have * <p>It stores the default configuration for each change, and any per-package overrides that have
* been configured. * been configured.
@@ -65,18 +64,38 @@ final class CompatConfig {
@GuardedBy("mChanges") @GuardedBy("mChanges")
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>(); private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
private OverrideValidatorImpl mOverrideValidator; private final OverrideValidatorImpl mOverrideValidator;
@VisibleForTesting @VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) { CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this); mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
} }
static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
CompatConfig config = new CompatConfig(androidBuildClassifier, context);
config.initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "etc", "compatconfig"));
config.initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "system_ext", "etc", "compatconfig"));
List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos();
for (ApexManager.ActiveApexInfo apex : apexes) {
config.initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
config.invalidateCache();
return config;
}
/** /**
* Add a change. This is intended to be used by code that reads change config from the * Adds a change.
* filesystem. This should be done at system startup time.
* *
* @param change The change to add. Any change with the same ID will be overwritten. * <p>This is intended to be used by code that reads change config from the filesystem. This
* should be done at system startup time.
*
* <p>Any change with the same ID will be overwritten.
*
* @param change the change to add
*/ */
void addChange(CompatChange change) { void addChange(CompatChange change) {
synchronized (mChanges) { synchronized (mChanges) {
@@ -86,13 +105,15 @@ final class CompatConfig {
} }
/** /**
* Retrieves the set of disabled changes for a given app. Any change ID not in the returned * Retrieves the set of disabled changes for a given app.
* array is by default enabled for the app.
* *
* @param app The app in question * <p>Any change ID not in the returned array is by default enabled for the app.
* @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 * <p>We use a primitive array to minimize memory footprint: every app process will store this
* overhead as much as possible. * array statically so we aim to reduce overhead as much as possible.
*
* @param app the app in question
* @return a sorted long array of change IDs
*/ */
long[] getDisabledChanges(ApplicationInfo app) { long[] getDisabledChanges(ApplicationInfo app) {
LongArray disabled = new LongArray(); LongArray disabled = new LongArray();
@@ -110,10 +131,10 @@ final class CompatConfig {
} }
/** /**
* Look up a change ID by name. * Looks up a change ID by name.
* *
* @param name Name of the change to look up * @param name name of the change to look up
* @return The change ID, or {@code -1} if no change with that name exists. * @return the change ID, or {@code -1} if no change with that name exists
*/ */
long lookupChangeId(String name) { long lookupChangeId(String name) {
synchronized (mChanges) { synchronized (mChanges) {
@@ -127,10 +148,10 @@ final class CompatConfig {
} }
/** /**
* Find if a given change is enabled for a given application. * Checks if a given change is enabled for a given application.
* *
* @param changeId The ID of the change in question * @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 * @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.
*/ */
@@ -146,10 +167,10 @@ final class CompatConfig {
} }
/** /**
* Find if a given change will be enabled for a given package name, prior to installation. * Checks if a given change will be enabled for a given package name after the installation.
* *
* @param changeId The ID of the change in question * @param changeId the ID of the change in question
* @param packageName Package name to check for * @param packageName package name to check for
* @return {@code true} if the change would be enabled for this package name. Also returns * @return {@code true} if the change would be enabled for this package name. Also returns
* {@code true} if the change ID is not known, as unknown changes are enabled by default. * {@code true} if the change ID is not known, as unknown changes are enabled by default.
*/ */
@@ -165,22 +186,22 @@ final class CompatConfig {
} }
/** /**
* Overrides the enabled state for a given change and app. This method is intended to be used * Overrides the enabled state for a given change and app.
* *only* for debugging purposes, ultimately invoked either by an adb command, or from some
* developer settings UI.
* *
* <p>Note, package overrides are not persistent and will be lost on system or runtime restart. * <p>This method is intended to be used *only* for debugging purposes, ultimately invoked
* either by an adb command, or from some developer settings UI.
* *
* @param changeId The ID of the change to be overridden. Note, this call will succeed even * <p>Note: package overrides are not persistent and will be lost on system or runtime restart.
* if *
* this change is not known; it will only have any effect if any code in the * @param changeId the ID of the change to be overridden. Note, this call will succeed even
* platform is gated on the ID given. * if this change is not known; it will only have any effect if any code in
* @param packageName The app package name to override the change for. * the platform is gated on the ID given.
* @param enabled If the change should be enabled or disabled. * @param packageName the app package name to override the change for
* @return {@code true} if the change existed before adding the override. * @param enabled if the change should be enabled or disabled
* @return {@code true} if the change existed before adding the override
* @throws IllegalStateException if overriding is not allowed
*/ */
boolean addOverride(long changeId, String packageName, boolean enabled) boolean addOverride(long changeId, String packageName, boolean enabled) {
throws SecurityException {
boolean alreadyKnown = true; boolean alreadyKnown = true;
OverrideAllowedState allowedState = OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName); mOverrideValidator.getOverrideAllowedState(changeId, packageName);
@@ -201,18 +222,14 @@ final class CompatConfig {
break; break;
default: default:
throw new IllegalStateException("Should only be able to override changes that " throw new IllegalStateException("Should only be able to override changes that "
+ "are allowed or can be deferred."); + "are allowed or can be deferred.");
} }
invalidateCache(); invalidateCache();
} }
return alreadyKnown; return alreadyKnown;
} }
/** /** Checks whether the change is known to the compat config. */
* Check whether the change is known to the compat config.
*
* @return {@code true} if the change is known.
*/
boolean isKnownChangeId(long changeId) { boolean isKnownChangeId(long changeId) {
synchronized (mChanges) { synchronized (mChanges) {
CompatChange c = mChanges.get(changeId); CompatChange c = mChanges.get(changeId);
@@ -221,16 +238,13 @@ final class CompatConfig {
} }
/** /**
* Returns the maximum sdk version for which this change can be opted in (or -1 if it is not * Returns the maximum SDK version for which this change can be opted in (or -1 if it is not
* target sdk gated). * target SDK gated).
*/ */
int maxTargetSdkForChangeIdOptIn(long changeId) { int maxTargetSdkForChangeIdOptIn(long changeId) {
synchronized (mChanges) { synchronized (mChanges) {
CompatChange c = mChanges.get(changeId); CompatChange c = mChanges.get(changeId);
if (c == null) { if (c != null && c.getEnableSinceTargetSdk() != -1) {
return -1;
}
if (c.getEnableSinceTargetSdk() != -1) {
return c.getEnableSinceTargetSdk() - 1; return c.getEnableSinceTargetSdk() - 1;
} }
return -1; return -1;
@@ -243,10 +257,7 @@ final class CompatConfig {
boolean isLoggingOnly(long changeId) { boolean isLoggingOnly(long changeId) {
synchronized (mChanges) { synchronized (mChanges) {
CompatChange c = mChanges.get(changeId); CompatChange c = mChanges.get(changeId);
if (c == null) { return c != null && c.getLoggingOnly();
return false;
}
return c.getLoggingOnly();
} }
} }
@@ -256,24 +267,21 @@ final class CompatConfig {
boolean isDisabled(long changeId) { boolean isDisabled(long changeId) {
synchronized (mChanges) { synchronized (mChanges) {
CompatChange c = mChanges.get(changeId); CompatChange c = mChanges.get(changeId);
if (c == null) { return c != null && c.getDisabled();
return false;
}
return c.getDisabled();
} }
} }
/** /**
* Removes an override previously added via {@link #addOverride(long, String, boolean)}. This * Removes an override previously added via {@link #addOverride(long, String, boolean)}.
* 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. * <p>This restores the default behaviour for the given change and app, once any app processes
* @param packageName The app package name that was overridden. * have been restarted.
*
* @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; * @return {@code true} if an override existed;
*/ */
boolean removeOverride(long changeId, String packageName) boolean removeOverride(long changeId, String packageName) {
throws SecurityException {
boolean overrideExists = false; boolean overrideExists = false;
synchronized (mChanges) { synchronized (mChanges) {
CompatChange c = mChanges.get(changeId); CompatChange c = mChanges.get(changeId);
@@ -299,13 +307,12 @@ final class CompatConfig {
/** /**
* Overrides the enabled state for a given change and app. * Overrides the enabled state for a given change and app.
* *
* <p>Note, package overrides are not persistent and will be lost on system or runtime restart. * <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. * @param packageName app for which the overrides will be applied
*/ */
void addOverrides(CompatibilityChangeConfig overrides, String packageName) void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
throws RemoteException, SecurityException {
synchronized (mChanges) { synchronized (mChanges) {
for (Long changeId : overrides.enabledChanges()) { for (Long changeId : overrides.enabledChanges()) {
addOverride(changeId, packageName, true); addOverride(changeId, packageName, true);
@@ -324,9 +331,9 @@ final class CompatConfig {
* *
* <p>This restores the default behaviour for the given app. * <p>This restores the default behaviour for the given app.
* *
* @param packageName The package for which the overrides should be purged. * @param packageName the package for which the overrides should be purged
*/ */
void removePackageOverrides(String packageName) throws SecurityException { void removePackageOverrides(String packageName) {
synchronized (mChanges) { synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) { for (int i = 0; i < mChanges.size(); ++i) {
CompatChange change = mChanges.valueAt(i); CompatChange change = mChanges.valueAt(i);
@@ -337,8 +344,7 @@ final class CompatConfig {
} }
private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName, private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName,
int targetSdkVersion) int targetSdkVersion) {
throws RemoteException {
LongArray allowed = new LongArray(); LongArray allowed = new LongArray();
synchronized (mChanges) { synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) { for (int i = 0; i < mChanges.size(); ++i) {
@@ -348,7 +354,7 @@ final class CompatConfig {
} }
OverrideAllowedState allowedState = OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(change.getId(), mOverrideValidator.getOverrideAllowedState(change.getId(),
packageName); packageName);
if (allowedState.state == OverrideAllowedState.ALLOWED) { if (allowedState.state == OverrideAllowedState.ALLOWED) {
allowed.add(change.getId()); allowed.add(change.getId());
} }
@@ -361,10 +367,9 @@ final class CompatConfig {
* Enables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for * Enables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for
* {@param packageName}. * {@param packageName}.
* *
* @return The number of changes that were toggled. * @return the number of changes that were toggled
*/ */
int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
throws RemoteException {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion); long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) { for (long changeId : changes) {
addOverride(changeId, packageName, true); addOverride(changeId, packageName, true);
@@ -372,15 +377,13 @@ final class CompatConfig {
return changes.length; return changes.length;
} }
/** /**
* Disables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for * Disables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for
* {@param packageName}. * {@param packageName}.
* *
* @return The number of changes that were toggled. * @return the number of changes that were toggled
*/ */
int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
throws RemoteException {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion); long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) { for (long changeId : changes) {
addOverride(changeId, packageName, false); addOverride(changeId, packageName, false);
@@ -425,7 +428,7 @@ final class CompatConfig {
/** /**
* Dumps the current list of compatibility config information. * Dumps the current list of compatibility config information.
* *
* @param pw The {@link PrintWriter} instance to which the information will be dumped. * @param pw {@link PrintWriter} instance to which the information will be dumped
*/ */
void dumpConfig(PrintWriter pw) { void dumpConfig(PrintWriter pw) {
synchronized (mChanges) { synchronized (mChanges) {
@@ -441,13 +444,10 @@ final class CompatConfig {
} }
/** /**
* Get the config for a given app. * Returns config for a given app.
* *
* @param applicationInfo the {@link ApplicationInfo} for which the info should be dumped. * @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.
*/ */
CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) { CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) {
Set<Long> enabled = new HashSet<>(); Set<Long> enabled = new HashSet<>();
Set<Long> disabled = new HashSet<>(); Set<Long> disabled = new HashSet<>();
@@ -467,7 +467,7 @@ final class CompatConfig {
/** /**
* Dumps all the compatibility change information. * Dumps all the compatibility change information.
* *
* @return An array of {@link CompatibilityChangeInfo} with the current changes. * @return an array of {@link CompatibilityChangeInfo} with the current changes
*/ */
CompatibilityChangeInfo[] dumpChanges() { CompatibilityChangeInfo[] dumpChanges() {
synchronized (mChanges) { synchronized (mChanges) {
@@ -480,22 +480,6 @@ final class CompatConfig {
} }
} }
static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
CompatConfig config = new CompatConfig(androidBuildClassifier, context);
config.initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "etc", "compatconfig"));
config.initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "system_ext", "etc", "compatconfig"));
List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos();
for (ApexManager.ActiveApexInfo apex : apexes) {
config.initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
config.invalidateCache();
return config;
}
void initConfigFromLib(File libraryDir) { void initConfigFromLib(File libraryDir) {
if (!libraryDir.exists() || !libraryDir.isDirectory()) { if (!libraryDir.exists() || !libraryDir.isDirectory()) {
Slog.d(TAG, "No directory " + libraryDir + ", skipping"); Slog.d(TAG, "No directory " + libraryDir + ", skipping");
@@ -526,6 +510,7 @@ final class CompatConfig {
private void invalidateCache() { private void invalidateCache() {
ChangeIdStateCache.invalidate(); ChangeIdStateCache.invalidate();
} }
/** /**
* Rechecks all the existing overrides for a package. * Rechecks all the existing overrides for a package.
*/ */

View File

@@ -63,45 +63,43 @@ public class PlatformCompat extends IPlatformCompat.Stub {
private final ChangeReporter mChangeReporter; private final ChangeReporter mChangeReporter;
private final CompatConfig mCompatConfig; private final CompatConfig mCompatConfig;
private static int sMinTargetSdk = Build.VERSION_CODES.Q;
public PlatformCompat(Context context) { public PlatformCompat(Context context) {
mContext = context; mContext = context;
mChangeReporter = new ChangeReporter( mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
ChangeReporter.SOURCE_SYSTEM_SERVER);
mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext); mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
} }
@VisibleForTesting @VisibleForTesting
PlatformCompat(Context context, CompatConfig compatConfig) { PlatformCompat(Context context, CompatConfig compatConfig) {
mContext = context; mContext = context;
mChangeReporter = new ChangeReporter( mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
ChangeReporter.SOURCE_SYSTEM_SERVER);
mCompatConfig = compatConfig; mCompatConfig = compatConfig;
registerPackageReceiver(context); registerPackageReceiver(context);
} }
@Override @Override
public void reportChange(long changeId, ApplicationInfo appInfo) { public void reportChange(long changeId, ApplicationInfo appInfo) {
checkCompatChangeLogPermission(); reportChangeByUid(changeId, appInfo.uid);
reportChange(changeId, appInfo.uid,
ChangeReporter.STATE_LOGGED);
} }
@Override @Override
public void reportChangeByPackageName(long changeId, String packageName, int userId) { public void reportChangeByPackageName(long changeId, String packageName,
checkCompatChangeLogPermission(); @UserIdInt int userId) {
ApplicationInfo appInfo = getApplicationInfo(packageName, userId); ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
if (appInfo == null) { if (appInfo != null) {
return; reportChangeByUid(changeId, appInfo.uid);
} }
reportChange(changeId, appInfo);
} }
@Override @Override
public void reportChangeByUid(long changeId, int uid) { public void reportChangeByUid(long changeId, int uid) {
checkCompatChangeLogPermission(); checkCompatChangeLogPermission();
reportChange(changeId, uid, ChangeReporter.STATE_LOGGED); reportChangeInternal(changeId, uid, ChangeReporter.STATE_LOGGED);
}
private void reportChangeInternal(long changeId, int uid, int state) {
mChangeReporter.reportChange(uid, changeId, state);
} }
@Override @Override
@@ -110,28 +108,6 @@ public class PlatformCompat extends IPlatformCompat.Stub {
return isChangeEnabledInternal(changeId, appInfo); return isChangeEnabledInternal(changeId, appInfo);
} }
/**
* Internal version of the above method, without logging. Does not perform costly permission
* check.
* TODO(b/167551701): Remove this method and add 'loggability' as a changeid property.
*/
public boolean isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo) {
return mCompatConfig.isChangeEnabled(changeId, appInfo);
}
/**
* Internal version of {@link #isChangeEnabled(long, ApplicationInfo)}. Does not perform costly
* permission check.
*/
public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) {
boolean enabled = isChangeEnabledInternalNoLogging(changeId, appInfo);
if (appInfo != null) {
reportChange(changeId, appInfo.uid,
enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED);
}
return enabled;
}
@Override @Override
public boolean isChangeEnabledByPackageName(long changeId, String packageName, public boolean isChangeEnabledByPackageName(long changeId, String packageName,
@UserIdInt int userId) { @UserIdInt int userId) {
@@ -140,7 +116,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
if (appInfo == null) { if (appInfo == null) {
return mCompatConfig.willChangeBeEnabled(changeId, packageName); return mCompatConfig.willChangeBeEnabled(changeId, packageName);
} }
return isChangeEnabled(changeId, appInfo); return isChangeEnabledInternal(changeId, appInfo);
} }
@Override @Override
@@ -152,81 +128,82 @@ public class PlatformCompat extends IPlatformCompat.Stub {
} }
boolean enabled = true; boolean enabled = true;
for (String packageName : packages) { for (String packageName : packages) {
enabled = enabled && isChangeEnabledByPackageName(changeId, packageName, enabled &= isChangeEnabledByPackageName(changeId, packageName,
UserHandle.getUserId(uid)); UserHandle.getUserId(uid));
} }
return enabled; return enabled;
} }
/** /**
* Register a listener for change state overrides. Only one listener per change is allowed. * Internal version of the above method, without logging.
* *
* <p>{@code listener.onCompatChange(String)} method is guaranteed to be called with * <p>Does not perform costly permission check.
* packageName before the app is killed upon an override change. The state of a change is not * TODO(b/167551701): Remove this method and add 'loggability' as a changeid property.
* guaranteed to change when {@code listener.onCompatChange(String)} is called.
*
* @param changeId to get updates for
* @param listener the listener that will be called upon a potential change for package.
* @throws IllegalStateException if a listener was already registered for changeId
* @returns {@code true} if a change with changeId was already known, or (@code false}
* otherwise.
*/ */
public boolean registerListener(long changeId, CompatChange.ChangeListener listener) { public boolean isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo) {
return mCompatConfig.registerListener(changeId, listener); return mCompatConfig.isChangeEnabled(changeId, appInfo);
}
/**
* Internal version of {@link #isChangeEnabled(long, ApplicationInfo)}.
*
* <p>Does not perform costly permission check.
*/
public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) {
boolean enabled = isChangeEnabledInternalNoLogging(changeId, appInfo);
if (appInfo != null) {
reportChangeInternal(changeId, appInfo.uid,
enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED);
}
return enabled;
} }
@Override @Override
public void setOverrides(CompatibilityChangeConfig overrides, String packageName) public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
throws RemoteException, SecurityException {
checkCompatChangeOverridePermission(); checkCompatChangeOverridePermission();
mCompatConfig.addOverrides(overrides, packageName); mCompatConfig.addOverrides(overrides, packageName);
killPackage(packageName); killPackage(packageName);
} }
@Override @Override
public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
throws RemoteException, SecurityException {
checkCompatChangeOverridePermission(); checkCompatChangeOverridePermission();
mCompatConfig.addOverrides(overrides, packageName); mCompatConfig.addOverrides(overrides, packageName);
} }
@Override @Override
public int enableTargetSdkChanges(String packageName, int targetSdkVersion) public int enableTargetSdkChanges(String packageName, int targetSdkVersion) {
throws RemoteException, SecurityException {
checkCompatChangeOverridePermission(); checkCompatChangeOverridePermission();
int numChanges = mCompatConfig.enableTargetSdkChangesForPackage(packageName, int numChanges =
targetSdkVersion); mCompatConfig.enableTargetSdkChangesForPackage(packageName, targetSdkVersion);
killPackage(packageName); killPackage(packageName);
return numChanges; return numChanges;
} }
@Override @Override
public int disableTargetSdkChanges(String packageName, int targetSdkVersion) public int disableTargetSdkChanges(String packageName, int targetSdkVersion) {
throws RemoteException, SecurityException {
checkCompatChangeOverridePermission(); checkCompatChangeOverridePermission();
int numChanges = mCompatConfig.disableTargetSdkChangesForPackage(packageName, int numChanges =
targetSdkVersion); mCompatConfig.disableTargetSdkChangesForPackage(packageName, targetSdkVersion);
killPackage(packageName); killPackage(packageName);
return numChanges; return numChanges;
} }
@Override @Override
public void clearOverrides(String packageName) throws RemoteException, SecurityException { public void clearOverrides(String packageName) {
checkCompatChangeOverridePermission(); checkCompatChangeOverridePermission();
mCompatConfig.removePackageOverrides(packageName); mCompatConfig.removePackageOverrides(packageName);
killPackage(packageName); killPackage(packageName);
} }
@Override @Override
public void clearOverridesForTest(String packageName) public void clearOverridesForTest(String packageName) {
throws RemoteException, SecurityException {
checkCompatChangeOverridePermission(); checkCompatChangeOverridePermission();
mCompatConfig.removePackageOverrides(packageName); mCompatConfig.removePackageOverrides(packageName);
} }
@Override @Override
public boolean clearOverride(long changeId, String packageName) public boolean clearOverride(long changeId, String packageName) {
throws RemoteException, SecurityException {
checkCompatChangeOverridePermission(); checkCompatChangeOverridePermission();
boolean existed = mCompatConfig.removeOverride(changeId, packageName); boolean existed = mCompatConfig.removeOverride(changeId, packageName);
killPackage(packageName); killPackage(packageName);
@@ -247,18 +224,13 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override @Override
public CompatibilityChangeInfo[] listUIChanges() { public CompatibilityChangeInfo[] listUIChanges() {
return Arrays.stream(listAllChanges()).filter( return Arrays.stream(listAllChanges()).filter(this::isShownInUI).toArray(
x -> isShownInUI(x)).toArray(CompatibilityChangeInfo[]::new); CompatibilityChangeInfo[]::new);
} }
/** /** Checks whether the change is known to the compat config. */
* Check whether the change is known to the compat config.
*
* @return {@code true} if the change is known.
*/
public boolean isKnownChangeId(long changeId) { public boolean isKnownChangeId(long changeId) {
return mCompatConfig.isKnownChangeId(changeId); return mCompatConfig.isKnownChangeId(changeId);
} }
/** /**
@@ -286,7 +258,9 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return; if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) {
return;
}
checkCompatChangeReadAndLogPermission(); checkCompatChangeReadAndLogPermission();
mCompatConfig.dumpConfig(pw); mCompatConfig.dumpConfig(pw);
} }
@@ -298,7 +272,8 @@ public class PlatformCompat extends IPlatformCompat.Stub {
/** /**
* Clears information stored about events reported on behalf of an app. * Clears information stored about events reported on behalf of an app.
* To be called once upon app start or end. A second call would be a no-op. *
* <p>To be called once upon app start or end. A second call would be a no-op.
* *
* @param appInfo the app to reset * @param appInfo the app to reset
*/ */
@@ -311,13 +286,9 @@ public class PlatformCompat extends IPlatformCompat.Stub {
packageName, 0, userId, userId); packageName, 0, userId, userId);
} }
private void reportChange(long changeId, int uid, int state) {
mChangeReporter.reportChange(uid, changeId, state);
}
private void killPackage(String packageName) { private void killPackage(String packageName) {
int uid = LocalServices.getService(PackageManagerInternal.class).getPackageUid(packageName, int uid = LocalServices.getService(PackageManagerInternal.class).getPackageUid(packageName,
0, UserHandle.myUserId()); 0, UserHandle.myUserId());
if (uid < 0) { if (uid < 0) {
Slog.w(TAG, "Didn't find package " + packageName + " on device."); Slog.w(TAG, "Didn't find package " + packageName + " on device.");
@@ -325,21 +296,18 @@ public class PlatformCompat extends IPlatformCompat.Stub {
} }
Slog.d(TAG, "Killing package " + packageName + " (UID " + uid + ")."); Slog.d(TAG, "Killing package " + packageName + " (UID " + uid + ").");
killUid(UserHandle.getAppId(uid), killUid(UserHandle.getAppId(uid));
UserHandle.USER_ALL, "PlatformCompat overrides");
} }
private void killUid(int appId, int userId, String reason) { private void killUid(int appId) {
final long identity = Binder.clearCallingIdentity(); final long identity = Binder.clearCallingIdentity();
try { try {
IActivityManager am = ActivityManager.getService(); IActivityManager am = ActivityManager.getService();
if (am != null) { if (am != null) {
try { am.killUid(appId, UserHandle.USER_ALL, "PlatformCompat overrides");
am.killUid(appId, userId, reason);
} catch (RemoteException e) {
/* ignore - same process */
}
} }
} catch (RemoteException e) {
/* ignore - same process */
} finally { } finally {
Binder.restoreCallingIdentity(identity); Binder.restoreCallingIdentity(identity);
} }
@@ -350,13 +318,12 @@ public class PlatformCompat extends IPlatformCompat.Stub {
if (Binder.getCallingUid() == SYSTEM_UID) { if (Binder.getCallingUid() == SYSTEM_UID) {
return; return;
} }
if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE) if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE) != PERMISSION_GRANTED) {
!= PERMISSION_GRANTED) {
throw new SecurityException("Cannot log compat change usage"); throw new SecurityException("Cannot log compat change usage");
} }
} }
private void checkCompatChangeReadPermission() throws SecurityException { private void checkCompatChangeReadPermission() {
// Don't check for permissions within the system process // Don't check for permissions within the system process
if (Binder.getCallingUid() == SYSTEM_UID) { if (Binder.getCallingUid() == SYSTEM_UID) {
return; return;
@@ -367,7 +334,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
} }
} }
private void checkCompatChangeOverridePermission() throws SecurityException { private void checkCompatChangeOverridePermission() {
// Don't check for permissions within the system process // Don't check for permissions within the system process
if (Binder.getCallingUid() == SYSTEM_UID) { if (Binder.getCallingUid() == SYSTEM_UID) {
return; return;
@@ -378,7 +345,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
} }
} }
private void checkCompatChangeReadAndLogPermission() throws SecurityException { private void checkCompatChangeReadAndLogPermission() {
checkCompatChangeReadPermission(); checkCompatChangeReadPermission();
checkCompatChangeLogPermission(); checkCompatChangeLogPermission();
} }
@@ -391,16 +358,34 @@ public class PlatformCompat extends IPlatformCompat.Stub {
return false; return false;
} }
if (change.getEnableSinceTargetSdk() > 0) { if (change.getEnableSinceTargetSdk() > 0) {
if (change.getEnableSinceTargetSdk() < sMinTargetSdk) { return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q;
return false;
}
} }
return true; return true;
} }
/**
* Registers a listener for change state overrides.
*
* <p>Only one listener per change is allowed.
*
* <p>{@code listener.onCompatChange(String)} method is guaranteed to be called with
* packageName before the app is killed upon an override change. The state of a change is not
* guaranteed to change when {@code listener.onCompatChange(String)} is called.
*
* @param changeId to get updates for
* @param listener the listener that will be called upon a potential change for package
* @return {@code true} if a change with changeId was already known, or (@code false}
* otherwise
* @throws IllegalStateException if a listener was already registered for changeId
*/
public boolean registerListener(long changeId, CompatChange.ChangeListener listener) {
return mCompatConfig.registerListener(changeId, listener);
}
/** /**
* Registers a broadcast receiver that listens for package install, replace or remove. * Registers a broadcast receiver that listens for package install, replace or remove.
* @param context the context where the receiver should be registered. *
* @param context the context where the receiver should be registered
*/ */
public void registerPackageReceiver(Context context) { public void registerPackageReceiver(Context context) {
final BroadcastReceiver receiver = new BroadcastReceiver() { final BroadcastReceiver receiver = new BroadcastReceiver() {
@@ -429,8 +414,8 @@ public class PlatformCompat extends IPlatformCompat.Stub {
} }
/** /**
* Register the observer for * Registers the observer for
* {@link android.provider.Settings.Global#FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT} * {@link android.provider.Settings.Global#FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT}.
*/ */
public void registerContentObserver() { public void registerContentObserver() {
mCompatConfig.registerContentObserver(); mCompatConfig.registerContentObserver();