Query overlays using package and name
Fabricated RROs identifiers are made using package name and overlay name
combinations. In the future, packages with multiple <overlay> tags will
use overlay name to distinguish between the tags.
This changes the OMS to manage overlays through OverlayIdentifiers.
The identifier is a overlay package and name combination. The name field
is optional so overlays without an overlay name can be managed.
This change simplifies the logic required to update the set of target
packages affected by overlay changes.
Bug: 172471315
Test: OverlayDeviceTests
Test: OverlayImpl tests
Change-Id: Iac679828d7480e5c7a2c1fe4ea8b73401d8d487f
diff --git a/core/java/android/content/om/CriticalOverlayInfo.java b/core/java/android/content/om/CriticalOverlayInfo.java
new file mode 100644
index 0000000..8d14f43
--- /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 0b950b4..a99e792 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;
@@ -66,6 +67,17 @@
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 0000000..d1c7770
--- /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 0000000..454d0d1
--- /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<OverlayIdentifier> CREATOR
+ = new Parcelable.Creator<OverlayIdentifier>() {
+ @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 517e4bd..8c316d7 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -26,6 +26,8 @@
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 @@
* @hide
*/
@SystemApi
-public final class OverlayInfo implements Parcelable {
+public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
/** @hide */
@IntDef(prefix = "STATE_", value = {
@@ -143,6 +145,14 @@
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
*
* @hide
@@ -201,6 +211,8 @@
*/
public final boolean isMutable;
+ private OverlayIdentifier mIdentifierCached;
+
/**
* Create a new OverlayInfo based on source with an updated state.
*
@@ -210,17 +222,27 @@
* @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 @@
/** @hide */
public OverlayInfo(Parcel source) {
packageName = source.readString();
+ overlayName = source.readString();
targetPackageName = source.readString();
targetOverlayableName = source.readString();
category = source.readString();
@@ -247,9 +270,10 @@
}
/**
- * Returns package name of the current overlay.
+ * {@inheritDoc}
* @hide
*/
+ @Override
@SystemApi
@NonNull
public String getPackageName() {
@@ -257,9 +281,20 @@
}
/**
- * 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 @@
/**
* Returns the category of the current overlay.
- * @hide\
+ *
+ * @hide
*/
@SystemApi
@Nullable
@@ -278,6 +314,7 @@
/**
* Returns user handle for which this overlay applies to.
+ *
* @hide
*/
@SystemApi
@@ -287,15 +324,29 @@
}
/**
- * 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 @@
@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 @@
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 @@
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 @@
@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 7c14c28..0f7e01b 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -205,6 +205,25 @@
}
/**
+ * 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
* overlay priority with the highest priority at the end of the list.
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
index 1fa8973..d03ad9d 100644
--- a/core/java/android/content/om/OverlayManagerTransaction.java
+++ b/core/java/android/content/om/OverlayManagerTransaction.java
@@ -60,12 +60,12 @@
private OverlayManagerTransaction(@NonNull final Parcel source) {
final int size = source.readInt();
- mRequests = new ArrayList<Request>(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 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 @@
* 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 @@
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 76c01a7..3c0c131 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 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 @@
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 @@
FutureTask<Boolean> 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 @@
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 @@
}
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 636f4c8..8e4b9ef 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.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 @@
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 0b4f5e2..594fa47 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 @@
@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 @@
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 @@
List<OverlayInfo> 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 @@
List<OverlayInfo> 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 @@
List<OverlayInfo> 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 @@
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 @@
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 420f755..5587203 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 @@
@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 a86255e..d275433 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 @@
@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 51c4118..72cba8b 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 @@
@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 1e2e054..88afcb2 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 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.om.OverlayIdentifier;
import android.os.UserHandle;
/**
@@ -26,6 +27,7 @@
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 @@
}
/** 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 eeb2655..e87bf55 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 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 @@
* 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 @@
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 @@
// 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 @@
// 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 8121a43e9..2d540de 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 @@
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 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 @@
}
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 @@
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 @@
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 fd2fb1f..b0e2922 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.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.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.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 @@
private final OverlayActorEnforcer mActorEnforcer;
- private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> {
- persistSettings();
- FgThread.getHandler().post(() -> {
- List<String> 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 @@
// 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<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
- updatePackageManager(targets, users.get(i).id);
+ updatePackageManager(mImpl.updateOverlaysForUser(users.get(i).id));
}
}
}
@@ -333,9 +323,7 @@
// ensure overlays in the settings are up-to-date, and propagate
// any asset changes to the rest of the system
synchronized (mLock) {
- final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
- final List<String> affectedTargets = updatePackageManager(targets, newUserId);
- updateActivityManager(affectedTargets, newUserId);
+ updateActivityManager(updatePackageManager(mImpl.updateOverlaysForUser(newUserId)));
}
persistSettings();
} finally {
@@ -417,19 +405,11 @@
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 @@
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 @@
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 @@
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 @@
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 @@
if (userId != UserHandle.USER_NULL) {
try {
traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_ADDED");
- final ArrayList<String> targets;
synchronized (mLock) {
- targets = mImpl.updateOverlaysForUser(userId);
+ updatePackageManager(mImpl.updateOverlaysForUser(userId));
}
- updatePackageManager(targets, userId);
} finally {
traceEnd(TRACE_TAG_RRO);
}
@@ -628,16 +578,22 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
private Optional<PackageAndUser> 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<PackageAndUser> opt1 =
- mImpl.setEnabled(request.packageName, true, request.userId);
+ mImpl.setEnabled(request.overlay, true, realUserId);
Optional<PackageAndUser> 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 @@
executeRequest(request).ifPresent(target -> {
Set<String> userTargets = transactionTargets.get(target.userId);
if (userTargets == null) {
- userTargets = new ArraySet<String>();
+ userTargets = new ArraySet<>();
transactionTargets.put(target.userId, userTargets);
}
userTargets.add(target.packageName);
@@ -975,10 +949,7 @@
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 @@
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 @@
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 @@
};
private static final class PackageManagerHelperImpl implements PackageManagerHelper {
-
+ private static class AndroidPackageUsers {
+ private AndroidPackage mPackage;
+ private final Set<Integer> 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 @@
// 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<HashMap<String, PackageInfo>> mCache = new SparseArray<>();
+ private final ArrayMap<String, AndroidPackageUsers> mCache = new ArrayMap<>();
+ private final Set<Integer> mInitializedUsers = new ArraySet<>();
PackageManagerHelperImpl(Context context) {
mContext = context;
@@ -1133,29 +1111,112 @@
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<String, AndroidPackage> initializeForUser(final int userId) {
+ if (!mInitializedUsers.contains(userId)) {
+ mInitializedUsers.add(userId);
+ mPackageManagerInternal.forEachInstalledPackage(
+ (pkg) -> addPackageUser(pkg, userId), userId);
+ }
+
+ final ArrayMap<String, AndroidPackage> 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
@@ -1179,15 +1240,6 @@
}
@Override
- public List<PackageInfo> getOverlayPackages(final int userId) {
- final List<PackageInfo> 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(
PackageManagerInternal.PACKAGE_OVERLAY_CONFIG_SIGNATURE,
@@ -1200,16 +1252,14 @@
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 @@
@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 @@
mContext.enforceCallingOrSelfPermission(permission, message);
}
- public PackageInfo getCachedPackageInfo(@NonNull final String packageName,
- final int userId) {
- final HashMap<String, PackageInfo> 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<String, PackageInfo> 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<String, PackageInfo> 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 @@
}
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 @@
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<String, PackageInfo> map = mCache.get(userId);
- for (Map.Entry<String, PackageInfo> 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<PackageAndUser> updatedTargets) {
+ if (CollectionUtils.isEmpty(updatedTargets)) {
+ return;
+ }
+ persistSettings();
+ final SparseArray<ArraySet<String>> userTargets = groupTargetsByUserId(updatedTargets);
+ FgThread.getHandler().post(() -> {
+ for (int i = 0, n = userTargets.size(); i < n; i++) {
+ final ArraySet<String> targets = userTargets.valueAt(i);
+ final int userId = userTargets.keyAt(i);
+
+ // Update the overlay paths in package manager.
+ final List<String> 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<ArraySet<String>> groupTargetsByUserId(
+ @Nullable final Set<PackageAndUser> targetsAndUsers) {
+ final SparseArray<ArraySet<String>> userTargets = new SparseArray<>();
+ CollectionUtils.forEach(targetsAndUsers, target -> {
+ ArraySet<String> 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<String> 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 @@
* Tell the activity manager to tell a set of packages to reload their
* resources.
*/
- private void updateActivityManager(List<String> targetPackageNames, final int userId) {
+ private void updateActivityManager(@NonNull List<String> targetPackageNames, final int userId) {
final IActivityManager am = ActivityManager.getService();
try {
am.scheduleApplicationInfoChanged(targetPackageNames, userId);
@@ -1354,16 +1415,33 @@
}
}
- private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) {
- return updatePackageManager(Collections.singletonList(targetPackageNames), userId);
+ private void updateActivityManager(@NonNull SparseArray<List<String>> targetPackageNames) {
+ for (int i = 0, n = targetPackageNames.size(); i < n; i++) {
+ updateActivityManager(targetPackageNames.valueAt(i), targetPackageNames.keyAt(i));
+ }
+ }
+
+ @NonNull
+ private SparseArray<List<String>> updatePackageManager(@Nullable Set<PackageAndUser> targets) {
+ if (CollectionUtils.isEmpty(targets)) {
+ return new SparseArray<>();
+ }
+ final SparseArray<List<String>> affectedTargets = new SparseArray<>();
+ final SparseArray<ArraySet<String>> 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<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
+ @NonNull
+ private List<String> updatePackageManager(@NonNull Collection<String> 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 c547c36..6eb4edc 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 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.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 @@
* 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 @@
* 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<String> updateOverlaysForUser(final int newUserId) {
+ @NonNull
+ ArraySet<PackageAndUser> updateOverlaysForUser(final int newUserId) {
if (DEBUG) {
Slog.d(TAG, "updateOverlaysForUser newUserId=" + newUserId);
}
- final Set<String> packagesToUpdateAssets = new ArraySet<>();
- final ArrayMap<String, List<OverlayInfo>> tmp = mSettings.getOverlaysForUser(newUserId);
- final int tmpSize = tmp.size();
- final ArrayMap<String, OverlayInfo> storedOverlayInfos = new ArrayMap<>(tmpSize);
- for (int i = 0; i < tmpSize; i++) {
- final List<OverlayInfo> 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<PackageAndUser> updatedTargets = new ArraySet<>();
+ final ArrayMap<String, AndroidPackage> 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<PackageInfo> 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<String> 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 @@
// 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 @@
}
}
- return new ArrayList<>(packagesToUpdateAssets);
+ return updatedTargets;
}
void onUserRemoved(final int userId) {
@@ -258,236 +207,150 @@
mSettings.removeUser(userId);
}
- Optional<PackageAndUser> onTargetPackageAdded(@NonNull final String packageName,
+ @NonNull
+ Set<PackageAndUser> 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<PackageAndUser> onTargetPackageChanged(@NonNull final String packageName,
+ @NonNull
+ Set<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> updateAndRefreshOverlaysForTarget(
- @NonNull final String targetPackageName, final int userId, final int flags)
+ @NonNull
+ Set<PackageAndUser> onPackageReplacing(@NonNull final String pkgName, final int userId)
throws OperationFailedException {
- final List<OverlayInfo> 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<PackageAndUser> onPackageReplaced(@NonNull final String pkgName, final int userId)
+ throws OperationFailedException {
+ return reconcileSettingsForPackage(pkgName, userId, 0 /* flags */);
+ }
+
+ @NonNull
+ Set<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> removeOverlaysForUser(
+ @NonNull final Predicate<OverlayInfo> condition, final int userId) {
+ final List<OverlayInfo> overlays = mSettings.removeIf(
+ io -> userId == io.userId && condition.test(io) );
+ Set<PackageAndUser> 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<PackageAndUser> 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<OverlayInfo> 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<String> 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<PackageAndUser> onOverlayPackageAdded(@NonNull final String packageName,
- final int userId) throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
+ @NonNull
+ private Set<PackageAndUser> 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<PackageAndUser> 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<PackageAndUser> 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));
}
- return Optional.empty();
+
+ // Update the enabled state of the overlay.
+ if (updateState(currentInfo, userId, flags)) {
+ updatedTargets = CollectionUtils.add(updatedTargets,
+ new PackageAndUser(currentInfo.targetPackageName, userId));
+ }
} catch (OverlayManagerSettings.BadKeyException e) {
throw new OperationFailedException("failed to update settings", e);
}
+ return updatedTargets;
}
- Optional<PackageAndUser> onOverlayPackageReplacing(@NonNull final String packageName,
- final int userId) throws OperationFailedException {
+ @NonNull
+ private Set<PackageAndUser> 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<PackageAndUser> updatedTargets = Collections.emptySet();
+ updatedTargets = CollectionUtils.addAll(updatedTargets,
+ updateOverlaysForTarget(pkgName, userId, flags));
- Optional<PackageAndUser> 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<PackageAndUser> 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 @@
return mSettings.getOverlaysForUser(userId);
}
- Optional<PackageAndUser> setEnabled(@NonNull final String packageName, final boolean enable,
- final int userId) throws OperationFailedException {
+ Optional<PackageAndUser> 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 @@
}
}
- Optional<PackageAndUser> setEnabledExclusive(@NonNull final String packageName,
+ Optional<PackageAndUser> 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<OverlayInfo> allOverlays = getOverlayInfosForTarget(targetPackageName, userId);
+ // Remove the overlay to have enabled from the list of overlays to disable.
+ List<OverlayInfo> 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 @@
}
}
- 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<PackageAndUser> setPriority(@NonNull final String packageName,
- @NonNull final String newParentPackageName, final int userId)
+ Optional<PackageAndUser> 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<PackageAndUser> setHighestPriority(@NonNull final String packageName,
+ Optional<PackageAndUser> 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<PackageAndUser> setLowestPriority(@NonNull final String packageName, final int userId)
- throws OperationFailedException {
- if (DEBUG) {
- Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
- }
+ Optional<PackageAndUser> 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 @@
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 @@
/**
* 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 @@
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 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 0613dff..b46f368 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 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.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 @@
*/
private final ArrayList<SettingsItem> 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 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<SettingsItem> items = selectWhereTarget(targetPackageName, userId);
+ items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay);
+ return CollectionUtils.map(items, SettingsItem::getOverlayInfo);
}
ArrayMap<String, List<OverlayInfo>> 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<SettingsItem> items = selectWhereUser(userId);
+ items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay);
+
+ final ArrayMap<String, List<OverlayInfo>> 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<OverlayInfo> getOverlayInfosForPackage(@NonNull final String packageName,
+ final int userId) {
+ final List<SettingsItem> items = selectWhereOverlay(packageName, userId);
+ items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay);
+ return CollectionUtils.map(items, SettingsItem::getOverlayInfo);
+ }
+
+ @NonNull
+ List<OverlayInfo> removeIf(@NonNull final Predicate<OverlayInfo> predicate, final int userId) {
+ return removeIf(info -> (predicate.test(info) && info.userId == userId));
+ }
+
+ @NonNull
+ List<OverlayInfo> removeIf(final @NonNull Predicate<OverlayInfo> predicate) {
+ List<OverlayInfo> 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 @@
/**
* 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 @@
}
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 @@
/**
* 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 @@
/**
* 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 @@
break;
}
}
-
mItems.add(i + 1, item);
}
@@ -308,7 +348,12 @@
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 @@
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 @@
@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 @@
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 @@
public static void restore(@NonNull final ArrayList<SettingsItem> 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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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<SettingsItem> selectWhereUser(final int userId) {
- return mItems.stream().filter(item -> item.mUserId == userId);
+ private List<SettingsItem> selectWhereUser(final int userId) {
+ final List<SettingsItem> selectedItems = new ArrayList<>();
+ CollectionUtils.addIf(mItems, selectedItems, i -> i.mUserId == userId);
+ return selectedItems;
}
- private Stream<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName,
+ private List<SettingsItem> selectWhereOverlay(@NonNull final String packageName,
final int userId) {
- return selectWhereUser(userId)
- .filter(item -> item.getTargetPackageName().equals(targetPackageName));
+ final List<SettingsItem> 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<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName,
+ final int userId) {
+ final List<SettingsItem> 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 bf99bd6..c8a659a 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.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 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 @@
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 @@
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 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 @@
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<OverlayInfo> 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 b1a8b4e..750f5c3 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.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 @@
* @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<String, AndroidPackage> 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
*/
@@ -64,11 +96,6 @@
Map<String, Map<String, String>> getNamedActors();
/**
- * @see PackageManagerInternal#getOverlayPackages(int)
- */
- List<PackageInfo> getOverlayPackages(int userId);
-
- /**
* Read from the APK and AndroidManifest of a package to return the overlayable defined for
* a given name.
*
@@ -80,19 +107,6 @@
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 3a292de..05ba532 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.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.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 @@
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 @@
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 @@
) {
fun toOverlayInfo() = OverlayInfo(
OVERLAY_PKG,
+ "",
targetPackageName,
targetOverlayableName,
null,
@@ -283,11 +285,10 @@
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 @@
var namedActorsMap: Map<String, Map<String, String>> = 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 @@
override fun getNamedActors() = namedActorsMap
+ override fun isInstantApp(packageName: String, userId: Int): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ override fun initializeForUser(userId: Int): ArrayMap<String, AndroidPackage> {
+ throw UnsupportedOperationException()
+ }
+
@Throws(IOException::class)
override fun getOverlayableForTarget(
packageName: String,
@@ -394,9 +403,6 @@
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 @@
}
}
- 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<PackageInfo> {
+ 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 5468fba..55cd772 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.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.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<String> a = impl.updateOverlaysForUser(USER);
- assertEquals(1, a.size());
- assertTrue(a.contains(TARGET));
+ final ArraySet<PackageAndUser> 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<String> b = impl.updateOverlaysForUser(USER);
- assertEquals(1, b.size());
- assertTrue(b.contains(TARGET));
-
- // do nothing, expect no change
- final List<String> 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<String> d = impl.updateOverlaysForUser(USER);
- assertEquals(2, d.size());
- assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target")));
-
- // do nothing, expect no change
- final List<String> f = impl.updateOverlaysForUser(USER);
- assertEquals(1, f.size());
- assertTrue(f.contains("some.other.target"));
+ final ArraySet<PackageAndUser> 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 @@
@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 @@
@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<Boolean, Boolean> 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 @@
@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 @@
@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 @@
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 33dbcc0..f044a09 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.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 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 @@
@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 @@
@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<OverlayInfo> 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<OverlayInfo> 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<OverlayInfo> ois3 = impl.getOverlayInfosForTarget(TARGET, USER3);
assertNotNull(ois3);
@@ -97,10 +102,10 @@
@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<String, List<OverlayInfo>> everything = impl.getOverlaysForUser(USER);
@@ -109,13 +114,13 @@
final List<OverlayInfo> 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<OverlayInfo> 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<String, List<OverlayInfo>> everything2 = impl.getOverlaysForUser(USER2);
assertNotNull(everything2);
@@ -124,26 +129,26 @@
@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 @@
@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<PackageAndUser>, Optional<PackageAndUser>> 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<PackageAndUser>, Set<PackageAndUser>> 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 @@
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 @@
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 @@
@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 @@
@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 @@
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 2c477c8..2aad7cc 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.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 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 @@
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 @@
*
* @throws IllegalStateException if the package is currently installed
*/
- void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId)
+ Set<PackageAndUser> 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 @@
*
* @throws IllegalStateException if the package is not currently installed
*/
- Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> upgradePackage(
+ Pair<Set<PackageAndUser>, Set<PackageAndUser>> 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<PackageAndUser> opt1 = Optional.empty();
- if (replacedPackage.targetPackageName != null) {
- opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
- }
+
+ final Set<PackageAndUser> updatedPackages1 =
+ CollectionUtils.emptyIfNull(mImpl.onPackageReplacing(pkg.packageName, userId));
mState.add(pkg, userId);
- Optional<PackageAndUser> opt2;
- if (pkg.targetPackage == null) {
- opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId);
- } else {
- opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
- }
+ final Set<PackageAndUser> updatedPackages2 =
+ CollectionUtils.emptyIfNull(mImpl.onPackageReplaced(pkg.packageName, userId));
- return Pair.create(opt1, opt2);
+ return Pair.create(updatedPackages1, updatedPackages2);
}
/**
@@ -208,17 +199,13 @@
*
* @throws IllegalStateException if the package is not currently installed
*/
- void uninstallPackage(String packageName, int userId) throws OperationFailedException {
+ Set<PackageAndUser> 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 @@
}
}
- List<Package> 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 @@
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 @@
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<String, AndroidPackage> initializeForUser(int userId) {
+ final ArrayMap<String, AndroidPackage> 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
@@ -371,14 +376,6 @@
}
@Override
- public List<PackageInfo> 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 e3e7768..e89ab23 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 @@
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.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 @@
@Test
public void testSettingsInitiallyEmpty() throws Exception {
- final int userId = 0;
- Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(userId);
+ final Map<String, List<OverlayInfo>> 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<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(OVERLAY_A0.userId);
- assertEquals(1, map.keySet().size());
- assertTrue(map.keySet().contains(OVERLAY_A0.targetPackageName));
+ final Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(USER_0);
+ assertEquals(Set.of(TARGET_PACKAGE), map.keySet());
- List<OverlayInfo> 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<OverlayInfo> 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.<String, List<OverlayInfo>>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<OverlayInfo> 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<OverlayInfo> 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 @@
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<OverlayInfo> 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<OverlayInfo> 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 @@
@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 @@
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 @@
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 @@
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 @@
"<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n"
+ "<overlays version='" + version + "'>\n"
+ "<item packageName='com.test.overlay'\n"
+ + " overlayName='test'\n"
+ " userId='1234'\n"
+ " targetPackageName='com.test.target'\n"
+ " baseCodePath='/data/app/com.test.overlay-1/base.apk'\n"
@@ -373,20 +320,22 @@
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
mSettings.restore(is);
- OverlayInfo oi = mSettings.getOverlayInfo("com.test.overlay", 1234);
+ final OverlayIdentifier identifier = new OverlayIdentifier("com.test.overlay", "test");
+ OverlayInfo oi = mSettings.getOverlayInfo(identifier, 1234);
assertNotNull(oi);
assertEquals("com.test.overlay", oi.packageName);
+ assertEquals("test", oi.overlayName);
assertEquals("com.test.target", oi.targetPackageName);
assertEquals("/data/app/com.test.overlay-1/base.apk", oi.baseCodePath);
assertEquals(1234, oi.userId);
assertEquals(STATE_DISABLED, oi.state);
- assertFalse(mSettings.getEnabled("com.test.overlay", 1234));
+ assertFalse(mSettings.getEnabled(identifier, 1234));
}
@Test
public void testPersistAndRestore() throws Exception {
- insert(OVERLAY_A0);
- insert(OVERLAY_B1);
+ insertSetting(OVERLAY_A_USER0);
+ insertSetting(OVERLAY_B_USER1);
ByteArrayOutputStream os = new ByteArrayOutputStream();
mSettings.persist(os);
@@ -394,11 +343,11 @@
OverlayManagerSettings newSettings = new OverlayManagerSettings();
newSettings.restore(is);
- OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId);
- assertEquals(OVERLAY_A0, a);
+ OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A, USER_0);
+ assertEquals(OVERLAY_A_USER0, a);
- OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B1.packageName, OVERLAY_B1.userId);
- assertEquals(OVERLAY_B1, b);
+ OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B, USER_1);
+ assertEquals(OVERLAY_B_USER1, b);
}
private int countXmlTags(InputStream in, String tagToLookFor) throws Exception {
@@ -433,43 +382,52 @@
return count;
}
- private void insert(OverlayInfo oi) throws Exception {
- mSettings.init(oi.packageName, oi.userId, oi.targetPackageName, null, oi.baseCodePath,
- true, false,0, oi.category);
- mSettings.setState(oi.packageName, oi.userId, oi.state);
- mSettings.setEnabled(oi.packageName, oi.userId, false);
+ private void insertSetting(OverlayInfo oi) throws Exception {
+ mSettings.init(oi.getOverlayIdentifier(), oi.userId, oi.targetPackageName, null,
+ oi.baseCodePath, true, false,0, oi.category);
+ mSettings.setState(oi.getOverlayIdentifier(), oi.userId, oi.state);
+ mSettings.setEnabled(oi.getOverlayIdentifier(), oi.userId, false);
}
private static void assertContains(final OverlayManagerSettings settings,
final OverlayInfo oi) {
- assertContains(settings, oi.packageName, oi.userId);
- }
-
- private static void assertContains(final OverlayManagerSettings settings,
- final String packageName, int userId) {
try {
- settings.getOverlayInfo(packageName, userId);
+ settings.getOverlayInfo(oi.getOverlayIdentifier(), oi.userId);
} catch (OverlayManagerSettings.BadKeyException e) {
- fail(String.format("settings does not contain packageName=%s userId=%d",
- packageName, userId));
+ fail(String.format("settings does not contain overlay=%s userId=%d",
+ oi.getOverlayIdentifier(), oi.userId));
}
}
private static void assertDoesNotContain(final OverlayManagerSettings settings,
final OverlayInfo oi) {
- assertDoesNotContain(settings, oi.packageName, oi.userId);
+ assertDoesNotContain(settings, oi.getOverlayIdentifier(), oi.userId);
}
private static void assertDoesNotContain(final OverlayManagerSettings settings,
- final String packageName, int userId) {
+ final OverlayIdentifier overlay, int userId) {
try {
- settings.getOverlayInfo(packageName, userId);
- fail(String.format("settings contains packageName=%s userId=%d", packageName, userId));
+ settings.getOverlayInfo(overlay, userId);
+ fail(String.format("settings contains overlay=%s userId=%d", overlay, userId));
} catch (OverlayManagerSettings.BadKeyException e) {
// do nothing: we expect to end up here
}
}
+ private static OverlayInfo createInfo(@NonNull OverlayIdentifier identifier, int userId) {
+ return new OverlayInfo(
+ identifier.getPackageName(),
+ identifier.getOverlayName(),
+ "com.test.target",
+ null,
+ "some-category",
+ "/data/app/" + identifier + "/base.apk",
+ STATE_DISABLED,
+ userId,
+ 0,
+ true);
+ }
+
private static void assertContains(int[] haystack, int needle) {
List<Integer> list = IntStream.of(haystack)
.boxed()
@@ -490,16 +448,11 @@
}
}
- private static void assertListsAreEqual(List<OverlayInfo> list, OverlayInfo... array) {
- List<OverlayInfo> other = Stream.of(array)
- .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
- assertListsAreEqual(list, other);
- }
-
- private static void assertListsAreEqual(List<OverlayInfo> list, List<OverlayInfo> other) {
- if (!list.equals(other)) {
+ private static void assertListsAreEqual(
+ @NonNull List<OverlayInfo> expected, @Nullable List<OverlayInfo> 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)));
}
}
}