From 93ca0db1cafbbb60de6b4fb1a01954bdaa2e3e9f Mon Sep 17 00:00:00 2001 From: tomnatan Date: Wed, 24 Feb 2021 17:45:03 +0000 Subject: [PATCH] Add support for loading a static overrides file located in /product. Note that the dynamic overrides file in the /data/misc/appcompat will be loaded after the static file is loaded and take precedence over it (overrides in the dynamic file will override corresponding ones in the static file). This static file will be used to inject hard coded per app configuration. Bug: 180396167 Test: atest FrameworksServicesTests:CompatConfigTest Change-Id: I73569744e718274255885d270ab8285956647f4c --- .../android/server/compat/CompatChange.java | 91 ++++++------ .../android/server/compat/CompatConfig.java | 35 ++++- .../server/compat/CompatConfigTest.java | 131 ++++++++++++++++-- 3 files changed, 189 insertions(+), 68 deletions(-) diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index df83df9a73fb8..5cf478a3ef1f1 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -79,6 +79,15 @@ public final class CompatChange extends CompatibilityChangeInfo { 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 name Short descriptive name. @@ -93,15 +102,10 @@ public final class CompatChange extends CompatibilityChangeInfo { boolean overridable) { super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly, description, overridable); - } - /** - * @param change an object generated by services/core/xsd/platform-compat-config.xsd - */ - public CompatChange(Change change) { - super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(), - change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(), - change.getDescription(), change.getOverridable()); + // Initialize override maps. + mEvaluatedOverrides = new HashMap<>(); + mRawOverrides = new HashMap<>(); } void registerListener(ChangeListener listener) { @@ -127,18 +131,13 @@ public final class CompatChange extends CompatibilityChangeInfo { throw new IllegalArgumentException( "Can't add overrides for a logging only change " + toString()); } - if (mEvaluatedOverrides == null) { - mEvaluatedOverrides = new HashMap<>(); - } mEvaluatedOverrides.put(pname, enabled); notifyListener(pname); } private void removePackageOverrideInternal(String pname) { - if (mEvaluatedOverrides != null) { - if (mEvaluatedOverrides.remove(pname) != null) { - notifyListener(pname); - } + if (mEvaluatedOverrides.remove(pname) != null) { + notifyListener(pname); } } @@ -157,9 +156,6 @@ public final class CompatChange extends CompatibilityChangeInfo { throw new IllegalArgumentException( "Can't add overrides for a logging only change " + toString()); } - if (mRawOverrides == null) { - mRawOverrides = new HashMap<>(); - } mRawOverrides.put(packageName, override); recheckOverride(packageName, allowedState, context); } @@ -212,7 +208,7 @@ public final class CompatChange extends CompatibilityChangeInfo { } 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. @@ -223,7 +219,7 @@ public final class CompatChange extends CompatibilityChangeInfo { */ boolean removePackageOverride(String pname, OverrideAllowedState allowedState, Context context) { - if (mRawOverrides != null && (mRawOverrides.remove(pname) != null)) { + if (mRawOverrides.remove(pname) != null) { recheckOverride(pname, allowedState, context); return true; } @@ -241,7 +237,7 @@ public final class CompatChange extends CompatibilityChangeInfo { if (app == null) { return defaultValue(); } - if (mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(app.packageName)) { + if (mEvaluatedOverrides.containsKey(app.packageName)) { return mEvaluatedOverrides.get(app.packageName); } if (getDisabled()) { @@ -289,7 +285,7 @@ public final class CompatChange extends CompatibilityChangeInfo { * @return true if there is such override */ private boolean hasOverride(String packageName) { - return mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(packageName); + return mEvaluatedOverrides.containsKey(packageName); } /** @@ -298,20 +294,15 @@ public final class CompatChange extends CompatibilityChangeInfo { * @return true if there is such a deferred override */ 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) { - if (mRawOverrides == null) { - mRawOverrides = new HashMap<>(); - } - mRawOverrides.clear(); - - if (mEvaluatedOverrides == null) { - mEvaluatedOverrides = new HashMap<>(); - } - mEvaluatedOverrides.clear(); - // Load deferred overrides for backwards compatibility if (changeOverrides.getDeferred() != null) { for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) { @@ -345,34 +336,30 @@ public final class CompatChange extends CompatibilityChangeInfo { } ChangeOverrides saveOverrides() { - if (mRawOverrides == null || mRawOverrides.isEmpty()) { + if (mRawOverrides.isEmpty()) { return null; } ChangeOverrides changeOverrides = new ChangeOverrides(); changeOverrides.setChangeId(getId()); ChangeOverrides.Raw rawOverrides = new ChangeOverrides.Raw(); List rawList = rawOverrides.getRawOverrideValue(); - if (mRawOverrides != null) { - for (Map.Entry entry : mRawOverrides.entrySet()) { - RawOverrideValue override = new RawOverrideValue(); - override.setPackageName(entry.getKey()); - override.setMinVersionCode(entry.getValue().getMinVersionCode()); - override.setMaxVersionCode(entry.getValue().getMaxVersionCode()); - override.setEnabled(entry.getValue().getEnabled()); - rawList.add(override); - } + for (Map.Entry entry : mRawOverrides.entrySet()) { + RawOverrideValue override = new RawOverrideValue(); + override.setPackageName(entry.getKey()); + override.setMinVersionCode(entry.getValue().getMinVersionCode()); + override.setMaxVersionCode(entry.getValue().getMaxVersionCode()); + override.setEnabled(entry.getValue().getEnabled()); + rawList.add(override); } changeOverrides.setRaw(rawOverrides); ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated(); List validatedList = validatedOverrides.getOverrideValue(); - if (mEvaluatedOverrides != null) { - for (Map.Entry entry : mEvaluatedOverrides.entrySet()) { - OverrideValue override = new OverrideValue(); - override.setPackageName(entry.getKey()); - override.setEnabled(entry.getValue()); - validatedList.add(override); - } + for (Map.Entry entry : mEvaluatedOverrides.entrySet()) { + OverrideValue override = new OverrideValue(); + override.setPackageName(entry.getKey()); + override.setEnabled(entry.getValue()); + validatedList.add(override); } changeOverrides.setValidated(validatedOverrides); return changeOverrides; @@ -394,10 +381,10 @@ public final class CompatChange extends CompatibilityChangeInfo { if (getLoggingOnly()) { sb.append("; loggingOnly"); } - if (mEvaluatedOverrides != null && mEvaluatedOverrides.size() > 0) { + if (!mEvaluatedOverrides.isEmpty()) { sb.append("; packageOverrides=").append(mEvaluatedOverrides); } - if (mRawOverrides != null && mRawOverrides.size() > 0) { + if (!mRawOverrides.isEmpty()) { sb.append("; rawOverrides=").append(mRawOverrides); } if (getOverridable()) { diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index 66a652053857e..2c053b4219047 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -67,6 +67,7 @@ final class CompatConfig { private static final String TAG = "CompatConfig"; 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"; @GuardedBy("mChanges") @@ -94,8 +95,7 @@ final class CompatConfig { config.initConfigFromLib(Environment.buildPath( apex.apexDirectory, "etc", "compatconfig")); } - File overridesFile = new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE); - config.initOverrides(overridesFile); + config.initOverrides(); config.invalidateCache(); return config; } @@ -525,10 +525,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()) { - mOverridesFile = overridesFile; - // There have not been any overrides added yet. + // Overrides file doesn't exist. return; } @@ -548,7 +572,6 @@ final class CompatConfig { Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString()); return; } - mOverridesFile = overridesFile; } /** diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index 8b0e948579fbf..b6b6932c4a934 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -602,12 +602,12 @@ public class CompatConfigTest { .addEnableSinceSdkChangeWithId(2, 2L) .build(); compatConfig.forceNonDebuggableFinalForTest(true); - compatConfig.initOverrides(overridesFile); + compatConfig.initOverrides(overridesFile, new File("")); when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt())) .thenReturn(ApplicationInfoBuilder.create() - .withPackageName("foo.bar") - .debuggable() - .build()); + .withPackageName("foo.bar") + .debuggable() + .build()); when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt())) .thenThrow(new NameNotFoundException()); @@ -649,7 +649,7 @@ public class CompatConfigTest { .addEnableSinceSdkChangeWithId(2, 2L) .build(); compatConfig.forceNonDebuggableFinalForTest(true); - compatConfig.initOverrides(overridesFile); + compatConfig.initOverrides(overridesFile, new File("")); compatConfig.addOverrides(new CompatibilityOverrideConfig(Collections.singletonMap(1L, new PackageOverride.Builder() @@ -673,11 +673,11 @@ public class CompatConfigTest { } @Test - public void testLoadOverridesRaw() throws Exception { + public void testInitOverridesRaw() throws Exception { File tempDir = createTempDir(); File overridesFile = new File(tempDir, "overrides.xml"); // 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 = "" + "\n" + " \n" @@ -709,7 +709,7 @@ public class CompatConfigTest { .addEnableSinceSdkChangeWithId(2, 2L) .build(); compatConfig.forceNonDebuggableFinalForTest(true); - compatConfig.initOverrides(overridesFile); + compatConfig.initOverrides(overridesFile, new File("")); ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() .withPackageName("foo.bar") .withVersionCode(100L) @@ -728,7 +728,7 @@ public class CompatConfigTest { } @Test - public void testLoadOverridesDeferred() throws Exception { + public void testInitOverridesDeferred() throws Exception { File tempDir = createTempDir(); File overridesFile = new File(tempDir, "overrides.xml"); // Change 1 is enabled for foo.bar (validated) @@ -754,7 +754,7 @@ public class CompatConfigTest { .addEnableSinceSdkChangeWithId(2, 2L) .build(); compatConfig.forceNonDebuggableFinalForTest(true); - compatConfig.initOverrides(overridesFile); + compatConfig.initOverrides(overridesFile, new File("")); ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() .withPackageName("foo.bar") .debuggable() @@ -767,4 +767,115 @@ public class CompatConfigTest { assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue(); 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 = "" + + "" + + "" + + "" + + " \n" + + " \n" + + "" + + "" + + "" + + "" + + " \n" + + " \n" + + "" + + "" + + ""; + 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 = "" + + "" + + "" + + "" + + " \n" + + " \n" + + " \n" + + " \n" + + "" + + "" + + "" + + "" + + " \n" + + " \n" + + "" + + "" + + ""; + 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("\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n"); + } }