Merge changes Idd7d86f9,I73569744

* changes:
  Do not enable changes newer than the current sdk
  Add support for loading a static overrides file located in /product.
This commit is contained in:
Andrei-Valentin Onea
2021-03-12 13:28:50 +00:00
committed by Gerrit Code Review
10 changed files with 304 additions and 82 deletions

View File

@@ -31,4 +31,14 @@ public class AndroidBuildClassifier {
public boolean isFinalBuild() { public boolean isFinalBuild() {
return "REL".equals(Build.VERSION.CODENAME); return "REL".equals(Build.VERSION.CODENAME);
} }
/**
* The current platform SDK version.
*/
public int platformTargetSdk() {
if (isFinalBuild()) {
return Build.VERSION.SDK_INT;
}
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
} }

View File

@@ -34,7 +34,8 @@ public final class OverrideAllowedState implements Parcelable {
DISABLED_NON_TARGET_SDK, DISABLED_NON_TARGET_SDK,
DISABLED_TARGET_SDK_TOO_HIGH, DISABLED_TARGET_SDK_TOO_HIGH,
DEFERRED_VERIFICATION, DEFERRED_VERIFICATION,
LOGGING_ONLY_CHANGE LOGGING_ONLY_CHANGE,
PLATFORM_TOO_OLD
}) })
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface State { public @interface State {
@@ -65,6 +66,10 @@ public final class OverrideAllowedState implements Parcelable {
* Change is marked as logging only, and cannot be toggled. * Change is marked as logging only, and cannot be toggled.
*/ */
public static final int LOGGING_ONLY_CHANGE = 5; public static final int LOGGING_ONLY_CHANGE = 5;
/**
* Change is gated by a target sdk version newer than the current platform sdk version.
*/
public static final int PLATFORM_TOO_OLD = 6;
@State @State
public final int state; public final int state;
@@ -123,6 +128,11 @@ public final class OverrideAllowedState implements Parcelable {
throw new SecurityException(String.format( throw new SecurityException(String.format(
"Cannot override %1$d because it is marked as a logging-only change.", "Cannot override %1$d because it is marked as a logging-only change.",
changeId)); changeId));
case PLATFORM_TOO_OLD:
throw new SecurityException(String.format(
"Cannot override %1$d for %2$s because the change's targetSdk threshold "
+ "(%3$d) is above the platform sdk.",
changeId, packageName, changeIdTargetSdk));
} }
} }
@@ -170,6 +180,8 @@ public final class OverrideAllowedState implements Parcelable {
return "DEFERRED_VERIFICATION"; return "DEFERRED_VERIFICATION";
case LOGGING_ONLY_CHANGE: case LOGGING_ONLY_CHANGE:
return "LOGGING_ONLY_CHANGE"; return "LOGGING_ONLY_CHANGE";
case PLATFORM_TOO_OLD:
return "PLATFORM_TOO_OLD";
} }
return "UNKNOWN"; return "UNKNOWN";
} }

View File

@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeInfo; import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.OverrideAllowedState; import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change; import com.android.server.compat.config.Change;
@@ -55,7 +56,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
* A change ID to be used only in the CTS test for this SystemApi * A change ID to be used only in the CTS test for this SystemApi
*/ */
@ChangeId @ChangeId
@EnabledSince(targetSdkVersion = 1235) // Needs to be > test APK targetSdkVersion. @EnabledSince(targetSdkVersion = 31) // Needs to be > test APK targetSdkVersion.
static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id. static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id.
/** /**
@@ -79,6 +80,15 @@ public final class CompatChange extends CompatibilityChangeInfo {
this(changeId, null, -1, -1, false, false, null, false); this(changeId, null, -1, -1, false, false, null, false);
} }
/**
* @param change an object generated by services/core/xsd/platform-compat-config.xsd
*/
public CompatChange(Change change) {
this(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(),
change.getDescription(), change.getOverridable());
}
/** /**
* @param changeId Unique ID for the change. See {@link android.compat.Compatibility}. * @param changeId Unique ID for the change. See {@link android.compat.Compatibility}.
* @param name Short descriptive name. * @param name Short descriptive name.
@@ -93,15 +103,10 @@ public final class CompatChange extends CompatibilityChangeInfo {
boolean overridable) { boolean overridable) {
super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly, super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly,
description, overridable); description, overridable);
}
/** // Initialize override maps.
* @param change an object generated by services/core/xsd/platform-compat-config.xsd mEvaluatedOverrides = new HashMap<>();
*/ mRawOverrides = new HashMap<>();
public CompatChange(Change change) {
super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(),
change.getDescription(), change.getOverridable());
} }
void registerListener(ChangeListener listener) { void registerListener(ChangeListener listener) {
@@ -127,18 +132,13 @@ public final class CompatChange extends CompatibilityChangeInfo {
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());
} }
if (mEvaluatedOverrides == null) {
mEvaluatedOverrides = new HashMap<>();
}
mEvaluatedOverrides.put(pname, enabled); mEvaluatedOverrides.put(pname, enabled);
notifyListener(pname); notifyListener(pname);
} }
private void removePackageOverrideInternal(String pname) { private void removePackageOverrideInternal(String pname) {
if (mEvaluatedOverrides != null) { if (mEvaluatedOverrides.remove(pname) != null) {
if (mEvaluatedOverrides.remove(pname) != null) { notifyListener(pname);
notifyListener(pname);
}
} }
} }
@@ -157,9 +157,6 @@ public final class CompatChange extends CompatibilityChangeInfo {
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());
} }
if (mRawOverrides == null) {
mRawOverrides = new HashMap<>();
}
mRawOverrides.put(packageName, override); mRawOverrides.put(packageName, override);
recheckOverride(packageName, allowedState, context); recheckOverride(packageName, allowedState, context);
} }
@@ -212,7 +209,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
} }
boolean hasPackageOverride(String pname) { boolean hasPackageOverride(String pname) {
return mRawOverrides != null && mRawOverrides.containsKey(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.
@@ -223,7 +220,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
*/ */
boolean removePackageOverride(String pname, OverrideAllowedState allowedState, boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
Context context) { Context context) {
if (mRawOverrides != null && (mRawOverrides.remove(pname) != null)) { if (mRawOverrides.remove(pname) != null) {
recheckOverride(pname, allowedState, context); recheckOverride(pname, allowedState, context);
return true; return true;
} }
@@ -237,18 +234,24 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @param app Info about the app in question * @param app Info about the app in question
* @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 isEnabled(ApplicationInfo app) { boolean isEnabled(ApplicationInfo app, AndroidBuildClassifier buildClassifier) {
if (app == null) { if (app == null) {
return defaultValue(); return defaultValue();
} }
if (mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(app.packageName)) { if (mEvaluatedOverrides.containsKey(app.packageName)) {
return mEvaluatedOverrides.get(app.packageName); return mEvaluatedOverrides.get(app.packageName);
} }
if (getDisabled()) { if (getDisabled()) {
return false; return false;
} }
if (getEnableSinceTargetSdk() != -1) { if (getEnableSinceTargetSdk() != -1) {
return app.targetSdkVersion >= getEnableSinceTargetSdk(); // If the change is gated by a platform version newer than the one currently installed
// on the device, disregard the app's target sdk version.
int compareSdk = Math.min(app.targetSdkVersion, buildClassifier.platformTargetSdk());
if (compareSdk != app.targetSdkVersion) {
compareSdk = app.targetSdkVersion;
}
return compareSdk >= getEnableSinceTargetSdk();
} }
return true; return true;
} }
@@ -289,7 +292,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @return true if there is such override * @return true if there is such override
*/ */
private boolean hasOverride(String packageName) { private boolean hasOverride(String packageName) {
return mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(packageName); return mEvaluatedOverrides.containsKey(packageName);
} }
/** /**
@@ -298,20 +301,15 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @return true if there is such a deferred override * @return true if there is such a deferred override
*/ */
private boolean hasRawOverride(String packageName) { private boolean hasRawOverride(String packageName) {
return mRawOverrides != null && mRawOverrides.containsKey(packageName); return mRawOverrides.containsKey(packageName);
}
void clearOverrides() {
mRawOverrides.clear();
mEvaluatedOverrides.clear();
} }
void loadOverrides(ChangeOverrides changeOverrides) { void loadOverrides(ChangeOverrides changeOverrides) {
if (mRawOverrides == null) {
mRawOverrides = new HashMap<>();
}
mRawOverrides.clear();
if (mEvaluatedOverrides == null) {
mEvaluatedOverrides = new HashMap<>();
}
mEvaluatedOverrides.clear();
// 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()) {
@@ -345,34 +343,30 @@ public final class CompatChange extends CompatibilityChangeInfo {
} }
ChangeOverrides saveOverrides() { ChangeOverrides saveOverrides() {
if (mRawOverrides == null || mRawOverrides.isEmpty()) { if (mRawOverrides.isEmpty()) {
return null; return null;
} }
ChangeOverrides changeOverrides = new ChangeOverrides(); ChangeOverrides changeOverrides = new ChangeOverrides();
changeOverrides.setChangeId(getId()); changeOverrides.setChangeId(getId());
ChangeOverrides.Raw rawOverrides = new ChangeOverrides.Raw(); ChangeOverrides.Raw rawOverrides = new ChangeOverrides.Raw();
List<RawOverrideValue> rawList = rawOverrides.getRawOverrideValue(); List<RawOverrideValue> rawList = rawOverrides.getRawOverrideValue();
if (mRawOverrides != null) { for (Map.Entry<String, PackageOverride> entry : mRawOverrides.entrySet()) {
for (Map.Entry<String, PackageOverride> entry : mRawOverrides.entrySet()) { RawOverrideValue override = new RawOverrideValue();
RawOverrideValue override = new RawOverrideValue(); override.setPackageName(entry.getKey());
override.setPackageName(entry.getKey()); override.setMinVersionCode(entry.getValue().getMinVersionCode());
override.setMinVersionCode(entry.getValue().getMinVersionCode()); override.setMaxVersionCode(entry.getValue().getMaxVersionCode());
override.setMaxVersionCode(entry.getValue().getMaxVersionCode()); override.setEnabled(entry.getValue().getEnabled());
override.setEnabled(entry.getValue().getEnabled()); rawList.add(override);
rawList.add(override);
}
} }
changeOverrides.setRaw(rawOverrides); changeOverrides.setRaw(rawOverrides);
ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated(); ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated();
List<OverrideValue> validatedList = validatedOverrides.getOverrideValue(); List<OverrideValue> validatedList = validatedOverrides.getOverrideValue();
if (mEvaluatedOverrides != null) { for (Map.Entry<String, Boolean> entry : mEvaluatedOverrides.entrySet()) {
for (Map.Entry<String, Boolean> entry : mEvaluatedOverrides.entrySet()) { OverrideValue override = new OverrideValue();
OverrideValue override = new OverrideValue(); override.setPackageName(entry.getKey());
override.setPackageName(entry.getKey()); override.setEnabled(entry.getValue());
override.setEnabled(entry.getValue()); validatedList.add(override);
validatedList.add(override);
}
} }
changeOverrides.setValidated(validatedOverrides); changeOverrides.setValidated(validatedOverrides);
return changeOverrides; return changeOverrides;
@@ -394,10 +388,10 @@ public final class CompatChange extends CompatibilityChangeInfo {
if (getLoggingOnly()) { if (getLoggingOnly()) {
sb.append("; loggingOnly"); sb.append("; loggingOnly");
} }
if (mEvaluatedOverrides != null && mEvaluatedOverrides.size() > 0) { if (!mEvaluatedOverrides.isEmpty()) {
sb.append("; packageOverrides=").append(mEvaluatedOverrides); sb.append("; packageOverrides=").append(mEvaluatedOverrides);
} }
if (mRawOverrides != null && mRawOverrides.size() > 0) { if (!mRawOverrides.isEmpty()) {
sb.append("; rawOverrides=").append(mRawOverrides); sb.append("; rawOverrides=").append(mRawOverrides);
} }
if (getOverridable()) { if (getOverridable()) {

View File

@@ -67,18 +67,21 @@ final class CompatConfig {
private static final String TAG = "CompatConfig"; private static final String TAG = "CompatConfig";
private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat"; private static final String APP_COMPAT_DATA_DIR = "/data/misc/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";
@GuardedBy("mChanges") @GuardedBy("mChanges")
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>(); private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
private final OverrideValidatorImpl mOverrideValidator; private final OverrideValidatorImpl mOverrideValidator;
private final AndroidBuildClassifier mAndroidBuildClassifier;
private Context mContext; private Context mContext;
private File mOverridesFile; private File mOverridesFile;
@VisibleForTesting @VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) { CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this); mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
mAndroidBuildClassifier = androidBuildClassifier;
mContext = context; mContext = context;
} }
@@ -94,8 +97,7 @@ final class CompatConfig {
config.initConfigFromLib(Environment.buildPath( config.initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig")); apex.apexDirectory, "etc", "compatconfig"));
} }
File overridesFile = new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE); config.initOverrides();
config.initOverrides(overridesFile);
config.invalidateCache(); config.invalidateCache();
return config; return config;
} }
@@ -133,7 +135,7 @@ final class CompatConfig {
synchronized (mChanges) { synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) { for (int i = 0; i < mChanges.size(); ++i) {
CompatChange c = mChanges.valueAt(i); CompatChange c = mChanges.valueAt(i);
if (!c.isEnabled(app)) { if (!c.isEnabled(app, mAndroidBuildClassifier)) {
disabled.add(c.getId()); disabled.add(c.getId());
} }
} }
@@ -175,7 +177,7 @@ final class CompatConfig {
// we know nothing about this change: default behaviour is enabled. // we know nothing about this change: default behaviour is enabled.
return true; return true;
} }
return c.isEnabled(app); return c.isEnabled(app, mAndroidBuildClassifier);
} }
} }
@@ -475,7 +477,7 @@ final class CompatConfig {
synchronized (mChanges) { synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) { for (int i = 0; i < mChanges.size(); ++i) {
CompatChange c = mChanges.valueAt(i); CompatChange c = mChanges.valueAt(i);
if (c.isEnabled(applicationInfo)) { if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) {
enabled.add(c.getId()); enabled.add(c.getId());
} else { } else {
disabled.add(c.getId()); disabled.add(c.getId());
@@ -525,10 +527,34 @@ final class CompatConfig {
} }
} }
void initOverrides(File overridesFile) { private void initOverrides() {
initOverrides(new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE),
new File(STATIC_OVERRIDES_PRODUCT_DIR, OVERRIDES_FILE));
}
@VisibleForTesting
void initOverrides(File dynamicOverridesFile, File staticOverridesFile) {
// Clear overrides from all changes before loading.
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
mChanges.valueAt(i).clearOverrides();
}
}
loadOverrides(staticOverridesFile);
mOverridesFile = dynamicOverridesFile;
loadOverrides(dynamicOverridesFile);
if (staticOverridesFile.exists()) {
// Only save overrides if there is a static overrides file.
saveOverrides();
}
}
private void loadOverrides(File overridesFile) {
if (!overridesFile.exists()) { if (!overridesFile.exists()) {
mOverridesFile = overridesFile; // Overrides file doesn't exist.
// There have not been any overrides added yet.
return; return;
} }
@@ -548,7 +574,6 @@ final class CompatConfig {
Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString()); Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString());
return; return;
} }
mOverridesFile = overridesFile;
} }
/** /**

View File

@@ -22,6 +22,7 @@ import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARG
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE; import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH; import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE; import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
@@ -85,6 +86,9 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub {
if (debuggableBuild) { if (debuggableBuild) {
return new OverrideAllowedState(ALLOWED, -1, -1); return new OverrideAllowedState(ALLOWED, -1, -1);
} }
if (maxTargetSdk >= mAndroidBuildClassifier.platformTargetSdk()) {
return new OverrideAllowedState(PLATFORM_TOO_OLD, -1, maxTargetSdk);
}
PackageManager packageManager = mContext.getPackageManager(); PackageManager packageManager = mContext.getPackageManager();
if (packageManager == null) { if (packageManager == null) {
throw new IllegalStateException("No PackageManager!"); throw new IllegalStateException("No PackageManager!");

View File

@@ -66,18 +66,22 @@ public class PlatformCompat extends IPlatformCompat.Stub {
private final Context mContext; private final Context mContext;
private final ChangeReporter mChangeReporter; private final ChangeReporter mChangeReporter;
private final CompatConfig mCompatConfig; private final CompatConfig mCompatConfig;
private final AndroidBuildClassifier mBuildClassifier;
public PlatformCompat(Context context) { public PlatformCompat(Context context) {
mContext = context; mContext = context;
mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER); mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext); mBuildClassifier = new AndroidBuildClassifier();
mCompatConfig = CompatConfig.create(mBuildClassifier, mContext);
} }
@VisibleForTesting @VisibleForTesting
PlatformCompat(Context context, CompatConfig compatConfig) { PlatformCompat(Context context, CompatConfig compatConfig,
AndroidBuildClassifier buildClassifier) {
mContext = context; mContext = context;
mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER); mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
mCompatConfig = compatConfig; mCompatConfig = compatConfig;
mBuildClassifier = buildClassifier;
registerPackageReceiver(context); registerPackageReceiver(context);
} }
@@ -392,7 +396,8 @@ public class PlatformCompat extends IPlatformCompat.Stub {
return false; return false;
} }
if (change.getEnableSinceTargetSdk() > 0) { if (change.getEnableSinceTargetSdk() > 0) {
return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q; return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q
&& change.getEnableSinceTargetSdk() <= mBuildClassifier.platformTargetSdk();
} }
return true; return true;
} }

View File

@@ -120,6 +120,11 @@ class CompatConfigBuilder {
return this; return this;
} }
CompatConfigBuilder addEnabledSinceApexChangeWithId(int sdk, long id) {
mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "", false));
return this;
}
CompatConfig build() { CompatConfig build() {
CompatConfig config = new CompatConfig(mBuildClassifier, mContext); CompatConfig config = new CompatConfig(mBuildClassifier, mContext);
config.forceNonDebuggableFinalForTest(false); config.forceNonDebuggableFinalForTest(false);

View File

@@ -86,6 +86,7 @@ public class CompatConfigTest {
// Assume userdebug/eng non-final build // Assume userdebug/eng non-final build
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true); when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false); when(mBuildClassifier.isFinalBuild()).thenReturn(false);
when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
ChangeIdStateCache.disable(); ChangeIdStateCache.disable();
when(mPackageManager.getApplicationInfo(anyString(), anyInt())) when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
.thenThrow(new NameNotFoundException()); .thenThrow(new NameNotFoundException());
@@ -566,6 +567,34 @@ public class CompatConfigTest {
ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue(); ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
} }
@Test
public void testReadApexConfig() throws IOException {
String configXml = "<config>"
+ "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
+ "<compat-change id=\"1235\" name=\"MY_CHANGE2\" disabled=\"true\" />"
+ "<compat-change id=\"1236\" name=\"MY_CHANGE3\" />"
+ "<compat-change id=\"1237\" name=\"MY_CHANGE4\" enableSinceTargetSdk=\"31\" />"
+ "</config>";
File dir = createTempDir();
writeToFile(dir, "platform_compat_config.xml", configXml);
CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
compatConfig.forceNonDebuggableFinalForTest(false);
compatConfig.initConfigFromLib(dir);
assertThat(compatConfig.isChangeEnabled(1234L,
ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse();
assertThat(compatConfig.isChangeEnabled(1234L,
ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
assertThat(compatConfig.isChangeEnabled(1235L,
ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse();
assertThat(compatConfig.isChangeEnabled(1236L,
ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
assertThat(compatConfig.isChangeEnabled(1237L,
ApplicationInfoBuilder.create().withTargetSdk(31).build())).isTrue();
}
@Test @Test
public void testReadConfigMultipleFiles() throws IOException { public void testReadConfigMultipleFiles() throws IOException {
String configXml1 = "<config>" String configXml1 = "<config>"
@@ -602,12 +631,12 @@ public class CompatConfigTest {
.addEnableSinceSdkChangeWithId(2, 2L) .addEnableSinceSdkChangeWithId(2, 2L)
.build(); .build();
compatConfig.forceNonDebuggableFinalForTest(true); compatConfig.forceNonDebuggableFinalForTest(true);
compatConfig.initOverrides(overridesFile); compatConfig.initOverrides(overridesFile, new File(""));
when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt())) when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
.thenReturn(ApplicationInfoBuilder.create() .thenReturn(ApplicationInfoBuilder.create()
.withPackageName("foo.bar") .withPackageName("foo.bar")
.debuggable() .debuggable()
.build()); .build());
when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt())) when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
.thenThrow(new NameNotFoundException()); .thenThrow(new NameNotFoundException());
@@ -649,7 +678,7 @@ public class CompatConfigTest {
.addEnableSinceSdkChangeWithId(2, 2L) .addEnableSinceSdkChangeWithId(2, 2L)
.build(); .build();
compatConfig.forceNonDebuggableFinalForTest(true); compatConfig.forceNonDebuggableFinalForTest(true);
compatConfig.initOverrides(overridesFile); compatConfig.initOverrides(overridesFile, new File(""));
compatConfig.addOverrides(new CompatibilityOverrideConfig(Collections.singletonMap(1L, compatConfig.addOverrides(new CompatibilityOverrideConfig(Collections.singletonMap(1L,
new PackageOverride.Builder() new PackageOverride.Builder()
@@ -673,11 +702,11 @@ public class CompatConfigTest {
} }
@Test @Test
public void testLoadOverridesRaw() throws Exception { public void testInitOverridesRaw() throws Exception {
File tempDir = createTempDir(); File tempDir = createTempDir();
File overridesFile = new File(tempDir, "overrides.xml"); File overridesFile = new File(tempDir, "overrides.xml");
// Change 1 is enabled for foo.bar (validated) // Change 1 is enabled for foo.bar (validated)
// Change 2 is disabled for bar.baz (deferred) // Change 2 is disabled for bar.baz (raw)
String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<overrides>\n" + "<overrides>\n"
+ " <change-overrides changeId=\"1\">\n" + " <change-overrides changeId=\"1\">\n"
@@ -709,7 +738,7 @@ public class CompatConfigTest {
.addEnableSinceSdkChangeWithId(2, 2L) .addEnableSinceSdkChangeWithId(2, 2L)
.build(); .build();
compatConfig.forceNonDebuggableFinalForTest(true); compatConfig.forceNonDebuggableFinalForTest(true);
compatConfig.initOverrides(overridesFile); compatConfig.initOverrides(overridesFile, new File(""));
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
.withPackageName("foo.bar") .withPackageName("foo.bar")
.withVersionCode(100L) .withVersionCode(100L)
@@ -728,7 +757,7 @@ public class CompatConfigTest {
} }
@Test @Test
public void testLoadOverridesDeferred() throws Exception { public void testInitOverridesDeferred() throws Exception {
File tempDir = createTempDir(); File tempDir = createTempDir();
File overridesFile = new File(tempDir, "overrides.xml"); File overridesFile = new File(tempDir, "overrides.xml");
// Change 1 is enabled for foo.bar (validated) // Change 1 is enabled for foo.bar (validated)
@@ -754,7 +783,7 @@ public class CompatConfigTest {
.addEnableSinceSdkChangeWithId(2, 2L) .addEnableSinceSdkChangeWithId(2, 2L)
.build(); .build();
compatConfig.forceNonDebuggableFinalForTest(true); compatConfig.forceNonDebuggableFinalForTest(true);
compatConfig.initOverrides(overridesFile); compatConfig.initOverrides(overridesFile, new File(""));
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
.withPackageName("foo.bar") .withPackageName("foo.bar")
.debuggable() .debuggable()
@@ -767,4 +796,115 @@ public class CompatConfigTest {
assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue(); assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse(); assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
} }
@Test
public void testInitOverridesWithStaticFile() throws Exception {
File tempDir = createTempDir();
File dynamicOverridesFile = new File(tempDir, "dynamic_overrides.xml");
File staticOverridesFile = new File(tempDir, "static_overrides.xml");
// Change 1 is enabled for foo.bar (raw)
// Change 2 is disabled for bar.baz (raw)
String dynamicXmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<overrides>"
+ "<change-overrides changeId=\"1\">"
+ "<raw>"
+ " <raw-override-value packageName=\"foo.bar\" "
+ "minVersionCode=\"-9223372036854775808\" "
+ "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ " </raw-override-value>\n"
+ "</raw>"
+ "</change-overrides>"
+ "<change-overrides changeId=\"2\">"
+ "<raw>"
+ " <raw-override-value packageName=\"bar.baz\" "
+ "minVersionCode=\"-9223372036854775808\" "
+ "maxVersionCode=\"9223372036854775807\" enabled=\"false\">\n"
+ " </raw-override-value>\n"
+ "</raw>"
+ "</change-overrides>"
+ "</overrides>";
writeToFile(tempDir, "dynamic_overrides.xml", dynamicXmlData);
// Change 2 is enabled for foo.bar and bar.baz (raw)
// Change 3 is enabled for bar.baz (raw)
String staticXmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<overrides>"
+ "<change-overrides changeId=\"2\">"
+ "<raw>"
+ " <raw-override-value packageName=\"foo.bar\" "
+ "minVersionCode=\"-9223372036854775808\" "
+ "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ " </raw-override-value>\n"
+ " <raw-override-value packageName=\"bar.baz\" "
+ "minVersionCode=\"-9223372036854775808\" "
+ "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ " </raw-override-value>\n"
+ "</raw>"
+ "</change-overrides>"
+ "<change-overrides changeId=\"3\">"
+ "<raw>"
+ " <raw-override-value packageName=\"bar.baz\" "
+ "minVersionCode=\"-9223372036854775808\" "
+ "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ " </raw-override-value>\n"
+ "</raw>"
+ "</change-overrides>"
+ "</overrides>";
writeToFile(tempDir, "static_overrides.xml", staticXmlData);
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
.addDisabledChangeWithId(1L)
.addDisabledChangeWithId(2L)
.addDisabledChangeWithId(3L)
.build();
compatConfig.forceNonDebuggableFinalForTest(true);
// Adding an override that will be cleared after initOverrides is called.
compatConfig.addOverride(1L, "bar.baz", true);
compatConfig.initOverrides(dynamicOverridesFile, staticOverridesFile);
when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
.thenThrow(new NameNotFoundException());
when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
.thenThrow(new NameNotFoundException());
assertThat(compatConfig.willChangeBeEnabled(1L, "foo.bar")).isTrue();
assertThat(compatConfig.willChangeBeEnabled(2L, "foo.bar")).isTrue();
assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
assertThat(compatConfig.willChangeBeEnabled(3L, "bar.baz")).isTrue();
assertThat(readFile(dynamicOverridesFile))
.isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<overrides>\n"
+ " <change-overrides changeId=\"1\">\n"
+ " <validated>\n"
+ " </validated>\n"
+ " <raw>\n"
+ " <raw-override-value packageName=\"foo.bar\" "
+ "minVersionCode=\"-9223372036854775808\" "
+ "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ " </raw-override-value>\n"
+ " </raw>\n"
+ " </change-overrides>\n"
+ " <change-overrides changeId=\"2\">\n"
+ " <validated>\n"
+ " </validated>\n"
+ " <raw>\n"
+ " <raw-override-value packageName=\"foo.bar\" "
+ "minVersionCode=\"-9223372036854775808\" "
+ "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ " </raw-override-value>\n"
+ " <raw-override-value packageName=\"bar.baz\" "
+ "minVersionCode=\"-9223372036854775808\" "
+ "maxVersionCode=\"9223372036854775807\" enabled=\"false\">\n"
+ " </raw-override-value>\n"
+ " </raw>\n"
+ " </change-overrides>\n"
+ " <change-overrides changeId=\"3\">\n"
+ " <validated>\n"
+ " </validated>\n"
+ " <raw>\n"
+ " <raw-override-value packageName=\"bar.baz\" "
+ "minVersionCode=\"-9223372036854775808\" "
+ "maxVersionCode=\"9223372036854775807\" enabled=\"true\">\n"
+ " </raw-override-value>\n"
+ " </raw>\n"
+ " </change-overrides>\n"
+ "</overrides>\n");
}
} }

View File

@@ -22,6 +22,7 @@ import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARG
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE; import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH; import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE; import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@@ -52,6 +53,7 @@ public class OverrideValidatorImplTest {
private static final int TARGET_SDK = 10; private static final int TARGET_SDK = 10;
private static final int TARGET_SDK_BEFORE = 9; private static final int TARGET_SDK_BEFORE = 9;
private static final int TARGET_SDK_AFTER = 11; private static final int TARGET_SDK_AFTER = 11;
private static final int PLATFORM_SDK_VERSION = 30;
@Mock @Mock
private PackageManager mPackageManager; private PackageManager mPackageManager;
@@ -61,6 +63,7 @@ public class OverrideValidatorImplTest {
private AndroidBuildClassifier debuggableBuild() { private AndroidBuildClassifier debuggableBuild() {
AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
when(buildClassifier.isDebuggableBuild()).thenReturn(true); when(buildClassifier.isDebuggableBuild()).thenReturn(true);
when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
return buildClassifier; return buildClassifier;
} }
@@ -68,6 +71,7 @@ public class OverrideValidatorImplTest {
AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
when(buildClassifier.isDebuggableBuild()).thenReturn(false); when(buildClassifier.isDebuggableBuild()).thenReturn(false);
when(buildClassifier.isFinalBuild()).thenReturn(false); when(buildClassifier.isFinalBuild()).thenReturn(false);
when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
return buildClassifier; return buildClassifier;
} }
@@ -75,6 +79,7 @@ public class OverrideValidatorImplTest {
AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
when(buildClassifier.isDebuggableBuild()).thenReturn(false); when(buildClassifier.isDebuggableBuild()).thenReturn(false);
when(buildClassifier.isFinalBuild()).thenReturn(true); when(buildClassifier.isFinalBuild()).thenReturn(true);
when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
return buildClassifier; return buildClassifier;
} }
@@ -332,6 +337,26 @@ public class OverrideValidatorImplTest {
TARGET_SDK_BEFORE)); TARGET_SDK_BEFORE));
} }
@Test
public void getOverrideAllowedState_targetSdkChangeGreaterThanOsVersion_rejectOverride()
throws Exception {
final AndroidBuildClassifier buildClassifier = finalBuild();
CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
.addEnabledSinceApexChangeWithId(PLATFORM_SDK_VERSION + 1, 1).build();
IOverrideValidator overrideValidator = config.getOverrideValidator();
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
.thenReturn(ApplicationInfoBuilder.create()
.withPackageName(PACKAGE_NAME)
.debuggable()
.build());
OverrideAllowedState stateTargetSdkLessChange =
overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
assertThat(stateTargetSdkLessChange).isEqualTo(
new OverrideAllowedState(PLATFORM_TOO_OLD, -1,
PLATFORM_SDK_VERSION));
}
@Test @Test
public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride() public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride()
throws Exception { throws Exception {

View File

@@ -78,11 +78,12 @@ public class PlatformCompatTest {
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException()); .thenThrow(new PackageManager.NameNotFoundException());
mCompatConfig = new CompatConfig(mBuildClassifier, mContext); mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
// Assume userdebug/eng non-final build // Assume userdebug/eng non-final build
mCompatConfig.forceNonDebuggableFinalForTest(false); mCompatConfig.forceNonDebuggableFinalForTest(false);
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true); when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false); when(mBuildClassifier.isFinalBuild()).thenReturn(false);
when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
} }
@@ -99,7 +100,7 @@ public class PlatformCompatTest {
.addLoggingOnlyChangeWithId(7L) .addLoggingOnlyChangeWithId(7L)
.addOverridableChangeWithId(8L) .addOverridableChangeWithId(8L)
.build(); .build();
mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly( assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false), new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false), new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -125,8 +126,9 @@ public class PlatformCompatTest {
.addEnableSinceSdkChangeWithId(Build.VERSION_CODES.Q, 5L) .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
.addEnableSinceSdkChangeWithId(Build.VERSION_CODES.R, 6L) .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.R, 6L)
.addLoggingOnlyChangeWithId(7L) .addLoggingOnlyChangeWithId(7L)
.addEnableSinceSdkChangeWithId(31, 8L)
.build(); .build();
mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly( assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false), new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false), new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -144,7 +146,7 @@ public class PlatformCompatTest {
.addEnableAfterSdkChangeWithId(Build.VERSION_CODES.O, 3L) .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.O, 3L)
.build(); .build();
mCompatConfig.forceNonDebuggableFinalForTest(true); mCompatConfig.forceNonDebuggableFinalForTest(true);
mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
// Before adding overrides. // Before adding overrides.
assertThat(mPlatformCompat.isChangeEnabledByPackageName(1, PACKAGE_NAME, 0)).isTrue(); assertThat(mPlatformCompat.isChangeEnabledByPackageName(1, PACKAGE_NAME, 0)).isTrue();