From 5c8ba79373ef7a950f6bfb87e7aa694f29ace243 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Thu, 28 Jan 2021 11:52:45 -0800 Subject: [PATCH] Query overlays using package and name Fabricated RROs identifiers are made using package name and overlay name combinations. In the future, packages with multiple tags will use overlay name to distinguish between the tags. This changes the OMS to manage overlays through OverlayIdentifiers. The identifier is a overlay package and name combination. The name field is optional so overlays without an overlay name can be managed. This change simplifies the logic required to update the set of target packages affected by overlay changes. Bug: 172471315 Test: OverlayDeviceTests Test: OverlayImpl tests Change-Id: Iac679828d7480e5c7a2c1fe4ea8b73401d8d487f --- .../content/om/CriticalOverlayInfo.java | 60 ++ .../android/content/om/IOverlayManager.aidl | 12 + .../android/content/om/OverlayIdentifier.aidl | 19 + .../android/content/om/OverlayIdentifier.java | 208 ++++++ core/java/android/content/om/OverlayInfo.java | 86 ++- .../android/content/om/OverlayManager.java | 19 + .../content/om/OverlayManagerTransaction.java | 30 +- .../overlaytest/LocalOverlayManager.java | 31 +- .../android/overlaytest/OverlayBaseTest.java | 10 +- .../android/overlaytest/TransactionTest.java | 30 +- .../overlaytest/WithMultipleOverlaysTest.java | 8 +- .../android/overlaytest/WithOverlayTest.java | 6 +- .../overlaytest/WithoutOverlayTest.java | 8 +- .../java/com/android/server/om/DumpState.java | 13 +- .../com/android/server/om/IdmapManager.java | 60 +- .../server/om/OverlayActorEnforcer.java | 27 +- .../server/om/OverlayManagerService.java | 482 +++++++----- .../server/om/OverlayManagerServiceImpl.java | 704 +++++++----------- .../server/om/OverlayManagerSettings.java | 269 ++++--- .../server/om/OverlayManagerShellCommand.java | 70 +- .../server/om/PackageManagerHelper.java | 50 +- .../server/om/OverlayActorEnforcerTests.kt | 55 +- .../OverlayManagerServiceImplRebootTests.java | 101 ++- .../om/OverlayManagerServiceImplTests.java | 119 +-- .../OverlayManagerServiceImplTestsBase.java | 115 ++- .../om/OverlayManagerSettingsTests.java | 353 ++++----- 26 files changed, 1657 insertions(+), 1288 deletions(-) create mode 100644 core/java/android/content/om/CriticalOverlayInfo.java create mode 100644 core/java/android/content/om/OverlayIdentifier.aidl create mode 100644 core/java/android/content/om/OverlayIdentifier.java diff --git a/core/java/android/content/om/CriticalOverlayInfo.java b/core/java/android/content/om/CriticalOverlayInfo.java new file mode 100644 index 0000000000000..8d14f4360f078 --- /dev/null +++ b/core/java/android/content/om/CriticalOverlayInfo.java @@ -0,0 +1,60 @@ +/* + * 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.content.om; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +/** + * A subset of {@link OverlayInfo} fields that when changed cause the overlay's settings to be + * completely reinitialized. + * + * @hide + */ +public interface CriticalOverlayInfo { + + /** + * @return the package name of the overlay. + */ + @NonNull + String getPackageName(); + + /** + * @return the unique name of the overlay within its containing package. + */ + @Nullable + String getOverlayName(); + + /** + * @return the target package name of the overlay. + */ + @NonNull + String getTargetPackageName(); + + /** + * @return the name of the target overlayable declaration. + */ + @Nullable + String getTargetOverlayableName(); + + /** + * @return an identifier representing the current overlay. + */ + @NonNull + OverlayIdentifier getOverlayIdentifier(); +} diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl index 0b950b4612854..a99e79294f760 100644 --- a/core/java/android/content/om/IOverlayManager.aidl +++ b/core/java/android/content/om/IOverlayManager.aidl @@ -16,6 +16,7 @@ package android.content.om; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.content.om.OverlayManagerTransaction; @@ -65,6 +66,17 @@ interface IOverlayManager { @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) OverlayInfo getOverlayInfo(in String packageName, in int userId); + /** + * Returns information about the overlay with the given package name for the + * specified user. + * + * @param packageName The name of the overlay package. + * @param userId The user to get the OverlayInfo for. + * @return The OverlayInfo for the overlay package; or null if no such + * overlay package exists. + */ + OverlayInfo getOverlayInfoByIdentifier(in OverlayIdentifier packageName, in int userId); + /** * Request that an overlay package be enabled or disabled when possible to * do so. diff --git a/core/java/android/content/om/OverlayIdentifier.aidl b/core/java/android/content/om/OverlayIdentifier.aidl new file mode 100644 index 0000000000000..d1c7770f91686 --- /dev/null +++ b/core/java/android/content/om/OverlayIdentifier.aidl @@ -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 android.content.om; + +parcelable OverlayIdentifier; diff --git a/core/java/android/content/om/OverlayIdentifier.java b/core/java/android/content/om/OverlayIdentifier.java new file mode 100644 index 0000000000000..454d0d120f2f8 --- /dev/null +++ b/core/java/android/content/om/OverlayIdentifier.java @@ -0,0 +1,208 @@ +/* + * 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.content.om; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.util.Objects; + +/** + * A key used to uniquely identify a Runtime Resource Overlay (RRO). + * + * An overlay always belongs to a package and may optionally have a name associated with it. + * The name helps uniquely identify a particular overlay within a package. + * @hide + */ +/** @hide */ +@DataClass(genConstructor = false, genBuilder = false, genHiddenBuilder = false, + genEqualsHashCode = true, genToString = false) +public class OverlayIdentifier implements Parcelable { + /** + * The package name containing or owning the overlay. + */ + @Nullable + private final String mPackageName; + + /** + * The unique name within the package of the overlay. + */ + @Nullable + private final String mOverlayName; + + /** + * Creates an identifier from a package and unique name within the package. + * + * @param packageName the package containing or owning the overlay + * @param overlayName the unique name of the overlay within the package + */ + public OverlayIdentifier(@NonNull String packageName, @Nullable String overlayName) { + mPackageName = packageName; + mOverlayName = overlayName; + } + + /** + * Creates an identifier for an overlay without a name. + * + * @param packageName the package containing or owning the overlay + */ + public OverlayIdentifier(@NonNull String packageName) { + mPackageName = packageName; + mOverlayName = null; + } + + @Override + public String toString() { + return mOverlayName == null ? mPackageName : mPackageName + ":" + mOverlayName; + } + + /** @hide */ + public static OverlayIdentifier fromString(@NonNull String text) { + final String[] parts = text.split(":", 2); + if (parts.length == 2) { + return new OverlayIdentifier(parts[0], parts[1]); + } else { + return new OverlayIdentifier(parts[0]); + } + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/om/OverlayIdentifier.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Retrieves the package name containing or owning the overlay. + */ + @DataClass.Generated.Member + public @Nullable String getPackageName() { + return mPackageName; + } + + /** + * Retrieves the unique name within the package of the overlay. + */ + @DataClass.Generated.Member + public @Nullable String getOverlayName() { + return mOverlayName; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(OverlayIdentifier other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + OverlayIdentifier that = (OverlayIdentifier) o; + //noinspection PointlessBooleanExpression + return true + && Objects.equals(mPackageName, that.mPackageName) + && Objects.equals(mOverlayName, that.mOverlayName); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + Objects.hashCode(mPackageName); + _hash = 31 * _hash + Objects.hashCode(mOverlayName); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mPackageName != null) flg |= 0x1; + if (mOverlayName != null) flg |= 0x2; + dest.writeByte(flg); + if (mPackageName != null) dest.writeString(mPackageName); + if (mOverlayName != null) dest.writeString(mOverlayName); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected OverlayIdentifier(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + String packageName = (flg & 0x1) == 0 ? null : in.readString(); + String overlayName = (flg & 0x2) == 0 ? null : in.readString(); + + this.mPackageName = packageName; + this.mOverlayName = overlayName; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public OverlayIdentifier[] newArray(int size) { + return new OverlayIdentifier[size]; + } + + @Override + public OverlayIdentifier createFromParcel(@NonNull Parcel in) { + return new OverlayIdentifier(in); + } + }; + + @DataClass.Generated( + time = 1612482438728L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/om/OverlayIdentifier.java", + inputSignatures = "private final @android.annotation.Nullable java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mOverlayName\npublic @java.lang.Override java.lang.String toString()\npublic static android.content.om.OverlayIdentifier fromString(java.lang.String)\nclass OverlayIdentifier extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genHiddenBuilder=false, genEqualsHashCode=true, genToString=false)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java index 517e4bda68d74..8c316d7998ace 100644 --- a/core/java/android/content/om/OverlayInfo.java +++ b/core/java/android/content/om/OverlayInfo.java @@ -26,6 +26,8 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.annotations.VisibleForTesting; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; @@ -37,7 +39,7 @@ import java.util.Objects; * @hide */ @SystemApi -public final class OverlayInfo implements Parcelable { +public final class OverlayInfo implements CriticalOverlayInfo, Parcelable { /** @hide */ @IntDef(prefix = "STATE_", value = { @@ -142,6 +144,14 @@ public final class OverlayInfo implements Parcelable { @NonNull public final String packageName; + /** + * The unique name within the package of the overlay. + * + * @hide + */ + @Nullable + public final String overlayName; + /** * Package name of the target package * @@ -201,6 +211,8 @@ public final class OverlayInfo implements Parcelable { */ public final boolean isMutable; + private OverlayIdentifier mIdentifierCached; + /** * Create a new OverlayInfo based on source with an updated state. * @@ -210,17 +222,27 @@ public final class OverlayInfo implements Parcelable { * @hide */ public OverlayInfo(@NonNull OverlayInfo source, @State int state) { - this(source.packageName, source.targetPackageName, source.targetOverlayableName, - source.category, source.baseCodePath, state, source.userId, source.priority, - source.isMutable); + this(source.packageName, source.overlayName, source.targetPackageName, + source.targetOverlayableName, source.category, source.baseCodePath, state, + source.userId, source.priority, source.isMutable); } /** @hide */ + @VisibleForTesting public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName, @Nullable String targetOverlayableName, @Nullable String category, - @NonNull String baseCodePath, int state, int userId, + @NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable) { + this(packageName, null /* overlayName */, targetPackageName, targetOverlayableName, + category, baseCodePath, state, userId, priority, isMutable); + } + + /** @hide */ + public OverlayInfo(@NonNull String packageName, @Nullable String overlayName, + @NonNull String targetPackageName, @Nullable String targetOverlayableName, + @Nullable String category, @NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable) { this.packageName = packageName; + this.overlayName = overlayName; this.targetPackageName = targetPackageName; this.targetOverlayableName = targetOverlayableName; this.category = category; @@ -235,6 +257,7 @@ public final class OverlayInfo implements Parcelable { /** @hide */ public OverlayInfo(Parcel source) { packageName = source.readString(); + overlayName = source.readString(); targetPackageName = source.readString(); targetOverlayableName = source.readString(); category = source.readString(); @@ -247,9 +270,10 @@ public final class OverlayInfo implements Parcelable { } /** - * Returns package name of the current overlay. + * {@inheritDoc} * @hide */ + @Override @SystemApi @NonNull public String getPackageName() { @@ -257,9 +281,20 @@ public final class OverlayInfo implements Parcelable { } /** - * Returns the target package name of the current overlay. + * {@inheritDoc} * @hide */ + @Override + @Nullable + public String getOverlayName() { + return overlayName; + } + + /** + * {@inheritDoc} + * @hide + */ + @Override @SystemApi @NonNull public String getTargetPackageName() { @@ -268,7 +303,8 @@ public final class OverlayInfo implements Parcelable { /** * Returns the category of the current overlay. - * @hide\ + * + * @hide */ @SystemApi @Nullable @@ -278,6 +314,7 @@ public final class OverlayInfo implements Parcelable { /** * Returns user handle for which this overlay applies to. + * * @hide */ @SystemApi @@ -287,15 +324,29 @@ public final class OverlayInfo implements Parcelable { } /** - * Returns name of the target overlayable declaration. + * {@inheritDoc} * @hide */ + @Override @SystemApi @Nullable public String getTargetOverlayableName() { return targetOverlayableName; } + /** + * {@inheritDoc} + * @hide + */ + @Override + @NonNull + public OverlayIdentifier getOverlayIdentifier() { + if (mIdentifierCached == null) { + mIdentifierCached = new OverlayIdentifier(packageName, overlayName); + } + return mIdentifierCached; + } + @SuppressWarnings("ConstantConditions") private void ensureValidState() { if (packageName == null) { @@ -330,6 +381,7 @@ public final class OverlayInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(packageName); + dest.writeString(overlayName); dest.writeString(targetPackageName); dest.writeString(targetOverlayableName); dest.writeString(category); @@ -410,6 +462,7 @@ public final class OverlayInfo implements Parcelable { result = prime * result + userId; result = prime * result + state; result = prime * result + ((packageName == null) ? 0 : packageName.hashCode()); + result = prime * result + ((overlayName == null) ? 0 : overlayName.hashCode()); result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode()); result = prime * result + ((targetOverlayableName == null) ? 0 : targetOverlayableName.hashCode()); @@ -439,6 +492,9 @@ public final class OverlayInfo implements Parcelable { if (!packageName.equals(other.packageName)) { return false; } + if (!Objects.equals(overlayName, other.overlayName)) { + return false; + } if (!targetPackageName.equals(other.targetPackageName)) { return false; } @@ -457,9 +513,13 @@ public final class OverlayInfo implements Parcelable { @NonNull @Override public String toString() { - return "OverlayInfo { overlay=" + packageName + ", targetPackage=" + targetPackageName - + ((targetOverlayableName == null) ? "" - : ", targetOverlayable=" + targetOverlayableName) - + ", state=" + state + " (" + stateToString(state) + "), userId=" + userId + " }"; + return "OverlayInfo {" + + "packageName=" + packageName + + ", overlayName=" + overlayName + + ", targetPackage=" + targetPackageName + + ", targetOverlayable=" + targetOverlayableName + + ", state=" + state + " (" + stateToString(state) + ")," + + ", userId=" + userId + + " }"; } } diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java index 7c14c2891d017..0f7e01b234c25 100644 --- a/core/java/android/content/om/OverlayManager.java +++ b/core/java/android/content/om/OverlayManager.java @@ -204,6 +204,25 @@ public class OverlayManager { } } + /** + * Returns information about the overlay represented by the identifier for the specified user. + * + * @param overlay the identifier representing the overlay + * @param userHandle the user of which to get overlay state info + * @return the overlay info or null if the overlay cannot be found + * + * @hide + */ + @Nullable + public OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier overlay, + @NonNull final UserHandle userHandle) { + try { + return mService.getOverlayInfoByIdentifier(overlay, userHandle.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Returns information about all overlays for the given target package for * the specified user. The returned list is ordered according to the diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java index 1fa8973c35b56..d03ad9d28a4fd 100644 --- a/core/java/android/content/om/OverlayManagerTransaction.java +++ b/core/java/android/content/om/OverlayManagerTransaction.java @@ -60,12 +60,12 @@ public class OverlayManagerTransaction private OverlayManagerTransaction(@NonNull final Parcel source) { final int size = source.readInt(); - mRequests = new ArrayList(size); + mRequests = new ArrayList<>(size); for (int i = 0; i < size; i++) { final int request = source.readInt(); - final String packageName = source.readString(); + final OverlayIdentifier overlay = source.readParcelable(null); final int userId = source.readInt(); - mRequests.add(new Request(request, packageName, userId)); + mRequests.add(new Request(request, overlay, userId)); } } @@ -97,20 +97,20 @@ public class OverlayManagerTransaction public static final int TYPE_SET_DISABLED = 1; @RequestType public final int type; - public final String packageName; + public final OverlayIdentifier overlay; public final int userId; - public Request(@RequestType final int type, @NonNull final String packageName, + public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay, final int userId) { this.type = type; - this.packageName = packageName; + this.overlay = overlay; this.userId = userId; } @Override public String toString() { - return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}", - type, typeToString(), packageName, userId); + return String.format("Request{type=0x%02x (%s), overlay=%s, userId=%d}", + type, typeToString(), overlay, userId); } /** @@ -152,22 +152,22 @@ public class OverlayManagerTransaction * longer affect the resources of the target package. If the target is * currently running, its outdated resources will be replaced by new ones. * - * @param packageName The name of the overlay package. + * @param overlay The name of the overlay package. * @param enable true to enable the overlay, false to disable it. * @return this Builder object, so you can chain additional requests */ - public Builder setEnabled(@NonNull String packageName, boolean enable) { - return setEnabled(packageName, enable, UserHandle.myUserId()); + public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable) { + return setEnabled(overlay, enable, UserHandle.myUserId()); } /** * @hide */ - public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) { - checkNotNull(packageName); + public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable, int userId) { + checkNotNull(overlay); @Request.RequestType final int type = enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED; - mRequests.add(new Request(type, packageName, userId)); + mRequests.add(new Request(type, overlay, userId)); return this; } @@ -195,7 +195,7 @@ public class OverlayManagerTransaction for (int i = 0; i < size; i++) { final Request req = mRequests.get(i); dest.writeInt(req.type); - dest.writeString(req.packageName); + dest.writeParcelable(req.overlay, flags); dest.writeInt(req.userId); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java index 76c01a7e11259..3c0c1311a1aa8 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java @@ -20,6 +20,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; import android.annotation.NonNull; import android.content.Context; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayManager; import android.content.om.OverlayManagerTransaction; import android.os.UserHandle; @@ -32,14 +33,14 @@ import java.util.concurrent.FutureTask; class LocalOverlayManager { private static final long TIMEOUT = 30; - public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable, - @NonNull final String[] overlaysToDisable) throws Exception { + public static void toggleOverlaysAndWait(@NonNull final OverlayIdentifier[] overlaysToEnable, + @NonNull final OverlayIdentifier[] overlaysToDisable) throws Exception { final int userId = UserHandle.myUserId(); OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder(); - for (String pkg : overlaysToEnable) { + for (OverlayIdentifier pkg : overlaysToEnable) { builder.setEnabled(pkg, true, userId); } - for (String pkg : overlaysToDisable) { + for (OverlayIdentifier pkg : overlaysToDisable) { builder.setEnabled(pkg, false, userId); } OverlayManagerTransaction transaction = builder.build(); @@ -48,7 +49,7 @@ class LocalOverlayManager { FutureTask task = new FutureTask<>(() -> { while (true) { final String[] paths = ctx.getResources().getAssets().getApkPaths(); - if (arrayTailContains(paths, overlaysToEnable) + if (arrayTailContainsOverlays(paths, overlaysToEnable) && arrayDoesNotContain(paths, overlaysToDisable)) { return true; } @@ -64,15 +65,15 @@ class LocalOverlayManager { task.get(TIMEOUT, SECONDS); } - private static boolean arrayTailContains(@NonNull final String[] array, - @NonNull final String[] substrings) { - if (array.length < substrings.length) { + private static boolean arrayTailContainsOverlays(@NonNull final String[] array, + @NonNull final OverlayIdentifier[] overlays) { + if (array.length < overlays.length) { return false; } - for (int i = 0; i < substrings.length; i++) { - String a = array[array.length - substrings.length + i]; - String s = substrings[i]; - if (!a.contains(s)) { + for (int i = 0; i < overlays.length; i++) { + String a = array[array.length - overlays.length + i]; + OverlayIdentifier s = overlays[i]; + if (!a.contains(s.getPackageName())) { return false; } } @@ -80,10 +81,10 @@ class LocalOverlayManager { } private static boolean arrayDoesNotContain(@NonNull final String[] array, - @NonNull final String[] substrings) { - for (String s : substrings) { + @NonNull final OverlayIdentifier[] overlays) { + for (OverlayIdentifier s : overlays) { for (String a : array) { - if (a.contains(s)) { + if (a.contains(s.getPackageName())) { return false; } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java index 636f4c8b59cab..8e4b9efafccdf 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.content.Context; +import android.content.om.OverlayIdentifier; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -58,9 +59,12 @@ public abstract class OverlayBaseTest { static final int MODE_SINGLE_OVERLAY = 1; static final int MODE_MULTIPLE_OVERLAYS = 2; - static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one"; - static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two"; - static final String FRAMEWORK_OVERLAY_PKG = "com.android.overlaytest.framework"; + static final OverlayIdentifier APP_OVERLAY_ONE_PKG = + new OverlayIdentifier("com.android.overlaytest.app_overlay_one"); + static final OverlayIdentifier APP_OVERLAY_TWO_PKG = + new OverlayIdentifier("com.android.overlaytest.app_overlay_two"); + static final OverlayIdentifier FRAMEWORK_OVERLAY_PKG = + new OverlayIdentifier("com.android.overlaytest.framework"); protected OverlayBaseTest(int mode) { mMode = mode; diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java index 0b4f5e2271694..594fa47c5dc62 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java @@ -16,14 +16,19 @@ package com.android.overlaytest; +import static com.android.overlaytest.OverlayBaseTest.APP_OVERLAY_ONE_PKG; +import static com.android.overlaytest.OverlayBaseTest.APP_OVERLAY_TWO_PKG; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.testng.Assert.assertThrows; import android.content.Context; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.content.om.OverlayManager; import android.content.om.OverlayManagerTransaction; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.UserHandle; @@ -40,8 +45,6 @@ import java.util.List; @RunWith(JUnit4.class) @MediumTest public class TransactionTest { - static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one"; - static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two"; private Context mContext; private Resources mResources; @@ -58,8 +61,8 @@ public class TransactionTest { mUserHandle = UserHandle.of(mUserId); LocalOverlayManager.toggleOverlaysAndWait( - new String[]{}, - new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG}); + new OverlayIdentifier[]{}, + new OverlayIdentifier[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG}); } @Test @@ -78,8 +81,8 @@ public class TransactionTest { List ois = mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle); assertEquals(ois.size(), 2); - assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG); - assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG); + assertEquals(ois.get(0).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG); + assertEquals(ois.get(1).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG); OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder() .setEnabled(APP_OVERLAY_TWO_PKG, true) @@ -92,8 +95,8 @@ public class TransactionTest { List ois2 = mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle); assertEquals(ois2.size(), 2); - assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG); - assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG); + assertEquals(ois2.get(0).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG); + assertEquals(ois2.get(1).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG); OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder() .setEnabled(APP_OVERLAY_TWO_PKG, false) @@ -105,8 +108,8 @@ public class TransactionTest { List ois3 = mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle); assertEquals(ois3.size(), 2); - assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG); - assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG); + assertEquals(ois3.get(0).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG); + assertEquals(ois3.get(1).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG); } @Test @@ -116,7 +119,7 @@ public class TransactionTest { OverlayManagerTransaction t = new OverlayManagerTransaction.Builder() .setEnabled(APP_OVERLAY_ONE_PKG, true) - .setEnabled("does-not-exist", true) + .setEnabled(new OverlayIdentifier("does-not-exist"), true) .setEnabled(APP_OVERLAY_TWO_PKG, true) .build(); assertThrows(SecurityException.class, () -> mOverlayManager.commit(t)); @@ -125,8 +128,9 @@ public class TransactionTest { assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId); } - private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) { - final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId)); + private void assertOverlayIsEnabled(final OverlayIdentifier overlay, boolean enabled, + int userId) { + final OverlayInfo oi = mOverlayManager.getOverlayInfo(overlay, UserHandle.of(userId)); assertNotNull(oi); assertEquals(oi.isEnabled(), enabled); } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java index 420f755c5251c..558720368cc6c 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java @@ -16,6 +16,8 @@ package com.android.overlaytest; +import android.content.om.OverlayIdentifier; + import androidx.test.filters.MediumTest; import org.junit.BeforeClass; @@ -32,7 +34,9 @@ public class WithMultipleOverlaysTest extends OverlayBaseTest { @BeforeClass public static void enableOverlay() throws Exception { LocalOverlayManager.toggleOverlaysAndWait( - new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG}, - new String[]{}); + new OverlayIdentifier[]{ + FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG + }, + new OverlayIdentifier[]{}); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java index a86255e96388c..d27543384f6c6 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java @@ -16,6 +16,8 @@ package com.android.overlaytest; +import android.content.om.OverlayIdentifier; + import androidx.test.filters.MediumTest; import org.junit.BeforeClass; @@ -32,7 +34,7 @@ public class WithOverlayTest extends OverlayBaseTest { @BeforeClass public static void enableOverlays() throws Exception { LocalOverlayManager.toggleOverlaysAndWait( - new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG}, - new String[]{APP_OVERLAY_TWO_PKG}); + new OverlayIdentifier[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG}, + new OverlayIdentifier[]{APP_OVERLAY_TWO_PKG}); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java index 51c411819b875..72cba8b87c8c2 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java @@ -16,6 +16,8 @@ package com.android.overlaytest; +import android.content.om.OverlayIdentifier; + import androidx.test.filters.MediumTest; import org.junit.BeforeClass; @@ -32,7 +34,9 @@ public class WithoutOverlayTest extends OverlayBaseTest { @BeforeClass public static void disableOverlays() throws Exception { LocalOverlayManager.toggleOverlaysAndWait( - new String[]{}, - new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG}); + new OverlayIdentifier[]{}, + new OverlayIdentifier[]{ + FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG + }); } } diff --git a/services/core/java/com/android/server/om/DumpState.java b/services/core/java/com/android/server/om/DumpState.java index 1e2e054bbc6cd..88afcb2f50418 100644 --- a/services/core/java/com/android/server/om/DumpState.java +++ b/services/core/java/com/android/server/om/DumpState.java @@ -18,6 +18,7 @@ package com.android.server.om; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.om.OverlayIdentifier; import android.os.UserHandle; /** @@ -26,6 +27,7 @@ import android.os.UserHandle; public final class DumpState { @UserIdInt private int mUserId = UserHandle.USER_ALL; @Nullable private String mPackageName; + @Nullable private String mOverlayName; @Nullable private String mField; private boolean mVerbose; @@ -38,14 +40,19 @@ public final class DumpState { } /** Sets the name of the package to dump the state for */ - public void setPackageName(String packageName) { - mPackageName = packageName; + public void setOverlyIdentifier(String overlayIdentifier) { + final OverlayIdentifier overlay = OverlayIdentifier.fromString(overlayIdentifier); + mPackageName = overlay.getPackageName(); + mOverlayName = overlay.getOverlayName(); } @Nullable public String getPackageName() { return mPackageName; } + @Nullable public String getOverlayName() { + return mOverlayName; + } - /** Sets the name of the field to dump the state of */ + /** Sets the name of the field to dump the state for */ public void setField(String field) { mField = field; } diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java index eeb26552e63da..e87bf558c1226 100644 --- a/services/core/java/com/android/server/om/IdmapManager.java +++ b/services/core/java/com/android/server/om/IdmapManager.java @@ -22,14 +22,14 @@ import static com.android.server.om.OverlayManagerService.TAG; import android.annotation.NonNull; import android.content.om.OverlayInfo; import android.content.om.OverlayableInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.os.Build.VERSION_CODES; import android.os.OverlayablePolicy; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Slog; +import com.android.server.pm.parsing.pkg.AndroidPackage; + import java.io.IOException; /** @@ -74,14 +74,14 @@ final class IdmapManager { * Creates the idmap for the target/overlay combination and returns whether the idmap file was * modified. */ - boolean createIdmap(@NonNull final PackageInfo targetPackage, - @NonNull final PackageInfo overlayPackage, int userId) { + boolean createIdmap(@NonNull final AndroidPackage targetPackage, + @NonNull final AndroidPackage overlayPackage, int userId) { if (DEBUG) { - Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and " - + overlayPackage.packageName); + Slog.d(TAG, "create idmap for " + targetPackage.getPackageName() + " and " + + overlayPackage.getPackageName()); } - final String targetPath = targetPackage.applicationInfo.getBaseCodePath(); - final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath(); + final String targetPath = targetPackage.getBaseApkPath(); + final String overlayPath = overlayPackage.getBaseApkPath(); try { int policies = calculateFulfilledPolicies(targetPackage, overlayPackage, userId); boolean enforce = enforceOverlayable(overlayPackage); @@ -113,22 +113,17 @@ final class IdmapManager { return mIdmapDaemon.idmapExists(oi.baseCodePath, oi.userId); } - boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) { - return mIdmapDaemon.idmapExists(overlayPackage.applicationInfo.getBaseCodePath(), userId); - } - /** * Checks if overlayable and policies should be enforced on the specified overlay for backwards * compatibility with pre-Q overlays. */ - private boolean enforceOverlayable(@NonNull final PackageInfo overlayPackage) { - final ApplicationInfo ai = overlayPackage.applicationInfo; - if (ai.targetSdkVersion >= VERSION_CODES.Q) { + private boolean enforceOverlayable(@NonNull final AndroidPackage overlayPackage) { + if (overlayPackage.getTargetSdkVersion() >= VERSION_CODES.Q) { // Always enforce policies for overlays targeting Q+. return true; } - if (ai.isVendor()) { + if (overlayPackage.isVendor()) { // If the overlay is on a pre-Q vendor partition, do not enforce overlayable // restrictions on this overlay because the pre-Q platform has no understanding of // overlayable. @@ -137,20 +132,19 @@ final class IdmapManager { // Do not enforce overlayable restrictions on pre-Q overlays that are signed with the // platform signature or that are preinstalled. - return !(ai.isSystemApp() || ai.isSignedWithPlatformKey()); + return !(overlayPackage.isSystem() || overlayPackage.isSignedWithPlatformKey()); } /** * Retrieves a bitmask for idmap2 that represents the policies the overlay fulfills. */ - private int calculateFulfilledPolicies(@NonNull final PackageInfo targetPackage, - @NonNull final PackageInfo overlayPackage, int userId) { - final ApplicationInfo ai = overlayPackage.applicationInfo; + private int calculateFulfilledPolicies(@NonNull final AndroidPackage targetPackage, + @NonNull final AndroidPackage overlayPackage, int userId) { int fulfilledPolicies = OverlayablePolicy.PUBLIC; // Overlay matches target signature - if (mPackageManager.signaturesMatching(targetPackage.packageName, - overlayPackage.packageName, userId)) { + if (mPackageManager.signaturesMatching(targetPackage.getPackageName(), + overlayPackage.getPackageName(), userId)) { fulfilledPolicies |= OverlayablePolicy.SIGNATURE; } @@ -164,52 +158,52 @@ final class IdmapManager { // preinstalled package, check if overlay matches its signature. if (!TextUtils.isEmpty(mConfigSignaturePackage) && mPackageManager.signaturesMatching(mConfigSignaturePackage, - overlayPackage.packageName, + overlayPackage.getPackageName(), userId)) { fulfilledPolicies |= OverlayablePolicy.CONFIG_SIGNATURE; } // Vendor partition (/vendor) - if (ai.isVendor()) { + if (overlayPackage.isVendor()) { return fulfilledPolicies | OverlayablePolicy.VENDOR_PARTITION; } // Product partition (/product) - if (ai.isProduct()) { + if (overlayPackage.isProduct()) { return fulfilledPolicies | OverlayablePolicy.PRODUCT_PARTITION; } // Odm partition (/odm) - if (ai.isOdm()) { + if (overlayPackage.isOdm()) { return fulfilledPolicies | OverlayablePolicy.ODM_PARTITION; } // Oem partition (/oem) - if (ai.isOem()) { + if (overlayPackage.isOem()) { return fulfilledPolicies | OverlayablePolicy.OEM_PARTITION; } // System_ext partition (/system_ext) is considered as system // Check this last since every partition except for data is scanned as system in the PMS. - if (ai.isSystemApp() || ai.isSystemExt()) { + if (overlayPackage.isSystem() || overlayPackage.isSystemExt()) { return fulfilledPolicies | OverlayablePolicy.SYSTEM_PARTITION; } return fulfilledPolicies; } - private boolean matchesActorSignature(@NonNull PackageInfo targetPackage, - @NonNull PackageInfo overlayPackage, int userId) { - String targetOverlayableName = overlayPackage.targetOverlayableName; + private boolean matchesActorSignature(@NonNull AndroidPackage targetPackage, + @NonNull AndroidPackage overlayPackage, int userId) { + String targetOverlayableName = overlayPackage.getOverlayTargetName(); if (targetOverlayableName != null) { try { OverlayableInfo overlayableInfo = mPackageManager.getOverlayableForTarget( - targetPackage.packageName, targetOverlayableName, userId); + targetPackage.getPackageName(), targetOverlayableName, userId); if (overlayableInfo != null && overlayableInfo.actor != null) { String actorPackageName = OverlayActorEnforcer.getPackageNameForActor( overlayableInfo.actor, mPackageManager.getNamedActors()).first; if (mPackageManager.signaturesMatching(actorPackageName, - overlayPackage.packageName, userId)) { + overlayPackage.getPackageName(), userId)) { return true; } } diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java index 8121a43e90601..2d540de69660a 100644 --- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java +++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java @@ -19,8 +19,6 @@ package com.android.server.om; import android.annotation.NonNull; import android.content.om.OverlayInfo; import android.content.om.OverlayableInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.net.Uri; import android.os.Process; import android.text.TextUtils; @@ -29,6 +27,7 @@ import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; +import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.IOException; import java.util.List; @@ -114,12 +113,13 @@ public class OverlayActorEnforcer { } final String targetPackageName = overlayInfo.targetPackageName; - final PackageInfo targetPkgInfo = mPackageManager.getPackageInfo(targetPackageName, userId); + final AndroidPackage targetPkgInfo = mPackageManager.getPackageForUser(targetPackageName, + userId); if (targetPkgInfo == null) { return ActorState.TARGET_NOT_FOUND; } - if ((targetPkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + if (targetPkgInfo.isDebuggable()) { return ActorState.ALLOWED; } @@ -189,23 +189,18 @@ public class OverlayActorEnforcer { return actorUriState; } - String packageName = actorUriPair.first; - PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, userId); - if (packageInfo == null) { - return ActorState.MISSING_APP_INFO; - } - - ApplicationInfo appInfo = packageInfo.applicationInfo; - if (appInfo == null) { - return ActorState.MISSING_APP_INFO; + String actorPackageName = actorUriPair.first; + AndroidPackage actorPackage = mPackageManager.getPackageForUser(actorPackageName, userId); + if (actorPackage == null) { + return ActorState.ACTOR_NOT_FOUND; } // Currently only pre-installed apps can be actors - if (!appInfo.isSystemApp()) { + if (!actorPackage.isSystem()) { return ActorState.ACTOR_NOT_PREINSTALLED; } - if (ArrayUtils.contains(callingPackageNames, packageName)) { + if (ArrayUtils.contains(callingPackageNames, actorPackageName)) { return ActorState.ALLOWED; } @@ -231,7 +226,7 @@ public class OverlayActorEnforcer { NO_NAMED_ACTORS, MISSING_NAMESPACE, MISSING_ACTOR_NAME, - MISSING_APP_INFO, + ACTOR_NOT_FOUND, ACTOR_NOT_PREINSTALLED, INVALID_ACTOR, ALLOWED diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index fd2fb1fcab39d..b0e2922b5e366 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -43,11 +43,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.om.IOverlayManager; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.content.om.OverlayManagerTransaction; import android.content.om.OverlayableInfo; import android.content.pm.IPackageManager; -import android.content.pm.PackageInfo; import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; import android.content.pm.overlay.OverlayPaths; @@ -71,12 +71,14 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.content.om.OverlayConfig; +import com.android.internal.util.CollectionUtils; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; + import com.android.server.pm.UserManagerService; +import com.android.server.pm.parsing.pkg.AndroidPackage; import libcore.util.EmptyArray; @@ -92,13 +94,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.function.Consumer; /** * Service to manage asset overlays. @@ -247,15 +247,6 @@ public final class OverlayManagerService extends SystemService { private final OverlayActorEnforcer mActorEnforcer; - private final Consumer mPropagateOverlayChange = (pair) -> { - persistSettings(); - FgThread.getHandler().post(() -> { - List affectedTargets = updatePackageManager(pair.packageName, pair.userId); - updateActivityManager(affectedTargets, pair.userId); - broadcastActionOverlayChanged(pair.packageName, pair.userId); - }); - }; - public OverlayManagerService(@NonNull final Context context) { super(context); try { @@ -315,8 +306,7 @@ public final class OverlayManagerService extends SystemService { // Initialize any users that can't be switched to, as their state would // never be setup in onSwitchUser(). We will switch to the system user right // after this, and its state will be setup there. - final List targets = mImpl.updateOverlaysForUser(users.get(i).id); - updatePackageManager(targets, users.get(i).id); + updatePackageManager(mImpl.updateOverlaysForUser(users.get(i).id)); } } } @@ -333,9 +323,7 @@ public final class OverlayManagerService extends SystemService { // ensure overlays in the settings are up-to-date, and propagate // any asset changes to the rest of the system synchronized (mLock) { - final List targets = mImpl.updateOverlaysForUser(newUserId); - final List affectedTargets = updatePackageManager(targets, newUserId); - updateActivityManager(affectedTargets, newUserId); + updateActivityManager(updatePackageManager(mImpl.updateOverlaysForUser(newUserId))); } persistSettings(); } finally { @@ -417,19 +405,11 @@ public final class OverlayManagerService extends SystemService { traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName); for (final int userId : userIds) { synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, - false); - if (pi != null && !pi.applicationInfo.isInstantApp()) { - mPackageManager.cachePackageInfo(packageName, userId, pi); - + final AndroidPackage pkg = mPackageManager.onPackageAdded( + packageName, userId); + if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) { try { - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageAdded(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } else { - mImpl.onTargetPackageAdded(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } + updateTargetPackages(mImpl.onPackageAdded(packageName, userId)); } catch (OperationFailedException e) { Slog.e(TAG, "onPackageAdded internal error", e); } @@ -447,19 +427,11 @@ public final class OverlayManagerService extends SystemService { traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName); for (int userId : userIds) { synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, - false); - if (pi != null && pi.applicationInfo.isInstantApp()) { - mPackageManager.cachePackageInfo(packageName, userId, pi); - + final AndroidPackage pkg = mPackageManager.onPackageUpdated( + packageName, userId); + if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) { try { - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageChanged(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } else { - mImpl.onTargetPackageChanged(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } + updateTargetPackages(mImpl.onPackageChanged(packageName, userId)); } catch (OperationFailedException e) { Slog.e(TAG, "onPackageChanged internal error", e); } @@ -477,12 +449,11 @@ public final class OverlayManagerService extends SystemService { traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName); for (int userId : userIds) { synchronized (mLock) { - mPackageManager.forgetPackageInfo(packageName, userId); - final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); - if (oi != null) { + final AndroidPackage pkg = mPackageManager.onPackageUpdated( + packageName, userId); + if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) { try { - mImpl.onOverlayPackageReplacing(packageName, userId) - .ifPresent(mPropagateOverlayChange); + updateTargetPackages(mImpl.onPackageReplacing(packageName, userId)); } catch (OperationFailedException e) { Slog.e(TAG, "onPackageReplacing internal error", e); } @@ -500,18 +471,11 @@ public final class OverlayManagerService extends SystemService { traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName); for (int userId : userIds) { synchronized (mLock) { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, - false); - if (pi != null && !pi.applicationInfo.isInstantApp()) { - mPackageManager.cachePackageInfo(packageName, userId, pi); + final AndroidPackage pkg = mPackageManager.onPackageUpdated( + packageName, userId); + if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) { try { - if (pi.isOverlayPackage()) { - mImpl.onOverlayPackageReplaced(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } else { - mImpl.onTargetPackageReplaced(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } + updateTargetPackages(mImpl.onPackageReplaced(packageName, userId)); } catch (OperationFailedException e) { Slog.e(TAG, "onPackageReplaced internal error", e); } @@ -529,20 +493,8 @@ public final class OverlayManagerService extends SystemService { traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName); for (int userId : userIds) { synchronized (mLock) { - mPackageManager.forgetPackageInfo(packageName, userId); - final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId); - - try { - if (oi != null) { - mImpl.onOverlayPackageRemoved(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } else { - mImpl.onTargetPackageRemoved(packageName, userId) - .ifPresent(mPropagateOverlayChange); - } - } catch (OperationFailedException e) { - Slog.e(TAG, "onPackageRemoved internal error", e); - } + mPackageManager.onPackageRemoved(packageName, userId); + updateTargetPackages(mImpl.onPackageRemoved(packageName, userId)); } } } finally { @@ -560,11 +512,9 @@ public final class OverlayManagerService extends SystemService { if (userId != UserHandle.USER_NULL) { try { traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_ADDED"); - final ArrayList targets; synchronized (mLock) { - targets = mImpl.updateOverlaysForUser(userId); + updatePackageManager(mImpl.updateOverlaysForUser(userId)); } - updatePackageManager(targets, userId); } finally { traceEnd(TRACE_TAG_RRO); } @@ -628,16 +578,22 @@ public final class OverlayManagerService extends SystemService { @Override public OverlayInfo getOverlayInfo(@Nullable final String packageName, final int userIdArg) { - if (packageName == null) { + return getOverlayInfoByIdentifier(new OverlayIdentifier(packageName), userIdArg); + } + + @Override + public OverlayInfo getOverlayInfoByIdentifier(@Nullable final OverlayIdentifier overlay, + final int userIdArg) { + if (overlay == null || overlay.getPackageName() == null) { return null; } try { - traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + packageName); + traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + overlay); final int realUserId = handleIncomingUser(userIdArg, "getOverlayInfo"); synchronized (mLock) { - return mImpl.getOverlayInfo(packageName, realUserId); + return mImpl.getOverlayInfo(overlay, realUserId); } } finally { traceEnd(TRACE_TAG_RRO); @@ -653,15 +609,17 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setEnabled " + packageName + " " + enable); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "setEnabled"); - enforceActor(packageName, "setEnabled", realUserId); + enforceActor(overlay, "setEnabled", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setEnabled(packageName, enable, realUserId) - .ifPresent(mPropagateOverlayChange); + mImpl.setEnabled(overlay, enable, realUserId) + .ifPresent(OverlayManagerService.this::updateTargetPackages); return true; } catch (OperationFailedException e) { return false; @@ -684,16 +642,18 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusive " + packageName + " " + enable); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "setEnabledExclusive"); - enforceActor(packageName, "setEnabledExclusive", realUserId); + enforceActor(overlay, "setEnabledExclusive", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setEnabledExclusive(packageName, + mImpl.setEnabledExclusive(overlay, false /* withinCategory */, realUserId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(OverlayManagerService.this::updateTargetPackages); return true; } catch (OperationFailedException e) { return false; @@ -716,17 +676,19 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusiveInCategory " + packageName); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "setEnabledExclusiveInCategory"); - enforceActor(packageName, "setEnabledExclusiveInCategory", realUserId); + enforceActor(overlay, "setEnabledExclusiveInCategory", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setEnabledExclusive(packageName, + mImpl.setEnabledExclusive(overlay, true /* withinCategory */, realUserId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(OverlayManagerService.this::updateTargetPackages); return true; } catch (OperationFailedException e) { return false; @@ -750,15 +712,18 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setPriority " + packageName + " " + parentPackageName); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); + final OverlayIdentifier parentOverlay = new OverlayIdentifier(parentPackageName); final int realUserId = handleIncomingUser(userIdArg, "setPriority"); - enforceActor(packageName, "setPriority", realUserId); + enforceActor(overlay, "setPriority", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setPriority(packageName, parentPackageName, realUserId) - .ifPresent(mPropagateOverlayChange); + mImpl.setPriority(overlay, parentOverlay, realUserId) + .ifPresent(OverlayManagerService.this::updateTargetPackages); return true; } catch (OperationFailedException e) { return false; @@ -780,15 +745,17 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setHighestPriority " + packageName); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "setHighestPriority"); - enforceActor(packageName, "setHighestPriority", realUserId); + enforceActor(overlay, "setHighestPriority", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setHighestPriority(packageName, realUserId) - .ifPresent(mPropagateOverlayChange); + mImpl.setHighestPriority(overlay, realUserId) + .ifPresent(OverlayManagerService.this::updateTargetPackages); return true; } catch (OperationFailedException e) { return false; @@ -810,15 +777,17 @@ public final class OverlayManagerService extends SystemService { try { traceBegin(TRACE_TAG_RRO, "OMS#setLowestPriority " + packageName); + + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "setLowestPriority"); - enforceActor(packageName, "setLowestPriority", realUserId); + enforceActor(overlay, "setLowestPriority", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { try { - mImpl.setLowestPriority(packageName, realUserId) - .ifPresent(mPropagateOverlayChange); + mImpl.setLowestPriority(overlay, realUserId) + .ifPresent(OverlayManagerService.this::updateTargetPackages); return true; } catch (OperationFailedException e) { return false; @@ -858,12 +827,17 @@ public final class OverlayManagerService extends SystemService { return; } + final OverlayIdentifier overlay = new OverlayIdentifier(packageName); final int realUserId = handleIncomingUser(userIdArg, "invalidateCachesForOverlay"); - enforceActor(packageName, "invalidateCachesForOverlay", realUserId); + enforceActor(overlay, "invalidateCachesForOverlay", realUserId); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - mImpl.removeIdmapForOverlay(packageName, realUserId); + try { + mImpl.removeIdmapForOverlay(overlay, realUserId); + } catch (OperationFailedException e) { + Slog.w(TAG, "invalidate caches for overlay '" + overlay + "' failed", e); + } } } finally { Binder.restoreCallingIdentity(ident); @@ -896,22 +870,22 @@ public final class OverlayManagerService extends SystemService { private Optional executeRequest( @NonNull final OverlayManagerTransaction.Request request) throws Exception { final int realUserId = handleIncomingUser(request.userId, request.typeToString()); - enforceActor(request.packageName, request.typeToString(), realUserId); + enforceActor(request.overlay, request.typeToString(), realUserId); final long ident = Binder.clearCallingIdentity(); try { switch (request.type) { case TYPE_SET_ENABLED: Optional opt1 = - mImpl.setEnabled(request.packageName, true, request.userId); + mImpl.setEnabled(request.overlay, true, realUserId); Optional opt2 = - mImpl.setHighestPriority(request.packageName, request.userId); + mImpl.setHighestPriority(request.overlay, realUserId); // Both setEnabled and setHighestPriority affected the same // target package and user: if both return non-empty // Optionals, they are identical return opt1.isPresent() ? opt1 : opt2; case TYPE_SET_DISABLED: - return mImpl.setEnabled(request.packageName, false, request.userId); + return mImpl.setEnabled(request.overlay, false, realUserId); default: throw new IllegalArgumentException("unsupported request: " + request); } @@ -945,7 +919,7 @@ public final class OverlayManagerService extends SystemService { executeRequest(request).ifPresent(target -> { Set userTargets = transactionTargets.get(target.userId); if (userTargets == null) { - userTargets = new ArraySet(); + userTargets = new ArraySet<>(); transactionTargets.put(target.userId, userTargets); } userTargets.add(target.packageName); @@ -975,10 +949,7 @@ public final class OverlayManagerService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { // schedule apps to refresh - for (int index = 0; index < affectedPackagesToUpdate.size(); index++) { - final int userId = affectedPackagesToUpdate.keyAt(index); - updateActivityManager(affectedPackagesToUpdate.valueAt(index), userId); - } + updateActivityManager(affectedPackagesToUpdate); // broadcast the ACTION_OVERLAY_CHANGED intents for (int index = 0; index < transactionTargets.size(); index++) { @@ -1059,12 +1030,12 @@ public final class OverlayManagerService extends SystemService { dumpState.setField(arg); break; default: - dumpState.setPackageName(arg); + dumpState.setOverlyIdentifier(arg); break; } } if (dumpState.getPackageName() == null && opti < args.length) { - dumpState.setPackageName(args[opti]); + dumpState.setOverlyIdentifier(args[opti]); opti++; } @@ -1101,12 +1072,12 @@ public final class OverlayManagerService extends SystemService { getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, message); } - private void enforceActor(String packageName, String methodName, int realUserId) - throws SecurityException { - OverlayInfo overlayInfo = mImpl.getOverlayInfo(packageName, realUserId); + private void enforceActor(@NonNull OverlayIdentifier overlay, @NonNull String methodName, + int realUserId) throws SecurityException { + OverlayInfo overlayInfo = mImpl.getOverlayInfo(overlay, realUserId); if (overlayInfo == null) { throw new IllegalArgumentException("Unable to retrieve overlay information for " - + packageName); + + overlay); } int callingUid = Binder.getCallingUid(); @@ -1115,7 +1086,13 @@ public final class OverlayManagerService extends SystemService { }; private static final class PackageManagerHelperImpl implements PackageManagerHelper { - + private static class AndroidPackageUsers { + private AndroidPackage mPackage; + private final Set mInstalledUsers = new ArraySet<>(); + private AndroidPackageUsers(@NonNull AndroidPackage pkg) { + this.mPackage = pkg; + } + } private final Context mContext; private final IPackageManager mPackageManager; private final PackageManagerInternal mPackageManagerInternal; @@ -1125,7 +1102,8 @@ public final class OverlayManagerService extends SystemService { // intent, querying the PackageManagerService for the actual current // state may lead to contradictions within OMS. Better then to lag // behind until all pending intents have been processed. - private final SparseArray> mCache = new SparseArray<>(); + private final ArrayMap mCache = new ArrayMap<>(); + private final Set mInitializedUsers = new ArraySet<>(); PackageManagerHelperImpl(Context context) { mContext = context; @@ -1133,29 +1111,112 @@ public final class OverlayManagerService extends SystemService { mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); } - public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId, - final boolean useCache) { - if (useCache) { - final PackageInfo cachedPi = getCachedPackageInfo(packageName, userId); - if (cachedPi != null) { - return cachedPi; + /** + * Initializes the helper for the user. This only needs to be invoked one time before + * packages of this user are queried. + * @param userId the user id to initialize + * @return a map of package name to all packages installed in the user + */ + @NonNull + public ArrayMap initializeForUser(final int userId) { + if (!mInitializedUsers.contains(userId)) { + mInitializedUsers.add(userId); + mPackageManagerInternal.forEachInstalledPackage( + (pkg) -> addPackageUser(pkg, userId), userId); + } + + final ArrayMap userPackages = new ArrayMap<>(); + for (int i = 0, n = mCache.size(); i < n; i++) { + final AndroidPackageUsers pkg = mCache.valueAt(i); + if (pkg.mInstalledUsers.contains(userId)) { + userPackages.put(mCache.keyAt(i), pkg.mPackage); } } - try { - final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0, userId); - if (useCache && pi != null) { - cachePackageInfo(packageName, userId, pi); - } - return pi; - } catch (RemoteException e) { - // Intentionally left empty. - } - return null; + return userPackages; } @Override - public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) { - return getPackageInfo(packageName, userId, true); + @Nullable + public AndroidPackage getPackageForUser(@NonNull final String packageName, + final int userId) { + final AndroidPackageUsers pkg = mCache.get(packageName); + if (pkg != null && pkg.mInstalledUsers.contains(userId)) { + return pkg.mPackage; + } + try { + if (!mPackageManager.isPackageAvailable(packageName, userId)) { + return null; + } + } catch (RemoteException e) { + Slog.w(TAG, "Failed to check availability of package '" + packageName + + "' for user " + userId, e); + return null; + } + return addPackageUser(packageName, userId); + } + + @NonNull + private AndroidPackage addPackageUser(@NonNull final String packageName, + final int user) { + final AndroidPackage pkg = mPackageManagerInternal.getPackage(packageName); + if (pkg == null) { + Slog.w(TAG, "Android package for '" + packageName + "' could not be found;" + + " continuing as if package was never added", new Throwable()); + return null; + } + return addPackageUser(pkg, user); + } + + @NonNull + private AndroidPackage addPackageUser(@NonNull final AndroidPackage pkg, + final int user) { + AndroidPackageUsers pkgUsers = mCache.get(pkg.getPackageName()); + if (pkgUsers == null) { + pkgUsers = new AndroidPackageUsers(pkg); + mCache.put(pkg.getPackageName(), pkgUsers); + } else { + pkgUsers.mPackage = pkg; + } + pkgUsers.mInstalledUsers.add(user); + return pkgUsers.mPackage; + } + + + @NonNull + private void removePackageUser(@NonNull final String packageName, final int user) { + final AndroidPackageUsers pkgUsers = mCache.get(packageName); + if (pkgUsers == null) { + return; + } + removePackageUser(pkgUsers, user); + } + + @NonNull + private void removePackageUser(@NonNull final AndroidPackageUsers pkg, final int user) { + pkg.mInstalledUsers.remove(user); + if (pkg.mInstalledUsers.isEmpty()) { + mCache.remove(pkg.mPackage.getPackageName()); + } + } + + @Nullable + public AndroidPackage onPackageAdded(@NonNull final String packageName, final int userId) { + return addPackageUser(packageName, userId); + } + + @Nullable + public AndroidPackage onPackageUpdated(@NonNull final String packageName, + final int userId) { + return addPackageUser(packageName, userId); + } + + public void onPackageRemoved(@NonNull final String packageName, final int userId) { + removePackageUser(packageName, userId); + } + + @Override + public boolean isInstantApp(@NonNull final String packageName, final int userId) { + return mPackageManagerInternal.isInstantApp(packageName, userId); } @NonNull @@ -1178,15 +1239,6 @@ public final class OverlayManagerService extends SystemService { return false; } - @Override - public List getOverlayPackages(final int userId) { - final List overlays = mPackageManagerInternal.getOverlayPackages(userId); - for (final PackageInfo info : overlays) { - cachePackageInfo(info.packageName, userId, info); - } - return overlays; - } - @Override public String getConfigSignaturePackage() { final String[] pkgs = mPackageManagerInternal.getKnownPackageNames( @@ -1200,16 +1252,14 @@ public final class OverlayManagerService extends SystemService { public OverlayableInfo getOverlayableForTarget(@NonNull String packageName, @NonNull String targetOverlayableName, int userId) throws IOException { - PackageInfo packageInfo = getPackageInfo(packageName, userId); + final AndroidPackage packageInfo = getPackageForUser(packageName, userId); if (packageInfo == null) { throw new IOException("Unable to get target package"); } - String baseCodePath = packageInfo.applicationInfo.getBaseCodePath(); - ApkAssets apkAssets = null; try { - apkAssets = ApkAssets.loadFromPath(baseCodePath); + apkAssets = ApkAssets.loadFromPath(packageInfo.getBaseApkPath()); return apkAssets.getOverlayableInfo(targetOverlayableName); } finally { if (apkAssets != null) { @@ -1224,16 +1274,14 @@ public final class OverlayManagerService extends SystemService { @Override public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) throws IOException { - PackageInfo packageInfo = getPackageInfo(targetPackageName, userId); + AndroidPackage packageInfo = getPackageForUser(targetPackageName, userId); if (packageInfo == null) { throw new IOException("Unable to get target package"); } - String baseCodePath = packageInfo.applicationInfo.getBaseCodePath(); - ApkAssets apkAssets = null; try { - apkAssets = ApkAssets.loadFromPath(baseCodePath); + apkAssets = ApkAssets.loadFromPath(packageInfo.getBaseApkPath()); return apkAssets.definesOverlayable(); } finally { if (apkAssets != null) { @@ -1250,35 +1298,10 @@ public final class OverlayManagerService extends SystemService { mContext.enforceCallingOrSelfPermission(permission, message); } - public PackageInfo getCachedPackageInfo(@NonNull final String packageName, - final int userId) { - final HashMap map = mCache.get(userId); - return map == null ? null : map.get(packageName); - } - - public void cachePackageInfo(@NonNull final String packageName, final int userId, - @NonNull final PackageInfo pi) { - HashMap map = mCache.get(userId); - if (map == null) { - map = new HashMap<>(); - mCache.put(userId, map); - } - map.put(packageName, pi); - } - - public void forgetPackageInfo(@NonNull final String packageName, final int userId) { - final HashMap map = mCache.get(userId); - if (map == null) { - return; - } - map.remove(packageName); - if (map.isEmpty()) { - mCache.delete(userId); - } - } - public void forgetAllPackageInfos(final int userId) { - mCache.delete(userId); + for (int i = 0, n = mCache.size(); i < n; i++) { + removePackageUser(mCache.valueAt(i), userId); + } } @Nullable @@ -1292,19 +1315,12 @@ public final class OverlayManagerService extends SystemService { } private static final String TAB1 = " "; - private static final String TAB2 = TAB1 + TAB1; public void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) { - pw.println("PackageInfo cache"); + pw.println("AndroidPackage cache"); if (!dumpState.isVerbose()) { - int count = 0; - final int n = mCache.size(); - for (int i = 0; i < n; i++) { - final int userId = mCache.keyAt(i); - count += mCache.get(userId).size(); - } - pw.println(TAB1 + count + " package(s)"); + pw.println(TAB1 + mCache.size() + " package(s)"); return; } @@ -1313,25 +1329,70 @@ public final class OverlayManagerService extends SystemService { return; } - final int n = mCache.size(); - for (int i = 0; i < n; i++) { - final int userId = mCache.keyAt(i); - pw.println(TAB1 + "User " + userId); - final HashMap map = mCache.get(userId); - for (Map.Entry entry : map.entrySet()) { - pw.println(TAB2 + entry.getKey() + ": " + entry.getValue()); - } + for (int i = 0, n = mCache.size(); i < n; i++) { + final String packageName = mCache.keyAt(i); + final AndroidPackageUsers pkg = mCache.valueAt(i); + pw.print(TAB1 + packageName + ": " + pkg.mPackage + " users="); + pw.println(TextUtils.join(", ", pkg.mInstalledUsers)); } } } + private void updateTargetPackages(@Nullable PackageAndUser updatedTarget) { + if (updatedTarget != null) { + updateTargetPackages(Set.of(updatedTarget)); + } + } + + private void updateTargetPackages(@Nullable Set updatedTargets) { + if (CollectionUtils.isEmpty(updatedTargets)) { + return; + } + persistSettings(); + final SparseArray> userTargets = groupTargetsByUserId(updatedTargets); + FgThread.getHandler().post(() -> { + for (int i = 0, n = userTargets.size(); i < n; i++) { + final ArraySet targets = userTargets.valueAt(i); + final int userId = userTargets.keyAt(i); + + // Update the overlay paths in package manager. + final List affectedPackages = updatePackageManager(targets, userId); + updateActivityManager(affectedPackages, userId); + + // Overlays targeting shared libraries may cause more packages to need to be + // refreshed. + broadcastActionOverlayChanged(targets, userId); + } + }); + } + + @Nullable + private static SparseArray> groupTargetsByUserId( + @Nullable final Set targetsAndUsers) { + final SparseArray> userTargets = new SparseArray<>(); + CollectionUtils.forEach(targetsAndUsers, target -> { + ArraySet targets = userTargets.get(target.userId); + if (targets == null) { + targets = new ArraySet<>(); + userTargets.put(target.userId, targets); + } + targets.add(target.packageName); + }); + return userTargets; + } + // Helper methods to update other parts of the system or read/write // settings: these methods should never call into each other! - private void broadcastActionOverlayChanged(@NonNull final String targetPackageName, + private static void broadcastActionOverlayChanged(@NonNull final Set targetPackages, final int userId) { + CollectionUtils.forEach(targetPackages, + target -> broadcastActionOverlayChanged(target, userId)); + } + + private static void broadcastActionOverlayChanged(String targetPackage, final int userId) { final Intent intent = new Intent(ACTION_OVERLAY_CHANGED, - Uri.fromParts("package", targetPackageName, null)); + Uri.fromParts("package", targetPackage, null)); intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); try { ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null, @@ -1345,7 +1406,7 @@ public final class OverlayManagerService extends SystemService { * Tell the activity manager to tell a set of packages to reload their * resources. */ - private void updateActivityManager(List targetPackageNames, final int userId) { + private void updateActivityManager(@NonNull List targetPackageNames, final int userId) { final IActivityManager am = ActivityManager.getService(); try { am.scheduleApplicationInfoChanged(targetPackageNames, userId); @@ -1354,16 +1415,33 @@ public final class OverlayManagerService extends SystemService { } } - private ArrayList updatePackageManager(String targetPackageNames, final int userId) { - return updatePackageManager(Collections.singletonList(targetPackageNames), userId); + private void updateActivityManager(@NonNull SparseArray> targetPackageNames) { + for (int i = 0, n = targetPackageNames.size(); i < n; i++) { + updateActivityManager(targetPackageNames.valueAt(i), targetPackageNames.keyAt(i)); + } + } + + @NonNull + private SparseArray> updatePackageManager(@Nullable Set targets) { + if (CollectionUtils.isEmpty(targets)) { + return new SparseArray<>(); + } + final SparseArray> affectedTargets = new SparseArray<>(); + final SparseArray> userTargets = groupTargetsByUserId(targets); + for (int i = 0, n = userTargets.size(); i < n; i++) { + final int userId = userTargets.keyAt(i); + affectedTargets.put(userId, updatePackageManager(userTargets.valueAt(i), userId)); + } + return affectedTargets; } /** * Updates the target packages' set of enabled overlays in PackageManager. * @return the package names of affected targets (a superset of - * targetPackageNames: the target themserlves and shared libraries) + * targetPackageNames: the target themselves and shared libraries) */ - private ArrayList updatePackageManager(@NonNull Collection targetPackageNames, + @NonNull + private List updatePackageManager(@NonNull Collection targetPackageNames, final int userId) { try { traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames); diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index c547c36a8033d..6eb4edcbf1b0a 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -28,9 +28,9 @@ import static com.android.server.om.OverlayManagerService.TAG; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.om.CriticalOverlayInfo; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.overlay.OverlayPaths; import android.text.TextUtils; import android.util.ArrayMap; @@ -38,16 +38,17 @@ import android.util.ArraySet; import android.util.Slog; import com.android.internal.content.om.OverlayConfig; -import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; +import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Iterator; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; /** * Internal implementation of OverlayManagerService. @@ -85,29 +86,25 @@ final class OverlayManagerServiceImpl { * should either scrap the overlay manager's previous settings or merge the old * settings with the new. */ - private boolean mustReinitializeOverlay(@NonNull final PackageInfo theTruth, + private boolean mustReinitializeOverlay(@NonNull final AndroidPackage theTruth, @Nullable final OverlayInfo oldSettings) { if (oldSettings == null) { return true; } - if (!Objects.equals(theTruth.overlayTarget, oldSettings.targetPackageName)) { + if (!Objects.equals(theTruth.getOverlayTarget(), oldSettings.targetPackageName)) { return true; } - if (!Objects.equals(theTruth.targetOverlayableName, oldSettings.targetOverlayableName)) { + if (!Objects.equals(theTruth.getOverlayTargetName(), oldSettings.targetOverlayableName)) { return true; } - - boolean isMutable = isPackageConfiguredMutable(theTruth.packageName); + boolean isMutable = isPackageConfiguredMutable(theTruth); if (isMutable != oldSettings.isMutable) { return true; } - // If an immutable overlay changes its configured enabled state, reinitialize the overlay. - if (!isMutable && isPackageConfiguredEnabled(theTruth.packageName) - != oldSettings.isEnabled()) { + if (!isMutable && isPackageConfiguredEnabled(theTruth) != oldSettings.isEnabled()) { return true; } - return false; } @@ -129,87 +126,33 @@ final class OverlayManagerServiceImpl { * of two sets: the set of targets with currently active overlays, and the * set of targets that had, but no longer have, active overlays. */ - ArrayList updateOverlaysForUser(final int newUserId) { + @NonNull + ArraySet updateOverlaysForUser(final int newUserId) { if (DEBUG) { Slog.d(TAG, "updateOverlaysForUser newUserId=" + newUserId); } - final Set packagesToUpdateAssets = new ArraySet<>(); - final ArrayMap> tmp = mSettings.getOverlaysForUser(newUserId); - final int tmpSize = tmp.size(); - final ArrayMap storedOverlayInfos = new ArrayMap<>(tmpSize); - for (int i = 0; i < tmpSize; i++) { - final List chunk = tmp.valueAt(i); - final int chunkSize = chunk.size(); - for (int j = 0; j < chunkSize; j++) { - final OverlayInfo oi = chunk.get(j); - storedOverlayInfos.put(oi.packageName, oi); - } - } + // Remove the settings of all overlays that are no longer installed for this user. + final ArraySet updatedTargets = new ArraySet<>(); + final ArrayMap userPackages = mPackageManager.initializeForUser( + newUserId); + CollectionUtils.addAll(updatedTargets, removeOverlaysForUser( + (info) -> !userPackages.containsKey(info.packageName), newUserId)); - // Reset overlays if something critical like the target package name - // has changed - List overlayPackages = mPackageManager.getOverlayPackages(newUserId); - final int overlayPackagesSize = overlayPackages.size(); - for (int i = 0; i < overlayPackagesSize; i++) { - final PackageInfo overlayPackage = overlayPackages.get(i); - final OverlayInfo oi = storedOverlayInfos.get(overlayPackage.packageName); - - int priority = getPackageConfiguredPriority(overlayPackage.packageName); - if (mustReinitializeOverlay(overlayPackage, oi)) { - // if targetPackageName has changed the package that *used* to - // be the target must also update its assets - if (oi != null) { - packagesToUpdateAssets.add(oi.targetPackageName); - } - - mSettings.init(overlayPackage.packageName, newUserId, - overlayPackage.overlayTarget, - overlayPackage.targetOverlayableName, - overlayPackage.applicationInfo.getBaseCodePath(), - isPackageConfiguredMutable(overlayPackage.packageName), - isPackageConfiguredEnabled(overlayPackage.packageName), - priority, overlayPackage.overlayCategory); - } else if (priority != oi.priority) { - mSettings.setPriority(overlayPackage.packageName, newUserId, priority); - packagesToUpdateAssets.add(oi.targetPackageName); - } - - storedOverlayInfos.remove(overlayPackage.packageName); - } - - // any OverlayInfo left in storedOverlayInfos is no longer - // installed and should be removed - final int storedOverlayInfosSize = storedOverlayInfos.size(); - for (int i = 0; i < storedOverlayInfosSize; i++) { - final OverlayInfo oi = storedOverlayInfos.valueAt(i); - mSettings.remove(oi.packageName, oi.userId); - removeIdmapIfPossible(oi); - packagesToUpdateAssets.add(oi.targetPackageName); - } - - // make sure every overlay's state is up-to-date; this needs to happen - // after old overlays have been removed, or we risk removing a - // legitimate idmap file if a new overlay package has the same apk path - // as the removed overlay package used to have - for (int i = 0; i < overlayPackagesSize; i++) { - final PackageInfo overlayPackage = overlayPackages.get(i); + // Update the state of all installed packages containing overlays, and initialize new + // overlays that are not currently in the settings. + for (int i = 0, n = userPackages.size(); i < n; i++) { + final AndroidPackage pkg = userPackages.valueAt(i); try { - updateState(overlayPackage.overlayTarget, overlayPackage.packageName, - newUserId, 0); - } catch (OverlayManagerSettings.BadKeyException e) { - Slog.e(TAG, "failed to update settings", e); - mSettings.remove(overlayPackage.packageName, newUserId); - } - packagesToUpdateAssets.add(overlayPackage.overlayTarget); - } + CollectionUtils.addAll(updatedTargets, + updatePackageOverlays(pkg, newUserId, 0 /* flags */)); - // remove target packages that are not installed - final Iterator iter = packagesToUpdateAssets.iterator(); - while (iter.hasNext()) { - String targetPackageName = iter.next(); - if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) { - iter.remove(); + // When a new user is switched to for the first time, package manager must be + // informed of the overlay paths for all packages installed in the user. + updatedTargets.add(new PackageAndUser(pkg.getPackageName(), newUserId)); + } catch (OperationFailedException e) { + Slog.e(TAG, "failed to initialize overlays of '" + pkg.getPackageName() + + "' for user " + newUserId + "", e); } } @@ -232,14 +175,20 @@ final class OverlayManagerServiceImpl { // Enable the default overlay if its category does not have a single overlay enabled. for (final String defaultOverlay : mDefaultOverlays) { try { - final OverlayInfo oi = mSettings.getOverlayInfo(defaultOverlay, newUserId); + // OverlayConfig is the new preferred way to enable overlays by default. This legacy + // default enabled method was created before overlays could have a name specified. + // Only allow enabling overlays without a name using this mechanism. + final OverlayIdentifier overlay = new OverlayIdentifier(defaultOverlay); + + final OverlayInfo oi = mSettings.getOverlayInfo(overlay, newUserId); if (!enabledCategories.contains(oi.category)) { Slog.w(TAG, "Enabling default overlay '" + defaultOverlay + "' for target '" + oi.targetPackageName + "' in category '" + oi.category + "' for user " + newUserId); - mSettings.setEnabled(oi.packageName, newUserId, true); - if (updateState(oi.targetPackageName, oi.packageName, newUserId, 0)) { - packagesToUpdateAssets.add(oi.targetPackageName); + mSettings.setEnabled(overlay, newUserId, true); + if (updateState(oi, newUserId, 0)) { + CollectionUtils.add(updatedTargets, + new PackageAndUser(oi.targetPackageName, oi.userId)); } } } catch (OverlayManagerSettings.BadKeyException e) { @@ -248,7 +197,7 @@ final class OverlayManagerServiceImpl { } } - return new ArrayList<>(packagesToUpdateAssets); + return updatedTargets; } void onUserRemoved(final int userId) { @@ -258,236 +207,150 @@ final class OverlayManagerServiceImpl { mSettings.removeUser(userId); } - Optional onTargetPackageAdded(@NonNull final String packageName, + @NonNull + Set onPackageAdded(@NonNull final String pkgName, final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId); - } - - return updateAndRefreshOverlaysForTarget(packageName, userId, 0); + return reconcileSettingsForPackage(pkgName, userId, 0 /* flags */); } - Optional onTargetPackageChanged(@NonNull final String packageName, + @NonNull + Set onPackageChanged(@NonNull final String pkgName, final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId); - } - - return updateAndRefreshOverlaysForTarget(packageName, userId, 0); + return reconcileSettingsForPackage(pkgName, userId, 0 /* flags */); } - Optional onTargetPackageReplacing(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId=" - + userId); - } - - return updateAndRefreshOverlaysForTarget(packageName, userId, 0); - } - - Optional onTargetPackageReplaced(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId); - } - - return updateAndRefreshOverlaysForTarget(packageName, userId, 0); - } - - Optional onTargetPackageRemoved(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId); - } - - return updateAndRefreshOverlaysForTarget(packageName, userId, 0); - } - - /** - * Update the state of any overlays for this target. - */ - private Optional updateAndRefreshOverlaysForTarget( - @NonNull final String targetPackageName, final int userId, final int flags) + @NonNull + Set onPackageReplacing(@NonNull final String pkgName, final int userId) throws OperationFailedException { - final List targetOverlays = mSettings.getOverlaysForTarget(targetPackageName, - userId); + return reconcileSettingsForPackage(pkgName, userId, FLAG_OVERLAY_IS_BEING_REPLACED); + } - // Update the state for any overlay that targets this package. + @NonNull + Set onPackageReplaced(@NonNull final String pkgName, final int userId) + throws OperationFailedException { + return reconcileSettingsForPackage(pkgName, userId, 0 /* flags */); + } + + @NonNull + Set onPackageRemoved(@NonNull final String pkgName, final int userId) { + if (DEBUG) { + Slog.d(TAG, "onPackageRemoved pkgName=" + pkgName + " userId=" + userId); + } + // Update the state of all overlays that target this package. + final Set targets = updateOverlaysForTarget(pkgName, userId, 0 /* flags */); + + // Remove all the overlays this package declares. + return CollectionUtils.addAll(targets, + removeOverlaysForUser(oi -> pkgName.equals(oi.packageName), userId)); + } + + @NonNull + private Set removeOverlaysForUser( + @NonNull final Predicate condition, final int userId) { + final List overlays = mSettings.removeIf( + io -> userId == io.userId && condition.test(io) ); + Set targets = Collections.emptySet(); + for (int i = 0, n = overlays.size(); i < n; i++) { + final OverlayInfo info = overlays.get(i); + targets = CollectionUtils.add(targets, + new PackageAndUser(info.targetPackageName, userId)); + + // Remove the idmap if the overlay is no longer installed for any user. + removeIdmapIfPossible(info); + } + return targets; + } + + @NonNull + private Set updateOverlaysForTarget(@NonNull final String targetPackage, + final int userId, final int flags) { boolean modified = false; - for (final OverlayInfo oi : targetOverlays) { - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, - userId); - if (overlayPackage == null) { - modified |= mSettings.remove(oi.packageName, oi.userId); - removeIdmapIfPossible(oi); - } else { - try { - modified |= updateState(targetPackageName, oi.packageName, userId, flags); - } catch (OverlayManagerSettings.BadKeyException e) { - Slog.e(TAG, "failed to update settings", e); - modified |= mSettings.remove(oi.packageName, userId); - } + final List overlays = mSettings.getOverlaysForTarget(targetPackage, userId); + for (int i = 0, n = overlays.size(); i < n; i++) { + final OverlayInfo oi = overlays.get(i); + try { + modified |= updateState(oi, userId, flags); + } catch (OverlayManagerSettings.BadKeyException e) { + Slog.e(TAG, "failed to update settings", e); + modified |= mSettings.remove(oi.getOverlayIdentifier(), userId); } } - if (!modified) { - // Update the overlay paths of the target within package manager if necessary. - final List enabledOverlayPaths = new ArrayList<>(targetOverlays.size()); - - // Framework overlays are first in the overlay paths of a package within PackageManager. - for (final OverlayInfo oi : mSettings.getOverlaysForTarget("android", userId)) { - if (oi.isEnabled()) { - enabledOverlayPaths.add(oi.baseCodePath); - } - } - - for (final OverlayInfo oi : targetOverlays) { - if (oi.isEnabled()) { - enabledOverlayPaths.add(oi.baseCodePath); - } - } - - // TODO(): Use getEnabledOverlayPaths(userId, targetPackageName) instead of - // resourceDirs if in the future resourceDirs contains APKs other than overlays - PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, userId); - ApplicationInfo appInfo = packageInfo == null ? null : packageInfo.applicationInfo; - String[] resourceDirs = appInfo == null ? null : appInfo.resourceDirs; - - // If the lists aren't the same length, the enabled overlays have changed - if (ArrayUtils.size(resourceDirs) != enabledOverlayPaths.size()) { - modified = true; - } else if (resourceDirs != null) { - // If any element isn't equal, an overlay or the order of overlays has changed - for (int index = 0; index < resourceDirs.length; index++) { - if (!resourceDirs[index].equals(enabledOverlayPaths.get(index))) { - modified = true; - break; - } - } - } + return Collections.emptySet(); } - - if (modified) { - return Optional.of(new PackageAndUser(targetPackageName, userId)); - } - return Optional.empty(); + return Set.of(new PackageAndUser(targetPackage, userId)); } - Optional onOverlayPackageAdded(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId); + @NonNull + private Set updatePackageOverlays(@NonNull AndroidPackage pkg, + final int userId, final int flags) throws OperationFailedException { + if (pkg.getOverlayTarget() == null) { + // This package does not have overlays declared in its manifest. + return Collections.emptySet(); } - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found"); - return onOverlayPackageRemoved(packageName, userId); - } - - mSettings.init(packageName, userId, overlayPackage.overlayTarget, - overlayPackage.targetOverlayableName, - overlayPackage.applicationInfo.getBaseCodePath(), - isPackageConfiguredMutable(overlayPackage.packageName), - isPackageConfiguredEnabled(overlayPackage.packageName), - getPackageConfiguredPriority(overlayPackage.packageName), - overlayPackage.overlayCategory); + Set updatedTargets = Collections.emptySet(); + final OverlayIdentifier overlay = new OverlayIdentifier(pkg.getPackageName()); + final int priority = getPackageConfiguredPriority(pkg); try { - if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) { - return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); - } - return Optional.empty(); - } catch (OverlayManagerSettings.BadKeyException e) { - mSettings.remove(packageName, userId); - throw new OperationFailedException("failed to update settings", e); - } - } + OverlayInfo currentInfo = mSettings.getNullableOverlayInfo(overlay, userId); + if (mustReinitializeOverlay(pkg, currentInfo)) { + if (currentInfo != null) { + // If the targetPackageName has changed, the package that *used* to + // be the target must also update its assets. + updatedTargets = CollectionUtils.add(updatedTargets, + new PackageAndUser(currentInfo.targetPackageName, userId)); + } - Optional onOverlayPackageChanged(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId); - } - - try { - final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); - if (updateState(oi.targetPackageName, packageName, userId, 0)) { - return Optional.of(new PackageAndUser(oi.targetPackageName, userId)); + currentInfo = mSettings.init(overlay, userId, pkg.getOverlayTarget(), + pkg.getOverlayTargetName(), pkg.getBaseApkPath(), + isPackageConfiguredMutable(pkg), + isPackageConfiguredEnabled(pkg), + getPackageConfiguredPriority(pkg), pkg.getOverlayCategory()); + } else if (priority != currentInfo.priority) { + // Changing the priority of an overlay does not cause its settings to be + // reinitialized. Reorder the overlay and update its target package. + mSettings.setPriority(overlay, userId, priority); + updatedTargets = CollectionUtils.add(updatedTargets, + new PackageAndUser(currentInfo.targetPackageName, userId)); + } + + // Update the enabled state of the overlay. + if (updateState(currentInfo, userId, flags)) { + updatedTargets = CollectionUtils.add(updatedTargets, + new PackageAndUser(currentInfo.targetPackageName, userId)); } - return Optional.empty(); } catch (OverlayManagerSettings.BadKeyException e) { throw new OperationFailedException("failed to update settings", e); } + return updatedTargets; } - Optional onOverlayPackageReplacing(@NonNull final String packageName, - final int userId) throws OperationFailedException { + @NonNull + private Set reconcileSettingsForPackage(@NonNull final String pkgName, + final int userId, final int flags) throws OperationFailedException { if (DEBUG) { - Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId=" - + userId); + Slog.d(TAG, "reconcileSettingsForPackage pkgName=" + pkgName + " userId=" + userId); } - try { - final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); - if (updateState(oi.targetPackageName, packageName, userId, - FLAG_OVERLAY_IS_BEING_REPLACED)) { - removeIdmapIfPossible(oi); - return Optional.of(new PackageAndUser(oi.targetPackageName, userId)); - } - return Optional.empty(); - } catch (OverlayManagerSettings.BadKeyException e) { - throw new OperationFailedException("failed to update settings", e); - } - } + // Update the state of overlays that target this package. + Set updatedTargets = Collections.emptySet(); + updatedTargets = CollectionUtils.addAll(updatedTargets, + updateOverlaysForTarget(pkgName, userId, flags)); - Optional onOverlayPackageReplaced(@NonNull final String packageName, - final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId=" - + userId); - } - - final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId); + // Realign the overlay settings with PackageManager's view of the package. + final AndroidPackage pkg = mPackageManager.getPackageForUser(pkgName, userId); if (pkg == null) { - Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found"); - return onOverlayPackageRemoved(packageName, userId); + return onPackageRemoved(pkgName, userId); } - try { - final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId); - if (mustReinitializeOverlay(pkg, oldOi)) { - mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName, - pkg.applicationInfo.getBaseCodePath(), - isPackageConfiguredMutable(pkg.packageName), - isPackageConfiguredEnabled(pkg.packageName), - getPackageConfiguredPriority(pkg.packageName), pkg.overlayCategory); - } - - if (updateState(pkg.overlayTarget, packageName, userId, 0)) { - return Optional.of(new PackageAndUser(pkg.overlayTarget, userId)); - } - return Optional.empty(); - } catch (OverlayManagerSettings.BadKeyException e) { - throw new OperationFailedException("failed to update settings", e); - } + // Update the state of the overlays this package declares in its manifest. + updatedTargets = CollectionUtils.addAll(updatedTargets, + updatePackageOverlays(pkg, userId, flags)); + return updatedTargets; } - Optional onOverlayPackageRemoved(@NonNull final String packageName, - final int userId) throws OperationFailedException { - try { - final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId); - if (mSettings.remove(packageName, userId)) { - removeIdmapIfPossible(overlayInfo); - return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId)); - } - return Optional.empty(); - } catch (OverlayManagerSettings.BadKeyException e) { - throw new OperationFailedException("failed to remove overlay", e); - } - } - - OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) { + OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier packageName, final int userId) { try { return mSettings.getOverlayInfo(packageName, userId); } catch (OverlayManagerSettings.BadKeyException e) { @@ -504,30 +367,23 @@ final class OverlayManagerServiceImpl { return mSettings.getOverlaysForUser(userId); } - Optional setEnabled(@NonNull final String packageName, final boolean enable, - final int userId) throws OperationFailedException { + Optional setEnabled(@NonNull final OverlayIdentifier overlay, + final boolean enable, final int userId) throws OperationFailedException { if (DEBUG) { - Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d", - packageName, enable, userId)); - } - - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - throw new OperationFailedException( - String.format("failed to find overlay package %s for user %d", - packageName, userId)); + Slog.d(TAG, String.format("setEnabled overlay=%s enable=%s userId=%d", + overlay, enable, userId)); } try { - final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); + final OverlayInfo oi = mSettings.getOverlayInfo(overlay, userId); if (!oi.isMutable) { // Ignore immutable overlays. throw new OperationFailedException( "cannot enable immutable overlay packages in runtime"); } - boolean modified = mSettings.setEnabled(packageName, userId, enable); - modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0); + boolean modified = mSettings.setEnabled(overlay, userId, enable); + modified |= updateState(oi, userId, 0); if (modified) { return Optional.of(new PackageAndUser(oi.targetPackageName, userId)); @@ -538,60 +394,50 @@ final class OverlayManagerServiceImpl { } } - Optional setEnabledExclusive(@NonNull final String packageName, + Optional setEnabledExclusive(@NonNull final OverlayIdentifier overlay, boolean withinCategory, final int userId) throws OperationFailedException { if (DEBUG) { - Slog.d(TAG, String.format("setEnabledExclusive packageName=%s" - + " withinCategory=%s userId=%d", packageName, withinCategory, userId)); - } - - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - throw new OperationFailedException(String.format( - "failed to find overlay package %s for user %d", packageName, userId)); + Slog.d(TAG, String.format("setEnabledExclusive overlay=%s" + + " withinCategory=%s userId=%d", overlay, withinCategory, userId)); } try { - final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); - final String targetPackageName = oi.targetPackageName; + final OverlayInfo enabledInfo = mSettings.getOverlayInfo(overlay, userId); + if (!enabledInfo.isMutable) { + throw new OperationFailedException( + "cannot enable immutable overlay packages in runtime"); + } - List allOverlays = getOverlayInfosForTarget(targetPackageName, userId); + // Remove the overlay to have enabled from the list of overlays to disable. + List allOverlays = getOverlayInfosForTarget(enabledInfo.targetPackageName, + userId); + allOverlays.remove(enabledInfo); boolean modified = false; - - // Disable all other overlays. - allOverlays.remove(oi); for (int i = 0; i < allOverlays.size(); i++) { final OverlayInfo disabledInfo = allOverlays.get(i); - final String disabledOverlayPackageName = disabledInfo.packageName; - final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo( - disabledOverlayPackageName, userId); - if (disabledOverlayPackageInfo == null) { - modified |= mSettings.remove(disabledOverlayPackageName, userId); - continue; - } - + final OverlayIdentifier disabledOverlay = disabledInfo.getOverlayIdentifier(); if (!disabledInfo.isMutable) { // Don't touch immutable overlays. continue; } - if (withinCategory && !Objects.equals(disabledOverlayPackageInfo.overlayCategory, - oi.category)) { + if (withinCategory && !Objects.equals(disabledInfo.category, + enabledInfo.category)) { // Don't touch overlays from other categories. continue; } // Disable the overlay. - modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false); - modified |= updateState(targetPackageName, disabledOverlayPackageName, userId, 0); + modified |= mSettings.setEnabled(disabledOverlay, userId, false); + modified |= updateState(disabledInfo, userId, 0); } // Enable the selected overlay. - modified |= mSettings.setEnabled(packageName, userId, true); - modified |= updateState(targetPackageName, packageName, userId, 0); + modified |= mSettings.setEnabled(overlay, userId, true); + modified |= updateState(enabledInfo, userId, 0); if (modified) { - return Optional.of(new PackageAndUser(targetPackageName, userId)); + return Optional.of(new PackageAndUser(enabledInfo.targetPackageName, userId)); } return Optional.empty(); } catch (OverlayManagerSettings.BadKeyException e) { @@ -599,87 +445,90 @@ final class OverlayManagerServiceImpl { } } - private boolean isPackageConfiguredMutable(@NonNull final String packageName) { - return mOverlayConfig.isMutable(packageName); + private boolean isPackageConfiguredMutable(@NonNull final AndroidPackage overlay) { + // TODO(162841629): Support overlay name in OverlayConfig + return mOverlayConfig.isMutable(overlay.getPackageName()); } - private int getPackageConfiguredPriority(@NonNull final String packageName) { - return mOverlayConfig.getPriority(packageName); + private int getPackageConfiguredPriority(@NonNull final AndroidPackage overlay) { + // TODO(162841629): Support overlay name in OverlayConfig + return mOverlayConfig.getPriority(overlay.getPackageName()); } - private boolean isPackageConfiguredEnabled(@NonNull final String packageName) { - return mOverlayConfig.isEnabled(packageName); + private boolean isPackageConfiguredEnabled(@NonNull final AndroidPackage overlay) { + // TODO(162841629): Support overlay name in OverlayConfig + return mOverlayConfig.isEnabled(overlay.getPackageName()); } - Optional setPriority(@NonNull final String packageName, - @NonNull final String newParentPackageName, final int userId) + Optional setPriority(@NonNull final OverlayIdentifier overlay, + @NonNull final OverlayIdentifier newParentOverlay, final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName=" - + newParentPackageName + " userId=" + userId); - } + try { + if (DEBUG) { + Slog.d(TAG, "setPriority overlay=" + overlay + " newParentOverlay=" + + newParentOverlay + " userId=" + userId); + } - if (!isPackageConfiguredMutable(packageName)) { - throw new OperationFailedException(String.format( - "overlay package %s user %d is not updatable", packageName, userId)); - } + final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId); + if (!overlayInfo.isMutable) { + // Ignore immutable overlays. + throw new OperationFailedException( + "cannot change priority of an immutable overlay package at runtime"); + } - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - throw new OperationFailedException(String.format( - "failed to find overlay package %s for user %d", packageName, userId)); + if (mSettings.setPriority(overlay, newParentOverlay, userId)) { + return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId)); + } + return Optional.empty(); + } catch (OverlayManagerSettings.BadKeyException e) { + throw new OperationFailedException("failed to update settings", e); } - - if (mSettings.setPriority(packageName, newParentPackageName, userId)) { - return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); - } - return Optional.empty(); } - Optional setHighestPriority(@NonNull final String packageName, + Optional setHighestPriority(@NonNull final OverlayIdentifier overlay, final int userId) throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId); - } + try{ + if (DEBUG) { + Slog.d(TAG, "setHighestPriority overlay=" + overlay + " userId=" + userId); + } - if (!isPackageConfiguredMutable(packageName)) { - throw new OperationFailedException(String.format( - "overlay package %s user %d is not updatable", packageName, userId)); - } + final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId); + if (!overlayInfo.isMutable) { + // Ignore immutable overlays. + throw new OperationFailedException( + "cannot change priority of an immutable overlay package at runtime"); + } - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - throw new OperationFailedException(String.format( - "failed to find overlay package %s for user %d", packageName, userId)); + if (mSettings.setHighestPriority(overlay, userId)) { + return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId)); + } + return Optional.empty(); + } catch (OverlayManagerSettings.BadKeyException e) { + throw new OperationFailedException("failed to update settings", e); } - - if (mSettings.setHighestPriority(packageName, userId)) { - return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); - } - return Optional.empty(); } - Optional setLowestPriority(@NonNull final String packageName, final int userId) - throws OperationFailedException { - if (DEBUG) { - Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId); - } + Optional setLowestPriority(@NonNull final OverlayIdentifier overlay, + final int userId) throws OperationFailedException { + try{ + if (DEBUG) { + Slog.d(TAG, "setLowestPriority packageName=" + overlay + " userId=" + userId); + } - if (!isPackageConfiguredMutable(packageName)) { - throw new OperationFailedException(String.format( - "overlay package %s user %d is not updatable", packageName, userId)); - } + final OverlayInfo overlayInfo = mSettings.getOverlayInfo(overlay, userId); + if (!overlayInfo.isMutable) { + // Ignore immutable overlays. + throw new OperationFailedException( + "cannot change priority of an immutable overlay package at runtime"); + } - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null) { - throw new OperationFailedException(String.format( - "failed to find overlay package %s for user %d", packageName, userId)); + if (mSettings.setLowestPriority(overlay, userId)) { + return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId)); + } + return Optional.empty(); + } catch (OverlayManagerSettings.BadKeyException e) { + throw new OperationFailedException("failed to update settings", e); } - - if (mSettings.setLowestPriority(packageName, userId)) { - return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId)); - } - return Optional.empty(); } void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) { @@ -693,9 +542,14 @@ final class OverlayManagerServiceImpl { return mDefaultOverlays; } - void removeIdmapForOverlay(String packageName, int userId) { - final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); - removeIdmapIfPossible(oi); + void removeIdmapForOverlay(OverlayIdentifier overlay, int userId) + throws OperationFailedException { + try { + final OverlayInfo oi = mSettings.getOverlayInfo(overlay, userId); + removeIdmapIfPossible(oi); + } catch (OverlayManagerSettings.BadKeyException e) { + throw new OperationFailedException("failed to update settings", e); + } } OverlayPaths getEnabledOverlayPaths(@NonNull final String targetPackageName, @@ -717,49 +571,50 @@ final class OverlayManagerServiceImpl { /** * Returns true if the settings/state was modified, false otherwise. */ - private boolean updateState(@NonNull final String targetPackageName, - @NonNull final String overlayPackageName, final int userId, final int flags) - throws OverlayManagerSettings.BadKeyException { + private boolean updateState(@NonNull final CriticalOverlayInfo info, + final int userId, final int flags) throws OverlayManagerSettings.BadKeyException { + final OverlayIdentifier overlay = info.getOverlayIdentifier(); + final AndroidPackage targetPackage = mPackageManager.getPackageForUser( + info.getTargetPackageName(), userId); + final AndroidPackage overlayPackage = mPackageManager.getPackageForUser( + info.getPackageName(), userId); - final PackageInfo targetPackage = mPackageManager.getPackageInfo(targetPackageName, userId); - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName, - userId); + boolean modified = false; + if (overlayPackage == null) { + removeIdmapIfPossible(mSettings.getOverlayInfo(overlay, userId)); + return mSettings.remove(overlay, userId); + } // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native // layers. - boolean modified = false; - if (targetPackage != null && overlayPackage != null - && !("android".equals(targetPackageName) - && !isPackageConfiguredMutable(overlayPackageName))) { + if (targetPackage != null && !("android".equals(info.getTargetPackageName()) + && !isPackageConfiguredMutable(overlayPackage))) { modified |= mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); } - if (overlayPackage != null) { - modified |= mSettings.setBaseCodePath(overlayPackageName, userId, - overlayPackage.applicationInfo.getBaseCodePath()); - modified |= mSettings.setCategory(overlayPackageName, userId, - overlayPackage.overlayCategory); - } + modified |= mSettings.setBaseCodePath(overlay, userId, overlayPackage.getBaseApkPath()); + modified |= mSettings.setCategory(overlay, userId, overlayPackage.getOverlayCategory()); - final @OverlayInfo.State int currentState = mSettings.getState(overlayPackageName, userId); - final @OverlayInfo.State int newState = calculateNewState(targetPackage, overlayPackage, + final @OverlayInfo.State int currentState = mSettings.getState(overlay, userId); + final OverlayInfo updatedOverlayInfo = mSettings.getOverlayInfo(overlay, userId); + final @OverlayInfo.State int newState = calculateNewState(updatedOverlayInfo, targetPackage, userId, flags); if (currentState != newState) { if (DEBUG) { Slog.d(TAG, String.format("%s:%d: %s -> %s", - overlayPackageName, userId, + overlay, userId, OverlayInfo.stateToString(currentState), OverlayInfo.stateToString(newState))); } - modified |= mSettings.setState(overlayPackageName, userId, newState); + modified |= mSettings.setState(overlay, userId, newState); } + return modified; } - private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage, - @Nullable final PackageInfo overlayPackage, final int userId, final int flags) + private @OverlayInfo.State int calculateNewState(@NonNull final OverlayInfo info, + @Nullable final AndroidPackage targetPackage, final int userId, final int flags) throws OverlayManagerSettings.BadKeyException { - if ((flags & FLAG_TARGET_IS_BEING_REPLACED) != 0) { return STATE_TARGET_IS_BEING_REPLACED; } @@ -768,20 +623,15 @@ final class OverlayManagerServiceImpl { return STATE_OVERLAY_IS_BEING_REPLACED; } - // assert expectation on overlay package: can only be null if the flags are used - if (DEBUG && overlayPackage == null) { - throw new IllegalArgumentException("null overlay package not compatible with no flags"); - } - if (targetPackage == null) { return STATE_MISSING_TARGET; } - if (!mIdmapManager.idmapExists(overlayPackage, userId)) { + if (!mIdmapManager.idmapExists(info)) { return STATE_NO_IDMAP; } - final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId); + final boolean enabled = mSettings.getEnabled(info.getOverlayIdentifier(), userId); return enabled ? STATE_ENABLED : STATE_DISABLED; } @@ -810,7 +660,7 @@ final class OverlayManagerServiceImpl { final int[] userIds = mSettings.getUsers(); for (int userId : userIds) { try { - final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId); + final OverlayInfo tmp = mSettings.getOverlayInfo(oi.getOverlayIdentifier(), userId); if (tmp != null && tmp.isEnabled()) { // someone is still using the idmap file -> we cannot remove it return; diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java index 0613dff31da59..b46f368f0dadb 100644 --- a/services/core/java/com/android/server/om/OverlayManagerSettings.java +++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java @@ -21,6 +21,7 @@ import static com.android.server.om.OverlayManagerService.TAG; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.os.UserHandle; import android.util.ArrayMap; @@ -30,22 +31,20 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; +import java.util.function.Predicate; import java.util.stream.Stream; /** @@ -68,34 +67,45 @@ final class OverlayManagerSettings { */ private final ArrayList mItems = new ArrayList<>(); - void init(@NonNull final String packageName, final int userId, + @NonNull + OverlayInfo init(@NonNull final OverlayIdentifier overlay, final int userId, @NonNull final String targetPackageName, @Nullable final String targetOverlayableName, @NonNull final String baseCodePath, boolean isMutable, boolean isEnabled, int priority, @Nullable String overlayCategory) { - remove(packageName, userId); - insert(new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName, - baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled, isMutable, priority, - overlayCategory)); + remove(overlay, userId); + final SettingsItem item = new SettingsItem(overlay, userId, targetPackageName, + targetOverlayableName, baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled, + isMutable, priority, overlayCategory); + insert(item); + return item.getOverlayInfo(); } /** * Returns true if the settings were modified, false if they remain the same. */ - boolean remove(@NonNull final String packageName, final int userId) { - final int idx = select(packageName, userId); + boolean remove(@NonNull final OverlayIdentifier overlay, final int userId) { + final int idx = select(overlay, userId); if (idx < 0) { return false; } - mItems.remove(idx); return true; } - @NonNull OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) + @NonNull OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier overlay, final int userId) throws BadKeyException { - final int idx = select(packageName, userId); + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); + } + return mItems.get(idx).getOverlayInfo(); + } + + @Nullable + OverlayInfo getNullableOverlayInfo(@NonNull final OverlayIdentifier overlay, final int userId) { + final int idx = select(overlay, userId); + if (idx < 0) { + return null; } return mItems.get(idx).getOverlayInfo(); } @@ -103,28 +113,29 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setBaseCodePath(@NonNull final String packageName, final int userId, + boolean setBaseCodePath(@NonNull final OverlayIdentifier overlay, final int userId, @NonNull final String path) throws BadKeyException { - final int idx = select(packageName, userId); + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).setBaseCodePath(path); } - boolean setCategory(@NonNull final String packageName, final int userId, + boolean setCategory(@NonNull final OverlayIdentifier overlay, final int userId, @Nullable String category) throws BadKeyException { - final int idx = select(packageName, userId); + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).setCategory(category); } - boolean getEnabled(@NonNull final String packageName, final int userId) throws BadKeyException { - final int idx = select(packageName, userId); + boolean getEnabled(@NonNull final OverlayIdentifier overlay, final int userId) + throws BadKeyException { + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).isEnabled(); } @@ -132,20 +143,20 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setEnabled(@NonNull final String packageName, final int userId, final boolean enable) - throws BadKeyException { - final int idx = select(packageName, userId); + boolean setEnabled(@NonNull final OverlayIdentifier overlay, final int userId, + final boolean enable) throws BadKeyException { + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).setEnabled(enable); } - @OverlayInfo.State int getState(@NonNull final String packageName, final int userId) + @OverlayInfo.State int getState(@NonNull final OverlayIdentifier overlay, final int userId) throws BadKeyException { - final int idx = select(packageName, userId); + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).getState(); } @@ -153,11 +164,11 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setState(@NonNull final String packageName, final int userId, + boolean setState(@NonNull final OverlayIdentifier overlay, final int userId, final @OverlayInfo.State int state) throws BadKeyException { - final int idx = select(packageName, userId); + final int idx = select(overlay, userId); if (idx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } return mItems.get(idx).setState(state); } @@ -166,53 +177,83 @@ final class OverlayManagerSettings { final int userId) { // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be // ignored in OverlayManagerService. - return selectWhereTarget(targetPackageName, userId) - .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName())) - .map(SettingsItem::getOverlayInfo) - .collect(Collectors.toList()); + final List items = selectWhereTarget(targetPackageName, userId); + items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay); + return CollectionUtils.map(items, SettingsItem::getOverlayInfo); } ArrayMap> getOverlaysForUser(final int userId) { // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be // ignored in OverlayManagerService. - return selectWhereUser(userId) - .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName())) - .map(SettingsItem::getOverlayInfo) - .collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new, - Collectors.toList())); + final List items = selectWhereUser(userId); + items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay); + + final ArrayMap> targetInfos = new ArrayMap<>(); + for (int i = 0, n = items.size(); i < n; i++) { + final SettingsItem item = items.get(i); + targetInfos.computeIfAbsent(item.mTargetPackageName, (String) -> new ArrayList<>()) + .add(item.getOverlayInfo()); + } + return targetInfos; + } + + @NonNull List getOverlayInfosForPackage(@NonNull final String packageName, + final int userId) { + final List items = selectWhereOverlay(packageName, userId); + items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay); + return CollectionUtils.map(items, SettingsItem::getOverlayInfo); + } + + @NonNull + List removeIf(@NonNull final Predicate predicate, final int userId) { + return removeIf(info -> (predicate.test(info) && info.userId == userId)); + } + + @NonNull + List removeIf(final @NonNull Predicate predicate) { + List removed = null; + for (int i = mItems.size() - 1; i >= 0; i--) { + final OverlayInfo info = mItems.get(i).getOverlayInfo(); + if (predicate.test(info)) { + mItems.remove(i); + removed = CollectionUtils.add(removed, info); + } + } + return CollectionUtils.emptyIfNull(removed); } int[] getUsers() { return mItems.stream().mapToInt(SettingsItem::getUserId).distinct().toArray(); } + private static boolean isImmutableFrameworkOverlay(@NonNull SettingsItem item) { + return !item.isMutable() && "android".equals(item.getTargetPackageName()); + } + /** * Returns true if the settings were modified, false if they remain the same. */ boolean removeUser(final int userId) { - boolean removed = false; - for (int i = 0; i < mItems.size(); i++) { - final SettingsItem item = mItems.get(i); + return mItems.removeIf(item -> { if (item.getUserId() == userId) { if (DEBUG) { - Slog.d(TAG, "Removing overlay " + item.mPackageName + " for user " + userId + Slog.d(TAG, "Removing overlay " + item.mOverlay + " for user " + userId + " from settings because user was removed"); } - mItems.remove(i); - removed = true; - i--; + return true; } - } - return removed; + return false; + }); } /** * Reassigns the priority of an overlay maintaining the values of the overlays other settings. */ - void setPriority(@NonNull final String packageName, final int userId, final int priority) { - final int moveIdx = select(packageName, userId); + void setPriority(@NonNull final OverlayIdentifier overlay, final int userId, + final int priority) throws BadKeyException { + final int moveIdx = select(overlay, userId); if (moveIdx < 0) { - throw new BadKeyException(packageName, userId); + throw new BadKeyException(overlay, userId); } final SettingsItem itemToMove = mItems.get(moveIdx); @@ -224,17 +265,17 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setPriority(@NonNull final String packageName, - @NonNull final String newParentPackageName, final int userId) { - if (packageName.equals(newParentPackageName)) { + boolean setPriority(@NonNull final OverlayIdentifier overlay, + @NonNull final OverlayIdentifier newOverlay, final int userId) { + if (overlay.equals(newOverlay)) { return false; } - final int moveIdx = select(packageName, userId); + final int moveIdx = select(overlay, userId); if (moveIdx < 0) { return false; } - final int parentIdx = select(newParentPackageName, userId); + final int parentIdx = select(newOverlay, userId); if (parentIdx < 0) { return false; } @@ -248,7 +289,7 @@ final class OverlayManagerSettings { } mItems.remove(moveIdx); - final int newParentIdx = select(newParentPackageName, userId) + 1; + final int newParentIdx = select(newOverlay, userId) + 1; mItems.add(newParentIdx, itemToMove); return moveIdx != newParentIdx; } @@ -256,8 +297,8 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setLowestPriority(@NonNull final String packageName, final int userId) { - final int idx = select(packageName, userId); + boolean setLowestPriority(@NonNull final OverlayIdentifier overlay, final int userId) { + final int idx = select(overlay, userId); if (idx <= 0) { // If the item doesn't exist or is already the lowest, don't change anything. return false; @@ -272,8 +313,8 @@ final class OverlayManagerSettings { /** * Returns true if the settings were modified, false if they remain the same. */ - boolean setHighestPriority(@NonNull final String packageName, final int userId) { - final int idx = select(packageName, userId); + boolean setHighestPriority(@NonNull final OverlayIdentifier overlay, final int userId) { + final int idx = select(overlay, userId); // If the item doesn't exist or is already the highest, don't change anything. if (idx < 0 || idx == mItems.size() - 1) { @@ -297,7 +338,6 @@ final class OverlayManagerSettings { break; } } - mItems.add(i + 1, item); } @@ -308,7 +348,12 @@ final class OverlayManagerSettings { items = items.filter(item -> item.mUserId == dumpState.getUserId()); } if (dumpState.getPackageName() != null) { - items = items.filter(item -> item.mPackageName.equals(dumpState.getPackageName())); + items = items.filter(item -> item.mOverlay.getPackageName() + .equals(dumpState.getPackageName())); + } + if (dumpState.getOverlayName() != null) { + items = items.filter(item -> item.mOverlay.getOverlayName() + .equals(dumpState.getOverlayName())); } // display items @@ -322,10 +367,11 @@ final class OverlayManagerSettings { private void dumpSettingsItem(@NonNull final IndentingPrintWriter pw, @NonNull final SettingsItem item) { - pw.println(item.mPackageName + ":" + item.getUserId() + " {"); + pw.println(item.mOverlay + ":" + item.getUserId() + " {"); pw.increaseIndent(); - pw.println("mPackageName...........: " + item.mPackageName); + pw.println("mPackageName...........: " + item.mOverlay.getPackageName()); + pw.println("mOverlayName...........: " + item.mOverlay.getOverlayName()); pw.println("mUserId................: " + item.getUserId()); pw.println("mTargetPackageName.....: " + item.getTargetPackageName()); pw.println("mTargetOverlayableName.: " + item.getTargetOverlayableName()); @@ -344,7 +390,10 @@ final class OverlayManagerSettings { @NonNull final SettingsItem item, @NonNull final String field) { switch (field) { case "packagename": - pw.println(item.mPackageName); + pw.println(item.mOverlay.getPackageName()); + break; + case "overlayname": + pw.println(item.mOverlay.getOverlayName()); break; case "userid": pw.println(item.mUserId); @@ -392,6 +441,7 @@ final class OverlayManagerSettings { private static final String ATTR_BASE_CODE_PATH = "baseCodePath"; private static final String ATTR_IS_ENABLED = "isEnabled"; private static final String ATTR_PACKAGE_NAME = "packageName"; + private static final String ATTR_OVERLAY_NAME = "overlayName"; private static final String ATTR_STATE = "state"; private static final String ATTR_TARGET_PACKAGE_NAME = "targetPackageName"; private static final String ATTR_TARGET_OVERLAYABLE_NAME = "targetOverlayableName"; @@ -406,24 +456,19 @@ final class OverlayManagerSettings { public static void restore(@NonNull final ArrayList table, @NonNull final InputStream is) throws IOException, XmlPullParserException { + table.clear(); + final TypedXmlPullParser parser = Xml.resolvePullParser(is); + XmlUtils.beginDocument(parser, TAG_OVERLAYS); + final int version = parser.getAttributeInt(null, ATTR_VERSION); + if (version != CURRENT_VERSION) { + upgrade(version); + } - { - table.clear(); - final TypedXmlPullParser parser = Xml.resolvePullParser(is); - XmlUtils.beginDocument(parser, TAG_OVERLAYS); - int version = parser.getAttributeInt(null, ATTR_VERSION); - if (version != CURRENT_VERSION) { - upgrade(version); - } - int depth = parser.getDepth(); - - while (XmlUtils.nextElementWithin(parser, depth)) { - switch (parser.getName()) { - case TAG_ITEM: - final SettingsItem item = restoreRow(parser, depth + 1); - table.add(item); - break; - } + final int depth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, depth)) { + if (TAG_ITEM.equals(parser.getName())) { + final SettingsItem item = restoreRow(parser, depth + 1); + table.add(item); } } } @@ -447,7 +492,9 @@ final class OverlayManagerSettings { private static SettingsItem restoreRow(@NonNull final TypedXmlPullParser parser, final int depth) throws IOException, XmlPullParserException { - final String packageName = XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME); + final OverlayIdentifier overlay = new OverlayIdentifier( + XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME), + XmlUtils.readStringAttribute(parser, ATTR_OVERLAY_NAME)); final int userId = parser.getAttributeInt(null, ATTR_USER_ID); final String targetPackageName = XmlUtils.readStringAttribute(parser, ATTR_TARGET_PACKAGE_NAME); @@ -460,7 +507,7 @@ final class OverlayManagerSettings { final int priority = parser.getAttributeInt(null, ATTR_PRIORITY); final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY); - return new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName, + return new SettingsItem(overlay, userId, targetPackageName, targetOverlayableName, baseCodePath, state, isEnabled, !isStatic, priority, category); } @@ -484,7 +531,8 @@ final class OverlayManagerSettings { private static void persistRow(@NonNull final TypedXmlSerializer xml, @NonNull final SettingsItem item) throws IOException { xml.startTag(null, TAG_ITEM); - XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mPackageName); + XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mOverlay.getPackageName()); + XmlUtils.writeStringAttribute(xml, ATTR_OVERLAY_NAME, item.mOverlay.getOverlayName()); xml.attributeInt(null, ATTR_USER_ID, item.mUserId); XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName); XmlUtils.writeStringAttribute(xml, ATTR_TARGET_OVERLAYABLE_NAME, @@ -501,7 +549,7 @@ final class OverlayManagerSettings { private static final class SettingsItem { private final int mUserId; - private final String mPackageName; + private final OverlayIdentifier mOverlay; private final String mTargetPackageName; private final String mTargetOverlayableName; private String mBaseCodePath; @@ -512,12 +560,12 @@ final class OverlayManagerSettings { private int mPriority; private String mCategory; - SettingsItem(@NonNull final String packageName, final int userId, + SettingsItem(@NonNull final OverlayIdentifier overlay, final int userId, @NonNull final String targetPackageName, @Nullable final String targetOverlayableName, @NonNull final String baseCodePath, final @OverlayInfo.State int state, final boolean isEnabled, final boolean isMutable, final int priority, @Nullable String category) { - mPackageName = packageName; + mOverlay = overlay; mUserId = userId; mTargetPackageName = targetPackageName; mTargetOverlayableName = targetOverlayableName; @@ -596,8 +644,9 @@ final class OverlayManagerSettings { private OverlayInfo getOverlayInfo() { if (mCache == null) { - mCache = new OverlayInfo(mPackageName, mTargetPackageName, mTargetOverlayableName, - mCategory, mBaseCodePath, mState, mUserId, mPriority, mIsMutable); + mCache = new OverlayInfo(mOverlay.getPackageName(), mOverlay.getOverlayName(), + mTargetPackageName, mTargetOverlayableName, mCategory, mBaseCodePath, + mState, mUserId, mPriority, mIsMutable); } return mCache; } @@ -620,30 +669,40 @@ final class OverlayManagerSettings { } } - private int select(@NonNull final String packageName, final int userId) { + private int select(@NonNull final OverlayIdentifier overlay, final int userId) { final int n = mItems.size(); for (int i = 0; i < n; i++) { final SettingsItem item = mItems.get(i); - if (item.mUserId == userId && item.mPackageName.equals(packageName)) { + if (item.mUserId == userId && item.mOverlay.equals(overlay)) { return i; } } return -1; } - private Stream selectWhereUser(final int userId) { - return mItems.stream().filter(item -> item.mUserId == userId); + private List selectWhereUser(final int userId) { + final List selectedItems = new ArrayList<>(); + CollectionUtils.addIf(mItems, selectedItems, i -> i.mUserId == userId); + return selectedItems; } - private Stream selectWhereTarget(@NonNull final String targetPackageName, + private List selectWhereOverlay(@NonNull final String packageName, final int userId) { - return selectWhereUser(userId) - .filter(item -> item.getTargetPackageName().equals(targetPackageName)); + final List items = selectWhereUser(userId); + items.removeIf(i -> !i.mOverlay.getPackageName().equals(packageName)); + return items; } - static final class BadKeyException extends RuntimeException { - BadKeyException(@NonNull final String packageName, final int userId) { - super("Bad key mPackageName=" + packageName + " mUserId=" + userId); + private List selectWhereTarget(@NonNull final String targetPackageName, + final int userId) { + final List items = selectWhereUser(userId); + items.removeIf(i -> !i.getTargetPackageName().equals(targetPackageName)); + return items; + } + + static final class BadKeyException extends Exception { + BadKeyException(@NonNull final OverlayIdentifier overlay, final int userId) { + super("Bad key '" + overlay + "' for user " + userId ); } } } diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java index bf99bd6248cd8..c8a659ae5c36a 100644 --- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java @@ -20,7 +20,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.om.IOverlayManager; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; +import android.content.om.OverlayManagerTransaction; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; @@ -33,6 +35,7 @@ import android.util.TypedValue; import java.io.PrintWriter; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -89,35 +92,36 @@ final class OverlayManagerShellCommand extends ShellCommand { out.println("Overlay manager (overlay) commands:"); out.println(" help"); out.println(" Print this help text."); - out.println(" dump [--verbose] [--user USER_ID] [[FIELD] PACKAGE]"); + out.println(" dump [--verbose] [--user USER_ID] [[FIELD] PACKAGE[:NAME]]"); out.println(" Print debugging information about the overlay manager."); - out.println(" With optional parameter PACKAGE, limit output to the specified"); - out.println(" package. With optional parameter FIELD, limit output to"); + out.println(" With optional parameters PACKAGE and NAME, limit output to the specified"); + out.println(" overlay or target. With optional parameter FIELD, limit output to"); out.println(" the value of that SettingsItem field. Field names are"); out.println(" case insensitive and out.println the m prefix can be omitted,"); out.println(" so the following are equivalent: mState, mstate, State, state."); - out.println(" list [--user USER_ID] [PACKAGE]"); + out.println(" list [--user USER_ID] [PACKAGE[:NAME]]"); out.println(" Print information about target and overlay packages."); out.println(" Overlay packages are printed in priority order. With optional"); - out.println(" parameter PACKAGE, limit output to the specified package."); - out.println(" enable [--user USER_ID] PACKAGE"); - out.println(" Enable overlay package PACKAGE."); - out.println(" disable [--user USER_ID] PACKAGE"); - out.println(" Disable overlay package PACKAGE."); - out.println(" enable-exclusive [--user USER_ID] [--category] PACKAGE"); - out.println(" Enable overlay package PACKAGE and disable all other overlays for"); - out.println(" its target package. If the --category option is given, only disables"); - out.println(" other overlays in the same category."); + out.println(" parameters PACKAGE and NAME, limit output to the specified overlay or"); + out.println(" target."); + out.println(" enable [--user USER_ID] PACKAGE[:NAME]"); + out.println(" Enable overlay within or owned by PACKAGE with optional unique NAME."); + out.println(" disable [--user USER_ID] PACKAGE[:NAME]"); + out.println(" Disable overlay within or owned by PACKAGE with optional unique NAME."); + out.println(" enable-exclusive [--user USER_ID] [--category] PACKAGE[:NAME]"); + out.println(" Enable overlay within or owned by PACKAGE with optional unique NAME and"); + out.println(" disable all other overlays for its target package. If the --category"); + out.println(" option is given, only disables other overlays in the same category."); out.println(" set-priority [--user USER_ID] PACKAGE PARENT|lowest|highest"); - out.println(" Change the priority of the overlay PACKAGE to be just higher than"); - out.println(" the priority of PACKAGE_PARENT If PARENT is the special keyword"); + out.println(" Change the priority of the overlay to be just higher than"); + out.println(" the priority of PARENT If PARENT is the special keyword"); out.println(" 'lowest', change priority of PACKAGE to the lowest priority."); out.println(" If PARENT is the special keyword 'highest', change priority of"); out.println(" PACKAGE to the highest priority."); out.println(" lookup [--verbose] PACKAGE-TO-LOAD PACKAGE:TYPE/NAME"); out.println(" Load a package and print the value of a given resource"); out.println(" applying the current configuration and enabled overlays."); - out.println(" For a more fine-grained alernative, use 'idmap2 lookup'."); + out.println(" For a more fine-grained alternative, use 'idmap2 lookup'."); } private int runList() throws RemoteException { @@ -192,7 +196,7 @@ final class OverlayManagerShellCommand extends ShellCommand { status = "---"; break; } - out.println(String.format("%s %s", status, oi.packageName)); + out.println(String.format("%s %s", status, oi.getOverlayIdentifier())); } private int runEnableDisable(final boolean enable) throws RemoteException { @@ -211,8 +215,11 @@ final class OverlayManagerShellCommand extends ShellCommand { } } - final String packageName = getNextArgRequired(); - return mInterface.setEnabled(packageName, enable, userId) ? 0 : 1; + final OverlayIdentifier overlay = OverlayIdentifier.fromString(getNextArgRequired()); + mInterface.commit(new OverlayManagerTransaction.Builder() + .setEnabled(overlay, enable, userId) + .build()); + return 0; } private int runEnableExclusive() throws RemoteException { @@ -234,12 +241,27 @@ final class OverlayManagerShellCommand extends ShellCommand { return 1; } } - final String overlay = getNextArgRequired(); - if (inCategory) { - return mInterface.setEnabledExclusiveInCategory(overlay, userId) ? 0 : 1; - } else { - return mInterface.setEnabledExclusive(overlay, true, userId) ? 0 : 1; + + final OverlayIdentifier overlay = OverlayIdentifier.fromString(getNextArgRequired()); + final OverlayInfo overlayInfo = mInterface.getOverlayInfoByIdentifier(overlay, userId); + if (overlayInfo == null) { + err.println("Error: Unable to get overlay info of: " + overlay); + return 1; } + + final List overlaysForTarget = + mInterface.getOverlayInfosForTarget(overlayInfo.targetPackageName, userId); + final OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder(); + for (final OverlayInfo disableOverlay : overlaysForTarget) { + if ((inCategory && !Objects.equals(disableOverlay.category,overlayInfo.category)) + || !disableOverlay.isMutable) { + continue; + } + builder.setEnabled(disableOverlay.getOverlayIdentifier(), false, userId); + } + builder.setEnabled(overlayInfo.getOverlayIdentifier(), true, userId); + mInterface.commit(builder.build()); + return 0; } private int runSetPriority() throws RemoteException { diff --git a/services/core/java/com/android/server/om/PackageManagerHelper.java b/services/core/java/com/android/server/om/PackageManagerHelper.java index b1a8b4ee4d9fd..750f5c3ec06ba 100644 --- a/services/core/java/com/android/server/om/PackageManagerHelper.java +++ b/services/core/java/com/android/server/om/PackageManagerHelper.java @@ -22,10 +22,15 @@ import android.content.om.OverlayableInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.Slog; import com.android.server.pm.PackageManagerServiceUtils; +import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.IOException; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -36,6 +41,33 @@ import java.util.Map; * @hide */ interface PackageManagerHelper { + + /** + * Initializes the helper for the user. This only needs to be invoked one time before + * packages of this user are queried. + * @param userId the user id to initialize + * @return a map of package name to all packages installed in the user + */ + @NonNull + ArrayMap initializeForUser(final int userId); + + /** + * Retrieves the package information if it is installed for the user. + */ + @Nullable + AndroidPackage getPackageForUser(@NonNull final String packageName, final int userId); + + /** + * Returns whether the package is an instant app. + */ + boolean isInstantApp(@NonNull final String packageName, final int userId); + + /** + * @see PackageManager#getPackagesForUid(int) + */ + @Nullable + String[] getPackagesForUid(int uid); + /** * @return true if the target package has declared an overlayable */ @@ -63,11 +95,6 @@ interface PackageManagerHelper { @NonNull Map> getNamedActors(); - /** - * @see PackageManagerInternal#getOverlayPackages(int) - */ - List getOverlayPackages(int userId); - /** * Read from the APK and AndroidManifest of a package to return the overlayable defined for * a given name. @@ -79,19 +106,6 @@ interface PackageManagerHelper { @NonNull String targetOverlayableName, int userId) throws IOException; - /** - * @see PackageManager#getPackagesForUid(int) - */ - @Nullable - String[] getPackagesForUid(int uid); - - /** - * @param userId user to filter package visibility by - * @see PackageManager#getPackageInfo(String, int) - */ - @Nullable - PackageInfo getPackageInfo(@NonNull String packageName, int userId); - /** * @return true if {@link PackageManagerServiceUtils#compareSignatures} run on both packages * in the system returns {@link PackageManager#SIGNATURE_MATCH} diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt index 3a292deeaac9c..05ba532b6df5d 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt +++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt @@ -21,7 +21,9 @@ import android.content.om.OverlayableInfo import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.os.Process +import android.util.ArrayMap import com.android.server.om.OverlayActorEnforcer.ActorState +import com.android.server.pm.parsing.pkg.AndroidPackage import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat @@ -29,6 +31,7 @@ import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized +import org.mockito.Mockito import org.mockito.Mockito.spy import java.io.IOException @@ -125,11 +128,11 @@ class OverlayActorEnforcerTests { ActorState.TARGET_NOT_FOUND withCases { failure("nullPkgInfo") { targetPkgInfo = null } allowed("debuggable") { - targetPkgInfo = pkgInfo(TARGET_PKG).apply { - applicationInfo.flags = ApplicationInfo.FLAG_DEBUGGABLE + targetPkgInfo = androidPackage(TARGET_PKG).apply { + whenever(this.isDebuggable).thenReturn(true) } } - skip { targetPkgInfo = pkgInfo(TARGET_PKG) } + skip { targetPkgInfo = androidPackage(TARGET_PKG) } }, ActorState.NO_PACKAGES_FOR_UID withCases { failure("empty") { callingUid = EMPTY_UID } @@ -236,22 +239,20 @@ class OverlayActorEnforcerTests { mapOf(VALID_ACTOR_NAME to VALID_ACTOR_PKG)) } }, - ActorState.MISSING_APP_INFO withCases { + ActorState.ACTOR_NOT_FOUND withCases { failure("nullActorPkgInfo") { actorPkgInfo = null } failure("nullActorAppInfo") { - actorPkgInfo = PackageInfo().apply { applicationInfo = null } + actorPkgInfo = null } - skip { actorPkgInfo = pkgInfo(VALID_ACTOR_PKG) } + skip { actorPkgInfo = androidPackage(VALID_ACTOR_PKG) } }, ActorState.ACTOR_NOT_PREINSTALLED withCases { failure("notSystem") { - actorPkgInfo = pkgInfo(VALID_ACTOR_PKG).apply { - applicationInfo.flags = 0 - } + actorPkgInfo = androidPackage(VALID_ACTOR_PKG) } skip { - actorPkgInfo = pkgInfo(VALID_ACTOR_PKG).apply { - applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM + actorPkgInfo = androidPackage(VALID_ACTOR_PKG).apply { + whenever(this.isSystem).thenReturn(true) } } }, @@ -272,6 +273,7 @@ class OverlayActorEnforcerTests { ) { fun toOverlayInfo() = OverlayInfo( OVERLAY_PKG, + "", targetPackageName, targetOverlayableName, null, @@ -283,11 +285,10 @@ class OverlayActorEnforcerTests { private infix fun ActorState.withCases(block: TestCase.() -> Unit) = TestCase(this).apply(block) - private fun pkgInfo(pkgName: String): PackageInfo = mockThrowOnUnmocked { - this.packageName = pkgName - this.applicationInfo = ApplicationInfo().apply { - this.packageName = pkgName - } + private fun androidPackage(pkgName: String): AndroidPackage = mockThrowOnUnmocked { + whenever(this.packageName).thenReturn(pkgName) + whenever(this.isDebuggable).thenReturn(false) + whenever(this.isSystem).thenReturn(false) } private fun makeTestName(testCase: TestCase, caseName: String, type: Params.Type): String { @@ -363,8 +364,8 @@ class OverlayActorEnforcerTests { var namedActorsMap: Map> = emptyMap(), var hasPermission: Boolean = false, var targetOverlayableInfo: OverlayableInfo? = null, - var targetPkgInfo: PackageInfo? = null, - var actorPkgInfo: PackageInfo? = null, + var targetPkgInfo: AndroidPackage? = null, + var actorPkgInfo: AndroidPackage? = null, vararg val packageNames: String = arrayOf("com.test.actor.one") ) : PackageManagerHelper { @@ -375,6 +376,14 @@ class OverlayActorEnforcerTests { override fun getNamedActors() = namedActorsMap + override fun isInstantApp(packageName: String, userId: Int): Boolean { + throw UnsupportedOperationException() + } + + override fun initializeForUser(userId: Int): ArrayMap { + throw UnsupportedOperationException() + } + @Throws(IOException::class) override fun getOverlayableForTarget( packageName: String, @@ -394,9 +403,6 @@ class OverlayActorEnforcerTests { else -> null } - override fun getPackageInfo(packageName: String, userId: Int) = - listOfNotNull(targetPkgInfo, actorPkgInfo).find { it.packageName == packageName } - @Throws(IOException::class) // Mockito requires this checked exception to be declared override fun doesTargetDefineOverlayable(targetPackageName: String?, userId: Int): Boolean { return targetOverlayableInfo?.takeIf { @@ -411,11 +417,10 @@ class OverlayActorEnforcerTests { } } - override fun getConfigSignaturePackage(): String { - throw UnsupportedOperationException() - } + override fun getPackageForUser(packageName: String, userId: Int) = + listOfNotNull(targetPkgInfo, actorPkgInfo).find { it.packageName == packageName } - override fun getOverlayPackages(userId: Int): MutableList { + override fun getConfigSignaturePackage(): String { throw UnsupportedOperationException() } diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java index 5468fba59c109..55cd772ad7e07 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java @@ -21,7 +21,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; +import android.util.ArraySet; import androidx.test.runner.AndroidJUnit4; @@ -29,77 +31,66 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; -import java.util.List; import java.util.function.BiConsumer; @RunWith(AndroidJUnit4.class) public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceImplTestsBase { private static final String OVERLAY = "com.test.overlay"; + private static final OverlayIdentifier IDENTIFIER = new OverlayIdentifier(OVERLAY); private static final String TARGET = "com.test.target"; private static final int USER = 0; private static final String OVERLAY2 = OVERLAY + "2"; + private static final OverlayIdentifier IDENTIFIER2 = new OverlayIdentifier(OVERLAY2); @Test public void testUpdateOverlaysForUser() { final OverlayManagerServiceImpl impl = getImpl(); + final String otherTarget = "some.other.target"; addPackage(target(TARGET), USER); - addPackage(target("some.other.target"), USER); + addPackage(target(otherTarget), USER); addPackage(overlay(OVERLAY, TARGET), USER); // do nothing, expect no change - final List a = impl.updateOverlaysForUser(USER); - assertEquals(1, a.size()); - assertTrue(a.contains(TARGET)); + final ArraySet a = impl.updateOverlaysForUser(USER); + assertEquals(3, a.size()); + assertTrue(a.containsAll(Arrays.asList( + new PackageAndUser(TARGET, USER), + new PackageAndUser(otherTarget, USER), + new PackageAndUser(OVERLAY, USER)))); - // upgrade overlay, keep target - addPackage(overlay(OVERLAY, TARGET), USER); - - final List b = impl.updateOverlaysForUser(USER); - assertEquals(1, b.size()); - assertTrue(b.contains(TARGET)); - - // do nothing, expect no change - final List c = impl.updateOverlaysForUser(USER); - assertEquals(1, c.size()); - assertTrue(c.contains(TARGET)); - - // upgrade overlay, switch to new target - addPackage(overlay(OVERLAY, "some.other.target"), USER); - final List d = impl.updateOverlaysForUser(USER); - assertEquals(2, d.size()); - assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target"))); - - // do nothing, expect no change - final List f = impl.updateOverlaysForUser(USER); - assertEquals(1, f.size()); - assertTrue(f.contains("some.other.target")); + final ArraySet b = impl.updateOverlaysForUser(USER); + assertEquals(3, b.size()); + assertTrue(b.containsAll(Arrays.asList( + new PackageAndUser(TARGET, USER), + new PackageAndUser(otherTarget, USER), + new PackageAndUser(OVERLAY, USER)))); } @Test public void testImmutableEnabledChange() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o1); assertFalse(o1.isEnabled()); assertFalse(o1.isMutable); configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o2); assertTrue(o2.isEnabled()); assertFalse(o2.isMutable); configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o3); assertFalse(o3.isEnabled()); assertFalse(o3.isMutable); @@ -108,26 +99,26 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testMutableEnabledChangeHasNoEffect() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o1); assertFalse(o1.isEnabled()); assertTrue(o1.isMutable); configureSystemOverlay(OVERLAY, true /* mutable */, true /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o2); assertFalse(o2.isEnabled()); assertTrue(o2.isMutable); configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o3); assertFalse(o3.isEnabled()); assertTrue(o3.isMutable); @@ -136,13 +127,13 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testMutableEnabledToImmutableEnabled() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); final BiConsumer setOverlay = (mutable, enabled) -> { configureSystemOverlay(OVERLAY, mutable, enabled, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o); assertEquals(enabled, o.isEnabled()); assertEquals(mutable, o.isMutable); @@ -180,38 +171,38 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testMutablePriorityChange() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); - installNewPackage(overlay(OVERLAY2, TARGET), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY2, TARGET), USER); configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */); configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 1 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o1); assertEquals(0, o1.priority); assertFalse(o1.isEnabled()); - final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); + final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER); assertNotNull(o2); assertEquals(1, o2.priority); assertFalse(o2.isEnabled()); // Overlay priority changing between reboots should not affect enable state of mutable // overlays. - impl.setEnabled(OVERLAY, true, USER); + impl.setEnabled(IDENTIFIER, true, USER); // Reorder the overlays configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 1 /* priority */); configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o3); assertEquals(1, o3.priority); assertTrue(o3.isEnabled()); - final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER); + final OverlayInfo o4 = impl.getOverlayInfo(IDENTIFIER2, USER); assertNotNull(o4); assertEquals(0, o4.priority); assertFalse(o4.isEnabled()); @@ -220,19 +211,19 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testImmutablePriorityChange() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); - installNewPackage(overlay(OVERLAY2, TARGET), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY2, TARGET), USER); configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */); configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 1 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o1); assertEquals(0, o1.priority); assertTrue(o1.isEnabled()); - final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); + final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER); assertNotNull(o2); assertEquals(1, o2.priority); assertTrue(o2.isEnabled()); @@ -242,12 +233,12 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(o3); assertEquals(1, o3.priority); assertTrue(o3.isEnabled()); - final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER); + final OverlayInfo o4 = impl.getOverlayInfo(IDENTIFIER2, USER); assertNotNull(o4); assertEquals(0, o4.priority); assertTrue(o4.isEnabled()); diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java index 33dbcc0855be8..f044a09d7351f 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java @@ -28,6 +28,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.testng.Assert.assertThrows; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.util.Pair; @@ -39,19 +40,23 @@ import org.junit.runner.RunWith; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; @RunWith(AndroidJUnit4.class) public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase { private static final String OVERLAY = "com.test.overlay"; + private static final OverlayIdentifier IDENTIFIER = new OverlayIdentifier(OVERLAY); private static final String TARGET = "com.test.target"; private static final int USER = 0; private static final String OVERLAY2 = OVERLAY + "2"; private static final String TARGET2 = TARGET + "2"; + private static final OverlayIdentifier IDENTIFIER2 = new OverlayIdentifier(OVERLAY2); private static final int USER2 = USER + 1; private static final String OVERLAY3 = OVERLAY + "3"; + private static final OverlayIdentifier IDENTIFIER3 = new OverlayIdentifier(OVERLAY3); private static final int USER3 = USER2 + 1; private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.test.ref"; @@ -60,10 +65,10 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testGetOverlayInfo() throws Exception { - installNewPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); final OverlayManagerServiceImpl impl = getImpl(); - final OverlayInfo oi = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo oi = impl.getOverlayInfo(IDENTIFIER, USER); assertNotNull(oi); assertEquals(oi.packageName, OVERLAY); assertEquals(oi.targetPackageName, TARGET); @@ -72,19 +77,19 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testGetOverlayInfosForTarget() throws Exception { - installNewPackage(overlay(OVERLAY, TARGET), USER); - installNewPackage(overlay(OVERLAY2, TARGET), USER); - installNewPackage(overlay(OVERLAY3, TARGET), USER2); + installPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY2, TARGET), USER); + installPackage(overlay(OVERLAY3, TARGET), USER2); final OverlayManagerServiceImpl impl = getImpl(); final List ois = impl.getOverlayInfosForTarget(TARGET, USER); assertEquals(ois.size(), 2); - assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER))); - assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER2, USER))); final List ois2 = impl.getOverlayInfosForTarget(TARGET, USER2); assertEquals(ois2.size(), 1); - assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER2))); + assertTrue(ois2.contains(impl.getOverlayInfo(IDENTIFIER3, USER2))); final List ois3 = impl.getOverlayInfosForTarget(TARGET, USER3); assertNotNull(ois3); @@ -97,10 +102,10 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testGetOverlayInfosForUser() throws Exception { - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); - installNewPackage(overlay(OVERLAY2, TARGET), USER); - installNewPackage(overlay(OVERLAY3, TARGET2), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY2, TARGET), USER); + installPackage(overlay(OVERLAY3, TARGET2), USER); final OverlayManagerServiceImpl impl = getImpl(); final Map> everything = impl.getOverlaysForUser(USER); @@ -109,13 +114,13 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes final List ois = everything.get(TARGET); assertNotNull(ois); assertEquals(ois.size(), 2); - assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER))); - assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER2, USER))); final List ois2 = everything.get(TARGET2); assertNotNull(ois2); assertEquals(ois2.size(), 1); - assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER))); + assertTrue(ois2.contains(impl.getOverlayInfo(IDENTIFIER3, USER))); final Map> everything2 = impl.getOverlaysForUser(USER2); assertNotNull(everything2); @@ -124,26 +129,26 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testPriority() throws Exception { - installNewPackage(overlay(OVERLAY, TARGET), USER); - installNewPackage(overlay(OVERLAY2, TARGET), USER); - installNewPackage(overlay(OVERLAY3, TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); + installPackage(overlay(OVERLAY2, TARGET), USER); + installPackage(overlay(OVERLAY3, TARGET), USER); final OverlayManagerServiceImpl impl = getImpl(); - final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); - final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); - final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY3, USER); + final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER); + final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER); + final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER3, USER); assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3); - assertEquals(impl.setLowestPriority(OVERLAY3, USER), + assertEquals(impl.setLowestPriority(IDENTIFIER3, USER), Optional.of(new PackageAndUser(TARGET, USER))); assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2); - assertEquals(impl.setHighestPriority(OVERLAY3, USER), + assertEquals(impl.setHighestPriority(IDENTIFIER3, USER), Optional.of(new PackageAndUser(TARGET, USER))); assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3); - assertEquals(impl.setPriority(OVERLAY, OVERLAY2, USER), + assertEquals(impl.setPriority(IDENTIFIER, IDENTIFIER2, USER), Optional.of(new PackageAndUser(TARGET, USER))); assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3); } @@ -151,61 +156,63 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testOverlayInfoStateTransitions() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); - assertNull(impl.getOverlayInfo(OVERLAY, USER)); + assertNull(impl.getOverlayInfo(IDENTIFIER, USER)); - installNewPackage(overlay(OVERLAY, TARGET), USER); - assertState(STATE_MISSING_TARGET, OVERLAY, USER); + installPackage(overlay(OVERLAY, TARGET), USER); + assertState(STATE_MISSING_TARGET, IDENTIFIER, USER); final FakeDeviceState.PackageBuilder target = target(TARGET); - installNewPackage(target, USER); - assertState(STATE_DISABLED, OVERLAY, USER); + installPackage(target, USER); + assertState(STATE_DISABLED, IDENTIFIER, USER); - assertEquals(impl.setEnabled(OVERLAY, true, USER), + assertEquals(impl.setEnabled(IDENTIFIER, true, USER), Optional.of(new PackageAndUser(TARGET, USER))); - assertState(STATE_ENABLED, OVERLAY, USER); + assertState(STATE_ENABLED, IDENTIFIER, USER); // target upgrades do not change the state of the overlay upgradePackage(target, USER); - assertState(STATE_ENABLED, OVERLAY, USER); + assertState(STATE_ENABLED, IDENTIFIER, USER); uninstallPackage(TARGET, USER); - assertState(STATE_MISSING_TARGET, OVERLAY, USER); + assertState(STATE_MISSING_TARGET, IDENTIFIER, USER); - installNewPackage(target, USER); - assertState(STATE_ENABLED, OVERLAY, USER); + installPackage(target, USER); + assertState(STATE_ENABLED, IDENTIFIER, USER); } @Test public void testOnOverlayPackageUpgraded() throws Exception { final FakeDeviceState.PackageBuilder target = target(TARGET); final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET); - installNewPackage(target, USER); - installNewPackage(overlay, USER); + installPackage(target, USER); + installPackage(overlay, USER); upgradePackage(overlay, USER); // upgrade to a version where the overlay has changed its target final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target"); - final Pair, Optional> pair = - upgradePackage(overlay2, USER); - assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER))); - assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER))); + final Pair, Set> pair = upgradePackage(overlay2, USER); + assertEquals(pair.first, Set.of(new PackageAndUser(TARGET, USER))); + assertEquals( + Set.of(new PackageAndUser(TARGET, USER), + new PackageAndUser("some.other.target", USER)), + pair.second); } @Test public void testSetEnabledAtVariousConditions() throws Exception { final OverlayManagerServiceImpl impl = getImpl(); assertThrows(OverlayManagerServiceImpl.OperationFailedException.class, - () -> impl.setEnabled(OVERLAY, true, USER)); + () -> impl.setEnabled(IDENTIFIER, true, USER)); // request succeeded, and there was a change that needs to be // propagated to the rest of the system - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET), USER); - assertEquals(impl.setEnabled(OVERLAY, true, USER), + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET), USER); + assertEquals(impl.setEnabled(IDENTIFIER, true, USER), Optional.of(new PackageAndUser(TARGET, USER))); // request succeeded, but nothing changed - assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent()); + assertFalse(impl.setEnabled(IDENTIFIER, true, USER).isPresent()); } @Test @@ -214,8 +221,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes reinitializeImpl(); addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER); final FakeIdmapDaemon idmapd = getIdmapd(); final FakeDeviceState state = getState(); @@ -232,8 +239,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes reinitializeImpl(); addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); final FakeIdmapDaemon idmapd = getIdmapd(); final FakeDeviceState state = getState(); @@ -247,8 +254,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testConfigSignaturePolicyNoConfig() throws Exception { addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); final FakeIdmapDaemon idmapd = getIdmapd(); final FakeDeviceState state = getState(); @@ -261,8 +268,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testConfigSignaturePolicyNoRefPkg() throws Exception { - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); final FakeIdmapDaemon idmapd = getIdmapd(); final FakeDeviceState state = getState(); @@ -279,8 +286,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes reinitializeImpl(); addPackage(app(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER); - installNewPackage(target(TARGET), USER); - installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); + installPackage(target(TARGET), USER); + installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); final FakeIdmapDaemon idmapd = getIdmapd(); final FakeDeviceState state = getState(); diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java index 2c477c897b301..2aad7cc2f6849 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java @@ -24,11 +24,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.annotation.NonNull; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.content.om.OverlayInfo.State; import android.content.om.OverlayableInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -37,17 +36,19 @@ import android.util.Pair; import androidx.annotation.Nullable; import com.android.internal.content.om.OverlayConfig; +import com.android.internal.util.CollectionUtils; +import com.android.server.pm.parsing.pkg.AndroidPackage; import org.junit.Assert; import org.junit.Before; +import org.mockito.Mockito; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; +import java.util.Set; /** Base class for creating {@link OverlayManagerServiceImplTests} tests. */ class OverlayManagerServiceImplTestsBase { @@ -94,12 +95,11 @@ class OverlayManagerServiceImplTestsBase { mConfigSignaturePackageName = packageName; } - void assertState(@State int expected, final String overlayPackageName, int userId) { - final OverlayInfo info = mImpl.getOverlayInfo(overlayPackageName, userId); + void assertState(@State int expected, final OverlayIdentifier overlay, int userId) { + final OverlayInfo info = mImpl.getOverlayInfo(overlay, userId); if (info == null) { - throw new IllegalStateException("package not installed"); + throw new IllegalStateException("overlay '" + overlay + "' not installed"); } - final String msg = String.format("expected %s but was %s:", OverlayInfo.stateToString(expected), OverlayInfo.stateToString(info.state)); assertEquals(msg, expected, info.state); @@ -152,17 +152,13 @@ class OverlayManagerServiceImplTestsBase { * * @throws IllegalStateException if the package is currently installed */ - void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) + Set installPackage(FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException { if (mState.select(pkg.packageName, userId) != null) { throw new IllegalStateException("package " + pkg.packageName + " already installed"); } mState.add(pkg, userId); - if (pkg.targetPackage == null) { - mImpl.onTargetPackageAdded(pkg.packageName, userId); - } else { - mImpl.onOverlayPackageAdded(pkg.packageName, userId); - } + return CollectionUtils.emptyIfNull(mImpl.onPackageAdded(pkg.packageName, userId)); } /** @@ -178,26 +174,21 @@ class OverlayManagerServiceImplTestsBase { * * @throws IllegalStateException if the package is not currently installed */ - Pair, Optional> upgradePackage( + Pair, Set> upgradePackage( FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException { final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId); if (replacedPackage == null) { throw new IllegalStateException("package " + pkg.packageName + " not installed"); } - Optional opt1 = Optional.empty(); - if (replacedPackage.targetPackageName != null) { - opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId); - } + + final Set updatedPackages1 = + CollectionUtils.emptyIfNull(mImpl.onPackageReplacing(pkg.packageName, userId)); mState.add(pkg, userId); - Optional opt2; - if (pkg.targetPackage == null) { - opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId); - } else { - opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId); - } + final Set updatedPackages2 = + CollectionUtils.emptyIfNull(mImpl.onPackageReplaced(pkg.packageName, userId)); - return Pair.create(opt1, opt2); + return Pair.create(updatedPackages1, updatedPackages2); } /** @@ -208,17 +199,13 @@ class OverlayManagerServiceImplTestsBase { * * @throws IllegalStateException if the package is not currently installed */ - void uninstallPackage(String packageName, int userId) throws OperationFailedException { + Set uninstallPackage(String packageName, int userId) { final FakeDeviceState.Package pkg = mState.select(packageName, userId); if (pkg == null) { throw new IllegalStateException("package " + packageName+ " not installed"); } mState.remove(pkg.packageName); - if (pkg.targetPackageName == null) { - mImpl.onTargetPackageRemoved(pkg.packageName, userId); - } else { - mImpl.onOverlayPackageRemoved(pkg.packageName, userId); - } + return CollectionUtils.emptyIfNull(mImpl.onPackageRemoved(packageName, userId)); } /** Represents the state of packages installed on a fake device. */ @@ -247,11 +234,6 @@ class OverlayManagerServiceImplTestsBase { } } - List select(int userId) { - return mPackages.values().stream().filter(p -> p.installedUserIds.contains(userId)) - .collect(Collectors.toList()); - } - Package select(String packageName, int userId) { final Package pkg = mPackages.get(packageName); return pkg != null && pkg.installedUserIds.contains(userId) ? pkg : null; @@ -335,6 +317,21 @@ class OverlayManagerServiceImplTestsBase { this.apkPath = apkPath; this.certificate = certificate; } + + @Nullable + private AndroidPackage getPackageForUser(int user) { + if (!installedUserIds.contains(user)) { + return null; + } + final AndroidPackage pkg = Mockito.mock(AndroidPackage.class); + when(pkg.getPackageName()).thenReturn(packageName); + when(pkg.getBaseApkPath()).thenReturn(apkPath); + when(pkg.getLongVersionCode()).thenReturn((long) versionCode); + when(pkg.getOverlayTarget()).thenReturn(targetPackageName); + when(pkg.getOverlayTargetName()).thenReturn(targetOverlayableName); + when(pkg.getOverlayCategory()).thenReturn("Fake-category-" + targetPackageName); + return pkg; + } } } @@ -345,21 +342,29 @@ class OverlayManagerServiceImplTestsBase { mState = state; } + @NonNull @Override - public PackageInfo getPackageInfo(@NonNull String packageName, int userId) { - final FakeDeviceState.Package pkg = mState.select(packageName, userId); - if (pkg == null) { - return null; - } - final ApplicationInfo ai = new ApplicationInfo(); - ai.sourceDir = pkg.apkPath; - PackageInfo pi = new PackageInfo(); - pi.applicationInfo = ai; - pi.packageName = pkg.packageName; - pi.overlayTarget = pkg.targetPackageName; - pi.targetOverlayableName = pkg.targetOverlayableName; - pi.overlayCategory = "Fake-category-" + pkg.targetPackageName; - return pi; + public ArrayMap initializeForUser(int userId) { + final ArrayMap packages = new ArrayMap<>(); + mState.mPackages.forEach((key, value) -> { + final AndroidPackage pkg = value.getPackageForUser(userId); + if (pkg != null) { + packages.put(key, pkg); + } + }); + return packages; + } + + @Nullable + @Override + public AndroidPackage getPackageForUser(@NonNull String packageName, int userId) { + final FakeDeviceState.Package pkgState = mState.select(packageName, userId); + return pkgState == null ? null : pkgState.getPackageForUser(userId); + } + + @Override + public boolean isInstantApp(@NonNull String packageName, int userId) { + return false; } @Override @@ -370,14 +375,6 @@ class OverlayManagerServiceImplTestsBase { return pkg1 != null && pkg2 != null && pkg1.certificate.equals(pkg2.certificate); } - @Override - public List getOverlayPackages(int userId) { - return mState.select(userId).stream() - .filter(p -> p.targetPackageName != null) - .map(p -> getPackageInfo(p.packageName, userId)) - .collect(Collectors.toList()); - } - @Override public @NonNull String getConfigSignaturePackage() { return mConfigSignaturePackageName; diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java index e3e77689f8e67..e89ab237f801a 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java @@ -19,17 +19,20 @@ package com.android.server.om; import static android.content.om.OverlayInfo.STATE_DISABLED; import static android.content.om.OverlayInfo.STATE_ENABLED; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.text.TextUtils; import android.util.TypedXmlPullParser; import android.util.Xml; +import androidx.annotation.NonNull; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; @@ -43,67 +46,32 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.IntStream; -import java.util.stream.Stream; + +import javax.annotation.Nullable; @RunWith(AndroidJUnit4.class) public class OverlayManagerSettingsTests { private OverlayManagerSettings mSettings; + private static int USER_0 = 0; + private static int USER_1 = 1; - private static final OverlayInfo OVERLAY_A0 = new OverlayInfo( - "com.test.overlay_a", - "com.test.target", - null, - "some-category", - "/data/app/com.test.overlay_a-1/base.apk", - STATE_DISABLED, - 0, - 0, - true); + private static OverlayIdentifier OVERLAY_A = new OverlayIdentifier("com.test.overlay_a", + null /* overlayName */); + private static OverlayIdentifier OVERLAY_B = new OverlayIdentifier("com.test.overlay_b", + null /* overlayName */); + private static OverlayIdentifier OVERLAY_C = new OverlayIdentifier("com.test.overlay_c", + null /* overlayName */); - private static final OverlayInfo OVERLAY_B0 = new OverlayInfo( - "com.test.overlay_b", - "com.test.target", - null, - "some-category", - "/data/app/com.test.overlay_b-1/base.apk", - STATE_DISABLED, - 0, - 0, - true); + private static final OverlayInfo OVERLAY_A_USER0 = createInfo(OVERLAY_A, USER_0); + private static final OverlayInfo OVERLAY_B_USER0 = createInfo(OVERLAY_B, USER_0); + private static final OverlayInfo OVERLAY_C_USER0 = createInfo(OVERLAY_C, USER_0); - private static final OverlayInfo OVERLAY_C0 = new OverlayInfo( - "com.test.overlay_c", - "com.test.target", - null, - "some-category", - "/data/app/com.test.overlay_c-1/base.apk", - STATE_DISABLED, - 0, - 0, - true); + private static final OverlayInfo OVERLAY_A_USER1 = createInfo(OVERLAY_A, USER_1); + private static final OverlayInfo OVERLAY_B_USER1 = createInfo(OVERLAY_B, USER_1); - private static final OverlayInfo OVERLAY_A1 = new OverlayInfo( - "com.test.overlay_a", - "com.test.target", - null, - "some-category", - "/data/app/com.test.overlay_a-1/base.apk", - STATE_DISABLED, - 1, - 0, - true); - - private static final OverlayInfo OVERLAY_B1 = new OverlayInfo( - "com.test.overlay_b", - "com.test.target", - null, - "some-category", - "/data/app/com.test.overlay_b-1/base.apk", - STATE_DISABLED, - 1, - 0, - true); + private static final String TARGET_PACKAGE = "com.test.target"; @Before public void setUp() throws Exception { @@ -114,124 +82,112 @@ public class OverlayManagerSettingsTests { @Test public void testSettingsInitiallyEmpty() throws Exception { - final int userId = 0; - Map> map = mSettings.getOverlaysForUser(userId); + final Map> map = mSettings.getOverlaysForUser(0 /* userId */); assertEquals(0, map.size()); } @Test public void testBasicSetAndGet() throws Exception { - assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId); + assertDoesNotContain(mSettings, OVERLAY_A_USER0); - insert(OVERLAY_A0); - assertContains(mSettings, OVERLAY_A0); - OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId); - assertEquals(OVERLAY_A0, oi); + insertSetting(OVERLAY_A_USER0); + assertContains(mSettings, OVERLAY_A_USER0); + final OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_A, USER_0); + assertEquals(OVERLAY_A_USER0, oi); - assertTrue(mSettings.remove(OVERLAY_A0.packageName, OVERLAY_A0.userId)); - assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId); + assertTrue(mSettings.remove(OVERLAY_A, USER_0)); + assertDoesNotContain(mSettings, OVERLAY_A, USER_0); } @Test public void testGetUsers() throws Exception { - int[] users = mSettings.getUsers(); - assertEquals(0, users.length); + assertArrayEquals(new int[]{}, mSettings.getUsers()); - insert(OVERLAY_A0); - users = mSettings.getUsers(); - assertEquals(1, users.length); - assertContains(users, OVERLAY_A0.userId); + insertSetting(OVERLAY_A_USER0); + assertArrayEquals(new int[]{USER_0}, mSettings.getUsers()); - insert(OVERLAY_A1); - insert(OVERLAY_B1); - users = mSettings.getUsers(); - assertEquals(2, users.length); - assertContains(users, OVERLAY_A0.userId); - assertContains(users, OVERLAY_A1.userId); + insertSetting(OVERLAY_A_USER1); + insertSetting(OVERLAY_B_USER1); + assertArrayEquals(new int[]{USER_0, USER_1}, mSettings.getUsers()); } @Test public void testGetOverlaysForUser() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_A1); - insert(OVERLAY_B1); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_A_USER1); + insertSetting(OVERLAY_B_USER0); - Map> map = mSettings.getOverlaysForUser(OVERLAY_A0.userId); - assertEquals(1, map.keySet().size()); - assertTrue(map.keySet().contains(OVERLAY_A0.targetPackageName)); + final Map> map = mSettings.getOverlaysForUser(USER_0); + assertEquals(Set.of(TARGET_PACKAGE), map.keySet()); - List list = map.get(OVERLAY_A0.targetPackageName); - assertEquals(2, list.size()); - assertTrue(list.contains(OVERLAY_A0)); - assertTrue(list.contains(OVERLAY_B0)); + // Two overlays in user 0 target the same package + final List list = map.get(TARGET_PACKAGE); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0), list); - // getOverlaysForUser should never return null - map = mSettings.getOverlaysForUser(-1); - assertNotNull(map); - assertEquals(0, map.size()); + // No users installed for user 3 + assertEquals(Map.>of(), mSettings.getOverlaysForUser(3)); } @Test public void testRemoveUser() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_A1); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_A_USER1); - assertContains(mSettings, OVERLAY_A0); - assertContains(mSettings, OVERLAY_B0); - assertContains(mSettings, OVERLAY_A1); + assertContains(mSettings, OVERLAY_A_USER0); + assertContains(mSettings, OVERLAY_B_USER0); + assertContains(mSettings, OVERLAY_A_USER1); - mSettings.removeUser(OVERLAY_A0.userId); + mSettings.removeUser(USER_0); - assertDoesNotContain(mSettings, OVERLAY_A0); - assertDoesNotContain(mSettings, OVERLAY_B0); - assertContains(mSettings, OVERLAY_A1); + assertDoesNotContain(mSettings, OVERLAY_A_USER0); + assertDoesNotContain(mSettings, OVERLAY_B_USER0); + assertContains(mSettings, OVERLAY_A_USER1); } @Test public void testOrderOfNewlyAddedItems() throws Exception { // new items are appended to the list - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_C0); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_C_USER0); - List list = - mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); // overlays keep their positions when updated - mSettings.setState(OVERLAY_B0.packageName, OVERLAY_B0.userId, STATE_ENABLED); - OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_B0.packageName, OVERLAY_B0.userId); + mSettings.setState(OVERLAY_B, USER_0, STATE_ENABLED); + final OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_B, USER_0); + assertNotNull(oi); - list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, oi, OVERLAY_C0); + assertListsAreEqual(List.of(OVERLAY_A_USER0, oi, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); } @Test public void testSetPriority() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_C0); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_C_USER0); - List list = - mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); - boolean changed = mSettings.setPriority(OVERLAY_B0.packageName, OVERLAY_C0.packageName, - OVERLAY_B0.userId); - assertTrue(changed); - list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0); + assertTrue(mSettings.setPriority(OVERLAY_B, OVERLAY_C, USER_0)); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); - changed = - mSettings.setPriority(OVERLAY_B0.packageName, "does.not.exist", OVERLAY_B0.userId); - assertFalse(changed); - list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0); + // Nothing happens if the parent package cannot be found + assertFalse(mSettings.setPriority(OVERLAY_B, new OverlayIdentifier("does.not.exist"), + USER_0)); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); - OverlayInfo otherTarget = new OverlayInfo( + // An overlay should not affect the priority of overlays targeting a different package + final OverlayInfo otherTarget = new OverlayInfo( "com.test.overlay_other", + null, "com.test.some.other.target", null, "some-category", @@ -240,44 +196,34 @@ public class OverlayManagerSettingsTests { 0, 0, true); - insert(otherTarget); - changed = mSettings.setPriority(OVERLAY_A0.packageName, otherTarget.packageName, - OVERLAY_A0.userId); - assertFalse(changed); + insertSetting(otherTarget); + assertFalse(mSettings.setPriority(OVERLAY_A, otherTarget.getOverlayIdentifier(), USER_0)); } @Test public void testSetLowestPriority() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_C0); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_C_USER0); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); - List list = - mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0); - - boolean changed = mSettings.setLowestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId); - assertTrue(changed); - - list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_B0, OVERLAY_A0, OVERLAY_C0); + assertTrue(mSettings.setLowestPriority(OVERLAY_B, USER_0)); + assertListsAreEqual(List.of(OVERLAY_B_USER0, OVERLAY_A_USER0, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); } @Test public void testSetHighestPriority() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); - insert(OVERLAY_C0); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); + insertSetting(OVERLAY_C_USER0); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0), + mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0)); - List list = - mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0); - - boolean changed = mSettings.setHighestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId); - assertTrue(changed); - - list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId); - assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0); + assertTrue(mSettings.setHighestPriority(OVERLAY_B, USER_0)); + assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0), + mSettings.getOverlaysForTarget(OVERLAY_A_USER0.targetPackageName, USER_0)); } // tests: persist and restore @@ -294,8 +240,8 @@ public class OverlayManagerSettingsTests { @Test public void testPersistDifferentOverlaysSameUser() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_B0); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_B_USER0); ByteArrayOutputStream os = new ByteArrayOutputStream(); mSettings.persist(os); @@ -304,17 +250,17 @@ public class OverlayManagerSettingsTests { assertEquals(1, countXmlTags(xml, "overlays")); assertEquals(2, countXmlTags(xml, "item")); assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName", - OVERLAY_A0.packageName)); + OVERLAY_A.getPackageName())); assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName", - OVERLAY_B0.packageName)); + OVERLAY_B.getPackageName())); assertEquals(2, countXmlAttributesWhere(xml, "item", "userId", - Integer.toString(OVERLAY_A0.userId))); + Integer.toString(USER_0))); } @Test public void testPersistSameOverlayDifferentUsers() throws Exception { - insert(OVERLAY_A0); - insert(OVERLAY_A1); + insertSetting(OVERLAY_A_USER0); + insertSetting(OVERLAY_A_USER1); ByteArrayOutputStream os = new ByteArrayOutputStream(); mSettings.persist(os); @@ -323,17 +269,17 @@ public class OverlayManagerSettingsTests { assertEquals(1, countXmlTags(xml, "overlays")); assertEquals(2, countXmlTags(xml, "item")); assertEquals(2, countXmlAttributesWhere(xml, "item", "packageName", - OVERLAY_A0.packageName)); + OVERLAY_A.getPackageName())); assertEquals(1, countXmlAttributesWhere(xml, "item", "userId", - Integer.toString(OVERLAY_A0.userId))); + Integer.toString(USER_0))); assertEquals(1, countXmlAttributesWhere(xml, "item", "userId", - Integer.toString(OVERLAY_A1.userId))); + Integer.toString(USER_1))); } @Test public void testPersistEnabled() throws Exception { - insert(OVERLAY_A0); - mSettings.setEnabled(OVERLAY_A0.packageName, OVERLAY_A0.userId, true); + insertSetting(OVERLAY_A_USER0); + mSettings.setEnabled(OVERLAY_A, USER_0, true); ByteArrayOutputStream os = new ByteArrayOutputStream(); mSettings.persist(os); @@ -351,7 +297,7 @@ public class OverlayManagerSettingsTests { ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8")); mSettings.restore(is); - assertDoesNotContain(mSettings, "com.test.overlay", 0); + assertDoesNotContain(mSettings, new OverlayIdentifier("com.test.overlay"), 0); } @Test @@ -361,6 +307,7 @@ public class OverlayManagerSettingsTests { "\n" + "\n" + " list = IntStream.of(haystack) .boxed() @@ -490,16 +448,11 @@ public class OverlayManagerSettingsTests { } } - private static void assertListsAreEqual(List list, OverlayInfo... array) { - List other = Stream.of(array) - .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); - assertListsAreEqual(list, other); - } - - private static void assertListsAreEqual(List list, List other) { - if (!list.equals(other)) { + private static void assertListsAreEqual( + @NonNull List expected, @Nullable List actual) { + if (!expected.equals(actual)) { fail(String.format("lists [%s] and [%s] differ", - TextUtils.join(",", list), TextUtils.join(",", other))); + TextUtils.join(",", expected), TextUtils.join(",", actual))); } } }