Merge "Switch to ConcurrentHashMap for CompatConfig" into sc-v2-dev

This commit is contained in:
TreeHugger Robot
2021-09-16 15:44:31 +00:00
committed by Android (Google) Code Review
2 changed files with 160 additions and 298 deletions

View File

@@ -36,9 +36,9 @@ import com.android.server.compat.overrides.ChangeOverrides;
import com.android.server.compat.overrides.OverrideValue; import com.android.server.compat.overrides.OverrideValue;
import com.android.server.compat.overrides.RawOverrideValue; import com.android.server.compat.overrides.RawOverrideValue;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Represents the state of a single compatibility change. * Represents the state of a single compatibility change.
@@ -82,8 +82,8 @@ public final class CompatChange extends CompatibilityChangeInfo {
ChangeListener mListener = null; ChangeListener mListener = null;
private Map<String, Boolean> mEvaluatedOverrides; private ConcurrentHashMap<String, Boolean> mEvaluatedOverrides;
private Map<String, PackageOverride> mRawOverrides; private ConcurrentHashMap<String, PackageOverride> mRawOverrides;
public CompatChange(long changeId) { public CompatChange(long changeId) {
this(changeId, null, -1, -1, false, false, null, false); this(changeId, null, -1, -1, false, false, null, false);
@@ -114,11 +114,11 @@ public final class CompatChange extends CompatibilityChangeInfo {
description, overridable); description, overridable);
// Initialize override maps. // Initialize override maps.
mEvaluatedOverrides = new HashMap<>(); mEvaluatedOverrides = new ConcurrentHashMap<>();
mRawOverrides = new HashMap<>(); mRawOverrides = new ConcurrentHashMap<>();
} }
void registerListener(ChangeListener listener) { synchronized void registerListener(ChangeListener listener) {
if (mListener != null) { if (mListener != null) {
throw new IllegalStateException( throw new IllegalStateException(
"Listener for change " + toString() + " already registered."); "Listener for change " + toString() + " already registered.");
@@ -131,8 +131,6 @@ public final class CompatChange extends CompatibilityChangeInfo {
* Force the enabled state of this change for a given package name. The change will only take * Force the enabled state of this change for a given package name. The change will only take
* effect after that packages process is killed and restarted. * effect after that packages process is killed and restarted.
* *
* <p>Note, this method is not thread safe so callers must ensure thread safety.
*
* @param pname Package name to enable the change for. * @param pname Package name to enable the change for.
* @param enabled Whether or not to enable the change. * @param enabled Whether or not to enable the change.
*/ */
@@ -155,14 +153,12 @@ public final class CompatChange extends CompatibilityChangeInfo {
* Tentatively set the state of this change for a given package name. * Tentatively set the state of this change for a given package name.
* The override will only take effect after that package is installed, if applicable. * The override will only take effect after that package is installed, if applicable.
* *
* <p>Note, this method is not thread safe so callers must ensure thread safety.
*
* @param packageName Package name to tentatively enable the change for. * @param packageName Package name to tentatively enable the change for.
* @param override The package override to be set * @param override The package override to be set
* @param allowedState Whether the override is allowed. * @param allowedState Whether the override is allowed.
* @param versionCode The version code of the package. * @param versionCode The version code of the package.
*/ */
void addPackageOverride(String packageName, PackageOverride override, synchronized void addPackageOverride(String packageName, PackageOverride override,
OverrideAllowedState allowedState, @Nullable Long versionCode) { OverrideAllowedState allowedState, @Nullable Long versionCode) {
if (getLoggingOnly()) { if (getLoggingOnly()) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
@@ -185,12 +181,12 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @return {@code true} if the recheck yielded a result that requires invalidating caches * @return {@code true} if the recheck yielded a result that requires invalidating caches
* (a deferred override was consolidated or a regular override was removed). * (a deferred override was consolidated or a regular override was removed).
*/ */
boolean recheckOverride(String packageName, OverrideAllowedState allowedState, synchronized boolean recheckOverride(String packageName, OverrideAllowedState allowedState,
@Nullable Long versionCode) { @Nullable Long versionCode) {
boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED); boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED);
// If the app is not installed or no longer has raw overrides, evaluate to false // If the app is not installed or no longer has raw overrides, evaluate to false
if (versionCode == null || !hasRawOverride(packageName) || !allowed) { if (versionCode == null || !mRawOverrides.containsKey(packageName) || !allowed) {
removePackageOverrideInternal(packageName); removePackageOverrideInternal(packageName);
return false; return false;
} }
@@ -211,10 +207,6 @@ public final class CompatChange extends CompatibilityChangeInfo {
return true; return true;
} }
boolean hasPackageOverride(String pname) {
return mRawOverrides.containsKey(pname);
}
/** /**
* Remove any package override for the given package name, restoring the default behaviour. * Remove any package override for the given package name, restoring the default behaviour.
* *
@@ -224,9 +216,11 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @param allowedState Whether the override is allowed. * @param allowedState Whether the override is allowed.
* @param versionCode The version code of the package. * @param versionCode The version code of the package.
*/ */
boolean removePackageOverride(String pname, OverrideAllowedState allowedState, synchronized boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
@Nullable Long versionCode) { @Nullable Long versionCode) {
if (mRawOverrides.remove(pname) != null) { if (mRawOverrides.containsKey(pname)) {
allowedState.enforce(getId(), pname);
mRawOverrides.remove(pname);
recheckOverride(pname, allowedState, versionCode); recheckOverride(pname, allowedState, versionCode);
return true; return true;
} }
@@ -244,8 +238,11 @@ public final class CompatChange extends CompatibilityChangeInfo {
if (app == null) { if (app == null) {
return defaultValue(); return defaultValue();
} }
if (mEvaluatedOverrides.containsKey(app.packageName)) { if (app.packageName != null) {
return mEvaluatedOverrides.get(app.packageName); final Boolean enabled = mEvaluatedOverrides.get(app.packageName);
if (enabled != null) {
return enabled;
}
} }
if (getDisabled()) { if (getDisabled()) {
return false; return false;
@@ -269,9 +266,9 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @return {@code true} if the change should be enabled for the package. * @return {@code true} if the change should be enabled for the package.
*/ */
boolean willBeEnabled(String packageName) { boolean willBeEnabled(String packageName) {
if (hasRawOverride(packageName)) { final PackageOverride override = mRawOverrides.get(packageName);
int eval = mRawOverrides.get(packageName).evaluateForAllVersions(); if (override != null) {
switch (eval) { switch (override.evaluateForAllVersions()) {
case VALUE_ENABLED: case VALUE_ENABLED:
return true; return true;
case VALUE_DISABLED: case VALUE_DISABLED:
@@ -292,30 +289,12 @@ public final class CompatChange extends CompatibilityChangeInfo {
return !getDisabled(); return !getDisabled();
} }
/** synchronized void clearOverrides() {
* Checks whether a change has an override for a package.
* @param packageName name of the package
* @return true if there is such override
*/
private boolean hasOverride(String packageName) {
return mEvaluatedOverrides.containsKey(packageName);
}
/**
* Checks whether a change has a deferred override for a package.
* @param packageName name of the package
* @return true if there is such a deferred override
*/
private boolean hasRawOverride(String packageName) {
return mRawOverrides.containsKey(packageName);
}
void clearOverrides() {
mRawOverrides.clear(); mRawOverrides.clear();
mEvaluatedOverrides.clear(); mEvaluatedOverrides.clear();
} }
void loadOverrides(ChangeOverrides changeOverrides) { synchronized void loadOverrides(ChangeOverrides changeOverrides) {
// Load deferred overrides for backwards compatibility // Load deferred overrides for backwards compatibility
if (changeOverrides.getDeferred() != null) { if (changeOverrides.getDeferred() != null) {
for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) { for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
@@ -348,7 +327,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
} }
} }
ChangeOverrides saveOverrides() { synchronized ChangeOverrides saveOverrides() {
if (mRawOverrides.isEmpty()) { if (mRawOverrides.isEmpty()) {
return null; return null;
} }
@@ -406,7 +385,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
return sb.append(")").toString(); return sb.append(")").toString();
} }
private void notifyListener(String packageName) { private synchronized void notifyListener(String packageName) {
if (mListener != null) { if (mListener != null) {
mListener.onCompatChange(packageName); mListener.onCompatChange(packageName);
} }

View File

@@ -28,7 +28,6 @@ import android.content.pm.PackageManager;
import android.os.Environment; import android.os.Environment;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.LongArray; import android.util.LongArray;
import android.util.LongSparseArray;
import android.util.Slog; import android.util.Slog;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
@@ -55,11 +54,12 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.atomic.AtomicBoolean;
import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeConfigurationException;
@@ -76,9 +76,7 @@ final class CompatConfig {
private static final String STATIC_OVERRIDES_PRODUCT_DIR = "/product/etc/appcompat"; private static final String STATIC_OVERRIDES_PRODUCT_DIR = "/product/etc/appcompat";
private static final String OVERRIDES_FILE = "compat_framework_overrides.xml"; private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock(); private final ConcurrentHashMap<Long, CompatChange> mChanges = new ConcurrentHashMap<>();
@GuardedBy("mReadWriteLock")
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
private final OverrideValidatorImpl mOverrideValidator; private final OverrideValidatorImpl mOverrideValidator;
private final AndroidBuildClassifier mAndroidBuildClassifier; private final AndroidBuildClassifier mAndroidBuildClassifier;
@@ -113,21 +111,13 @@ final class CompatConfig {
/** /**
* Adds a change. * Adds a change.
* *
* <p>This is intended to be used by code that reads change config from the filesystem. This * <p>This is intended to be used by unit tests only.
* should be done at system startup time.
*
* <p>Any change with the same ID will be overwritten.
* *
* @param change the change to add * @param change the change to add
*/ */
@VisibleForTesting
void addChange(CompatChange change) { void addChange(CompatChange change) {
mReadWriteLock.writeLock().lock(); mChanges.put(change.getId(), change);
try {
mChanges.put(change.getId(), change);
invalidateCache();
} finally {
mReadWriteLock.writeLock().unlock();
}
} }
/** /**
@@ -143,20 +133,14 @@ final class CompatConfig {
*/ */
long[] getDisabledChanges(ApplicationInfo app) { long[] getDisabledChanges(ApplicationInfo app) {
LongArray disabled = new LongArray(); LongArray disabled = new LongArray();
mReadWriteLock.readLock().lock(); for (CompatChange c : mChanges.values()) {
try { if (!c.isEnabled(app, mAndroidBuildClassifier)) {
for (int i = 0; i < mChanges.size(); ++i) { disabled.add(c.getId());
CompatChange c = mChanges.valueAt(i);
if (!c.isEnabled(app, mAndroidBuildClassifier)) {
disabled.add(c.getId());
}
} }
} finally {
mReadWriteLock.readLock().unlock();
} }
// Note: we don't need to explicitly sort the array, as the behaviour of LongSparseArray final long[] sortedChanges = disabled.toArray();
// (mChanges) ensures it's already sorted. Arrays.sort(sortedChanges);
return disabled.toArray(); return sortedChanges;
} }
/** /**
@@ -166,15 +150,10 @@ final class CompatConfig {
* @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) {
mReadWriteLock.readLock().lock(); for (CompatChange c : mChanges.values()) {
try { if (TextUtils.equals(c.getName(), name)) {
for (int i = 0; i < mChanges.size(); ++i) { return c.getId();
if (TextUtils.equals(mChanges.valueAt(i).getName(), name)) {
return mChanges.keyAt(i);
}
} }
} finally {
mReadWriteLock.readLock().unlock();
} }
return -1; return -1;
} }
@@ -188,17 +167,12 @@ final class CompatConfig {
* change ID is not known, as unknown changes are enabled by default. * change ID is not known, as unknown changes are enabled by default.
*/ */
boolean isChangeEnabled(long changeId, ApplicationInfo app) { boolean isChangeEnabled(long changeId, ApplicationInfo app) {
mReadWriteLock.readLock().lock(); CompatChange c = mChanges.get(changeId);
try { if (c == null) {
CompatChange c = mChanges.get(changeId); // we know nothing about this change: default behaviour is enabled.
if (c == null) { return true;
// we know nothing about this change: default behaviour is enabled.
return true;
}
return c.isEnabled(app, mAndroidBuildClassifier);
} finally {
mReadWriteLock.readLock().unlock();
} }
return c.isEnabled(app, mAndroidBuildClassifier);
} }
/** /**
@@ -210,17 +184,12 @@ final class CompatConfig {
* {@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.
*/ */
boolean willChangeBeEnabled(long changeId, String packageName) { boolean willChangeBeEnabled(long changeId, String packageName) {
mReadWriteLock.readLock().lock(); CompatChange c = mChanges.get(changeId);
try { if (c == null) {
CompatChange c = mChanges.get(changeId); // we know nothing about this change: default behaviour is enabled.
if (c == null) { return true;
// we know nothing about this change: default behaviour is enabled.
return true;
}
return c.willBeEnabled(packageName);
} finally {
mReadWriteLock.readLock().unlock();
} }
return c.willBeEnabled(packageName);
} }
/** /**
@@ -239,7 +208,7 @@ final class CompatConfig {
* @return {@code true} if the change existed before adding the override * @return {@code true} if the change existed before adding the override
* @throws IllegalStateException if overriding is not allowed * @throws IllegalStateException if overriding is not allowed
*/ */
boolean addOverride(long changeId, String packageName, boolean enabled) { synchronized boolean addOverride(long changeId, String packageName, boolean enabled) {
boolean alreadyKnown = addOverrideUnsafe(changeId, packageName, boolean alreadyKnown = addOverrideUnsafe(changeId, packageName,
new PackageOverride.Builder().setEnabled(enabled).build()); new PackageOverride.Builder().setEnabled(enabled).build());
saveOverrides(); saveOverrides();
@@ -250,12 +219,11 @@ 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.
* *
* @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(CompatibilityOverrideConfig overrides, String packageName) { synchronized void addOverrides(CompatibilityOverrideConfig overrides, String packageName) {
for (Long changeId : overrides.overrides.keySet()) { for (Long changeId : overrides.overrides.keySet()) {
addOverrideUnsafe(changeId, packageName, overrides.overrides.get(changeId)); addOverrideUnsafe(changeId, packageName, overrides.overrides.get(changeId));
} }
@@ -265,36 +233,24 @@ final class CompatConfig {
private boolean addOverrideUnsafe(long changeId, String packageName, private boolean addOverrideUnsafe(long changeId, String packageName,
PackageOverride overrides) { PackageOverride overrides) {
boolean alreadyKnown = true; final AtomicBoolean alreadyKnown = new AtomicBoolean(true);
OverrideAllowedState allowedState = OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName); mOverrideValidator.getOverrideAllowedState(changeId, packageName);
allowedState.enforce(changeId, packageName); allowedState.enforce(changeId, packageName);
Long versionCode = getVersionCodeOrNull(packageName); Long versionCode = getVersionCodeOrNull(packageName);
mReadWriteLock.writeLock().lock();
try { final CompatChange c = mChanges.computeIfAbsent(changeId, (key) -> {
CompatChange c = mChanges.get(changeId); alreadyKnown.set(false);
if (c == null) { return new CompatChange(changeId);
alreadyKnown = false; });
c = new CompatChange(changeId); c.addPackageOverride(packageName, overrides, allowedState, versionCode);
addChange(c); invalidateCache();
} return alreadyKnown.get();
c.addPackageOverride(packageName, overrides, allowedState, versionCode);
invalidateCache();
} finally {
mReadWriteLock.writeLock().unlock();
}
return alreadyKnown;
} }
/** Checks whether the change is known to the compat config. */ /** Checks whether the change is known to the compat config. */
boolean isKnownChangeId(long changeId) { boolean isKnownChangeId(long changeId) {
mReadWriteLock.readLock().lock(); return mChanges.containsKey(changeId);
try {
CompatChange c = mChanges.get(changeId);
return c != null;
} finally {
mReadWriteLock.readLock().unlock();
}
} }
/** /**
@@ -302,55 +258,35 @@ final class CompatConfig {
* target SDK gated). * target SDK gated).
*/ */
int maxTargetSdkForChangeIdOptIn(long changeId) { int maxTargetSdkForChangeIdOptIn(long changeId) {
mReadWriteLock.readLock().lock(); CompatChange c = mChanges.get(changeId);
try { if (c != null && c.getEnableSinceTargetSdk() != -1) {
CompatChange c = mChanges.get(changeId); return c.getEnableSinceTargetSdk() - 1;
if (c != null && c.getEnableSinceTargetSdk() != -1) {
return c.getEnableSinceTargetSdk() - 1;
}
return -1;
} finally {
mReadWriteLock.readLock().unlock();
} }
return -1;
} }
/** /**
* Returns whether the change is marked as logging only. * Returns whether the change is marked as logging only.
*/ */
boolean isLoggingOnly(long changeId) { boolean isLoggingOnly(long changeId) {
mReadWriteLock.readLock().lock(); CompatChange c = mChanges.get(changeId);
try { return c != null && c.getLoggingOnly();
CompatChange c = mChanges.get(changeId);
return c != null && c.getLoggingOnly();
} finally {
mReadWriteLock.readLock().unlock();
}
} }
/** /**
* Returns whether the change is marked as disabled. * Returns whether the change is marked as disabled.
*/ */
boolean isDisabled(long changeId) { boolean isDisabled(long changeId) {
mReadWriteLock.readLock().lock(); CompatChange c = mChanges.get(changeId);
try { return c != null && c.getDisabled();
CompatChange c = mChanges.get(changeId);
return c != null && c.getDisabled();
} finally {
mReadWriteLock.readLock().unlock();
}
} }
/** /**
* Returns whether the change is overridable. * Returns whether the change is overridable.
*/ */
boolean isOverridable(long changeId) { boolean isOverridable(long changeId) {
mReadWriteLock.readLock().lock(); CompatChange c = mChanges.get(changeId);
try { return c != null && c.getOverridable();
CompatChange c = mChanges.get(changeId);
return c != null && c.getOverridable();
} finally {
mReadWriteLock.readLock().unlock();
}
} }
/** /**
@@ -363,10 +299,12 @@ final class CompatConfig {
* @param packageName the app package name 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) { synchronized boolean removeOverride(long changeId, String packageName) {
boolean overrideExists = removeOverrideUnsafe(changeId, packageName); boolean overrideExists = removeOverrideUnsafe(changeId, packageName);
saveOverrides(); if (overrideExists) {
invalidateCache(); saveOverrides();
invalidateCache();
}
return overrideExists; return overrideExists;
} }
@@ -376,14 +314,9 @@ final class CompatConfig {
*/ */
private boolean removeOverrideUnsafe(long changeId, String packageName) { private boolean removeOverrideUnsafe(long changeId, String packageName) {
Long versionCode = getVersionCodeOrNull(packageName); Long versionCode = getVersionCodeOrNull(packageName);
mReadWriteLock.writeLock().lock(); CompatChange c = mChanges.get(changeId);
try { if (c != null) {
CompatChange c = mChanges.get(changeId); return removeOverrideUnsafe(c, packageName, versionCode);
if (c != null) {
return removeOverrideUnsafe(c, packageName, versionCode);
}
} finally {
mReadWriteLock.writeLock().unlock();
} }
return false; return false;
} }
@@ -397,13 +330,7 @@ final class CompatConfig {
long changeId = change.getId(); long changeId = change.getId();
OverrideAllowedState allowedState = OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName); mOverrideValidator.getOverrideAllowedState(changeId, packageName);
if (change.hasPackageOverride(packageName)) { return change.removePackageOverride(packageName, allowedState, versionCode);
allowedState.enforce(changeId, packageName);
change.removePackageOverride(packageName, allowedState, versionCode);
invalidateCache();
return true;
}
return false;
} }
/** /**
@@ -414,19 +341,16 @@ final class CompatConfig {
* *
* @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) { synchronized void removePackageOverrides(String packageName) {
Long versionCode = getVersionCodeOrNull(packageName); Long versionCode = getVersionCodeOrNull(packageName);
mReadWriteLock.writeLock().lock(); boolean shouldInvalidateCache = false;
try { for (CompatChange change : mChanges.values()) {
for (int i = 0; i < mChanges.size(); ++i) { shouldInvalidateCache |= removeOverrideUnsafe(change, packageName, versionCode);
CompatChange change = mChanges.valueAt(i); }
removeOverrideUnsafe(change, packageName, versionCode); if (shouldInvalidateCache) {
} saveOverrides();
} finally { invalidateCache();
mReadWriteLock.writeLock().unlock();
} }
saveOverrides();
invalidateCache();
} }
/** /**
@@ -439,34 +363,31 @@ final class CompatConfig {
* @param overridesToRemove list of change IDs for which to restore the default behaviour. * @param overridesToRemove list of change IDs for which to restore the default behaviour.
* @param packageName the package for which the overrides should be purged * @param packageName the package for which the overrides should be purged
*/ */
void removePackageOverrides(CompatibilityOverridesToRemoveConfig overridesToRemove, synchronized void removePackageOverrides(CompatibilityOverridesToRemoveConfig overridesToRemove,
String packageName) { String packageName) {
boolean shouldInvalidateCache = false;
for (Long changeId : overridesToRemove.changeIds) { for (Long changeId : overridesToRemove.changeIds) {
removeOverrideUnsafe(changeId, packageName); shouldInvalidateCache |= removeOverrideUnsafe(changeId, packageName);
}
if (shouldInvalidateCache) {
saveOverrides();
invalidateCache();
} }
saveOverrides();
invalidateCache();
} }
private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName, private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName,
int targetSdkVersion) { int targetSdkVersion) {
LongArray allowed = new LongArray(); LongArray allowed = new LongArray();
mReadWriteLock.readLock().lock(); for (CompatChange change : mChanges.values()) {
try { if (change.getEnableSinceTargetSdk() != targetSdkVersion) {
for (int i = 0; i < mChanges.size(); ++i) { continue;
CompatChange change = mChanges.valueAt(i); }
if (change.getEnableSinceTargetSdk() != targetSdkVersion) { OverrideAllowedState allowedState =
continue; mOverrideValidator.getOverrideAllowedState(change.getId(),
} packageName);
OverrideAllowedState allowedState = if (allowedState.state == OverrideAllowedState.ALLOWED) {
mOverrideValidator.getOverrideAllowedState(change.getId(), allowed.add(change.getId());
packageName);
if (allowedState.state == OverrideAllowedState.ALLOWED) {
allowed.add(change.getId());
}
} }
} finally {
mReadWriteLock.readLock().unlock();
} }
return allowed.toArray(); return allowed.toArray();
} }
@@ -479,12 +400,15 @@ final class CompatConfig {
*/ */
int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) { int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion); long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
boolean shouldInvalidateCache = false;
for (long changeId : changes) { for (long changeId : changes) {
addOverrideUnsafe(changeId, packageName, shouldInvalidateCache |= addOverrideUnsafe(changeId, packageName,
new PackageOverride.Builder().setEnabled(true).build()); new PackageOverride.Builder().setEnabled(true).build());
} }
saveOverrides(); if (shouldInvalidateCache) {
invalidateCache(); saveOverrides();
invalidateCache();
}
return changes.length; return changes.length;
} }
@@ -496,30 +420,27 @@ final class CompatConfig {
*/ */
int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) { int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion); long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
boolean shouldInvalidateCache = false;
for (long changeId : changes) { for (long changeId : changes) {
addOverrideUnsafe(changeId, packageName, shouldInvalidateCache |= addOverrideUnsafe(changeId, packageName,
new PackageOverride.Builder().setEnabled(false).build()); new PackageOverride.Builder().setEnabled(false).build());
} }
saveOverrides(); if (shouldInvalidateCache) {
invalidateCache(); saveOverrides();
invalidateCache();
}
return changes.length; return changes.length;
} }
boolean registerListener(long changeId, CompatChange.ChangeListener listener) { boolean registerListener(long changeId, CompatChange.ChangeListener listener) {
boolean alreadyKnown = true; final AtomicBoolean alreadyKnown = new AtomicBoolean(true);
mReadWriteLock.writeLock().lock(); final CompatChange c = mChanges.computeIfAbsent(changeId, (key) -> {
try { alreadyKnown.set(false);
CompatChange c = mChanges.get(changeId); invalidateCache();
if (c == null) { return new CompatChange(changeId);
alreadyKnown = false; });
c = new CompatChange(changeId); c.registerListener(listener);
addChange(c); return alreadyKnown.get();
}
c.registerListener(listener);
} finally {
mReadWriteLock.writeLock().unlock();
}
return alreadyKnown;
} }
boolean defaultChangeIdValue(long changeId) { boolean defaultChangeIdValue(long changeId) {
@@ -537,12 +458,7 @@ final class CompatConfig {
@VisibleForTesting @VisibleForTesting
void clearChanges() { void clearChanges() {
mReadWriteLock.writeLock().lock(); mChanges.clear();
try {
mChanges.clear();
} finally {
mReadWriteLock.writeLock().unlock();
}
} }
/** /**
@@ -551,18 +467,12 @@ final class CompatConfig {
* @param pw {@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) {
mReadWriteLock.readLock().lock(); if (mChanges.size() == 0) {
try { pw.println("No compat overrides.");
if (mChanges.size() == 0) { return;
pw.println("No compat overrides."); }
return; for (CompatChange c : mChanges.values()) {
} pw.println(c.toString());
for (int i = 0; i < mChanges.size(); ++i) {
CompatChange c = mChanges.valueAt(i);
pw.println(c.toString());
}
} finally {
mReadWriteLock.readLock().unlock();
} }
} }
@@ -574,18 +484,12 @@ final class CompatConfig {
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<>();
mReadWriteLock.readLock().lock(); for (CompatChange c : mChanges.values()) {
try { if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) {
for (int i = 0; i < mChanges.size(); ++i) { enabled.add(c.getId());
CompatChange c = mChanges.valueAt(i); } else {
if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) { disabled.add(c.getId());
enabled.add(c.getId());
} else {
disabled.add(c.getId());
}
} }
} finally {
mReadWriteLock.readLock().unlock();
} }
return new CompatibilityChangeConfig(new ChangeConfig(enabled, disabled)); return new CompatibilityChangeConfig(new ChangeConfig(enabled, disabled));
} }
@@ -596,17 +500,12 @@ final class CompatConfig {
* @return an array of {@link CompatibilityChangeInfo} with the current changes * @return an array of {@link CompatibilityChangeInfo} with the current changes
*/ */
CompatibilityChangeInfo[] dumpChanges() { CompatibilityChangeInfo[] dumpChanges() {
mReadWriteLock.readLock().lock(); CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()];
try { int i = 0;
CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()]; for (CompatChange change : mChanges.values()) {
for (int i = 0; i < mChanges.size(); ++i) { changeInfos[i++] = new CompatibilityChangeInfo(change);
CompatChange change = mChanges.valueAt(i);
changeInfos[i] = new CompatibilityChangeInfo(change);
}
return changeInfos;
} finally {
mReadWriteLock.readLock().unlock();
} }
return changeInfos;
} }
void initConfigFromLib(File libraryDir) { void initConfigFromLib(File libraryDir) {
@@ -626,10 +525,12 @@ final class CompatConfig {
Config config = com.android.server.compat.config.XmlParser.read(in); Config config = com.android.server.compat.config.XmlParser.read(in);
for (Change change : config.getCompatChange()) { for (Change change : config.getCompatChange()) {
Slog.d(TAG, "Adding: " + change.toString()); Slog.d(TAG, "Adding: " + change.toString());
addChange(new CompatChange(change)); mChanges.put(change.getId(), new CompatChange(change));
} }
} catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
Slog.e(TAG, "Encountered an error while reading/parsing compat config file", e); Slog.e(TAG, "Encountered an error while reading/parsing compat config file", e);
} finally {
invalidateCache();
} }
} }
@@ -641,15 +542,12 @@ final class CompatConfig {
@VisibleForTesting @VisibleForTesting
void initOverrides(File dynamicOverridesFile, File staticOverridesFile) { void initOverrides(File dynamicOverridesFile, File staticOverridesFile) {
// Clear overrides from all changes before loading. // Clear overrides from all changes before loading.
mReadWriteLock.writeLock().lock();
try { for (CompatChange c : mChanges.values()) {
for (int i = 0; i < mChanges.size(); ++i) { c.clearOverrides();
mChanges.valueAt(i).clearOverrides();
}
} finally {
mReadWriteLock.writeLock().unlock();
} }
loadOverrides(staticOverridesFile); loadOverrides(staticOverridesFile);
mOverridesFile = dynamicOverridesFile; mOverridesFile = dynamicOverridesFile;
@@ -698,18 +596,12 @@ final class CompatConfig {
} }
synchronized (mOverridesFile) { synchronized (mOverridesFile) {
Overrides overrides = new Overrides(); Overrides overrides = new Overrides();
mReadWriteLock.readLock().lock(); List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
try { for (CompatChange c : mChanges.values()) {
List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides(); ChangeOverrides changeOverrides = c.saveOverrides();
for (int idx = 0; idx < mChanges.size(); ++idx) { if (changeOverrides != null) {
CompatChange c = mChanges.valueAt(idx); changeOverridesList.add(changeOverrides);
ChangeOverrides changeOverrides = c.saveOverrides();
if (changeOverrides != null) {
changeOverridesList.add(changeOverrides);
}
} }
} finally {
mReadWriteLock.readLock().unlock();
} }
// Create the file if it doesn't already exist // Create the file if it doesn't already exist
try { try {
@@ -741,20 +633,11 @@ final class CompatConfig {
void recheckOverrides(String packageName) { void recheckOverrides(String packageName) {
Long versionCode = getVersionCodeOrNull(packageName); Long versionCode = getVersionCodeOrNull(packageName);
boolean shouldInvalidateCache = false; boolean shouldInvalidateCache = false;
mReadWriteLock.readLock().lock(); for (CompatChange c : mChanges.values()) {
try { OverrideAllowedState allowedState =
for (int idx = 0; idx < mChanges.size(); ++idx) { mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(),
CompatChange c = mChanges.valueAt(idx); packageName);
if (!c.hasPackageOverride(packageName)) { shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, versionCode);
continue;
}
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(),
packageName);
shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, versionCode);
}
} finally {
mReadWriteLock.readLock().unlock();
} }
if (shouldInvalidateCache) { if (shouldInvalidateCache) {
invalidateCache(); invalidateCache();