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))); } } }