Merge "Add versioning to app compat framework" am: 80bbdfd100
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1585009 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Ib4796325c84396082256cec7c66fd1efdc623d36
This commit is contained in:
@@ -20,8 +20,16 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.RequiresPermission;
|
import android.annotation.RequiresPermission;
|
||||||
import android.annotation.SystemApi;
|
import android.annotation.SystemApi;
|
||||||
import android.compat.Compatibility;
|
import android.compat.Compatibility;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.ServiceManager;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
|
||||||
|
import com.android.internal.compat.CompatibilityOverrideConfig;
|
||||||
|
import com.android.internal.compat.IPlatformCompat;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CompatChanges APIs - to be used by platform code only (including mainline
|
* CompatChanges APIs - to be used by platform code only (including mainline
|
||||||
* modules).
|
* modules).
|
||||||
@@ -89,4 +97,25 @@ public final class CompatChanges {
|
|||||||
return QUERY_CACHE.query(ChangeIdStateQuery.byUid(changeId, uid));
|
return QUERY_CACHE.query(ChangeIdStateQuery.byUid(changeId, uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an app compat override for a given package. This will check whether the caller is allowed
|
||||||
|
* to perform this operation on the given apk and build. Only the installer package is allowed
|
||||||
|
* to set overrides on a non-debuggable final build and a non-test apk.
|
||||||
|
*
|
||||||
|
* @param packageName The package name of the app in question.
|
||||||
|
* @param overrides A map from changeId to the override applied for this change id.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG)
|
||||||
|
public static void setPackageOverride(String packageName,
|
||||||
|
Map<Long, PackageOverride> overrides) {
|
||||||
|
IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
|
||||||
|
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
|
||||||
|
CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides);
|
||||||
|
try {
|
||||||
|
platformCompat.setOverridesFromInstaller(config, packageName);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
211
core/java/android/app/compat/PackageOverride.java
Normal file
211
core/java/android/app/compat/PackageOverride.java
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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 android.app.compat;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An app compat override applied to a given package and change id pairing.
|
||||||
|
*
|
||||||
|
* A package override contains a list of version ranges with the desired boolean value of
|
||||||
|
* the override for the app in this version range. Ranges can be open ended in either direction.
|
||||||
|
* An instance of PackageOverride gets created via {@link Builder} and is immutable once created.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class PackageOverride implements Parcelable {
|
||||||
|
|
||||||
|
@IntDef({
|
||||||
|
VALUE_UNDEFINED,
|
||||||
|
VALUE_ENABLED,
|
||||||
|
VALUE_DISABLED
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
/** @hide */
|
||||||
|
public @interface EvaluatedOverride {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return value of {@link #evaluate(long)} and {@link #evaluateForAllVersions()} indicating that
|
||||||
|
* this PackageOverride does not define the value of the override for the given version.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final int VALUE_UNDEFINED = 0;
|
||||||
|
/**
|
||||||
|
* Return value of {@link #evaluate(long)} and {@link #evaluateForAllVersions()} indicating that
|
||||||
|
* the override evaluates to {@code true} for the given version.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final int VALUE_ENABLED = 1;
|
||||||
|
/**
|
||||||
|
* Return value of {@link #evaluate(long)} and {@link #evaluateForAllVersions()} indicating that
|
||||||
|
* the override evaluates to {@code fakse} for the given version.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final int VALUE_DISABLED = 2;
|
||||||
|
|
||||||
|
private final long mMinVersionCode;
|
||||||
|
private final long mMaxVersionCode;
|
||||||
|
private final boolean mEnabled;
|
||||||
|
|
||||||
|
private PackageOverride(long minVersionCode,
|
||||||
|
long maxVersionCode,
|
||||||
|
boolean enabled) {
|
||||||
|
this.mMinVersionCode = minVersionCode;
|
||||||
|
this.mMaxVersionCode = maxVersionCode;
|
||||||
|
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
|
||||||
|
* the specified version code, {@link #VALUE_UNDEFINED} is returned.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public @EvaluatedOverride int evaluate(long versionCode) {
|
||||||
|
if (versionCode >= mMinVersionCode && versionCode <= mMaxVersionCode) {
|
||||||
|
return mEnabled ? VALUE_ENABLED : VALUE_DISABLED;
|
||||||
|
}
|
||||||
|
return VALUE_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluate the override independent of version code, i.e. only return an evaluated value if
|
||||||
|
* this range covers all versions, otherwise {@link #VALUE_UNDEFINED} is returned.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public int evaluateForAllVersions() {
|
||||||
|
if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) {
|
||||||
|
return mEnabled ? VALUE_ENABLED : VALUE_DISABLED;
|
||||||
|
}
|
||||||
|
return VALUE_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the minimum version code the override applies to. */
|
||||||
|
public long getMinVersionCode() {
|
||||||
|
return mMinVersionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the minimum version code the override applies from. */
|
||||||
|
public long getMaxVersionCode() {
|
||||||
|
return mMaxVersionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the enabled value for the override. */
|
||||||
|
public boolean getEnabled() {
|
||||||
|
return mEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeLong(mMinVersionCode);
|
||||||
|
dest.writeLong(mMaxVersionCode);
|
||||||
|
dest.writeBoolean(mEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) {
|
||||||
|
return Boolean.toString(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.
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
private long mMinVersionCode = Long.MIN_VALUE;
|
||||||
|
private long mMaxVersionCode = Long.MAX_VALUE;
|
||||||
|
private boolean mEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the minimum version code the override should apply from.
|
||||||
|
*
|
||||||
|
* default value: {@code Long.MIN_VALUE}.
|
||||||
|
*/
|
||||||
|
public Builder setMinVersionCode(long minVersionCode) {
|
||||||
|
mMinVersionCode = minVersionCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum version code the override should apply to.
|
||||||
|
*
|
||||||
|
* default value: {@code Long.MAX_VALUE}.
|
||||||
|
*/
|
||||||
|
public Builder setMaxVersionCode(long maxVersionCode) {
|
||||||
|
mMaxVersionCode = maxVersionCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the override should be enabled for the given version range.
|
||||||
|
*
|
||||||
|
* default value: {@code false}.
|
||||||
|
*/
|
||||||
|
public Builder setEnabled(boolean enabled) {
|
||||||
|
mEnabled = enabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the {@link PackageOverride}.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if {@code minVersionCode} is larger than
|
||||||
|
* {@code maxVersionCode}.
|
||||||
|
*/
|
||||||
|
public PackageOverride build() {
|
||||||
|
if (mMinVersionCode > mMaxVersionCode) {
|
||||||
|
throw new IllegalArgumentException("minVersionCode must not be larger than "
|
||||||
|
+ "maxVersionCode");
|
||||||
|
}
|
||||||
|
return new PackageOverride(mMinVersionCode, mMaxVersionCode, mEnabled);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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 CompatibilityOverrideConfig;
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.app.compat.PackageOverride;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parcelable containing compat config overrides for a given application.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public final class CompatibilityOverrideConfig implements Parcelable {
|
||||||
|
public final Map<Long, PackageOverride> overrides;
|
||||||
|
|
||||||
|
public CompatibilityOverrideConfig(Map<Long, PackageOverride> overrides) {
|
||||||
|
this.overrides = overrides;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompatibilityOverrideConfig(Parcel in) {
|
||||||
|
int keyCount = in.readInt();
|
||||||
|
overrides = new HashMap<>();
|
||||||
|
for (int i = 0; i < keyCount; i++) {
|
||||||
|
long key = in.readLong();
|
||||||
|
PackageOverride override = in.readParcelable(PackageOverride.class.getClassLoader());
|
||||||
|
overrides.put(key, override);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(overrides.size());
|
||||||
|
for (Long key : overrides.keySet()) {
|
||||||
|
dest.writeLong(key);
|
||||||
|
dest.writeParcelable(overrides.get(key), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<CompatibilityOverrideConfig> CREATOR =
|
||||||
|
new Creator<CompatibilityOverrideConfig>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompatibilityOverrideConfig createFromParcel(Parcel in) {
|
||||||
|
return new CompatibilityOverrideConfig(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompatibilityOverrideConfig[] newArray(int size) {
|
||||||
|
return new CompatibilityOverrideConfig[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ import com.android.internal.compat.IOverrideValidator;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
parcelable CompatibilityChangeConfig;
|
parcelable CompatibilityChangeConfig;
|
||||||
|
parcelable CompatibilityOverrideConfig;
|
||||||
parcelable CompatibilityChangeInfo;
|
parcelable CompatibilityChangeInfo;
|
||||||
/**
|
/**
|
||||||
* Platform private API for talking with the PlatformCompat service.
|
* Platform private API for talking with the PlatformCompat service.
|
||||||
@@ -149,6 +150,17 @@ interface IPlatformCompat {
|
|||||||
*/
|
*/
|
||||||
void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
|
void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds overrides to compatibility changes.
|
||||||
|
*
|
||||||
|
* <p>Kills the app to allow the changes to take effect.
|
||||||
|
*
|
||||||
|
* @param overrides parcelable containing the compat change overrides to be applied
|
||||||
|
* @param packageName the package name of the app whose changes will be overridden
|
||||||
|
* @throws SecurityException if overriding changes is not permitted
|
||||||
|
*/
|
||||||
|
void setOverridesFromInstaller(in CompatibilityOverrideConfig overrides, in String packageName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds overrides to compatibility changes.
|
* Adds overrides to compatibility changes.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -16,15 +16,24 @@
|
|||||||
|
|
||||||
package com.android.server.compat;
|
package com.android.server.compat;
|
||||||
|
|
||||||
|
import static android.app.compat.PackageOverride.VALUE_DISABLED;
|
||||||
|
import static android.app.compat.PackageOverride.VALUE_ENABLED;
|
||||||
|
import static android.app.compat.PackageOverride.VALUE_UNDEFINED;
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.app.compat.PackageOverride;
|
||||||
import android.compat.annotation.ChangeId;
|
import android.compat.annotation.ChangeId;
|
||||||
import android.compat.annotation.EnabledSince;
|
import android.compat.annotation.EnabledSince;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
|
||||||
import com.android.internal.compat.CompatibilityChangeInfo;
|
import com.android.internal.compat.CompatibilityChangeInfo;
|
||||||
|
import com.android.internal.compat.OverrideAllowedState;
|
||||||
import com.android.server.compat.config.Change;
|
import com.android.server.compat.config.Change;
|
||||||
import com.android.server.compat.overrides.ChangeOverrides;
|
import com.android.server.compat.overrides.ChangeOverrides;
|
||||||
import com.android.server.compat.overrides.OverrideValue;
|
import com.android.server.compat.overrides.OverrideValue;
|
||||||
|
import com.android.server.compat.overrides.RawOverrideValue;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -36,7 +45,7 @@ import java.util.Map;
|
|||||||
* <p>A compatibility change has a default setting, determined by the {@code enableAfterTargetSdk}
|
* <p>A compatibility change has a default setting, determined by the {@code enableAfterTargetSdk}
|
||||||
* and {@code disabled} constructor parameters. If a change is {@code disabled}, this overrides any
|
* and {@code disabled} constructor parameters. If a change is {@code disabled}, this overrides any
|
||||||
* target SDK criteria set. These settings can be overridden for a specific package using
|
* target SDK criteria set. These settings can be overridden for a specific package using
|
||||||
* {@link #addPackageOverride(String, boolean)}.
|
* {@link #addPackageOverrideInternal(String, boolean)}.
|
||||||
*
|
*
|
||||||
* <p>Note, this class is not thread safe so callers must ensure thread safety.
|
* <p>Note, this class is not thread safe so callers must ensure thread safety.
|
||||||
*/
|
*/
|
||||||
@@ -63,8 +72,8 @@ public final class CompatChange extends CompatibilityChangeInfo {
|
|||||||
|
|
||||||
ChangeListener mListener = null;
|
ChangeListener mListener = null;
|
||||||
|
|
||||||
private Map<String, Boolean> mPackageOverrides;
|
private Map<String, Boolean> mEvaluatedOverrides;
|
||||||
private Map<String, Boolean> mDeferredOverrides;
|
private Map<String, PackageOverride> mRawOverrides;
|
||||||
|
|
||||||
public CompatChange(long changeId) {
|
public CompatChange(long changeId) {
|
||||||
this(changeId, null, -1, -1, false, false, null, false);
|
this(changeId, null, -1, -1, false, false, null, false);
|
||||||
@@ -113,18 +122,26 @@ public final class CompatChange extends CompatibilityChangeInfo {
|
|||||||
* @param pname Package name to enable the change for.
|
* @param pname Package name to enable the change for.
|
||||||
* @param enabled Whether or not to enable the change.
|
* @param enabled Whether or not to enable the change.
|
||||||
*/
|
*/
|
||||||
void addPackageOverride(String pname, boolean enabled) {
|
private void addPackageOverrideInternal(String pname, boolean enabled) {
|
||||||
if (getLoggingOnly()) {
|
if (getLoggingOnly()) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Can't add overrides for a logging only change " + toString());
|
"Can't add overrides for a logging only change " + toString());
|
||||||
}
|
}
|
||||||
if (mPackageOverrides == null) {
|
if (mEvaluatedOverrides == null) {
|
||||||
mPackageOverrides = new HashMap<>();
|
mEvaluatedOverrides = new HashMap<>();
|
||||||
}
|
}
|
||||||
mPackageOverrides.put(pname, enabled);
|
mEvaluatedOverrides.put(pname, enabled);
|
||||||
notifyListener(pname);
|
notifyListener(pname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removePackageOverrideInternal(String pname) {
|
||||||
|
if (mEvaluatedOverrides != null) {
|
||||||
|
if (mEvaluatedOverrides.remove(pname) != null) {
|
||||||
|
notifyListener(pname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tentatively set the state of this change for a given package name.
|
* Tentatively set the state of this change for a given package name.
|
||||||
* The override will only take effect after that package is installed, if applicable.
|
* The override will only take effect after that package is installed, if applicable.
|
||||||
@@ -132,17 +149,19 @@ public final class CompatChange extends CompatibilityChangeInfo {
|
|||||||
* <p>Note, this method is not thread safe so callers must ensure thread safety.
|
* <p>Note, this method is not thread safe so callers must ensure thread safety.
|
||||||
*
|
*
|
||||||
* @param packageName Package name to tentatively enable the change for.
|
* @param packageName Package name to tentatively enable the change for.
|
||||||
* @param enabled Whether or not to enable the change.
|
* @param override The package override to be set
|
||||||
*/
|
*/
|
||||||
void addPackageDeferredOverride(String packageName, boolean enabled) {
|
void addPackageOverride(String packageName, PackageOverride override,
|
||||||
|
OverrideAllowedState allowedState, Context context) {
|
||||||
if (getLoggingOnly()) {
|
if (getLoggingOnly()) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Can't add overrides for a logging only change " + toString());
|
"Can't add overrides for a logging only change " + toString());
|
||||||
}
|
}
|
||||||
if (mDeferredOverrides == null) {
|
if (mRawOverrides == null) {
|
||||||
mDeferredOverrides = new HashMap<>();
|
mRawOverrides = new HashMap<>();
|
||||||
}
|
}
|
||||||
mDeferredOverrides.put(packageName, enabled);
|
mRawOverrides.put(packageName, override);
|
||||||
|
recheckOverride(packageName, allowedState, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -157,24 +176,44 @@ public final class CompatChange extends CompatibilityChangeInfo {
|
|||||||
* @return {@code true} if the recheck yielded a result that requires invalidating caches
|
* @return {@code true} if the recheck yielded a result that requires invalidating caches
|
||||||
* (a deferred override was consolidated or a regular override was removed).
|
* (a deferred override was consolidated or a regular override was removed).
|
||||||
*/
|
*/
|
||||||
boolean recheckOverride(String packageName, boolean allowed) {
|
boolean recheckOverride(String packageName, OverrideAllowedState allowedState,
|
||||||
// A deferred override now is allowed by the policy, so promote it to a regular override.
|
Context context) {
|
||||||
if (hasDeferredOverride(packageName) && allowed) {
|
boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED);
|
||||||
boolean overrideValue = mDeferredOverrides.remove(packageName);
|
|
||||||
addPackageOverride(packageName, overrideValue);
|
Long version = null;
|
||||||
return true;
|
try {
|
||||||
|
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(
|
||||||
|
packageName, 0);
|
||||||
|
version = applicationInfo.longVersionCode;
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
// Do nothing
|
||||||
}
|
}
|
||||||
// A previously set override is no longer allowed by the policy, so make it deferred.
|
|
||||||
if (hasOverride(packageName) && !allowed) {
|
// If the app is not installed or no longer has raw overrides, evaluate to false
|
||||||
boolean overrideValue = mPackageOverrides.remove(packageName);
|
if (version == null || !hasRawOverride(packageName) || !allowed) {
|
||||||
addPackageDeferredOverride(packageName, overrideValue);
|
removePackageOverrideInternal(packageName);
|
||||||
// Notify because the override was removed.
|
return false;
|
||||||
notifyListener(packageName);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
// Evaluate the override based on its version
|
||||||
|
int overrideValue = mRawOverrides.get(packageName).evaluate(version);
|
||||||
|
switch (overrideValue) {
|
||||||
|
case VALUE_UNDEFINED:
|
||||||
|
removePackageOverrideInternal(packageName);
|
||||||
|
break;
|
||||||
|
case VALUE_ENABLED:
|
||||||
|
addPackageOverrideInternal(packageName, true);
|
||||||
|
break;
|
||||||
|
case VALUE_DISABLED:
|
||||||
|
addPackageOverrideInternal(packageName, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasPackageOverride(String pname) {
|
||||||
|
return mRawOverrides != null && 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.
|
||||||
*
|
*
|
||||||
@@ -182,15 +221,13 @@ public final class CompatChange extends CompatibilityChangeInfo {
|
|||||||
*
|
*
|
||||||
* @param pname Package name to reset to defaults for.
|
* @param pname Package name to reset to defaults for.
|
||||||
*/
|
*/
|
||||||
void removePackageOverride(String pname) {
|
boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
|
||||||
if (mPackageOverrides != null) {
|
Context context) {
|
||||||
if (mPackageOverrides.remove(pname) != null) {
|
if (mRawOverrides != null && (mRawOverrides.remove(pname) != null)) {
|
||||||
notifyListener(pname);
|
recheckOverride(pname, allowedState, context);
|
||||||
}
|
return true;
|
||||||
}
|
|
||||||
if (mDeferredOverrides != null) {
|
|
||||||
mDeferredOverrides.remove(pname);
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -204,8 +241,8 @@ public final class CompatChange extends CompatibilityChangeInfo {
|
|||||||
if (app == null) {
|
if (app == null) {
|
||||||
return defaultValue();
|
return defaultValue();
|
||||||
}
|
}
|
||||||
if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) {
|
if (mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(app.packageName)) {
|
||||||
return mPackageOverrides.get(app.packageName);
|
return mEvaluatedOverrides.get(app.packageName);
|
||||||
}
|
}
|
||||||
if (getDisabled()) {
|
if (getDisabled()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -223,8 +260,16 @@ public final class CompatChange extends CompatibilityChangeInfo {
|
|||||||
* @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 willBeEnabled(String packageName) {
|
boolean willBeEnabled(String packageName) {
|
||||||
if (hasDeferredOverride(packageName)) {
|
if (hasRawOverride(packageName)) {
|
||||||
return mDeferredOverrides.get(packageName);
|
int eval = mRawOverrides.get(packageName).evaluateForAllVersions();
|
||||||
|
switch (eval) {
|
||||||
|
case VALUE_ENABLED:
|
||||||
|
return true;
|
||||||
|
case VALUE_DISABLED:
|
||||||
|
return false;
|
||||||
|
case VALUE_UNDEFINED:
|
||||||
|
return defaultValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return defaultValue();
|
return defaultValue();
|
||||||
}
|
}
|
||||||
@@ -243,8 +288,8 @@ public final class CompatChange extends CompatibilityChangeInfo {
|
|||||||
* @param packageName name of the package
|
* @param packageName name of the package
|
||||||
* @return true if there is such override
|
* @return true if there is such override
|
||||||
*/
|
*/
|
||||||
boolean hasOverride(String packageName) {
|
private boolean hasOverride(String packageName) {
|
||||||
return mPackageOverrides != null && mPackageOverrides.containsKey(packageName);
|
return mEvaluatedOverrides != null && mEvaluatedOverrides.containsKey(packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -252,65 +297,77 @@ public final class CompatChange extends CompatibilityChangeInfo {
|
|||||||
* @param packageName name of the package
|
* @param packageName name of the package
|
||||||
* @return true if there is such a deferred override
|
* @return true if there is such a deferred override
|
||||||
*/
|
*/
|
||||||
boolean hasDeferredOverride(String packageName) {
|
private boolean hasRawOverride(String packageName) {
|
||||||
return mDeferredOverrides != null && mDeferredOverrides.containsKey(packageName);
|
return mRawOverrides != null && mRawOverrides.containsKey(packageName);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether a change has any package overrides.
|
|
||||||
* @return true if the change has at least one deferred override
|
|
||||||
*/
|
|
||||||
boolean hasAnyPackageOverride() {
|
|
||||||
return mDeferredOverrides != null && !mDeferredOverrides.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether a change has any deferred overrides.
|
|
||||||
* @return true if the change has at least one deferred override
|
|
||||||
*/
|
|
||||||
boolean hasAnyDeferredOverride() {
|
|
||||||
return mPackageOverrides != null && !mPackageOverrides.isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadOverrides(ChangeOverrides changeOverrides) {
|
void loadOverrides(ChangeOverrides changeOverrides) {
|
||||||
if (mDeferredOverrides == null) {
|
if (mRawOverrides == null) {
|
||||||
mDeferredOverrides = new HashMap<>();
|
mRawOverrides = new HashMap<>();
|
||||||
}
|
}
|
||||||
mDeferredOverrides.clear();
|
mRawOverrides.clear();
|
||||||
for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
|
|
||||||
mDeferredOverrides.put(override.getPackageName(), override.getEnabled());
|
if (mEvaluatedOverrides == null) {
|
||||||
|
mEvaluatedOverrides = new HashMap<>();
|
||||||
|
}
|
||||||
|
mEvaluatedOverrides.clear();
|
||||||
|
|
||||||
|
// Load deferred overrides for backwards compatibility
|
||||||
|
if (changeOverrides.getDeferred() != null) {
|
||||||
|
for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
|
||||||
|
mRawOverrides.put(override.getPackageName(),
|
||||||
|
new PackageOverride.Builder().setEnabled(
|
||||||
|
override.getEnabled()).build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPackageOverrides == null) {
|
// Load validated overrides. For backwards compatibility, we also add them to raw overrides.
|
||||||
mPackageOverrides = new HashMap<>();
|
if (changeOverrides.getValidated() != null) {
|
||||||
|
for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) {
|
||||||
|
mEvaluatedOverrides.put(override.getPackageName(), override.getEnabled());
|
||||||
|
mRawOverrides.put(override.getPackageName(),
|
||||||
|
new PackageOverride.Builder().setEnabled(
|
||||||
|
override.getEnabled()).build());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mPackageOverrides.clear();
|
|
||||||
for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) {
|
// Load raw overrides
|
||||||
mPackageOverrides.put(override.getPackageName(), override.getEnabled());
|
if (changeOverrides.getRaw() != null) {
|
||||||
|
for (RawOverrideValue override : changeOverrides.getRaw().getRawOverrideValue()) {
|
||||||
|
PackageOverride packageOverride = new PackageOverride.Builder()
|
||||||
|
.setMinVersionCode(override.getMinVersionCode())
|
||||||
|
.setMaxVersionCode(override.getMaxVersionCode())
|
||||||
|
.setEnabled(override.getEnabled())
|
||||||
|
.build();
|
||||||
|
mRawOverrides.put(override.getPackageName(), packageOverride);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeOverrides saveOverrides() {
|
ChangeOverrides saveOverrides() {
|
||||||
if (!hasAnyDeferredOverride() && !hasAnyPackageOverride()) {
|
if (mRawOverrides == null || mRawOverrides.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
ChangeOverrides changeOverrides = new ChangeOverrides();
|
ChangeOverrides changeOverrides = new ChangeOverrides();
|
||||||
changeOverrides.setChangeId(getId());
|
changeOverrides.setChangeId(getId());
|
||||||
ChangeOverrides.Deferred deferredOverrides = new ChangeOverrides.Deferred();
|
ChangeOverrides.Raw rawOverrides = new ChangeOverrides.Raw();
|
||||||
List<OverrideValue> deferredList = deferredOverrides.getOverrideValue();
|
List<RawOverrideValue> rawList = rawOverrides.getRawOverrideValue();
|
||||||
if (mDeferredOverrides != null) {
|
if (mRawOverrides != null) {
|
||||||
for (Map.Entry<String, Boolean> entry : mDeferredOverrides.entrySet()) {
|
for (Map.Entry<String, PackageOverride> entry : mRawOverrides.entrySet()) {
|
||||||
OverrideValue override = new OverrideValue();
|
RawOverrideValue override = new RawOverrideValue();
|
||||||
override.setPackageName(entry.getKey());
|
override.setPackageName(entry.getKey());
|
||||||
override.setEnabled(entry.getValue());
|
override.setMinVersionCode(entry.getValue().getMinVersionCode());
|
||||||
deferredList.add(override);
|
override.setMaxVersionCode(entry.getValue().getMaxVersionCode());
|
||||||
|
override.setEnabled(entry.getValue().getEnabled());
|
||||||
|
rawList.add(override);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
changeOverrides.setDeferred(deferredOverrides);
|
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 (mPackageOverrides != null) {
|
if (mEvaluatedOverrides != null) {
|
||||||
for (Map.Entry<String, Boolean> entry : mPackageOverrides.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());
|
||||||
@@ -337,11 +394,11 @@ public final class CompatChange extends CompatibilityChangeInfo {
|
|||||||
if (getLoggingOnly()) {
|
if (getLoggingOnly()) {
|
||||||
sb.append("; loggingOnly");
|
sb.append("; loggingOnly");
|
||||||
}
|
}
|
||||||
if (mPackageOverrides != null && mPackageOverrides.size() > 0) {
|
if (mEvaluatedOverrides != null && mEvaluatedOverrides.size() > 0) {
|
||||||
sb.append("; packageOverrides=").append(mPackageOverrides);
|
sb.append("; packageOverrides=").append(mEvaluatedOverrides);
|
||||||
}
|
}
|
||||||
if (mDeferredOverrides != null && mDeferredOverrides.size() > 0) {
|
if (mRawOverrides != null && mRawOverrides.size() > 0) {
|
||||||
sb.append("; deferredOverrides=").append(mDeferredOverrides);
|
sb.append("; rawOverrides=").append(mRawOverrides);
|
||||||
}
|
}
|
||||||
if (getOverridable()) {
|
if (getOverridable()) {
|
||||||
sb.append("; overridable");
|
sb.append("; overridable");
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.server.compat;
|
package com.android.server.compat;
|
||||||
|
|
||||||
import android.app.compat.ChangeIdStateCache;
|
import android.app.compat.ChangeIdStateCache;
|
||||||
|
import android.app.compat.PackageOverride;
|
||||||
import android.compat.Compatibility.ChangeConfig;
|
import android.compat.Compatibility.ChangeConfig;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
@@ -31,6 +32,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
|||||||
import com.android.internal.compat.AndroidBuildClassifier;
|
import com.android.internal.compat.AndroidBuildClassifier;
|
||||||
import com.android.internal.compat.CompatibilityChangeConfig;
|
import com.android.internal.compat.CompatibilityChangeConfig;
|
||||||
import com.android.internal.compat.CompatibilityChangeInfo;
|
import com.android.internal.compat.CompatibilityChangeInfo;
|
||||||
|
import com.android.internal.compat.CompatibilityOverrideConfig;
|
||||||
import com.android.internal.compat.IOverrideValidator;
|
import com.android.internal.compat.IOverrideValidator;
|
||||||
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;
|
||||||
@@ -70,11 +72,13 @@ final class CompatConfig {
|
|||||||
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
|
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
|
||||||
|
|
||||||
private final OverrideValidatorImpl mOverrideValidator;
|
private final OverrideValidatorImpl mOverrideValidator;
|
||||||
|
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);
|
||||||
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
|
static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) {
|
||||||
@@ -210,17 +214,33 @@ final class CompatConfig {
|
|||||||
* @throws IllegalStateException if overriding is not allowed
|
* @throws IllegalStateException if overriding is not allowed
|
||||||
*/
|
*/
|
||||||
boolean addOverride(long changeId, String packageName, boolean enabled) {
|
boolean addOverride(long changeId, String packageName, boolean enabled) {
|
||||||
boolean alreadyKnown = addOverrideUnsafe(changeId, packageName, enabled);
|
boolean alreadyKnown = addOverrideUnsafe(changeId, packageName,
|
||||||
|
new PackageOverride.Builder().setEnabled(enabled).build());
|
||||||
saveOverrides();
|
saveOverrides();
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
return alreadyKnown;
|
return alreadyKnown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsafe version of {@link #addOverride(long, String, boolean)}.
|
* Overrides the enabled state for a given change and app.
|
||||||
* It does not invalidate the cache nor save the overrides.
|
*
|
||||||
|
* <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.
|
||||||
*/
|
*/
|
||||||
private boolean addOverrideUnsafe(long changeId, String packageName, boolean enabled) {
|
void addOverrides(CompatibilityOverrideConfig overrides, String packageName) {
|
||||||
|
synchronized (mChanges) {
|
||||||
|
for (Long changeId : overrides.overrides.keySet()) {
|
||||||
|
addOverrideUnsafe(changeId, packageName, overrides.overrides.get(changeId));
|
||||||
|
}
|
||||||
|
saveOverrides();
|
||||||
|
invalidateCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean addOverrideUnsafe(long changeId, String packageName,
|
||||||
|
PackageOverride overrides) {
|
||||||
boolean alreadyKnown = true;
|
boolean alreadyKnown = true;
|
||||||
OverrideAllowedState allowedState =
|
OverrideAllowedState allowedState =
|
||||||
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
|
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
|
||||||
@@ -232,17 +252,8 @@ final class CompatConfig {
|
|||||||
c = new CompatChange(changeId);
|
c = new CompatChange(changeId);
|
||||||
addChange(c);
|
addChange(c);
|
||||||
}
|
}
|
||||||
switch (allowedState.state) {
|
c.addPackageOverride(packageName, overrides, allowedState, mContext);
|
||||||
case OverrideAllowedState.ALLOWED:
|
invalidateCache();
|
||||||
c.addPackageOverride(packageName, enabled);
|
|
||||||
break;
|
|
||||||
case OverrideAllowedState.DEFERRED_VERIFICATION:
|
|
||||||
c.addPackageDeferredOverride(packageName, enabled);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Should only be able to override changes that "
|
|
||||||
+ "are allowed or can be deferred.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return alreadyKnown;
|
return alreadyKnown;
|
||||||
}
|
}
|
||||||
@@ -311,47 +322,20 @@ final class CompatConfig {
|
|||||||
* It does not invalidate the cache nor save the overrides.
|
* It does not invalidate the cache nor save the overrides.
|
||||||
*/
|
*/
|
||||||
private boolean removeOverrideUnsafe(long changeId, String packageName) {
|
private boolean removeOverrideUnsafe(long changeId, String packageName) {
|
||||||
boolean overrideExists = false;
|
|
||||||
synchronized (mChanges) {
|
synchronized (mChanges) {
|
||||||
CompatChange c = mChanges.get(changeId);
|
CompatChange c = mChanges.get(changeId);
|
||||||
if (c != null) {
|
if (c != null) {
|
||||||
// Always allow removing a deferred override.
|
OverrideAllowedState allowedState =
|
||||||
if (c.hasDeferredOverride(packageName)) {
|
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
|
||||||
c.removePackageOverride(packageName);
|
if (c.hasPackageOverride(packageName)) {
|
||||||
overrideExists = true;
|
|
||||||
} else if (c.hasOverride(packageName)) {
|
|
||||||
// Regular overrides need to pass the policy.
|
|
||||||
overrideExists = true;
|
|
||||||
OverrideAllowedState allowedState =
|
|
||||||
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
|
|
||||||
allowedState.enforce(changeId, packageName);
|
allowedState.enforce(changeId, packageName);
|
||||||
c.removePackageOverride(packageName);
|
c.removePackageOverride(packageName, allowedState, mContext);
|
||||||
|
invalidateCache();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return overrideExists;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
synchronized (mChanges) {
|
|
||||||
for (Long changeId : overrides.enabledChanges()) {
|
|
||||||
addOverrideUnsafe(changeId, packageName, true);
|
|
||||||
}
|
|
||||||
for (Long changeId : overrides.disabledChanges()) {
|
|
||||||
addOverrideUnsafe(changeId, packageName, false);
|
|
||||||
|
|
||||||
}
|
|
||||||
saveOverrides();
|
|
||||||
invalidateCache();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -402,7 +386,8 @@ final class CompatConfig {
|
|||||||
int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
|
int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
|
||||||
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
|
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
|
||||||
for (long changeId : changes) {
|
for (long changeId : changes) {
|
||||||
addOverrideUnsafe(changeId, packageName, true);
|
addOverrideUnsafe(changeId, packageName,
|
||||||
|
new PackageOverride.Builder().setEnabled(true).build());
|
||||||
}
|
}
|
||||||
saveOverrides();
|
saveOverrides();
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
@@ -418,7 +403,8 @@ final class CompatConfig {
|
|||||||
int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
|
int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
|
||||||
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
|
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
|
||||||
for (long changeId : changes) {
|
for (long changeId : changes) {
|
||||||
addOverrideUnsafe(changeId, packageName, false);
|
addOverrideUnsafe(changeId, packageName,
|
||||||
|
new PackageOverride.Builder().setEnabled(false).build());
|
||||||
}
|
}
|
||||||
saveOverrides();
|
saveOverrides();
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
@@ -615,8 +601,7 @@ final class CompatConfig {
|
|||||||
CompatChange c = mChanges.valueAt(idx);
|
CompatChange c = mChanges.valueAt(idx);
|
||||||
OverrideAllowedState allowedState =
|
OverrideAllowedState allowedState =
|
||||||
mOverrideValidator.getOverrideAllowedState(c.getId(), packageName);
|
mOverrideValidator.getOverrideAllowedState(c.getId(), packageName);
|
||||||
boolean allowedOverride = (allowedState.state == OverrideAllowedState.ALLOWED);
|
shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext);
|
||||||
shouldInvalidateCache |= c.recheckOverride(packageName, allowedOverride);
|
|
||||||
}
|
}
|
||||||
if (shouldInvalidateCache) {
|
if (shouldInvalidateCache) {
|
||||||
invalidateCache();
|
invalidateCache();
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import static android.os.Process.SYSTEM_UID;
|
|||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.IActivityManager;
|
import android.app.IActivityManager;
|
||||||
|
import android.app.compat.PackageOverride;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -43,6 +44,7 @@ import com.android.internal.compat.AndroidBuildClassifier;
|
|||||||
import com.android.internal.compat.ChangeReporter;
|
import com.android.internal.compat.ChangeReporter;
|
||||||
import com.android.internal.compat.CompatibilityChangeConfig;
|
import com.android.internal.compat.CompatibilityChangeConfig;
|
||||||
import com.android.internal.compat.CompatibilityChangeInfo;
|
import com.android.internal.compat.CompatibilityChangeInfo;
|
||||||
|
import com.android.internal.compat.CompatibilityOverrideConfig;
|
||||||
import com.android.internal.compat.IOverrideValidator;
|
import com.android.internal.compat.IOverrideValidator;
|
||||||
import com.android.internal.compat.IPlatformCompat;
|
import com.android.internal.compat.IPlatformCompat;
|
||||||
import com.android.internal.util.DumpUtils;
|
import com.android.internal.util.DumpUtils;
|
||||||
@@ -51,6 +53,8 @@ import com.android.server.LocalServices;
|
|||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* System server internal API for gating and reporting compatibility changes.
|
* System server internal API for gating and reporting compatibility changes.
|
||||||
@@ -161,6 +165,22 @@ public class PlatformCompat extends IPlatformCompat.Stub {
|
|||||||
@Override
|
@Override
|
||||||
public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
|
public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
|
||||||
checkCompatChangeOverridePermission();
|
checkCompatChangeOverridePermission();
|
||||||
|
Map<Long, PackageOverride> overridesMap = new HashMap<>();
|
||||||
|
for (long change : overrides.enabledChanges()) {
|
||||||
|
overridesMap.put(change, new PackageOverride.Builder().setEnabled(true).build());
|
||||||
|
}
|
||||||
|
for (long change : overrides.disabledChanges()) {
|
||||||
|
overridesMap.put(change, new PackageOverride.Builder().setEnabled(false)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
mCompatConfig.addOverrides(new CompatibilityOverrideConfig(overridesMap), packageName);
|
||||||
|
killPackage(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOverridesFromInstaller(CompatibilityOverrideConfig overrides,
|
||||||
|
String packageName) {
|
||||||
|
checkCompatChangeOverridePermission();
|
||||||
mCompatConfig.addOverrides(overrides, packageName);
|
mCompatConfig.addOverrides(overrides, packageName);
|
||||||
killPackage(packageName);
|
killPackage(packageName);
|
||||||
}
|
}
|
||||||
@@ -168,7 +188,15 @@ public class PlatformCompat extends IPlatformCompat.Stub {
|
|||||||
@Override
|
@Override
|
||||||
public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
|
public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) {
|
||||||
checkCompatChangeOverridePermission();
|
checkCompatChangeOverridePermission();
|
||||||
mCompatConfig.addOverrides(overrides, packageName);
|
Map<Long, PackageOverride> overridesMap = new HashMap<>();
|
||||||
|
for (long change : overrides.enabledChanges()) {
|
||||||
|
overridesMap.put(change, new PackageOverride.Builder().setEnabled(true).build());
|
||||||
|
}
|
||||||
|
for (long change : overrides.disabledChanges()) {
|
||||||
|
overridesMap.put(change, new PackageOverride.Builder().setEnabled(false)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
mCompatConfig.addOverrides(new CompatibilityOverrideConfig(overridesMap), packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -27,6 +27,13 @@
|
|||||||
<xs:attribute type="xs:boolean" name="enabled" use="required" />
|
<xs:attribute type="xs:boolean" name="enabled" use="required" />
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:complexType name="raw-override-value">
|
||||||
|
<xs:attribute type="xs:string" name="packageName" use="required" />
|
||||||
|
<xs:attribute type="xs:long" name="minVersionCode" />
|
||||||
|
<xs:attribute type="xs:long" name="maxVersionCode" />
|
||||||
|
<xs:attribute type="xs:boolean" name="enabled" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
<xs:complexType name="change-overrides">
|
<xs:complexType name="change-overrides">
|
||||||
<xs:attribute type="xs:long" name="changeId" use="required"/>
|
<xs:attribute type="xs:long" name="changeId" use="required"/>
|
||||||
<xs:element name="validated">
|
<xs:element name="validated">
|
||||||
@@ -43,6 +50,13 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="raw">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="raw-override-value" type="raw-override-value" maxOccurs="unbounded" minOccurs="0" />
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
|
|
||||||
<xs:element name="overrides">
|
<xs:element name="overrides">
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ package com.android.server.compat.overrides {
|
|||||||
ctor public ChangeOverrides();
|
ctor public ChangeOverrides();
|
||||||
method public long getChangeId();
|
method public long getChangeId();
|
||||||
method public com.android.server.compat.overrides.ChangeOverrides.Deferred getDeferred();
|
method public com.android.server.compat.overrides.ChangeOverrides.Deferred getDeferred();
|
||||||
|
method public com.android.server.compat.overrides.ChangeOverrides.Raw getRaw();
|
||||||
method public com.android.server.compat.overrides.ChangeOverrides.Validated getValidated();
|
method public com.android.server.compat.overrides.ChangeOverrides.Validated getValidated();
|
||||||
method public void setChangeId(long);
|
method public void setChangeId(long);
|
||||||
method public void setDeferred(com.android.server.compat.overrides.ChangeOverrides.Deferred);
|
method public void setDeferred(com.android.server.compat.overrides.ChangeOverrides.Deferred);
|
||||||
|
method public void setRaw(com.android.server.compat.overrides.ChangeOverrides.Raw);
|
||||||
method public void setValidated(com.android.server.compat.overrides.ChangeOverrides.Validated);
|
method public void setValidated(com.android.server.compat.overrides.ChangeOverrides.Validated);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,6 +18,11 @@ package com.android.server.compat.overrides {
|
|||||||
method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
|
method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ChangeOverrides.Raw {
|
||||||
|
ctor public ChangeOverrides.Raw();
|
||||||
|
method public java.util.List<com.android.server.compat.overrides.RawOverrideValue> getRawOverrideValue();
|
||||||
|
}
|
||||||
|
|
||||||
public static class ChangeOverrides.Validated {
|
public static class ChangeOverrides.Validated {
|
||||||
ctor public ChangeOverrides.Validated();
|
ctor public ChangeOverrides.Validated();
|
||||||
method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
|
method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
|
||||||
@@ -34,6 +41,18 @@ package com.android.server.compat.overrides {
|
|||||||
method public java.util.List<com.android.server.compat.overrides.ChangeOverrides> getChangeOverrides();
|
method public java.util.List<com.android.server.compat.overrides.ChangeOverrides> getChangeOverrides();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class RawOverrideValue {
|
||||||
|
ctor public RawOverrideValue();
|
||||||
|
method public boolean getEnabled();
|
||||||
|
method public long getMaxVersionCode();
|
||||||
|
method public long getMinVersionCode();
|
||||||
|
method public String getPackageName();
|
||||||
|
method public void setEnabled(boolean);
|
||||||
|
method public void setMaxVersionCode(long);
|
||||||
|
method public void setMinVersionCode(long);
|
||||||
|
method public void setPackageName(String);
|
||||||
|
}
|
||||||
|
|
||||||
public class XmlParser {
|
public class XmlParser {
|
||||||
ctor public XmlParser();
|
ctor public XmlParser();
|
||||||
method public static com.android.server.compat.overrides.Overrides read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
|
method public static com.android.server.compat.overrides.Overrides read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class ApplicationInfoBuilder {
|
|||||||
private boolean mIsDebuggable;
|
private boolean mIsDebuggable;
|
||||||
private int mTargetSdk;
|
private int mTargetSdk;
|
||||||
private String mPackageName;
|
private String mPackageName;
|
||||||
|
private long mVersionCode;
|
||||||
|
|
||||||
private ApplicationInfoBuilder() {
|
private ApplicationInfoBuilder() {
|
||||||
mTargetSdk = -1;
|
mTargetSdk = -1;
|
||||||
@@ -46,6 +47,11 @@ class ApplicationInfoBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApplicationInfoBuilder withVersionCode(Long versionCode) {
|
||||||
|
mVersionCode = versionCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
ApplicationInfo build() {
|
ApplicationInfo build() {
|
||||||
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||||
if (mIsDebuggable) {
|
if (mIsDebuggable) {
|
||||||
@@ -53,6 +59,7 @@ class ApplicationInfoBuilder {
|
|||||||
}
|
}
|
||||||
applicationInfo.packageName = mPackageName;
|
applicationInfo.packageName = mPackageName;
|
||||||
applicationInfo.targetSdkVersion = mTargetSdk;
|
applicationInfo.targetSdkVersion = mTargetSdk;
|
||||||
|
applicationInfo.longVersionCode = mVersionCode;
|
||||||
return applicationInfo;
|
return applicationInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.server.compat;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.anyInt;
|
import static org.mockito.Mockito.anyInt;
|
||||||
import static org.mockito.Mockito.eq;
|
import static org.mockito.Mockito.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
@@ -25,6 +26,7 @@ import static org.mockito.Mockito.when;
|
|||||||
import static org.testng.Assert.assertThrows;
|
import static org.testng.Assert.assertThrows;
|
||||||
|
|
||||||
import android.app.compat.ChangeIdStateCache;
|
import android.app.compat.ChangeIdStateCache;
|
||||||
|
import android.app.compat.PackageOverride;
|
||||||
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;
|
||||||
@@ -33,6 +35,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
|||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
import com.android.internal.compat.AndroidBuildClassifier;
|
import com.android.internal.compat.AndroidBuildClassifier;
|
||||||
|
import com.android.internal.compat.CompatibilityOverrideConfig;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -46,6 +49,7 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@@ -83,6 +87,8 @@ public class CompatConfigTest {
|
|||||||
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
|
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
|
||||||
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
|
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
|
||||||
ChangeIdStateCache.disable();
|
ChangeIdStateCache.disable();
|
||||||
|
when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
|
||||||
|
.thenThrow(new NameNotFoundException());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -163,6 +169,10 @@ public class CompatConfigTest {
|
|||||||
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
|
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
|
||||||
.addDisabledChangeWithId(1234L)
|
.addDisabledChangeWithId(1234L)
|
||||||
.build();
|
.build();
|
||||||
|
ApplicationInfo info = ApplicationInfoBuilder.create()
|
||||||
|
.withPackageName("com.some.package").build();
|
||||||
|
when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
|
||||||
|
.thenReturn(info);
|
||||||
|
|
||||||
compatConfig.addOverride(1234L, "com.some.package", true);
|
compatConfig.addOverride(1234L, "com.some.package", true);
|
||||||
|
|
||||||
@@ -177,6 +187,10 @@ public class CompatConfigTest {
|
|||||||
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
|
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
|
||||||
.addEnabledChangeWithId(1234L)
|
.addEnabledChangeWithId(1234L)
|
||||||
.build();
|
.build();
|
||||||
|
ApplicationInfo info = ApplicationInfoBuilder.create()
|
||||||
|
.withPackageName("com.some.package").build();
|
||||||
|
when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
|
||||||
|
.thenReturn(info);
|
||||||
|
|
||||||
compatConfig.addOverride(1234L, "com.some.package", false);
|
compatConfig.addOverride(1234L, "com.some.package", false);
|
||||||
|
|
||||||
@@ -191,6 +205,10 @@ public class CompatConfigTest {
|
|||||||
CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
|
CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
|
||||||
compatConfig.forceNonDebuggableFinalForTest(false);
|
compatConfig.forceNonDebuggableFinalForTest(false);
|
||||||
|
|
||||||
|
ApplicationInfo info = ApplicationInfoBuilder.create()
|
||||||
|
.withPackageName("com.some.package").build();
|
||||||
|
when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
|
||||||
|
.thenReturn(info);
|
||||||
|
|
||||||
compatConfig.addOverride(1234L, "com.some.package", false);
|
compatConfig.addOverride(1234L, "com.some.package", false);
|
||||||
|
|
||||||
@@ -264,6 +282,71 @@ public class CompatConfigTest {
|
|||||||
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
|
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverrideWithAppVersion() throws Exception {
|
||||||
|
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
|
||||||
|
.withPackageName("com.installed.foo")
|
||||||
|
.withVersionCode(100L)
|
||||||
|
.debuggable().build();
|
||||||
|
when(mPackageManager.getApplicationInfo(eq("com.installed.foo"), anyInt()))
|
||||||
|
.thenReturn(applicationInfo);
|
||||||
|
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
|
||||||
|
.addDisabledChangeWithId(1234L).build();
|
||||||
|
when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
|
||||||
|
when(mBuildClassifier.isFinalBuild()).thenReturn(true);
|
||||||
|
|
||||||
|
// Add override that doesn't include the installed app version
|
||||||
|
CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(
|
||||||
|
Collections.singletonMap(1234L,
|
||||||
|
new PackageOverride.Builder()
|
||||||
|
.setMaxVersionCode(99L)
|
||||||
|
.setEnabled(true)
|
||||||
|
.build()));
|
||||||
|
compatConfig.addOverrides(config, "com.installed.foo");
|
||||||
|
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
|
||||||
|
|
||||||
|
// Add override that does include the installed app version
|
||||||
|
config = new CompatibilityOverrideConfig(
|
||||||
|
Collections.singletonMap(1234L,
|
||||||
|
new PackageOverride.Builder()
|
||||||
|
.setMinVersionCode(100L)
|
||||||
|
.setMaxVersionCode(100L)
|
||||||
|
.setEnabled(true)
|
||||||
|
.build()));
|
||||||
|
compatConfig.addOverrides(config, "com.installed.foo");
|
||||||
|
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testApplyDeferredOverridesAfterInstallingAppVersion() throws Exception {
|
||||||
|
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
|
||||||
|
.withPackageName("com.notinstalled.foo")
|
||||||
|
.withVersionCode(100L)
|
||||||
|
.debuggable().build();
|
||||||
|
when(mPackageManager.getApplicationInfo(eq("com.notinstalled.foo"), anyInt()))
|
||||||
|
.thenThrow(new NameNotFoundException());
|
||||||
|
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
|
||||||
|
.addDisabledChangeWithId(1234L).build();
|
||||||
|
when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
|
||||||
|
when(mBuildClassifier.isFinalBuild()).thenReturn(true);
|
||||||
|
|
||||||
|
// Add override before the app is available.
|
||||||
|
CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(
|
||||||
|
Collections.singletonMap(1234L, new PackageOverride.Builder()
|
||||||
|
.setMaxVersionCode(99L)
|
||||||
|
.setEnabled(true)
|
||||||
|
.build()));
|
||||||
|
compatConfig.addOverrides(config, "com.notinstalled.foo");
|
||||||
|
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
|
||||||
|
|
||||||
|
// Pretend the app is now installed.
|
||||||
|
when(mPackageManager.getApplicationInfo(eq("com.notinstalled.foo"), anyInt()))
|
||||||
|
.thenReturn(applicationInfo);
|
||||||
|
|
||||||
|
compatConfig.recheckOverrides("com.notinstalled.foo");
|
||||||
|
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testApplyDeferredOverrideClearsOverrideAfterUninstall() throws Exception {
|
public void testApplyDeferredOverrideClearsOverrideAfterUninstall() throws Exception {
|
||||||
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
|
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
|
||||||
@@ -384,6 +467,8 @@ public class CompatConfigTest {
|
|||||||
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
|
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
|
||||||
.withPackageName("com.some.package")
|
.withPackageName("com.some.package")
|
||||||
.build();
|
.build();
|
||||||
|
when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
|
||||||
|
.thenReturn(applicationInfo);
|
||||||
|
|
||||||
assertThat(compatConfig.addOverride(1234L, "com.some.package", false)).isTrue();
|
assertThat(compatConfig.addOverride(1234L, "com.some.package", false)).isTrue();
|
||||||
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
|
assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
|
||||||
@@ -404,6 +489,8 @@ public class CompatConfigTest {
|
|||||||
.withPackageName("foo.bar")
|
.withPackageName("foo.bar")
|
||||||
.withTargetSdk(2)
|
.withTargetSdk(2)
|
||||||
.build();
|
.build();
|
||||||
|
when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
|
||||||
|
.thenReturn(applicationInfo);
|
||||||
|
|
||||||
assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isFalse();
|
assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isFalse();
|
||||||
assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse();
|
assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse();
|
||||||
@@ -425,7 +512,8 @@ public class CompatConfigTest {
|
|||||||
.withPackageName("foo.bar")
|
.withPackageName("foo.bar")
|
||||||
.withTargetSdk(2)
|
.withTargetSdk(2)
|
||||||
.build();
|
.build();
|
||||||
|
when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
|
||||||
|
.thenReturn(applicationInfo);
|
||||||
assertThat(compatConfig.enableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1);
|
assertThat(compatConfig.enableTargetSdkChangesForPackage("foo.bar", 3)).isEqualTo(1);
|
||||||
assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isTrue();
|
assertThat(compatConfig.isChangeEnabled(3, applicationInfo)).isTrue();
|
||||||
assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse();
|
assertThat(compatConfig.isChangeEnabled(4, applicationInfo)).isFalse();
|
||||||
@@ -533,22 +621,114 @@ public class CompatConfigTest {
|
|||||||
+ " <override-value packageName=\"foo.bar\" enabled=\"true\">\n"
|
+ " <override-value packageName=\"foo.bar\" enabled=\"true\">\n"
|
||||||
+ " </override-value>\n"
|
+ " </override-value>\n"
|
||||||
+ " </validated>\n"
|
+ " </validated>\n"
|
||||||
+ " <deferred>\n"
|
+ " <raw>\n"
|
||||||
+ " </deferred>\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>\n"
|
||||||
+ " <change-overrides changeId=\"2\">\n"
|
+ " <change-overrides changeId=\"2\">\n"
|
||||||
+ " <validated>\n"
|
+ " <validated>\n"
|
||||||
+ " </validated>\n"
|
+ " </validated>\n"
|
||||||
+ " <deferred>\n"
|
+ " <raw>\n"
|
||||||
+ " <override-value packageName=\"bar.baz\" enabled=\"false\">\n"
|
+ " <raw-override-value packageName=\"bar.baz\" "
|
||||||
+ " </override-value>\n"
|
+ "minVersionCode=\"-9223372036854775808\" "
|
||||||
+ " </deferred>\n"
|
+ "maxVersionCode=\"9223372036854775807\" enabled=\"false\">\n"
|
||||||
|
+ " </raw-override-value>\n"
|
||||||
|
+ " </raw>\n"
|
||||||
+ " </change-overrides>\n"
|
+ " </change-overrides>\n"
|
||||||
+ "</overrides>\n");
|
+ "</overrides>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoadOverrides() throws Exception {
|
public void testSaveOverridesWithRanges() throws Exception {
|
||||||
|
File overridesFile = new File(createTempDir(), "overrides.xml");
|
||||||
|
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
|
||||||
|
.addDisabledChangeWithId(1L)
|
||||||
|
.addEnableSinceSdkChangeWithId(2, 2L)
|
||||||
|
.build();
|
||||||
|
compatConfig.forceNonDebuggableFinalForTest(true);
|
||||||
|
compatConfig.initOverrides(overridesFile);
|
||||||
|
|
||||||
|
compatConfig.addOverrides(new CompatibilityOverrideConfig(Collections.singletonMap(1L,
|
||||||
|
new PackageOverride.Builder()
|
||||||
|
.setMinVersionCode(99L)
|
||||||
|
.setMaxVersionCode(101L)
|
||||||
|
.setEnabled(true)
|
||||||
|
.build())), "foo.bar");
|
||||||
|
|
||||||
|
assertThat(readFile(overridesFile)).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=\"99\" maxVersionCode=\"101\" enabled=\"true\">\n"
|
||||||
|
+ " </raw-override-value>\n"
|
||||||
|
+ " </raw>\n"
|
||||||
|
+ " </change-overrides>\n"
|
||||||
|
+ "</overrides>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoadOverridesRaw() 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)
|
||||||
|
String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
|
||||||
|
+ "<overrides>\n"
|
||||||
|
+ " <change-overrides changeId=\"1\">\n"
|
||||||
|
+ " <validated>\n"
|
||||||
|
+ " <override-value packageName=\"foo.bar\" enabled=\"true\">\n"
|
||||||
|
+ " </override-value>\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=\"bar.baz\" "
|
||||||
|
+ "minVersionCode=\"-9223372036854775808\" "
|
||||||
|
+ "maxVersionCode=\"9223372036854775807\" enabled=\"false\">\n"
|
||||||
|
+ " </raw-override-value>\n"
|
||||||
|
+ " </raw>\n"
|
||||||
|
+ " </change-overrides>\n"
|
||||||
|
+ "</overrides>\n";
|
||||||
|
writeToFile(tempDir, "overrides.xml", xmlData);
|
||||||
|
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
|
||||||
|
.addDisabledChangeWithId(1L)
|
||||||
|
.addEnableSinceSdkChangeWithId(2, 2L)
|
||||||
|
.build();
|
||||||
|
compatConfig.forceNonDebuggableFinalForTest(true);
|
||||||
|
compatConfig.initOverrides(overridesFile);
|
||||||
|
ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
|
||||||
|
.withPackageName("foo.bar")
|
||||||
|
.withVersionCode(100L)
|
||||||
|
.debuggable()
|
||||||
|
.build();
|
||||||
|
when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
|
||||||
|
.thenReturn(applicationInfo);
|
||||||
|
when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
|
||||||
|
.thenThrow(new NameNotFoundException());
|
||||||
|
|
||||||
|
assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
|
||||||
|
assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
|
||||||
|
|
||||||
|
compatConfig.recheckOverrides("foo.bar");
|
||||||
|
assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoadOverridesDeferred() 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)
|
||||||
|
|||||||
@@ -196,6 +196,9 @@ public class PlatformCompatTest {
|
|||||||
mPlatformCompat.registerListener(1, mListener1);
|
mPlatformCompat.registerListener(1, mListener1);
|
||||||
mPlatformCompat.registerListener(2, mListener1);
|
mPlatformCompat.registerListener(2, mListener1);
|
||||||
|
|
||||||
|
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
|
||||||
|
.thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
|
||||||
|
|
||||||
mPlatformCompat.setOverrides(
|
mPlatformCompat.setOverrides(
|
||||||
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
||||||
PACKAGE_NAME);
|
PACKAGE_NAME);
|
||||||
@@ -208,6 +211,9 @@ public class PlatformCompatTest {
|
|||||||
mPlatformCompat.registerListener(1, mListener1);
|
mPlatformCompat.registerListener(1, mListener1);
|
||||||
mPlatformCompat.registerListener(2, mListener1);
|
mPlatformCompat.registerListener(2, mListener1);
|
||||||
|
|
||||||
|
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
|
||||||
|
.thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
|
||||||
|
|
||||||
mPlatformCompat.setOverrides(
|
mPlatformCompat.setOverrides(
|
||||||
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
||||||
PACKAGE_NAME);
|
PACKAGE_NAME);
|
||||||
@@ -219,6 +225,9 @@ public class PlatformCompatTest {
|
|||||||
public void testListenerCalledOnSetOverridesTwoListeners() throws Exception {
|
public void testListenerCalledOnSetOverridesTwoListeners() throws Exception {
|
||||||
mPlatformCompat.registerListener(1, mListener1);
|
mPlatformCompat.registerListener(1, mListener1);
|
||||||
|
|
||||||
|
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
|
||||||
|
.thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
|
||||||
|
|
||||||
mPlatformCompat.setOverrides(
|
mPlatformCompat.setOverrides(
|
||||||
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
||||||
PACKAGE_NAME);
|
PACKAGE_NAME);
|
||||||
@@ -244,6 +253,9 @@ public class PlatformCompatTest {
|
|||||||
mPlatformCompat.registerListener(1, mListener1);
|
mPlatformCompat.registerListener(1, mListener1);
|
||||||
mPlatformCompat.registerListener(2, mListener1);
|
mPlatformCompat.registerListener(2, mListener1);
|
||||||
|
|
||||||
|
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
|
||||||
|
.thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
|
||||||
|
|
||||||
mPlatformCompat.setOverrides(
|
mPlatformCompat.setOverrides(
|
||||||
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
||||||
PACKAGE_NAME);
|
PACKAGE_NAME);
|
||||||
@@ -252,9 +264,12 @@ public class PlatformCompatTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testListenerCalledOnSetOverridesTwoListenersForTest() throws Exception {
|
public void testListenerCalledOnSetOverridesForTestTwoListeners() throws Exception {
|
||||||
mPlatformCompat.registerListener(1, mListener1);
|
mPlatformCompat.registerListener(1, mListener1);
|
||||||
|
|
||||||
|
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
|
||||||
|
.thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
|
||||||
|
|
||||||
mPlatformCompat.setOverrides(
|
mPlatformCompat.setOverrides(
|
||||||
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
||||||
PACKAGE_NAME);
|
PACKAGE_NAME);
|
||||||
@@ -280,6 +295,9 @@ public class PlatformCompatTest {
|
|||||||
mPlatformCompat.registerListener(1, mListener1);
|
mPlatformCompat.registerListener(1, mListener1);
|
||||||
mPlatformCompat.registerListener(2, mListener2);
|
mPlatformCompat.registerListener(2, mListener2);
|
||||||
|
|
||||||
|
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
|
||||||
|
.thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
|
||||||
|
|
||||||
mPlatformCompat.setOverrides(
|
mPlatformCompat.setOverrides(
|
||||||
CompatibilityChangeConfigBuilder.create().enable(1L).build(),
|
CompatibilityChangeConfigBuilder.create().enable(1L).build(),
|
||||||
PACKAGE_NAME);
|
PACKAGE_NAME);
|
||||||
@@ -299,6 +317,9 @@ public class PlatformCompatTest {
|
|||||||
mPlatformCompat.registerListener(1, mListener1);
|
mPlatformCompat.registerListener(1, mListener1);
|
||||||
mPlatformCompat.registerListener(2, mListener2);
|
mPlatformCompat.registerListener(2, mListener2);
|
||||||
|
|
||||||
|
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
|
||||||
|
.thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
|
||||||
|
|
||||||
mPlatformCompat.setOverrides(
|
mPlatformCompat.setOverrides(
|
||||||
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
CompatibilityChangeConfigBuilder.create().enable(1L).disable(2L).build(),
|
||||||
PACKAGE_NAME);
|
PACKAGE_NAME);
|
||||||
@@ -318,6 +339,9 @@ public class PlatformCompatTest {
|
|||||||
mPlatformCompat.registerListener(1, mListener1);
|
mPlatformCompat.registerListener(1, mListener1);
|
||||||
mPlatformCompat.registerListener(2, mListener2);
|
mPlatformCompat.registerListener(2, mListener2);
|
||||||
|
|
||||||
|
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
|
||||||
|
.thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
|
||||||
|
|
||||||
mPlatformCompat.setOverrides(
|
mPlatformCompat.setOverrides(
|
||||||
CompatibilityChangeConfigBuilder.create().enable(1L).build(),
|
CompatibilityChangeConfigBuilder.create().enable(1L).build(),
|
||||||
PACKAGE_NAME);
|
PACKAGE_NAME);
|
||||||
@@ -336,6 +360,9 @@ public class PlatformCompatTest {
|
|||||||
public void testListenerCalledOnClearOverrideDoesntExist() throws Exception {
|
public void testListenerCalledOnClearOverrideDoesntExist() throws Exception {
|
||||||
mPlatformCompat.registerListener(1, mListener1);
|
mPlatformCompat.registerListener(1, mListener1);
|
||||||
|
|
||||||
|
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
|
||||||
|
.thenReturn(ApplicationInfoBuilder.create().withPackageName(PACKAGE_NAME).build());
|
||||||
|
|
||||||
mPlatformCompat.clearOverride(1, PACKAGE_NAME);
|
mPlatformCompat.clearOverride(1, PACKAGE_NAME);
|
||||||
// Listener not called when a non existing override is removed.
|
// Listener not called when a non existing override is removed.
|
||||||
verify(mListener1, never()).onCompatChange(PACKAGE_NAME);
|
verify(mListener1, never()).onCompatChange(PACKAGE_NAME);
|
||||||
|
|||||||
@@ -76,11 +76,11 @@ class PlatformCompatCommandNotInstalledTest {
|
|||||||
Params(enableDisable = null, targetSdk = 29, result = false),
|
Params(enableDisable = null, targetSdk = 29, result = false),
|
||||||
Params(enableDisable = null, targetSdk = 30, result = true),
|
Params(enableDisable = null, targetSdk = 30, result = true),
|
||||||
|
|
||||||
Params(enableDisable = true, targetSdk = 29, result = true),
|
Params(enableDisable = true, targetSdk = 29, result = false),
|
||||||
Params(enableDisable = true, targetSdk = 30, result = true),
|
Params(enableDisable = true, targetSdk = 30, result = true),
|
||||||
|
|
||||||
Params(enableDisable = false, targetSdk = 29, result = false),
|
Params(enableDisable = false, targetSdk = 29, result = false),
|
||||||
Params(enableDisable = false, targetSdk = 30, result = false)
|
Params(enableDisable = false, targetSdk = 30, result = true)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user