Create a new permission to override Overridable change ids

With this change we allow system packages with the new permission to
override ChangeIds specifically annotated as Overridable to set
overrides even on non-debuggable builds.

Bug: 174043039
Bug: 175874108
CTS-Coverage-Bug: 180396382
Test: atest FrameworksServicesTests:CompatConfigTest
Test: atest FrameworksServicesTests:PlatformCompatTest
Change-Id: Ib8d5d83b5fd62acb5808d10f5c413616f29ee65c
This commit is contained in:
Benjamin Franz
2021-02-09 10:30:38 +00:00
parent 30b3a36edc
commit 8ee7f9a4c3
13 changed files with 185 additions and 55 deletions

View File

@@ -187,6 +187,7 @@ package android {
field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS"; field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
field public static final String OBSERVE_SENSOR_PRIVACY = "android.permission.OBSERVE_SENSOR_PRIVACY"; field public static final String OBSERVE_SENSOR_PRIVACY = "android.permission.OBSERVE_SENSOR_PRIVACY";
field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS"; field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS";
field public static final String OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD = "android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD";
field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG"; field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT"; field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD"; field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD";
@@ -1227,6 +1228,21 @@ package android.app.compat {
method public static boolean isChangeEnabled(long); method public static boolean isChangeEnabled(long);
method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, int); method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, int);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void setPackageOverride(@NonNull String, @NonNull java.util.Map<java.lang.Long,android.app.compat.PackageOverride>);
}
public final class PackageOverride {
method public long getMaxVersionCode();
method public long getMinVersionCode();
method public boolean isEnabled();
}
public static final class PackageOverride.Builder {
ctor public PackageOverride.Builder();
method @NonNull public android.app.compat.PackageOverride build();
method @NonNull public android.app.compat.PackageOverride.Builder setEnabled(boolean);
method @NonNull public android.app.compat.PackageOverride.Builder setMaxVersionCode(long);
method @NonNull public android.app.compat.PackageOverride.Builder setMinVersionCode(long);
} }
} }

View File

@@ -104,16 +104,15 @@ public final class CompatChanges {
* *
* @param packageName The package name of the app in question. * @param packageName The package name of the app in question.
* @param overrides A map from changeId to the override applied for this change id. * @param overrides A map from changeId to the override applied for this change id.
* @hide
*/ */
@RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG) @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
public static void setPackageOverride(String packageName, public static void setPackageOverride(@NonNull String packageName,
Map<Long, PackageOverride> overrides) { @NonNull Map<Long, PackageOverride> overrides) {
IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides); CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides);
try { try {
platformCompat.setOverridesFromInstaller(config, packageName); platformCompat.setOverridesOnReleaseBuilds(config, packageName);
} catch (RemoteException e) { } catch (RemoteException e) {
e.rethrowFromSystemServer(); e.rethrowFromSystemServer();
} }

View File

@@ -17,8 +17,9 @@
package android.app.compat; package android.app.compat;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@@ -32,15 +33,16 @@ import java.lang.annotation.RetentionPolicy;
* *
* @hide * @hide
*/ */
public class PackageOverride implements Parcelable { @SystemApi
public final class PackageOverride {
/** @hide */
@IntDef({ @IntDef({
VALUE_UNDEFINED, VALUE_UNDEFINED,
VALUE_ENABLED, VALUE_ENABLED,
VALUE_DISABLED VALUE_DISABLED
}) })
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
/** @hide */
public @interface EvaluatedOverride { public @interface EvaluatedOverride {
} }
@@ -75,10 +77,6 @@ public class PackageOverride implements Parcelable {
this.mEnabled = enabled; this.mEnabled = enabled;
} }
private PackageOverride(Parcel in) {
this(in.readLong(), in.readLong(), in.readBoolean());
}
/** /**
* Evaluate the override for the given {@code versionCode}. If no override is defined for * Evaluate the override for the given {@code versionCode}. If no override is defined for
* the specified version code, {@link #VALUE_UNDEFINED} is returned. * the specified version code, {@link #VALUE_UNDEFINED} is returned.
@@ -114,24 +112,22 @@ public class PackageOverride implements Parcelable {
} }
/** Returns the enabled value for the override. */ /** Returns the enabled value for the override. */
public boolean getEnabled() { public boolean isEnabled() {
return mEnabled; return mEnabled;
} }
/** @hide */ /** @hide */
@Override public void writeToParcel(Parcel dest) {
public int describeContents() {
return 0;
}
/** @hide */
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mMinVersionCode); dest.writeLong(mMinVersionCode);
dest.writeLong(mMaxVersionCode); dest.writeLong(mMaxVersionCode);
dest.writeBoolean(mEnabled); dest.writeBoolean(mEnabled);
} }
/** @hide */
public static PackageOverride createFromParcel(Parcel in) {
return new PackageOverride(in.readLong(), in.readLong(), in.readBoolean());
}
/** @hide */ /** @hide */
@Override @Override
public String toString() { public String toString() {
@@ -141,25 +137,10 @@ public class PackageOverride implements Parcelable {
return String.format("[%d,%d,%b]", mMinVersionCode, mMaxVersionCode, mEnabled); return String.format("[%d,%d,%b]", mMinVersionCode, mMaxVersionCode, mEnabled);
} }
/** @hide */
public static final Creator<PackageOverride> CREATOR =
new Creator<PackageOverride>() {
@Override
public PackageOverride createFromParcel(Parcel in) {
return new PackageOverride(in);
}
@Override
public PackageOverride[] newArray(int size) {
return new PackageOverride[size];
}
};
/** /**
* Builder to construct a PackageOverride. * Builder to construct a PackageOverride.
*/ */
public static class Builder { public static final class Builder {
private long mMinVersionCode = Long.MIN_VALUE; private long mMinVersionCode = Long.MIN_VALUE;
private long mMaxVersionCode = Long.MAX_VALUE; private long mMaxVersionCode = Long.MAX_VALUE;
private boolean mEnabled; private boolean mEnabled;
@@ -169,6 +150,7 @@ public class PackageOverride implements Parcelable {
* *
* default value: {@code Long.MIN_VALUE}. * default value: {@code Long.MIN_VALUE}.
*/ */
@NonNull
public Builder setMinVersionCode(long minVersionCode) { public Builder setMinVersionCode(long minVersionCode) {
mMinVersionCode = minVersionCode; mMinVersionCode = minVersionCode;
return this; return this;
@@ -179,6 +161,7 @@ public class PackageOverride implements Parcelable {
* *
* default value: {@code Long.MAX_VALUE}. * default value: {@code Long.MAX_VALUE}.
*/ */
@NonNull
public Builder setMaxVersionCode(long maxVersionCode) { public Builder setMaxVersionCode(long maxVersionCode) {
mMaxVersionCode = maxVersionCode; mMaxVersionCode = maxVersionCode;
return this; return this;
@@ -189,6 +172,7 @@ public class PackageOverride implements Parcelable {
* *
* default value: {@code false}. * default value: {@code false}.
*/ */
@NonNull
public Builder setEnabled(boolean enabled) { public Builder setEnabled(boolean enabled) {
mEnabled = enabled; mEnabled = enabled;
return this; return this;
@@ -200,6 +184,7 @@ public class PackageOverride implements Parcelable {
* @throws IllegalArgumentException if {@code minVersionCode} is larger than * @throws IllegalArgumentException if {@code minVersionCode} is larger than
* {@code maxVersionCode}. * {@code maxVersionCode}.
*/ */
@NonNull
public PackageOverride build() { public PackageOverride build() {
if (mMinVersionCode > mMaxVersionCode) { if (mMinVersionCode > mMaxVersionCode) {
throw new IllegalArgumentException("minVersionCode must not be larger than " throw new IllegalArgumentException("minVersionCode must not be larger than "

View File

@@ -40,8 +40,7 @@ public final class CompatibilityOverrideConfig implements Parcelable {
overrides = new HashMap<>(); overrides = new HashMap<>();
for (int i = 0; i < keyCount; i++) { for (int i = 0; i < keyCount; i++) {
long key = in.readLong(); long key = in.readLong();
PackageOverride override = in.readParcelable(PackageOverride.class.getClassLoader()); overrides.put(key, PackageOverride.createFromParcel(in));
overrides.put(key, override);
} }
} }
@@ -55,7 +54,7 @@ public final class CompatibilityOverrideConfig implements Parcelable {
dest.writeInt(overrides.size()); dest.writeInt(overrides.size());
for (Long key : overrides.keySet()) { for (Long key : overrides.keySet()) {
dest.writeLong(key); dest.writeLong(key);
dest.writeParcelable(overrides.get(key), 0); overrides.get(key).writeToParcel(dest);
} }
} }

View File

@@ -151,15 +151,23 @@ interface IPlatformCompat {
void setOverrides(in CompatibilityChangeConfig overrides, in String packageName); void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
/** /**
* Adds overrides to compatibility changes. * Adds overrides to compatibility changes on release builds.
* *
* <p>Kills the app to allow the changes to take effect. * <p>The caller to this API needs to hold
* {@code android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD} and all change ids
* in {@code overrides} need to annotated with {@link android.compat.annotation.Overridable}.
*
* A release build in this definition means that {@link android.os.Build#IS_DEBUGGABLE} needs to
* be {@code false}.
*
* <p>Note that this does not kill the app, and therefore overrides read from the app process
* will not be updated. Overrides read from the system process do take effect.
* *
* @param overrides parcelable containing the compat change overrides to be applied * @param overrides parcelable containing the compat change overrides to be applied
* @param packageName the package name of the app whose changes will be overridden * @param packageName the package name of the app whose changes will be overridden
* @throws SecurityException if overriding changes is not permitted * @throws SecurityException if overriding changes is not permitted
*/ */
void setOverridesFromInstaller(in CompatibilityOverrideConfig overrides, in String packageName); void setOverridesOnReleaseBuilds(in CompatibilityOverrideConfig overrides, in String packageName);
/** /**
* Adds overrides to compatibility changes. * Adds overrides to compatibility changes.

View File

@@ -5560,10 +5560,17 @@
<permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"
android:protectionLevel="signature|privileged" /> android:protectionLevel="signature|privileged" />
<!-- Allows an app to override compat change config. <!-- Allows an app to override compat change config.
This permission only allows to override config on debuggable builds or test-apks and is
therefore a less powerful version of OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD.
@hide <p>Not for use by third-party applications.</p> --> @hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"
android:protectionLevel="signature|privileged" /> android:protectionLevel="signature|privileged" />
<uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/> <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/>
<!-- @SystemApi Allows an app to override compat change config on release builds.
Only ChangeIds that are annotated as @Overridable can be overridden on release builds.
@hide -->
<permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"
android:protectionLevel="signature|privileged" />
<!-- Allows input events to be monitored. Very dangerous! @hide --> <!-- Allows input events to be monitored. Very dangerous! @hide -->
<permission android:name="android.permission.MONITOR_INPUT" <permission android:name="android.permission.MONITOR_INPUT"

View File

@@ -23,7 +23,9 @@ import static android.app.compat.PackageOverride.VALUE_UNDEFINED;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.compat.PackageOverride; import android.app.compat.PackageOverride;
import android.compat.annotation.ChangeId; import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince; import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
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.content.pm.PackageManager;
@@ -59,6 +61,15 @@ public final class CompatChange extends CompatibilityChangeInfo {
@EnabledSince(targetSdkVersion = 31) // 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.
/**
* An overridable change ID to be used only in the CTS test for this SystemApi
*/
@ChangeId
@Disabled
@Overridable
static final long CTS_SYSTEM_API_OVERRIDABLE_CHANGEID = 174043039; // This is a bug id.
/** /**
* Callback listener for when compat changes are updated for a package. * Callback listener for when compat changes are updated for a package.
* See {@link #registerListener(ChangeListener)} for more details. * See {@link #registerListener(ChangeListener)} for more details.
@@ -211,6 +222,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
boolean hasPackageOverride(String pname) { boolean hasPackageOverride(String pname) {
return 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.
* *
@@ -355,7 +367,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
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().isEnabled());
rawList.add(override); rawList.add(override);
} }
changeOverrides.setRaw(rawOverrides); changeOverrides.setRaw(rawOverrides);

View File

@@ -303,6 +303,16 @@ final class CompatConfig {
} }
} }
/**
* Returns whether the change is overridable.
*/
boolean isOverridable(long changeId) {
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
return c != null && c.getOverridable();
}
}
/** /**
* Removes an override previously added via {@link #addOverride(long, String, boolean)}. * Removes an override previously added via {@link #addOverride(long, String, boolean)}.
* *
@@ -343,7 +353,7 @@ final class CompatConfig {
/** /**
* Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or * Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or
* {@link #addOverrides(CompatibilityChangeConfig, String)} for a certain package. * {@link #addOverrides(CompatibilityOverrideConfig, String)} for a certain package.
* *
* <p>This restores the default behaviour for the given app. * <p>This restores the default behaviour for the given app.
* *
@@ -632,8 +642,11 @@ final class CompatConfig {
} }
boolean shouldInvalidateCache = false; boolean shouldInvalidateCache = false;
for (CompatChange c: changes) { for (CompatChange c: changes) {
if (!c.hasPackageOverride(packageName)) {
continue;
}
OverrideAllowedState allowedState = OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(c.getId(), packageName); mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(), packageName);
shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext); shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext);
} }
if (shouldInvalidateCache) { if (shouldInvalidateCache) {

View File

@@ -16,6 +16,9 @@
package com.android.server.compat; package com.android.server.compat;
import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.compat.OverrideAllowedState.ALLOWED; import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION; import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK; import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
@@ -24,6 +27,7 @@ import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_S
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.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
import android.annotation.NonNull;
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.content.pm.PackageManager;
@@ -69,8 +73,25 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub {
mForceNonDebuggableFinalBuild = false; mForceNonDebuggableFinalBuild = false;
} }
/**
* Check the allowed state for the given changeId and packageName on a recheck.
*
* <p>Recheck happens when the given app is getting updated. In this case we cannot do a
* permission check on the caller, so we're using the fact that the override was present as
* proof that the original caller was allowed to set this override.
*/
OverrideAllowedState getOverrideAllowedStateForRecheck(long changeId,
@NonNull String packageName) {
return getOverrideAllowedStateInternal(changeId, packageName, true);
}
@Override @Override
public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) { public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) {
return getOverrideAllowedStateInternal(changeId, packageName, false);
}
private OverrideAllowedState getOverrideAllowedStateInternal(long changeId, String packageName,
boolean isRecheck) {
if (mCompatConfig.isLoggingOnly(changeId)) { if (mCompatConfig.isLoggingOnly(changeId)) {
return new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1); return new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1);
} }
@@ -99,6 +120,16 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub {
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
return new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1); return new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1);
} }
// If the change is annotated as @Overridable, apps with the specific permission can
// set the override even on production builds. When rechecking the override, e.g. during an
// app update we can bypass this check, as it wouldn't have been here in the first place.
if (mCompatConfig.isOverridable(changeId)
&& (isRecheck
|| mContext.checkCallingOrSelfPermission(
OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
== PERMISSION_GRANTED)) {
return new OverrideAllowedState(ALLOWED, -1, -1);
}
int appTargetSdk = applicationInfo.targetSdkVersion; int appTargetSdk = applicationInfo.targetSdkVersion;
// Only allow overriding debuggable apps. // Only allow overriding debuggable apps.
if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) { if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
@@ -130,5 +161,4 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub {
void forceNonDebuggableFinalForTest(boolean value) { void forceNonDebuggableFinalForTest(boolean value) {
mForceNonDebuggableFinalBuild = value; mForceNonDebuggableFinalBuild = value;
} }
} }

View File

@@ -18,6 +18,7 @@ package com.android.server.compat;
import static android.Manifest.permission.LOG_COMPAT_CHANGE; import static android.Manifest.permission.LOG_COMPAT_CHANGE;
import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD;
import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.SYSTEM_UID; import static android.os.Process.SYSTEM_UID;
@@ -182,11 +183,12 @@ public class PlatformCompat extends IPlatformCompat.Stub {
} }
@Override @Override
public void setOverridesFromInstaller(CompatibilityOverrideConfig overrides, public void setOverridesOnReleaseBuilds(CompatibilityOverrideConfig overrides,
String packageName) { String packageName) {
checkCompatChangeOverridePermission(); // TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods.
checkCompatChangeOverrideOverridablePermission();
checkAllCompatOverridesAreOverridable(overrides);
mCompatConfig.addOverrides(overrides, packageName); mCompatConfig.addOverrides(overrides, packageName);
killPackage(packageName);
} }
@Override @Override
@@ -383,6 +385,26 @@ public class PlatformCompat extends IPlatformCompat.Stub {
} }
} }
private void checkCompatChangeOverrideOverridablePermission() {
// Don't check for permissions within the system process
if (Binder.getCallingUid() == SYSTEM_UID) {
return;
}
if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
!= PERMISSION_GRANTED) {
throw new SecurityException("Cannot override compat change");
}
}
private void checkAllCompatOverridesAreOverridable(CompatibilityOverrideConfig overrides) {
for (Long changeId : overrides.overrides.keySet()) {
if (!mCompatConfig.isOverridable(changeId)) {
throw new SecurityException("Only change ids marked as Overridable can be "
+ "overridden.");
}
}
}
private void checkCompatChangeReadAndLogPermission() { private void checkCompatChangeReadAndLogPermission() {
checkCompatChangeReadPermission(); checkCompatChangeReadPermission();
checkCompatChangeLogPermission(); checkCompatChangeLogPermission();

View File

@@ -116,7 +116,7 @@ class CompatConfigBuilder {
} }
CompatConfigBuilder addOverridableChangeWithId(long id) { CompatConfigBuilder addOverridableChangeWithId(long id) {
mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", true)); mChanges.add(new CompatChange(id, "", -1, -1, true, false, "", true));
return this; return this;
} }

View File

@@ -259,6 +259,36 @@ public class CompatConfigTest {
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse(); assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
} }
@Test
public void testInstallerCanSetOverrides() throws Exception {
final long changeId = 1234L;
final int installerUid = 23;
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
.addOverridableChangeWithId(1234L)
.build();
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
.withPackageName("com.some.package")
.build();
PackageManager packageManager = mock(PackageManager.class);
when(mContext.getPackageManager()).thenReturn(packageManager);
when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
.thenReturn(applicationInfo);
// Force the validator to prevent overriding the change by using a user build.
when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
when(mBuildClassifier.isFinalBuild()).thenReturn(true);
CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(
Collections.singletonMap(1234L,
new PackageOverride.Builder()
.setMaxVersionCode(99L)
.setEnabled(true)
.build()));
compatConfig.addOverrides(config, "com.some.package");
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
}
@Test @Test
public void testApplyDeferredOverridesAfterInstallingApp() throws Exception { public void testApplyDeferredOverridesAfterInstallingApp() throws Exception {
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
@@ -639,9 +669,18 @@ public class CompatConfigTest {
.build()); .build());
when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt())) when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
.thenThrow(new NameNotFoundException()); .thenThrow(new NameNotFoundException());
compatConfig.addOverrides(
compatConfig.addOverride(1L, "foo.bar", true); new CompatibilityOverrideConfig(
compatConfig.addOverride(2L, "bar.baz", false); Collections.singletonMap(
1L,
new PackageOverride.Builder().setEnabled(true).build())),
"foo.bar");
compatConfig.addOverrides(
new CompatibilityOverrideConfig(
Collections.singletonMap(
2L,
new PackageOverride.Builder().setEnabled(false).build())),
"bar.baz");
assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<overrides>\n" + "<overrides>\n"

View File

@@ -113,7 +113,7 @@ public class PlatformCompatTest {
new CompatibilityChangeInfo( new CompatibilityChangeInfo(
6L, "", Build.VERSION_CODES.R, -1, false, false, "", false), 6L, "", Build.VERSION_CODES.R, -1, false, false, "", false),
new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "", false), new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "", false),
new CompatibilityChangeInfo(8L, "", -1, -1, false, true, "", true)); new CompatibilityChangeInfo(8L, "", -1, -1, true, false, "", true));
} }
@Test @Test