Enforce overriding methods for IPlatformCompat am: 465b214383 am: ed7fc22f27

Change-Id: Id4eafc4f2700b0c7e4578471059204dbc756fdd9
This commit is contained in:
Automerger Merge Worker
2020-01-08 18:54:13 +00:00
11 changed files with 555 additions and 128 deletions

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.compat;
import android.os.Build;
/**
* Platform private class for determining the type of Android build installed.
*
*/
public class AndroidBuildClassifier {
public boolean isDebuggableBuild() {
return Build.IS_DEBUGGABLE;
}
public boolean isFinalBuild() {
return "REL".equals(Build.VERSION.CODENAME);
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.compat;
import android.content.pm.ApplicationInfo;
import com.android.internal.compat.OverrideAllowedState;
/**
* Platform private API for determining whether a changeId can be overridden.
*
* {@hide}
*/
interface IOverrideValidator
{
/**
* Validation function.
* @param changeId id of the change to be toggled on or off.
* @param packageName package of the app for which the change should be overridden.
* @return {@link OverrideAllowedState} specifying whether the change can be overridden for
* the given package or a reason why not.
*/
OverrideAllowedState getOverrideAllowedState(long changeId, String packageName);
}

View File

@@ -17,6 +17,7 @@
package com.android.internal.compat;
import android.content.pm.ApplicationInfo;
import com.android.internal.compat.IOverrideValidator;
import java.util.Map;
parcelable CompatibilityChangeConfig;
@@ -195,4 +196,9 @@ interface IPlatformCompat
* @return An array of {@link CompatChangeInfo} known to the service.
*/
CompatibilityChangeInfo[] listAllChanges();
/**
* Get an instance that can determine whether a changeid can be overridden for a package name.
*/
IOverrideValidator getOverrideValidator();
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.compat;
parcelable OverrideAllowedState;

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.compat;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* This class contains all the possible override allowed states.
*/
public final class OverrideAllowedState implements Parcelable {
@IntDef({
ALLOWED,
DISABLED_NOT_DEBUGGABLE,
DISABLED_NON_TARGET_SDK,
DISABLED_TARGET_SDK_TOO_HIGH,
PACKAGE_DOES_NOT_EXIST
})
@Retention(RetentionPolicy.SOURCE)
public @interface State {
}
/**
* Change can be overridden.
*/
public static final int ALLOWED = 0;
/**
* Change cannot be overridden, due to the app not being debuggable.
*/
public static final int DISABLED_NOT_DEBUGGABLE = 1;
/**
* Change cannot be overridden, due to the build being non-debuggable and the change being
* non-targetSdk.
*/
public static final int DISABLED_NON_TARGET_SDK = 2;
/**
* Change cannot be overridden, due to the app's targetSdk being above the change's targetSdk.
*/
public static final int DISABLED_TARGET_SDK_TOO_HIGH = 3;
/**
* Package does not exist.
*/
public static final int PACKAGE_DOES_NOT_EXIST = 4;
@State
public final int state;
public final int appTargetSdk;
public final int changeIdTargetSdk;
private OverrideAllowedState(Parcel parcel) {
state = parcel.readInt();
appTargetSdk = parcel.readInt();
changeIdTargetSdk = parcel.readInt();
}
public OverrideAllowedState(@State int state, int appTargetSdk, int changeIdTargetSdk) {
this.state = state;
this.appTargetSdk = appTargetSdk;
this.changeIdTargetSdk = changeIdTargetSdk;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(state);
out.writeInt(appTargetSdk);
out.writeInt(changeIdTargetSdk);
}
/**
* Enforces the policy for overriding compat changes.
*
* @param changeId the change id that was attempted to be overridden.
* @param packageName the package for which the attempt was made.
* @throws SecurityException if the policy forbids this operation.
*/
public void enforce(long changeId, String packageName)
throws SecurityException {
switch (state) {
case ALLOWED:
return;
case DISABLED_NOT_DEBUGGABLE:
throw new SecurityException(
"Cannot override a change on a non-debuggable app and user build.");
case DISABLED_NON_TARGET_SDK:
throw new SecurityException(
"Cannot override a default enabled/disabled change on a user build.");
case DISABLED_TARGET_SDK_TOO_HIGH:
throw new SecurityException(String.format(
"Cannot override %1$d for %2$s because the app's targetSdk (%3$d) is "
+ "above the change's targetSdk threshold (%4$d)",
changeId, packageName, appTargetSdk, changeIdTargetSdk));
case PACKAGE_DOES_NOT_EXIST:
throw new SecurityException(String.format(
"Cannot override %1$d for %2$s because the package does not exist, and "
+ "the change is targetSdk gated.",
changeId, packageName));
}
}
public static final @NonNull
Parcelable.Creator<OverrideAllowedState> CREATOR =
new Parcelable.Creator<OverrideAllowedState>() {
public OverrideAllowedState createFromParcel(Parcel parcel) {
OverrideAllowedState info = new OverrideAllowedState(parcel);
return info;
}
public OverrideAllowedState[] newArray(int size) {
return new OverrideAllowedState[size];
}
};
}

View File

@@ -2899,33 +2899,37 @@ final class ActivityManagerShellCommand extends ShellCommand {
}
ArraySet<Long> enabled = new ArraySet<>();
ArraySet<Long> disabled = new ArraySet<>();
switch (toggleValue) {
case "enable":
enabled.add(changeId);
pw.println("Enabled change " + changeId + " for " + packageName + ".");
CompatibilityChangeConfig overrides =
new CompatibilityChangeConfig(
new Compatibility.ChangeConfig(enabled, disabled));
platformCompat.setOverrides(overrides, packageName);
return 0;
case "disable":
disabled.add(changeId);
pw.println("Disabled change " + changeId + " for " + packageName + ".");
overrides =
new CompatibilityChangeConfig(
new Compatibility.ChangeConfig(enabled, disabled));
platformCompat.setOverrides(overrides, packageName);
return 0;
case "reset":
if (platformCompat.clearOverride(changeId, packageName)) {
pw.println("Reset change " + changeId + " for " + packageName
+ " to default value.");
} else {
pw.println("No override exists for changeId " + changeId + ".");
}
return 0;
default:
pw.println("Invalid toggle value: '" + toggleValue + "'.");
try {
switch (toggleValue) {
case "enable":
enabled.add(changeId);
CompatibilityChangeConfig overrides =
new CompatibilityChangeConfig(
new Compatibility.ChangeConfig(enabled, disabled));
platformCompat.setOverrides(overrides, packageName);
pw.println("Enabled change " + changeId + " for " + packageName + ".");
return 0;
case "disable":
disabled.add(changeId);
overrides =
new CompatibilityChangeConfig(
new Compatibility.ChangeConfig(enabled, disabled));
platformCompat.setOverrides(overrides, packageName);
pw.println("Disabled change " + changeId + " for " + packageName + ".");
return 0;
case "reset":
if (platformCompat.clearOverride(changeId, packageName)) {
pw.println("Reset change " + changeId + " for " + packageName
+ " to default value.");
} else {
pw.println("No override exists for changeId " + changeId + ".");
}
return 0;
default:
pw.println("Invalid toggle value: '" + toggleValue + "'.");
}
} catch (SecurityException e) {
pw.println(e.getMessage());
}
return -1;
}

View File

@@ -17,8 +17,10 @@
package com.android.server.compat;
import android.compat.Compatibility.ChangeConfig;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Environment;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.LongArray;
import android.util.LongSparseArray;
@@ -26,8 +28,11 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
import com.android.server.compat.config.XmlParser;
@@ -54,22 +59,14 @@ final class CompatConfig {
private static final String TAG = "CompatConfig";
private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib(
Environment.buildPath(
Environment.getRootDirectory(), "etc", "compatconfig"));
@GuardedBy("mChanges")
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
@VisibleForTesting
CompatConfig() {
}
private IOverrideValidator mOverrideValidator;
/**
* @return The static instance of this class to be used within the system server.
*/
static CompatConfig get() {
return sInstance;
@VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
}
/**
@@ -159,8 +156,12 @@ final class CompatConfig {
* @param enabled If the change should be enabled or disabled.
* @return {@code true} if the change existed before adding the override.
*/
boolean addOverride(long changeId, String packageName, boolean enabled) {
boolean addOverride(long changeId, String packageName, boolean enabled)
throws RemoteException, SecurityException {
boolean alreadyKnown = true;
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
allowedState.enforce(changeId, packageName);
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c == null) {
@@ -185,6 +186,20 @@ final class CompatConfig {
}
}
/**
* Returns the minimum sdk version for which this change should be enabled (or 0 if it is not
* target sdk gated).
*/
int minTargetSdkForChangeId(long changeId) {
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c == null) {
return 0;
}
return c.getEnableAfterTargetSdk();
}
}
/**
* Removes an override previously added via {@link #addOverride(long, String, boolean)}. This
* restores the default behaviour for the given change and app, once any app processes have been
@@ -194,34 +209,44 @@ final class CompatConfig {
* @param packageName The app package name that was overridden.
* @return {@code true} if an override existed;
*/
boolean removeOverride(long changeId, String packageName) {
boolean removeOverride(long changeId, String packageName)
throws RemoteException, SecurityException {
boolean overrideExists = false;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c != null) {
overrideExists = true;
c.removePackageOverride(packageName);
try {
if (c != null) {
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
allowedState.enforce(changeId, packageName);
overrideExists = true;
c.removePackageOverride(packageName);
}
} catch (RemoteException e) {
// Should never occur, since validator is in the same process.
throw new RuntimeException("Unable to call override validator!", e);
}
}
return overrideExists;
}
/**
* Overrides the enabled state for a given change and app. This method is intended to be used
* *only* for debugging purposes.
* Overrides the enabled state for a given change and app.
*
* <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
*
* @param overrides list of overrides to default changes config.
* @param packageName app for which the overrides will be applied.
*/
void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
void addOverrides(CompatibilityChangeConfig overrides, String packageName)
throws RemoteException, SecurityException {
synchronized (mChanges) {
for (Long changeId : overrides.enabledChanges()) {
addOverride(changeId, packageName, true);
}
for (Long changeId : overrides.disabledChanges()) {
addOverride(changeId, packageName, false);
}
}
}
@@ -235,10 +260,22 @@ final class CompatConfig {
*
* @param packageName The package for which the overrides should be purged.
*/
void removePackageOverrides(String packageName) {
void removePackageOverrides(String packageName) throws RemoteException, SecurityException {
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
mChanges.valueAt(i).removePackageOverride(packageName);
try {
CompatChange change = mChanges.valueAt(i);
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(change.getId(),
packageName);
allowedState.enforce(change.getId(), packageName);
if (change != null) {
mChanges.valueAt(i).removePackageOverride(packageName);
}
} catch (RemoteException e) {
// Should never occur, since validator is in the same process.
throw new RuntimeException("Unable to call override validator!", e);
}
}
}
}
@@ -326,17 +363,23 @@ final class CompatConfig {
}
}
CompatConfig initConfigFromLib(File libraryDir) {
static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
CompatConfig config = new CompatConfig(androidBuildClassifier, context);
config.initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "etc", "compatconfig"));
return config;
}
void initConfigFromLib(File libraryDir) {
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
Slog.e(TAG, "No directory " + libraryDir + ", skipping");
return this;
return;
}
for (File f : libraryDir.listFiles()) {
Slog.d(TAG, "Found a config file: " + f.getPath());
//TODO(b/138222363): Handle duplicate ids across config files.
readConfig(f);
}
return this;
}
private void readConfig(File configFile) {
@@ -350,4 +393,7 @@ final class CompatConfig {
}
}
IOverrideValidator getOverrideValidator() {
return mOverrideValidator;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.compat;
import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
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.PACKAGE_DOES_NOT_EXIST;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
/**
* Implementation of the policy for allowing compat change overrides.
*/
public class OverrideValidatorImpl extends IOverrideValidator.Stub {
private AndroidBuildClassifier mAndroidBuildClassifier;
private Context mContext;
private CompatConfig mCompatConfig;
@VisibleForTesting
OverrideValidatorImpl(AndroidBuildClassifier androidBuildClassifier,
Context context, CompatConfig config) {
mAndroidBuildClassifier = androidBuildClassifier;
mContext = context;
mCompatConfig = config;
}
@Override
public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) {
boolean debuggableBuild = false;
boolean finalBuild = false;
debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild();
finalBuild = mAndroidBuildClassifier.isFinalBuild();
// Allow any override for userdebug or eng builds.
if (debuggableBuild) {
return new OverrideAllowedState(ALLOWED, -1, -1);
}
PackageManager packageManager = mContext.getPackageManager();
if (packageManager == null) {
throw new IllegalStateException("No PackageManager!");
}
ApplicationInfo applicationInfo;
try {
applicationInfo = packageManager.getApplicationInfo(packageName, 0);
} catch (NameNotFoundException e) {
return new OverrideAllowedState(PACKAGE_DOES_NOT_EXIST, -1, -1);
}
int appTargetSdk = applicationInfo.targetSdkVersion;
// Only allow overriding debuggable apps.
if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
return new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1);
}
int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId);
// Do not allow overriding non-target sdk gated changes on user builds
if (minTargetSdk == -1) {
return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk);
}
// Allow overriding any change for debuggable apps on non-final builds.
if (!finalBuild) {
return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
}
// Only allow to opt-in for a targetSdk gated change.
if (applicationInfo.targetSdkVersion < minTargetSdk) {
return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk);
}
return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, minTargetSdk);
}
}

View File

@@ -27,9 +27,12 @@ import android.os.UserHandle;
import android.util.Slog;
import android.util.StatsLog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.ChangeReporter;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.util.DumpUtils;
@@ -45,11 +48,21 @@ public class PlatformCompat extends IPlatformCompat.Stub {
private final Context mContext;
private final ChangeReporter mChangeReporter;
private final CompatConfig mCompatConfig;
public PlatformCompat(Context context) {
mContext = context;
mChangeReporter = new ChangeReporter(
StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
}
@VisibleForTesting
PlatformCompat(Context context, CompatConfig compatConfig) {
mContext = context;
mChangeReporter = new ChangeReporter(
StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
mCompatConfig = compatConfig;
}
@Override
@@ -74,7 +87,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) {
if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
reportChange(changeId, appInfo.uid,
StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
return true;
@@ -121,57 +134,59 @@ public class PlatformCompat extends IPlatformCompat.Stub {
* otherwise.
*/
public boolean registerListener(long changeId, CompatChange.ChangeListener listener) {
return CompatConfig.get().registerListener(changeId, listener);
return mCompatConfig.registerListener(changeId, listener);
}
@Override
public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
CompatConfig.get().addOverrides(overrides, packageName);
public void setOverrides(CompatibilityChangeConfig overrides, String packageName)
throws RemoteException, SecurityException {
mCompatConfig.addOverrides(overrides, packageName);
killPackage(packageName);
}
@Override
public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
CompatConfig.get().addOverrides(overrides, packageName);
public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName)
throws RemoteException, SecurityException {
mCompatConfig.addOverrides(overrides, packageName);
}
@Override
public void clearOverrides(String packageName) {
CompatConfig config = CompatConfig.get();
config.removePackageOverrides(packageName);
public void clearOverrides(String packageName) throws RemoteException, SecurityException {
mCompatConfig.removePackageOverrides(packageName);
killPackage(packageName);
}
@Override
public void clearOverridesForTest(String packageName) {
CompatConfig config = CompatConfig.get();
config.removePackageOverrides(packageName);
public void clearOverridesForTest(String packageName)
throws RemoteException, SecurityException {
mCompatConfig.removePackageOverrides(packageName);
}
@Override
public boolean clearOverride(long changeId, String packageName) {
boolean existed = CompatConfig.get().removeOverride(changeId, packageName);
public boolean clearOverride(long changeId, String packageName)
throws RemoteException, SecurityException {
boolean existed = mCompatConfig.removeOverride(changeId, packageName);
killPackage(packageName);
return existed;
}
@Override
public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
return CompatConfig.get().getAppConfig(appInfo);
return mCompatConfig.getAppConfig(appInfo);
}
@Override
public CompatibilityChangeInfo[] listAllChanges() {
return CompatConfig.get().dumpChanges();
return mCompatConfig.dumpChanges();
}
/**
* Check whether the change is known to the compat config.
* @param changeId
*
* @return {@code true} if the change is known.
*/
public boolean isKnownChangeId(long changeId) {
return CompatConfig.get().isKnownChangeId(changeId);
return mCompatConfig.isKnownChangeId(changeId);
}
@@ -181,11 +196,11 @@ public class PlatformCompat extends IPlatformCompat.Stub {
*
* @param appInfo The app in question
* @return A sorted long array of change IDs. We use a primitive array to minimize memory
* footprint: Every app process will store this array statically so we aim to reduce
* overhead as much as possible.
* footprint: Every app process will store this array statically so we aim to reduce
* overhead as much as possible.
*/
public long[] getDisabledChanges(ApplicationInfo appInfo) {
return CompatConfig.get().getDisabledChanges(appInfo);
return mCompatConfig.getDisabledChanges(appInfo);
}
/**
@@ -195,18 +210,24 @@ public class PlatformCompat extends IPlatformCompat.Stub {
* @return The change ID, or {@code -1} if no change with that name exists.
*/
public long lookupChangeId(String name) {
return CompatConfig.get().lookupChangeId(name);
return mCompatConfig.lookupChangeId(name);
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
CompatConfig.get().dumpConfig(pw);
mCompatConfig.dumpConfig(pw);
}
@Override
public IOverrideValidator getOverrideValidator() {
return mCompatConfig.getOverrideValidator();
}
/**
* Clears information stored about events reported on behalf of an app.
* To be called once upon app start or end. A second call would be a no-op.
*
* @param appInfo the app to reset
*/
public void resetReporting(ApplicationInfo appInfo) {

View File

@@ -18,12 +18,20 @@ package com.android.server.compat;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.compat.AndroidBuildClassifier;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.File;
import java.io.FileOutputStream;
@@ -34,6 +42,11 @@ import java.util.UUID;
@RunWith(AndroidJUnit4.class)
public class CompatConfigTest {
@Mock
private Context mContext;
@Mock
private AndroidBuildClassifier mBuildClassifier;
private ApplicationInfo makeAppInfo(String pName, int targetSdkVersion) {
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = pName;
@@ -54,43 +67,51 @@ public class CompatConfigTest {
os.close();
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
// Assume userdebug/eng non-final build
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
}
@Test
public void testUnknownChangeEnabled() {
CompatConfig pc = new CompatConfig();
public void testUnknownChangeEnabled() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isTrue();
}
@Test
public void testDisabledChangeDisabled() {
CompatConfig pc = new CompatConfig();
public void testDisabledChangeDisabled() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, ""));
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
}
@Test
public void testTargetSdkChangeDisabled() {
CompatConfig pc = new CompatConfig();
public void testTargetSdkChangeDisabled() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, null));
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
}
@Test
public void testTargetSdkChangeEnabled() {
CompatConfig pc = new CompatConfig();
public void testTargetSdkChangeEnabled() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, ""));
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
}
@Test
public void testDisabledOverrideTargetSdkChange() {
CompatConfig pc = new CompatConfig();
public void testDisabledOverrideTargetSdkChange() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null));
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isFalse();
}
@Test
public void testGetDisabledChanges() {
CompatConfig pc = new CompatConfig();
public void testGetDisabledChanges() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null));
pc.addChange(new CompatChange(2345L, "OTHER_CHANGE", -1, false, null));
assertThat(pc.getDisabledChanges(
@@ -98,8 +119,8 @@ public class CompatConfigTest {
}
@Test
public void testGetDisabledChangesSorted() {
CompatConfig pc = new CompatConfig();
public void testGetDisabledChangesSorted() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null));
pc.addChange(new CompatChange(123L, "OTHER_CHANGE", 2, true, null));
pc.addChange(new CompatChange(12L, "THIRD_CHANGE", 2, true, null));
@@ -108,8 +129,8 @@ public class CompatConfigTest {
}
@Test
public void testPackageOverrideEnabled() {
CompatConfig pc = new CompatConfig();
public void testPackageOverrideEnabled() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null)); // disabled
pc.addOverride(1234L, "com.some.package", true);
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue();
@@ -117,8 +138,8 @@ public class CompatConfigTest {
}
@Test
public void testPackageOverrideDisabled() {
CompatConfig pc = new CompatConfig();
public void testPackageOverrideDisabled() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
pc.addOverride(1234L, "com.some.package", false);
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
@@ -126,22 +147,22 @@ public class CompatConfigTest {
}
@Test
public void testPackageOverrideUnknownPackage() {
CompatConfig pc = new CompatConfig();
public void testPackageOverrideUnknownPackage() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.addOverride(1234L, "com.some.package", false);
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isTrue();
}
@Test
public void testPackageOverrideUnknownChange() {
CompatConfig pc = new CompatConfig();
public void testPackageOverrideUnknownChange() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isTrue();
}
@Test
public void testRemovePackageOverride() {
CompatConfig pc = new CompatConfig();
public void testRemovePackageOverride() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
pc.addOverride(1234L, "com.some.package", false);
pc.removeOverride(1234L, "com.some.package");
@@ -149,16 +170,16 @@ public class CompatConfigTest {
}
@Test
public void testLookupChangeId() {
CompatConfig pc = new CompatConfig();
public void testLookupChangeId() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
pc.addChange(new CompatChange(2345L, "ANOTHER_CHANGE", -1, false, null));
assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(1234L);
}
@Test
public void testLookupChangeIdNotPresent() {
CompatConfig pc = new CompatConfig();
public void testLookupChangeIdNotPresent() throws Exception {
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(-1L);
}
@@ -173,7 +194,7 @@ public class CompatConfigTest {
File dir = createTempDir();
writeToFile(dir, "platform_compat_config.xml", configXml);
CompatConfig pc = new CompatConfig();
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.initConfigFromLib(dir);
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
@@ -196,7 +217,7 @@ public class CompatConfigTest {
writeToFile(dir, "libcore_platform_compat_config.xml", configXml1);
writeToFile(dir, "frameworks_platform_compat_config.xml", configXml2);
CompatConfig pc = new CompatConfig();
CompatConfig pc = new CompatConfig(mBuildClassifier, mContext);
pc.initConfigFromLib(dir);
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();

View File

@@ -30,6 +30,7 @@ import android.compat.Compatibility;
import android.content.Context;
import android.content.pm.PackageManager;
import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.google.common.collect.ImmutableSet;
@@ -50,6 +51,10 @@ public class PlatformCompatTest {
private PackageManager mPackageManager;
@Mock
CompatChange.ChangeListener mListener1, mListener2;
CompatConfig mCompatConfig;
@Mock
AndroidBuildClassifier mBuildClassifier;
@Before
@@ -57,12 +62,15 @@ public class PlatformCompatTest {
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getPackageUid(eq(PACKAGE_NAME), eq(0))).thenThrow(
new PackageManager.NameNotFoundException());
CompatConfig.get().clearChanges();
mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
// Assume userdebug/eng non-final build
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
}
@Test
public void testRegisterListenerToSameIdThrows() {
PlatformCompat pc = new PlatformCompat(mContext);
public void testRegisterListenerToSameIdThrows() throws Exception {
PlatformCompat pc = new PlatformCompat(mContext, mCompatConfig);
// Registering a listener to change 1 is successful.
pc.registerListener(1, mListener1);
@@ -73,8 +81,8 @@ public class PlatformCompatTest {
}
@Test
public void testRegisterListenerReturn() {
PlatformCompat pc = new PlatformCompat(mContext);
public void testRegisterListenerReturn() throws Exception {
PlatformCompat pc = new PlatformCompat(mContext, mCompatConfig);
pc.setOverrides(
new CompatibilityChangeConfig(
@@ -88,8 +96,8 @@ public class PlatformCompatTest {
}
@Test
public void testListenerCalledOnSetOverrides() {
PlatformCompat pc = new PlatformCompat(mContext);
public void testListenerCalledOnSetOverrides() throws Exception {
PlatformCompat pc = new PlatformCompat(mContext, mCompatConfig);
pc.registerListener(1, mListener1);
pc.registerListener(2, mListener1);
@@ -103,8 +111,8 @@ public class PlatformCompatTest {
}
@Test
public void testListenerNotCalledOnWrongPackage() {
PlatformCompat pc = new PlatformCompat(mContext);
public void testListenerNotCalledOnWrongPackage() throws Exception {
PlatformCompat pc = new PlatformCompat(mContext, mCompatConfig);
pc.registerListener(1, mListener1);
pc.registerListener(2, mListener1);
@@ -118,8 +126,8 @@ public class PlatformCompatTest {
}
@Test
public void testListenerCalledOnSetOverridesTwoListeners() {
PlatformCompat pc = new PlatformCompat(mContext);
public void testListenerCalledOnSetOverridesTwoListeners() throws Exception {
PlatformCompat pc = new PlatformCompat(mContext, mCompatConfig);
pc.registerListener(1, mListener1);
final ImmutableSet<Long> enabled = ImmutableSet.of(1L);
@@ -148,8 +156,8 @@ public class PlatformCompatTest {
}
@Test
public void testListenerCalledOnSetOverridesForTest() {
PlatformCompat pc = new PlatformCompat(mContext);
public void testListenerCalledOnSetOverridesForTest() throws Exception {
PlatformCompat pc = new PlatformCompat(mContext, mCompatConfig);
pc.registerListener(1, mListener1);
pc.registerListener(2, mListener1);
@@ -163,8 +171,8 @@ public class PlatformCompatTest {
}
@Test
public void testListenerCalledOnSetOverridesTwoListenersForTest() {
PlatformCompat pc = new PlatformCompat(mContext);
public void testListenerCalledOnSetOverridesTwoListenersForTest() throws Exception {
PlatformCompat pc = new PlatformCompat(mContext, mCompatConfig);
pc.registerListener(1, mListener1);
final ImmutableSet<Long> enabled = ImmutableSet.of(1L);
@@ -192,8 +200,8 @@ public class PlatformCompatTest {
}
@Test
public void testListenerCalledOnClearOverrides() {
PlatformCompat pc = new PlatformCompat(mContext);
public void testListenerCalledOnClearOverrides() throws Exception {
PlatformCompat pc = new PlatformCompat(mContext, mCompatConfig);
pc.registerListener(1, mListener1);
pc.registerListener(2, mListener2);
@@ -214,8 +222,8 @@ public class PlatformCompatTest {
}
@Test
public void testListenerCalledOnClearOverridesMultipleOverrides() {
PlatformCompat pc = new PlatformCompat(mContext);
public void testListenerCalledOnClearOverridesMultipleOverrides() throws Exception {
PlatformCompat pc = new PlatformCompat(mContext, mCompatConfig);
pc.registerListener(1, mListener1);
pc.registerListener(2, mListener2);
@@ -236,8 +244,8 @@ public class PlatformCompatTest {
}
@Test
public void testListenerCalledOnClearOverrideExists() {
PlatformCompat pc = new PlatformCompat(mContext);
public void testListenerCalledOnClearOverrideExists() throws Exception {
PlatformCompat pc = new PlatformCompat(mContext, mCompatConfig);
pc.registerListener(1, mListener1);
pc.registerListener(2, mListener2);
@@ -258,8 +266,8 @@ public class PlatformCompatTest {
}
@Test
public void testListenerCalledOnClearOverrideDoesntExist() {
PlatformCompat pc = new PlatformCompat(mContext);
public void testListenerCalledOnClearOverrideDoesntExist() throws Exception {
PlatformCompat pc = new PlatformCompat(mContext, mCompatConfig);
pc.registerListener(1, mListener1);