Merge "Fix deadlock on CompatConfig.mChanges" into sc-dev

This commit is contained in:
Tom Natan
2021-05-25 12:16:15 +00:00
committed by Android (Google) Code Review
2 changed files with 42 additions and 38 deletions

View File

@@ -26,9 +26,7 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled; import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince; import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable; import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.android.internal.compat.AndroidBuildClassifier; import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeInfo; import com.android.internal.compat.CompatibilityChangeInfo;
@@ -161,15 +159,17 @@ public final class CompatChange extends CompatibilityChangeInfo {
* *
* @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 versionCode The version code of the package.
*/ */
void addPackageOverride(String packageName, PackageOverride override, void addPackageOverride(String packageName, PackageOverride override,
OverrideAllowedState allowedState, Context context) { OverrideAllowedState allowedState, @Nullable Long versionCode) {
if (getLoggingOnly()) { if (getLoggingOnly()) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Can't add overrides for a logging only change " + toString()); "Can't add overrides for a logging only change " + toString());
} }
mRawOverrides.put(packageName, override); mRawOverrides.put(packageName, override);
recheckOverride(packageName, allowedState, context); recheckOverride(packageName, allowedState, versionCode);
} }
/** /**
@@ -179,32 +179,24 @@ public final class CompatChange extends CompatibilityChangeInfo {
* overrides, check if they need to be demoted to deferred.</p> * overrides, check if they need to be demoted to deferred.</p>
* *
* @param packageName Package name to apply deferred overrides for. * @param packageName Package name to apply deferred overrides for.
* @param allowed Whether the override is allowed. * @param allowedState Whether the override is allowed.
* @param versionCode The version code of the package.
* *
* @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, boolean recheckOverride(String packageName, OverrideAllowedState allowedState,
Context context) { @Nullable Long versionCode) {
boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED); boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED);
Long version = null;
try {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(
packageName, 0);
version = applicationInfo.longVersionCode;
} catch (PackageManager.NameNotFoundException e) {
// Do nothing
}
// 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 (version == null || !hasRawOverride(packageName) || !allowed) { if (versionCode == null || !hasRawOverride(packageName) || !allowed) {
removePackageOverrideInternal(packageName); removePackageOverrideInternal(packageName);
return false; return false;
} }
// Evaluate the override based on its version // Evaluate the override based on its version
int overrideValue = mRawOverrides.get(packageName).evaluate(version); int overrideValue = mRawOverrides.get(packageName).evaluate(versionCode);
switch (overrideValue) { switch (overrideValue) {
case VALUE_UNDEFINED: case VALUE_UNDEFINED:
removePackageOverrideInternal(packageName); removePackageOverrideInternal(packageName);
@@ -229,11 +221,13 @@ public final class CompatChange extends CompatibilityChangeInfo {
* <p>Note, this method is not thread safe so callers must ensure thread safety. * <p>Note, this method is not thread safe so callers must ensure thread safety.
* *
* @param pname Package name to reset to defaults for. * @param pname Package name to reset to defaults for.
* @param allowedState Whether the override is allowed.
* @param versionCode The version code of the package.
*/ */
boolean removePackageOverride(String pname, OverrideAllowedState allowedState, boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
Context context) { @Nullable Long versionCode) {
if (mRawOverrides.remove(pname) != null) { if (mRawOverrides.remove(pname) != null) {
recheckOverride(pname, allowedState, context); recheckOverride(pname, allowedState, versionCode);
return true; return true;
} }
return false; return false;

View File

@@ -16,11 +16,13 @@
package com.android.server.compat; package com.android.server.compat;
import android.annotation.Nullable;
import android.app.compat.ChangeIdStateCache; import android.app.compat.ChangeIdStateCache;
import android.app.compat.PackageOverride; import android.app.compat.PackageOverride;
import android.compat.Compatibility.ChangeConfig; 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.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;
@@ -51,7 +53,6 @@ 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.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -249,6 +250,7 @@ final class CompatConfig {
OverrideAllowedState allowedState = OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName); mOverrideValidator.getOverrideAllowedState(changeId, packageName);
allowedState.enforce(changeId, packageName); allowedState.enforce(changeId, packageName);
Long versionCode = getVersionCodeOrNull(packageName);
synchronized (mChanges) { synchronized (mChanges) {
CompatChange c = mChanges.get(changeId); CompatChange c = mChanges.get(changeId);
if (c == null) { if (c == null) {
@@ -256,7 +258,7 @@ final class CompatConfig {
c = new CompatChange(changeId); c = new CompatChange(changeId);
addChange(c); addChange(c);
} }
c.addPackageOverride(packageName, overrides, allowedState, mContext); c.addPackageOverride(packageName, overrides, allowedState, versionCode);
invalidateCache(); invalidateCache();
} }
return alreadyKnown; return alreadyKnown;
@@ -336,6 +338,7 @@ final class CompatConfig {
* It does not invalidate the cache nor save the overrides. * It does not invalidate the cache nor save the overrides.
*/ */
private boolean removeOverrideUnsafe(long changeId, String packageName) { private boolean removeOverrideUnsafe(long changeId, String packageName) {
Long versionCode = getVersionCodeOrNull(packageName);
synchronized (mChanges) { synchronized (mChanges) {
CompatChange c = mChanges.get(changeId); CompatChange c = mChanges.get(changeId);
if (c != null) { if (c != null) {
@@ -343,7 +346,7 @@ final class CompatConfig {
mOverrideValidator.getOverrideAllowedState(changeId, packageName); mOverrideValidator.getOverrideAllowedState(changeId, packageName);
if (c.hasPackageOverride(packageName)) { if (c.hasPackageOverride(packageName)) {
allowedState.enforce(changeId, packageName); allowedState.enforce(changeId, packageName);
c.removePackageOverride(packageName, allowedState, mContext); c.removePackageOverride(packageName, allowedState, versionCode);
invalidateCache(); invalidateCache();
return true; return true;
} }
@@ -653,28 +656,35 @@ final class CompatConfig {
* Rechecks all the existing overrides for a package. * Rechecks all the existing overrides for a package.
*/ */
void recheckOverrides(String packageName) { void recheckOverrides(String packageName) {
// Local cache of compat changes. Holding a lock on mChanges for the whole duration of the Long versionCode = getVersionCodeOrNull(packageName);
// method will cause a deadlock.
List<CompatChange> changes;
synchronized (mChanges) { synchronized (mChanges) {
changes = new ArrayList<>(mChanges.size());
for (int idx = 0; idx < mChanges.size(); ++idx) {
changes.add(mChanges.valueAt(idx));
}
}
boolean shouldInvalidateCache = false; boolean shouldInvalidateCache = false;
for (CompatChange c: changes) { for (int idx = 0; idx < mChanges.size(); ++idx) {
CompatChange c = mChanges.valueAt(idx);
if (!c.hasPackageOverride(packageName)) { if (!c.hasPackageOverride(packageName)) {
continue; continue;
} }
OverrideAllowedState allowedState = OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(), packageName); mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(),
shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext); packageName);
shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, versionCode);
} }
if (shouldInvalidateCache) { if (shouldInvalidateCache) {
invalidateCache(); invalidateCache();
} }
} }
}
@Nullable
private Long getVersionCodeOrNull(String packageName) {
try {
ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo(
packageName, 0);
return applicationInfo.longVersionCode;
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
void registerContentObserver() { void registerContentObserver() {
mOverrideValidator.registerContentObserver(); mOverrideValidator.registerContentObserver();