diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java index a3487be3cb81e..55a6cab2f2d54 100644 --- a/core/java/android/content/om/OverlayInfo.java +++ b/core/java/android/content/om/OverlayInfo.java @@ -45,7 +45,7 @@ public final class OverlayInfo implements Parcelable { STATE_NO_IDMAP, STATE_DISABLED, STATE_ENABLED, - STATE_ENABLED_STATIC, + STATE_ENABLED_IMMUTABLE, // @Deprecated STATE_TARGET_IS_BEING_REPLACED, STATE_OVERLAY_IS_BEING_REPLACED, }) @@ -117,11 +117,12 @@ public final class OverlayInfo implements Parcelable { /** * The overlay package is currently enabled because it is marked as - * 'static'. It cannot be disabled but will change state if for instance + * 'immutable'. It cannot be disabled but will change state if for instance * its target is uninstalled. * @hide */ - public static final int STATE_ENABLED_STATIC = 6; + @Deprecated + public static final int STATE_ENABLED_IMMUTABLE = 6; /** * Overlay category: theme. @@ -180,21 +181,21 @@ public final class OverlayInfo implements Parcelable { public final int userId; /** - * Priority as read from the manifest. Used if isStatic is true. Not - * intended to be exposed to 3rd party. + * Priority as configured by {@link com.android.internal.content.om.OverlayConfig}. + * Not intended to be exposed to 3rd party. * * @hide */ public final int priority; /** - * isStatic as read from the manifest. If true, the overlay is - * unconditionally loaded and cannot be unloaded. Not intended to be + * isMutable as configured by {@link com.android.internal.content.om.OverlayConfig}. + * If false, the overlay is unconditionally loaded and cannot be unloaded. Not intended to be * exposed to 3rd party. * * @hide */ - public final boolean isStatic; + public final boolean isMutable; /** * Create a new OverlayInfo based on source with an updated state. @@ -207,14 +208,14 @@ public final class OverlayInfo implements Parcelable { 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.isStatic); + source.isMutable); } /** @hide */ public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName, @Nullable String targetOverlayableName, @Nullable String category, @NonNull String baseCodePath, int state, int userId, - int priority, boolean isStatic) { + int priority, boolean isMutable) { this.packageName = packageName; this.targetPackageName = targetPackageName; this.targetOverlayableName = targetOverlayableName; @@ -223,7 +224,7 @@ public final class OverlayInfo implements Parcelable { this.state = state; this.userId = userId; this.priority = priority; - this.isStatic = isStatic; + this.isMutable = isMutable; ensureValidState(); } @@ -237,7 +238,7 @@ public final class OverlayInfo implements Parcelable { state = source.readInt(); userId = source.readInt(); priority = source.readInt(); - isStatic = source.readBoolean(); + isMutable = source.readBoolean(); ensureValidState(); } @@ -307,7 +308,7 @@ public final class OverlayInfo implements Parcelable { case STATE_NO_IDMAP: case STATE_DISABLED: case STATE_ENABLED: - case STATE_ENABLED_STATIC: + case STATE_ENABLED_IMMUTABLE: case STATE_TARGET_IS_BEING_REPLACED: case STATE_OVERLAY_IS_BEING_REPLACED: break; @@ -331,7 +332,7 @@ public final class OverlayInfo implements Parcelable { dest.writeInt(state); dest.writeInt(userId); dest.writeInt(priority); - dest.writeBoolean(isStatic); + dest.writeBoolean(isMutable); } public static final @android.annotation.NonNull Parcelable.Creator CREATOR = @@ -360,7 +361,7 @@ public final class OverlayInfo implements Parcelable { public boolean isEnabled() { switch (state) { case STATE_ENABLED: - case STATE_ENABLED_STATIC: + case STATE_ENABLED_IMMUTABLE: return true; default: return false; @@ -386,8 +387,8 @@ public final class OverlayInfo implements Parcelable { return "STATE_DISABLED"; case STATE_ENABLED: return "STATE_ENABLED"; - case STATE_ENABLED_STATIC: - return "STATE_ENABLED_STATIC"; + case STATE_ENABLED_IMMUTABLE: + return "STATE_ENABLED_IMMUTABLE"; case STATE_TARGET_IS_BEING_REPLACED: return "STATE_TARGET_IS_BEING_REPLACED"; case STATE_OVERLAY_IS_BEING_REPLACED: diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java index 1a862fa016f5b..f699eb8c4639a 100644 --- a/core/java/com/android/internal/content/om/OverlayConfig.java +++ b/core/java/com/android/internal/content/om/OverlayConfig.java @@ -46,12 +46,13 @@ import java.util.function.Supplier; * @see OverlayConfigParser */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) -final public class OverlayConfig { +public class OverlayConfig { static final String TAG = "OverlayConfig"; // The default priority of an overlay that has not been configured. Overlays with default // priority have a higher precedence than configured overlays. - private static final int DEFAULT_PRIORITY = Integer.MAX_VALUE; + @VisibleForTesting + public static final int DEFAULT_PRIORITY = Integer.MAX_VALUE; @VisibleForTesting public static final class Configuration { diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 3c31f6a7f0d7e..ca3b7c7d95727 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -62,6 +62,7 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.content.om.OverlayConfig; import com.android.server.FgThread; import com.android.server.IoThread; import com.android.server.LocalServices; @@ -248,7 +249,8 @@ public final class OverlayManagerService extends SystemService { IdmapManager im = new IdmapManager(mPackageManager); mSettings = new OverlayManagerSettings(); mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings, - getDefaultOverlayPackages(), new OverlayChangeListener()); + OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(), + new OverlayChangeListener()); mActorEnforcer = new OverlayActorEnforcer(mPackageManager); final IntentFilter packageFilter = new IntentFilter(); @@ -835,7 +837,7 @@ public final class OverlayManagerService extends SystemService { case "basecodepath": case "state": case "isenabled": - case "isstatic": + case "ismutable": case "priority": case "category": dumpState.setField(arg); diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 9623542a29005..2493057e01212 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -18,7 +18,7 @@ package com.android.server.om; import static android.content.om.OverlayInfo.STATE_DISABLED; import static android.content.om.OverlayInfo.STATE_ENABLED; -import static android.content.om.OverlayInfo.STATE_ENABLED_STATIC; +import static android.content.om.OverlayInfo.STATE_ENABLED_IMMUTABLE; import static android.content.om.OverlayInfo.STATE_MISSING_TARGET; import static android.content.om.OverlayInfo.STATE_NO_IDMAP; import static android.content.om.OverlayInfo.STATE_OVERLAY_IS_BEING_REPLACED; @@ -37,6 +37,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import com.android.internal.content.om.OverlayConfig; import com.android.internal.util.ArrayUtils; import java.io.PrintWriter; @@ -69,6 +70,7 @@ final class OverlayManagerServiceImpl { private final PackageManagerHelper mPackageManager; private final IdmapManager mIdmapManager; private final OverlayManagerSettings mSettings; + private final OverlayConfig mOverlayConfig; private final String[] mDefaultOverlays; private final OverlayChangeListener mListener; @@ -83,7 +85,7 @@ final class OverlayManagerServiceImpl { * should either scrap the overlay manager's previous settings or merge the old * settings with the new. */ - private static boolean mustReinitializeOverlay(@NonNull final PackageInfo theTruth, + private boolean mustReinitializeOverlay(@NonNull final PackageInfo theTruth, @Nullable final OverlayInfo oldSettings) { if (oldSettings == null) { return true; @@ -94,27 +96,35 @@ final class OverlayManagerServiceImpl { if (!Objects.equals(theTruth.targetOverlayableName, oldSettings.targetOverlayableName)) { return true; } - if (theTruth.isStaticOverlayPackage() != oldSettings.isStatic) { + + boolean isMutable = isPackageConfiguredMutable(theTruth.packageName); + if (isMutable != oldSettings.isMutable) { return true; } - // a change in priority is only relevant for static RROs: specifically, - // a regular RRO should not have its state reset only because a change - // in priority - if (theTruth.isStaticOverlayPackage() - && theTruth.overlayPriority != oldSettings.priority) { + + if (getPackageConfiguredPriority(theTruth.packageName) != oldSettings.priority) { return true; } + + // If an immutable overlay changes its configured enabled state, reinitialize the overlay. + if (!isMutable && isPackageConfiguredEnabled(theTruth.packageName) + != oldSettings.isEnabled()) { + return true; + } + return false; } OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager, @NonNull final IdmapManager idmapManager, @NonNull final OverlayManagerSettings settings, + @NonNull final OverlayConfig overlayConfig, @NonNull final String[] defaultOverlays, @NonNull final OverlayChangeListener listener) { mPackageManager = packageManager; mIdmapManager = idmapManager; mSettings = settings; + mOverlayConfig = overlayConfig; mDefaultOverlays = defaultOverlays; mListener = listener; } @@ -162,8 +172,9 @@ final class OverlayManagerServiceImpl { overlayPackage.overlayTarget, overlayPackage.targetOverlayableName, overlayPackage.applicationInfo.getBaseCodePath(), - overlayPackage.isStaticOverlayPackage(), - overlayPackage.overlayPriority, + isPackageConfiguredMutable(overlayPackage.packageName), + isPackageConfiguredEnabled(overlayPackage.packageName), + getPackageConfiguredPriority(overlayPackage.packageName), overlayPackage.overlayCategory); } @@ -374,7 +385,9 @@ final class OverlayManagerServiceImpl { mSettings.init(packageName, userId, overlayPackage.overlayTarget, overlayPackage.targetOverlayableName, overlayPackage.applicationInfo.getBaseCodePath(), - overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority, + isPackageConfiguredMutable(overlayPackage.packageName), + isPackageConfiguredEnabled(overlayPackage.packageName), + getPackageConfiguredPriority(overlayPackage.packageName), overlayPackage.overlayCategory); try { if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) { @@ -439,8 +452,10 @@ final class OverlayManagerServiceImpl { mListener.onOverlaysChanged(pkg.overlayTarget, userId); } mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName, - pkg.applicationInfo.getBaseCodePath(), pkg.isStaticOverlayPackage(), - pkg.overlayPriority, pkg.overlayCategory); + pkg.applicationInfo.getBaseCodePath(), + isPackageConfiguredMutable(pkg.packageName), + isPackageConfiguredEnabled(pkg.packageName), + getPackageConfiguredPriority(pkg.packageName), pkg.overlayCategory); } if (updateState(pkg.overlayTarget, packageName, userId, 0)) { @@ -492,13 +507,13 @@ final class OverlayManagerServiceImpl { return false; } - // Ignore static overlays. - if (overlayPackage.isStaticOverlayPackage()) { - return false; - } - try { final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); + if (!oi.isMutable) { + // Ignore immutable overlays. + return false; + } + boolean modified = mSettings.setEnabled(packageName, userId, enable); modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0); @@ -534,7 +549,8 @@ final class OverlayManagerServiceImpl { // Disable all other overlays. allOverlays.remove(oi); for (int i = 0; i < allOverlays.size(); i++) { - final String disabledOverlayPackageName = allOverlays.get(i).packageName; + final OverlayInfo disabledInfo = allOverlays.get(i); + final String disabledOverlayPackageName = disabledInfo.packageName; final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo( disabledOverlayPackageName, userId); if (disabledOverlayPackageInfo == null) { @@ -542,8 +558,8 @@ final class OverlayManagerServiceImpl { continue; } - if (disabledOverlayPackageInfo.isStaticOverlayPackage()) { - // Don't touch static overlays. + if (!disabledInfo.isMutable) { + // Don't touch immutable overlays. continue; } if (withinCategory && !Objects.equals(disabledOverlayPackageInfo.overlayCategory, @@ -570,12 +586,16 @@ final class OverlayManagerServiceImpl { } } - private boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) { - final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); - if (overlayPackage == null || overlayPackage.isStaticOverlayPackage()) { - return false; - } - return true; + private boolean isPackageConfiguredMutable(@NonNull final String packageName) { + return mOverlayConfig.isMutable(packageName); + } + + private int getPackageConfiguredPriority(@NonNull final String packageName) { + return mOverlayConfig.getPriority(packageName); + } + + private boolean isPackageConfiguredEnabled(@NonNull final String packageName) { + return mOverlayConfig.isEnabled(packageName); } boolean setPriority(@NonNull final String packageName, @@ -585,7 +605,7 @@ final class OverlayManagerServiceImpl { + newParentPackageName + " userId=" + userId); } - if (!isPackageUpdatableOverlay(packageName, userId)) { + if (!isPackageConfiguredMutable(packageName)) { return false; } @@ -605,7 +625,7 @@ final class OverlayManagerServiceImpl { Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId); } - if (!isPackageUpdatableOverlay(packageName, userId)) { + if (!isPackageConfiguredMutable(packageName)) { return false; } @@ -625,7 +645,7 @@ final class OverlayManagerServiceImpl { Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId); } - if (!isPackageUpdatableOverlay(packageName, userId)) { + if (!isPackageConfiguredMutable(packageName)) { return false; } @@ -682,10 +702,10 @@ final class OverlayManagerServiceImpl { final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName, userId); - // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers. + // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native layers. if (targetPackage != null && overlayPackage != null && !("android".equals(targetPackageName) - && overlayPackage.isStaticOverlayPackage())) { + && !isPackageConfiguredMutable(overlayPackageName))) { mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); } @@ -737,10 +757,6 @@ final class OverlayManagerServiceImpl { return STATE_NO_IDMAP; } - if (overlayPackage.isStaticOverlayPackage()) { - return STATE_ENABLED_STATIC; - } - final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId); return enabled ? STATE_ENABLED : STATE_DISABLED; } diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java index b7346d4553196..6bccdfcf5bb2a 100644 --- a/services/core/java/com/android/server/om/OverlayManagerSettings.java +++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java @@ -67,32 +67,27 @@ final class OverlayManagerSettings { private final ArrayList mItems = new ArrayList<>(); void init(@NonNull final String packageName, final int userId, - @NonNull final String targetPackageName, @Nullable final String targetOverlayableName, - @NonNull final String baseCodePath, boolean isStatic, int priority, + @NonNull final String targetPackageName, @Nullable final String targetOverlayableName, + @NonNull final String baseCodePath, boolean isMutable, boolean isEnabled, int priority, @Nullable String overlayCategory) { remove(packageName, userId); final SettingsItem item = new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName, - baseCodePath, isStatic, priority, overlayCategory); - if (isStatic) { - // All static overlays are always enabled. - item.setEnabled(true); + baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled, isMutable, priority, + overlayCategory); - int i; - for (i = mItems.size() - 1; i >= 0; i--) { - SettingsItem parentItem = mItems.get(i); - if (parentItem.mIsStatic && parentItem.mPriority <= priority) { - break; - } + int i; + for (i = mItems.size() - 1; i >= 0; i--) { + SettingsItem parentItem = mItems.get(i); + if (parentItem.mPriority <= priority) { + break; } - int pos = i + 1; - if (pos == mItems.size()) { - mItems.add(item); - } else { - mItems.add(pos, item); - } - } else { + } + int pos = i + 1; + if (pos == mItems.size()) { mItems.add(item); + } else { + mItems.add(pos, item); } } @@ -182,19 +177,19 @@ final class OverlayManagerSettings { List getOverlaysForTarget(@NonNull final String targetPackageName, final int userId) { - // Static RROs targeting "android" are loaded from AssetManager, and so they should be + // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be // ignored in OverlayManagerService. return selectWhereTarget(targetPackageName, userId) - .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName()))) + .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName())) .map(SettingsItem::getOverlayInfo) .collect(Collectors.toList()); } ArrayMap> getOverlaysForUser(final int userId) { - // Static RROs targeting "android" are loaded from AssetManager, and so they should be + // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be // ignored in OverlayManagerService. return selectWhereUser(userId) - .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName()))) + .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName())) .map(SettingsItem::getOverlayInfo) .collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new, Collectors.toList())); @@ -320,7 +315,7 @@ final class OverlayManagerSettings { pw.println("mBaseCodePath..........: " + item.getBaseCodePath()); pw.println("mState.................: " + OverlayInfo.stateToString(item.getState())); pw.println("mIsEnabled.............: " + item.isEnabled()); - pw.println("mIsStatic..............: " + item.isStatic()); + pw.println("mIsMutable.............: " + item.isMutable()); pw.println("mPriority..............: " + item.mPriority); pw.println("mCategory..............: " + item.mCategory); @@ -352,8 +347,8 @@ final class OverlayManagerSettings { case "isenabled": pw.println(item.mIsEnabled); break; - case "isstatic": - pw.println(item.mIsStatic); + case "ismutable": + pw.println(item.mIsMutable); break; case "priority": pw.println(item.mPriority); @@ -446,7 +441,7 @@ final class OverlayManagerSettings { final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY); return new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName, - baseCodePath, state, isEnabled, isStatic, priority, category); + baseCodePath, state, isEnabled, !isStatic, priority, category); } public static void persist(@NonNull final ArrayList table, @@ -478,7 +473,7 @@ final class OverlayManagerSettings { XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.mBaseCodePath); XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.mState); XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled); - XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, item.mIsStatic); + XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable); XmlUtils.writeIntAttribute(xml, ATTR_PRIORITY, item.mPriority); XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory); xml.endTag(null, TAG_ITEM); @@ -494,36 +489,28 @@ final class OverlayManagerSettings { private @OverlayInfo.State int mState; private boolean mIsEnabled; private OverlayInfo mCache; - private boolean mIsStatic; + private boolean mIsMutable; private int mPriority; private String mCategory; SettingsItem(@NonNull final String packageName, 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 isStatic, - final int priority, @Nullable String category) { + final @OverlayInfo.State int state, final boolean isEnabled, + final boolean isMutable, final int priority, @Nullable String category) { mPackageName = packageName; mUserId = userId; mTargetPackageName = targetPackageName; mTargetOverlayableName = targetOverlayableName; mBaseCodePath = baseCodePath; mState = state; - mIsEnabled = isEnabled || isStatic; + mIsEnabled = isEnabled; mCategory = category; mCache = null; - mIsStatic = isStatic; + mIsMutable = isMutable; mPriority = priority; } - SettingsItem(@NonNull final String packageName, final int userId, - @NonNull final String targetPackageName, - @Nullable final String targetOverlayableName, @NonNull final String baseCodePath, - final boolean isStatic, final int priority, @Nullable String category) { - this(packageName, userId, targetPackageName, targetOverlayableName, baseCodePath, - OverlayInfo.STATE_UNKNOWN, false, isStatic, priority, category); - } - private String getTargetPackageName() { return mTargetPackageName; } @@ -567,7 +554,7 @@ final class OverlayManagerSettings { } private boolean setEnabled(boolean enable) { - if (mIsStatic) { + if (!mIsMutable) { return false; } @@ -591,7 +578,7 @@ final class OverlayManagerSettings { private OverlayInfo getOverlayInfo() { if (mCache == null) { mCache = new OverlayInfo(mPackageName, mTargetPackageName, mTargetOverlayableName, - mCategory, mBaseCodePath, mState, mUserId, mPriority, mIsStatic); + mCategory, mBaseCodePath, mState, mUserId, mPriority, mIsMutable); } return mCache; } @@ -600,8 +587,8 @@ final class OverlayManagerSettings { mCache = null; } - private boolean isStatic() { - return mIsStatic; + private boolean isMutable() { + return mIsMutable; } private int getPriority() { diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java index eb432759cb028..bf99bd6248cd8 100644 --- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java @@ -181,7 +181,7 @@ final class OverlayManagerShellCommand extends ShellCommand { private void printListOverlay(PrintWriter out, OverlayInfo oi) { String status; switch (oi.state) { - case OverlayInfo.STATE_ENABLED_STATIC: + case OverlayInfo.STATE_ENABLED_IMMUTABLE: case OverlayInfo.STATE_ENABLED: status = "[x]"; break; diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java new file mode 100644 index 0000000000000..c4fea77cf1bae --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.om; + +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 android.content.om.OverlayInfo; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceImplTestsBase { + + private static final String OVERLAY = "com.dummy.overlay"; + private static final String TARGET = "com.dummy.target"; + private static final int USER = 0; + + private static final String OVERLAY2 = OVERLAY + "2"; + + @Test + public void testUpdateOverlaysForUser() { + final OverlayManagerServiceImpl impl = getImpl(); + installTargetPackage(TARGET, USER); + installTargetPackage("some.other.target", USER); + installOverlayPackage(OVERLAY, TARGET, USER); + + // do nothing, expect no change + final List a = impl.updateOverlaysForUser(USER); + assertEquals(1, a.size()); + assertTrue(a.contains(TARGET)); + + // upgrade overlay, keep target + beginUpgradeOverlayPackage(OVERLAY, USER); + endUpgradeOverlayPackage(OVERLAY, TARGET, USER); + + final List b = impl.updateOverlaysForUser(USER); + assertEquals(1, b.size()); + assertTrue(b.contains(TARGET)); + + // do nothing, expect no change + final List c = impl.updateOverlaysForUser(USER); + assertEquals(1, c.size()); + assertTrue(c.contains(TARGET)); + + // upgrade overlay, switch to new target + addOverlayPackage(OVERLAY, "some.other.target", USER, true, false, 0); + final List d = impl.updateOverlaysForUser(USER); + assertEquals(2, d.size()); + assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target"))); + + // do nothing, expect no change + final List f = impl.updateOverlaysForUser(USER); + assertEquals(1, f.size()); + assertTrue(f.contains("some.other.target")); + } + + @Test + public void testImmutableEnabledChange() { + final OverlayManagerServiceImpl impl = getImpl(); + installTargetPackage(TARGET, USER); + + addOverlayPackage(OVERLAY, TARGET, USER, false, false, 0); + impl.updateOverlaysForUser(USER); + final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + assertNotNull(o1); + assertFalse(o1.isEnabled()); + assertFalse(o1.isMutable); + + addOverlayPackage(OVERLAY, TARGET, USER, false, true, 0); + impl.updateOverlaysForUser(USER); + final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER); + assertNotNull(o2); + assertTrue(o2.isEnabled()); + assertFalse(o2.isMutable); + + addOverlayPackage(OVERLAY, TARGET, USER, false, false, 0); + impl.updateOverlaysForUser(USER); + final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + assertNotNull(o3); + assertFalse(o3.isEnabled()); + assertFalse(o3.isMutable); + } + + @Test + public void testMutableEnabledChangeHasNoEffect() { + final OverlayManagerServiceImpl impl = getImpl(); + installTargetPackage(TARGET, USER); + + addOverlayPackage(OVERLAY, TARGET, USER, true, false, 0); + impl.updateOverlaysForUser(USER); + final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + assertNotNull(o1); + assertFalse(o1.isEnabled()); + assertTrue(o1.isMutable); + + addOverlayPackage(OVERLAY, TARGET, USER, true, true, 0); + impl.updateOverlaysForUser(USER); + final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER); + assertNotNull(o2); + assertFalse(o2.isEnabled()); + assertTrue(o2.isMutable); + + addOverlayPackage(OVERLAY, TARGET, USER, true, false, 0); + impl.updateOverlaysForUser(USER); + final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + assertNotNull(o3); + assertFalse(o3.isEnabled()); + assertTrue(o3.isMutable); + } + + @Test + public void testMutabilityChange() { + final OverlayManagerServiceImpl impl = getImpl(); + installTargetPackage(TARGET, USER); + + addOverlayPackage(OVERLAY, TARGET, USER, false, true, 0); + impl.updateOverlaysForUser(USER); + final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + assertNotNull(o1); + assertTrue(o1.isEnabled()); + assertFalse(o1.isMutable); + + addOverlayPackage(OVERLAY, TARGET, USER, true, false, 0); + impl.updateOverlaysForUser(USER); + final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER); + assertNotNull(o2); + assertFalse(o2.isEnabled()); + assertTrue(o2.isMutable); + + addOverlayPackage(OVERLAY, TARGET, USER, false, false, 0); + impl.updateOverlaysForUser(USER); + final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + assertNotNull(o3); + assertFalse(o3.isEnabled()); + assertFalse(o3.isMutable); + } + + @Test + public void testPriorityChange() { + final OverlayManagerServiceImpl impl = getImpl(); + installTargetPackage(TARGET, USER); + + addOverlayPackage(OVERLAY, TARGET, USER, false, true, 0); + addOverlayPackage(OVERLAY2, TARGET, USER, false, true, 1); + impl.updateOverlaysForUser(USER); + + final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); + assertNotNull(o1); + assertNotNull(o2); + assertEquals(0, o1.priority); + assertEquals(1, o2.priority); + + addOverlayPackage(OVERLAY, TARGET, USER, false, true, 1); + addOverlayPackage(OVERLAY2, TARGET, USER, false, true, 0); + impl.updateOverlaysForUser(USER); + + final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); + final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER); + assertNotNull(o3); + assertNotNull(o4); + assertEquals(1, o3.priority); + assertEquals(0, o4.priority); + } +} 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 c566dfc4a23e8..a428a97e51cdc 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java @@ -24,6 +24,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.content.om.OverlayInfo; @@ -33,6 +36,8 @@ import android.util.ArraySet; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.content.om.OverlayConfig; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,10 +50,7 @@ import java.util.Map; import java.util.Set; @RunWith(AndroidJUnit4.class) -public class OverlayManagerServiceImplTests { - private OverlayManagerServiceImpl mImpl; - private DummyDeviceState mState; - private DummyListener mListener; +public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase { private static final String OVERLAY = "com.dummy.overlay"; private static final String TARGET = "com.dummy.target"; @@ -61,25 +63,14 @@ public class OverlayManagerServiceImplTests { private static final String OVERLAY3 = OVERLAY + "3"; private static final int USER3 = USER2 + 1; - - @Before - public void setUp() throws Exception { - mState = new DummyDeviceState(); - mListener = new DummyListener(); - DummyPackageManagerHelper pmh = new DummyPackageManagerHelper(mState); - mImpl = new OverlayManagerServiceImpl(pmh, - new DummyIdmapManager(mState, pmh), - new OverlayManagerSettings(), - new String[0], - mListener); - } - // tests: basics @Test public void testGetOverlayInfo() throws Exception { - installOverlayPackage(OVERLAY, TARGET, USER, false); - final OverlayInfo oi = mImpl.getOverlayInfo(OVERLAY, USER); + installOverlayPackage(OVERLAY, TARGET, USER); + + final OverlayManagerServiceImpl impl = getImpl(); + final OverlayInfo oi = impl.getOverlayInfo(OVERLAY, USER); assertNotNull(oi); assertEquals(oi.packageName, OVERLAY); assertEquals(oi.targetPackageName, TARGET); @@ -88,87 +79,91 @@ public class OverlayManagerServiceImplTests { @Test public void testGetOverlayInfosForTarget() throws Exception { - installOverlayPackage(OVERLAY, TARGET, USER, false); - installOverlayPackage(OVERLAY2, TARGET, USER, false); + installOverlayPackage(OVERLAY, TARGET, USER); + installOverlayPackage(OVERLAY2, TARGET, USER); + installOverlayPackage(OVERLAY3, TARGET, USER2); - installOverlayPackage(OVERLAY3, TARGET, USER2, false); - - final List ois = mImpl.getOverlayInfosForTarget(TARGET, USER); + final OverlayManagerServiceImpl impl = getImpl(); + final List ois = impl.getOverlayInfosForTarget(TARGET, USER); assertEquals(ois.size(), 2); - assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY, USER))); - assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY2, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER))); - final List ois2 = mImpl.getOverlayInfosForTarget(TARGET, USER2); + final List ois2 = impl.getOverlayInfosForTarget(TARGET, USER2); assertEquals(ois2.size(), 1); - assertTrue(ois2.contains(mImpl.getOverlayInfo(OVERLAY3, USER2))); + assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER2))); - final List ois3 = mImpl.getOverlayInfosForTarget(TARGET, USER3); + final List ois3 = impl.getOverlayInfosForTarget(TARGET, USER3); assertNotNull(ois3); assertEquals(ois3.size(), 0); - final List ois4 = mImpl.getOverlayInfosForTarget("no.such.overlay", USER); + final List ois4 = impl.getOverlayInfosForTarget("no.such.overlay", USER); assertNotNull(ois4); assertEquals(ois4.size(), 0); } @Test public void testGetOverlayInfosForUser() throws Exception { - installOverlayPackage(OVERLAY, TARGET, USER, false); - installOverlayPackage(OVERLAY2, TARGET, USER, false); - installOverlayPackage(OVERLAY3, TARGET2, USER, false); + installTargetPackage(TARGET, USER); + installOverlayPackage(OVERLAY, TARGET, USER); + installOverlayPackage(OVERLAY2, TARGET, USER); + installOverlayPackage(OVERLAY3, TARGET2, USER); - final Map> everything = mImpl.getOverlaysForUser(USER); + final OverlayManagerServiceImpl impl = getImpl(); + final Map> everything = impl.getOverlaysForUser(USER); assertEquals(everything.size(), 2); final List ois = everything.get(TARGET); assertNotNull(ois); assertEquals(ois.size(), 2); - assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY, USER))); - assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY2, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER))); + assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER))); final List ois2 = everything.get(TARGET2); assertNotNull(ois2); assertEquals(ois2.size(), 1); - assertTrue(ois2.contains(mImpl.getOverlayInfo(OVERLAY3, USER))); + assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER))); - final Map> everything2 = mImpl.getOverlaysForUser(USER2); + final Map> everything2 = impl.getOverlaysForUser(USER2); assertNotNull(everything2); assertEquals(everything2.size(), 0); } @Test public void testPriority() throws Exception { - installOverlayPackage(OVERLAY, TARGET, USER, false); - installOverlayPackage(OVERLAY2, TARGET, USER, false); - installOverlayPackage(OVERLAY3, TARGET, USER, false); + installOverlayPackage(OVERLAY, TARGET, USER); + installOverlayPackage(OVERLAY2, TARGET, USER); + installOverlayPackage(OVERLAY3, TARGET, USER); - final OverlayInfo o1 = mImpl.getOverlayInfo(OVERLAY, USER); - final OverlayInfo o2 = mImpl.getOverlayInfo(OVERLAY2, USER); - final OverlayInfo o3 = mImpl.getOverlayInfo(OVERLAY3, 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); assertOverlayInfoList(TARGET, USER, o1, o2, o3); - assertTrue(mImpl.setLowestPriority(OVERLAY3, USER)); + assertTrue(impl.setLowestPriority(OVERLAY3, USER)); assertOverlayInfoList(TARGET, USER, o3, o1, o2); - assertTrue(mImpl.setHighestPriority(OVERLAY3, USER)); + assertTrue(impl.setHighestPriority(OVERLAY3, USER)); assertOverlayInfoList(TARGET, USER, o1, o2, o3); - assertTrue(mImpl.setPriority(OVERLAY, OVERLAY2, USER)); + assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER)); assertOverlayInfoList(TARGET, USER, o2, o1, o3); } @Test public void testOverlayInfoStateTransitions() throws Exception { - assertNull(mImpl.getOverlayInfo(OVERLAY, USER)); + final OverlayManagerServiceImpl impl = getImpl(); + assertNull(impl.getOverlayInfo(OVERLAY, USER)); - installOverlayPackage(OVERLAY, TARGET, USER, true); + installOverlayPackage(OVERLAY, TARGET, USER); assertState(STATE_MISSING_TARGET, OVERLAY, USER); installTargetPackage(TARGET, USER); assertState(STATE_DISABLED, OVERLAY, USER); - mImpl.setEnabled(OVERLAY, true, USER); + impl.setEnabled(OVERLAY, true, USER); assertState(STATE_ENABLED, OVERLAY, USER); // target upgrades do not change the state of the overlay @@ -185,320 +180,49 @@ public class OverlayManagerServiceImplTests { assertState(STATE_ENABLED, OVERLAY, USER); } - @Test - public void testUpdateOverlaysForUser() throws Exception { - installTargetPackage(TARGET, USER); - installTargetPackage("some.other.target", USER); - installOverlayPackage(OVERLAY, TARGET, USER, true); - - // do nothing, expect no change - List a = mImpl.updateOverlaysForUser(USER); - assertEquals(1, a.size()); - assertTrue(a.contains(TARGET)); - - // upgrade overlay, keep target - upgradeOverlayPackage(OVERLAY, TARGET, USER, true); - List b = mImpl.updateOverlaysForUser(USER); - assertEquals(1, b.size()); - assertTrue(b.contains(TARGET)); - - // do nothing, expect no change - List c = mImpl.updateOverlaysForUser(USER); - assertEquals(1, c.size()); - assertTrue(c.contains(TARGET)); - - // upgrade overlay, switch to new target - upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true); - List d = mImpl.updateOverlaysForUser(USER); - assertEquals(2, d.size()); - assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target"))); - - // do nothing, expect no change - List e = mImpl.updateOverlaysForUser(USER); - assertEquals(1, e.size()); - assertTrue(e.contains("some.other.target")); - } - @Test public void testOnOverlayPackageUpgraded() throws Exception { + final OverlayManagerServiceImpl impl = getImpl(); + final DummyListener listener = getListener(); installTargetPackage(TARGET, USER); - installOverlayPackage(OVERLAY, TARGET, USER, true); - mImpl.onOverlayPackageReplacing(OVERLAY, USER); - mListener.count = 0; - mImpl.onOverlayPackageReplaced(OVERLAY, USER); - assertEquals(1, mListener.count); + installOverlayPackage(OVERLAY, TARGET, USER); + impl.onOverlayPackageReplacing(OVERLAY, USER); + listener.count = 0; + impl.onOverlayPackageReplaced(OVERLAY, USER); + assertEquals(1, listener.count); // upgrade to a version where the overlay has changed its target - upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true); - mImpl.onOverlayPackageReplacing(OVERLAY, USER); - mListener.count = 0; - mImpl.onOverlayPackageReplaced(OVERLAY, USER); + beginUpgradeOverlayPackage(OVERLAY, USER); + listener.count = 0; + endUpgradeOverlayPackage(OVERLAY, "some.other.target", USER); // expect once for the old target package, once for the new target package - assertEquals(2, mListener.count); + assertEquals(2, listener.count); - upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true); - mImpl.onOverlayPackageReplacing(OVERLAY, USER); - mListener.count = 0; - mImpl.onOverlayPackageReplaced(OVERLAY, USER); - assertEquals(1, mListener.count); + beginUpgradeOverlayPackage(OVERLAY, USER); + listener.count = 0; + endUpgradeOverlayPackage(OVERLAY, "some.other.target", USER); + assertEquals(1, listener.count); } // tests: listener interface @Test public void testListener() throws Exception { - installOverlayPackage(OVERLAY, TARGET, USER, true); - assertEquals(1, mListener.count); - mListener.count = 0; + final OverlayManagerServiceImpl impl = getImpl(); + final DummyListener listener = getListener(); + installOverlayPackage(OVERLAY, TARGET, USER); + assertEquals(1, listener.count); + listener.count = 0; installTargetPackage(TARGET, USER); - assertEquals(1, mListener.count); - mListener.count = 0; + assertEquals(1, listener.count); + listener.count = 0; - mImpl.setEnabled(OVERLAY, true, USER); - assertEquals(1, mListener.count); - mListener.count = 0; + impl.setEnabled(OVERLAY, true, USER); + assertEquals(1, listener.count); + listener.count = 0; - mImpl.setEnabled(OVERLAY, true, USER); - assertEquals(0, mListener.count); - } - - // helper methods - - private void assertState(int expected, final String overlayPackageName, int userId) { - int actual = mImpl.getOverlayInfo(OVERLAY, USER).state; - String msg = String.format("expected %s but was %s:", - OverlayInfo.stateToString(expected), OverlayInfo.stateToString(actual)); - assertEquals(msg, expected, actual); - } - - private void assertOverlayInfoList(final String targetPackageName, int userId, - OverlayInfo... overlayInfos) { - final List expected = - mImpl.getOverlayInfosForTarget(targetPackageName, userId); - final List actual = Arrays.asList(overlayInfos); - assertEquals(expected, actual); - } - - private void installTargetPackage(String packageName, int userId) { - if (mState.select(packageName, userId) != null) { - throw new IllegalStateException("package already installed"); - } - mState.add(packageName, null, userId, false); - mImpl.onTargetPackageAdded(packageName, userId); - } - - private void beginUpgradeTargetPackage(String packageName, int userId) { - if (mState.select(packageName, userId) == null) { - throw new IllegalStateException("package not installed"); - } - mState.add(packageName, null, userId, false); - mImpl.onTargetPackageReplacing(packageName, userId); - } - - private void endUpgradeTargetPackage(String packageName, int userId) { - if (mState.select(packageName, userId) == null) { - throw new IllegalStateException("package not installed"); - } - mState.add(packageName, null, userId, false); - mImpl.onTargetPackageReplaced(packageName, userId); - } - - private void uninstallTargetPackage(String packageName, int userId) { - if (mState.select(packageName, userId) == null) { - throw new IllegalStateException("package not installed"); - } - mState.remove(packageName, userId); - mImpl.onTargetPackageRemoved(packageName, userId); - } - - private void installOverlayPackage(String packageName, String targetPackageName, int userId, - boolean canCreateIdmap) { - if (mState.select(packageName, userId) != null) { - throw new IllegalStateException("package already installed"); - } - mState.add(packageName, targetPackageName, userId, canCreateIdmap); - mImpl.onOverlayPackageAdded(packageName, userId); - } - - private void upgradeOverlayPackage(String packageName, String targetPackageName, int userId, - boolean canCreateIdmap) { - DummyDeviceState.Package pkg = mState.select(packageName, userId); - if (pkg == null) { - throw new IllegalStateException("package not installed, cannot upgrade"); - } - pkg.targetPackageName = targetPackageName; - pkg.canCreateIdmap = canCreateIdmap; - } - - private void uninstallOverlayPackage(String packageName, int userId) { - // implement this when adding support for downloadable overlays - throw new IllegalArgumentException("not implemented"); - } - - private static final class DummyDeviceState { - private List mPackages = new ArrayList<>(); - - public void add(String packageName, String targetPackageName, int userId, - boolean canCreateIdmap) { - remove(packageName, userId); - Package pkg = new Package(); - pkg.packageName = packageName; - pkg.targetPackageName = targetPackageName; - pkg.userId = userId; - pkg.canCreateIdmap = canCreateIdmap; - mPackages.add(pkg); - } - - public void remove(String packageName, int userId) { - final Iterator iter = mPackages.iterator(); - while (iter.hasNext()) { - final Package pkg = iter.next(); - if (pkg.packageName.equals(packageName) && pkg.userId == userId) { - iter.remove(); - return; - } - } - } - - public List select(int userId) { - List out = new ArrayList<>(); - final int packageCount = mPackages.size(); - for (int i = 0; i < packageCount; i++) { - final Package pkg = mPackages.get(i); - if (pkg.userId == userId) { - out.add(pkg); - } - } - return out; - } - - public Package select(String packageName, int userId) { - final int packageCount = mPackages.size(); - for (int i = 0; i < packageCount; i++) { - final Package pkg = mPackages.get(i); - if (pkg.packageName.equals(packageName) && pkg.userId == userId) { - return pkg; - } - } - return null; - } - - private static final class Package { - public String packageName; - public int userId; - public String targetPackageName; - public boolean canCreateIdmap; - } - } - - private static final class DummyPackageManagerHelper implements - OverlayManagerServiceImpl.PackageManagerHelper { - private final DummyDeviceState mState; - - DummyPackageManagerHelper(DummyDeviceState state) { - mState = state; - } - - @Override - public PackageInfo getPackageInfo(@NonNull String packageName, int userId) { - final DummyDeviceState.Package pkg = mState.select(packageName, userId); - if (pkg == null) { - return null; - } - ApplicationInfo ai = new ApplicationInfo(); - ai.sourceDir = String.format("%s/%s/base.apk", - pkg.targetPackageName == null ? "/system/app/" : "/vendor/overlay/", - pkg.packageName); - PackageInfo pi = new PackageInfo(); - pi.applicationInfo = ai; - pi.packageName = pkg.packageName; - pi.overlayTarget = pkg.targetPackageName; - pi.overlayCategory = "dummy-category-" + pkg.targetPackageName; - return pi; - } - - @Override - public boolean signaturesMatching(@NonNull String packageName1, - @NonNull String packageName2, int userId) { - return false; - } - - @Override - public List getOverlayPackages(int userId) { - List out = new ArrayList<>(); - final List packages = mState.select(userId); - final int packageCount = packages.size(); - for (int i = 0; i < packageCount; i++) { - final DummyDeviceState.Package pkg = packages.get(i); - if (pkg.targetPackageName != null) { - out.add(getPackageInfo(pkg.packageName, pkg.userId)); - } - } - return out; - } - } - - private static class DummyIdmapManager extends IdmapManager { - private final DummyDeviceState mState; - private Set mIdmapFiles = new ArraySet<>(); - - DummyIdmapManager(DummyDeviceState state, DummyPackageManagerHelper packageManagerHelper) { - super(packageManagerHelper); - mState = state; - } - - @Override - boolean createIdmap(@NonNull final PackageInfo targetPackage, - @NonNull final PackageInfo overlayPackage, int userId) { - final DummyDeviceState.Package t = mState.select(targetPackage.packageName, userId); - if (t == null) { - return false; - } - final DummyDeviceState.Package o = mState.select(overlayPackage.packageName, userId); - if (o == null) { - return false; - } - if (!o.canCreateIdmap) { - return false; - } - final String key = createKey(overlayPackage.packageName, userId); - mIdmapFiles.add(key); - return true; - } - - @Override - boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) { - final String key = createKey(oi.packageName, oi.userId); - if (!mIdmapFiles.contains(key)) { - return false; - } - mIdmapFiles.remove(key); - return true; - } - - @Override - boolean idmapExists(@NonNull final OverlayInfo oi) { - final String key = createKey(oi.packageName, oi.userId); - return mIdmapFiles.contains(key); - } - - @Override - boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) { - final String key = createKey(overlayPackage.packageName, userId); - return mIdmapFiles.contains(key); - } - - private String createKey(@NonNull final String packageName, final int userId) { - return String.format("%s:%d", packageName, userId); - } - } - - private static class DummyListener implements OverlayManagerServiceImpl.OverlayChangeListener { - public int count; - - public void onOverlaysChanged(@NonNull String targetPackage, int userId) { - count++; - } + impl.setEnabled(OVERLAY, true, USER); + assertEquals(0, listener.count); } } diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java new file mode 100644 index 0000000000000..a753aac39c489 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.om; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.content.om.OverlayInfo; +import android.content.om.OverlayInfo.State; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.util.ArraySet; + +import com.android.internal.content.om.OverlayConfig; + +import org.junit.Before; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** Base class for creating {@link OverlayManagerServiceImplTests} tests. */ +class OverlayManagerServiceImplTestsBase { + private OverlayManagerServiceImpl mImpl; + private DummyDeviceState mState; + private DummyListener mListener; + + @Before + public void setUp() { + mState = new DummyDeviceState(); + mListener = new DummyListener(); + final DummyPackageManagerHelper pmh = new DummyPackageManagerHelper(mState); + + mImpl = new OverlayManagerServiceImpl(pmh, + new DummyIdmapManager(mState, pmh), + new OverlayManagerSettings(), + mState.mOverlayConfig, + new String[0], + mListener); + } + + public OverlayManagerServiceImpl getImpl() { + return mImpl; + } + + public DummyListener getListener() { + return mListener; + } + + void assertState(@State int expected, final String overlayPackageName, int userId) { + final OverlayInfo info = mImpl.getOverlayInfo(overlayPackageName, userId); + if (info == null) { + throw new IllegalStateException("package not installed"); + } + + final String msg = String.format("expected %s but was %s:", + OverlayInfo.stateToString(expected), OverlayInfo.stateToString(info.state)); + assertEquals(msg, expected, info.state); + } + + void assertOverlayInfoList(final String targetPackageName, int userId, + OverlayInfo... overlayInfos) { + final List expected = + mImpl.getOverlayInfosForTarget(targetPackageName, userId); + final List actual = Arrays.asList(overlayInfos); + assertEquals(expected, actual); + } + + /** + * Creates an overlay configured through {@link OverlayConfig}. + * + * @throws IllegalStateException if the package is already installed + */ + void addOverlayPackage(String packageName, String targetPackageName, int userId, + boolean mutable, boolean enabled, int priority) { + mState.addOverlay(packageName, targetPackageName, userId, mutable, enabled, priority); + } + + /** + * Adds the target package to the device. + * + * This corresponds to when the OMS receives the + * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast. + * + * @throws IllegalStateException if the package is not currently installed + */ + void installTargetPackage(String packageName, int userId) { + if (mState.select(packageName, userId) != null) { + throw new IllegalStateException("package already installed"); + } + mState.addTarget(packageName, userId); + mImpl.onTargetPackageAdded(packageName, userId); + } + + /** + * Begins upgrading the target package. + * + * This corresponds to when the OMS receives the + * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast with the + * {@link android.content.Intent#EXTRA_REPLACING} extra. + * + * @throws IllegalStateException if the package is not currently installed + */ + void beginUpgradeTargetPackage(String packageName, int userId) { + if (mState.select(packageName, userId) == null) { + throw new IllegalStateException("package not installed"); + } + mImpl.onTargetPackageReplacing(packageName, userId); + } + + /** + * Ends upgrading the target package. + * + * This corresponds to when the OMS receives the + * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the + * {@link android.content.Intent#EXTRA_REPLACING} extra. + * + * @throws IllegalStateException if the package is not currently installed + */ + void endUpgradeTargetPackage(String packageName, int userId) { + if (mState.select(packageName, userId) == null) { + throw new IllegalStateException("package not installed"); + } + mState.addTarget(packageName, userId); + mImpl.onTargetPackageReplaced(packageName, userId); + } + + /** + * Removes the target package from the device. + * + * This corresponds to when the OMS receives the + * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast. + * + * @throws IllegalStateException if the package is not currently installed + */ + void uninstallTargetPackage(String packageName, int userId) { + if (mState.select(packageName, userId) == null) { + throw new IllegalStateException("package not installed"); + } + mState.remove(packageName, userId); + mImpl.onTargetPackageRemoved(packageName, userId); + } + + /** + * Adds the overlay package to the device. + * + * This corresponds to when the OMS receives the + * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast. + * + * @throws IllegalStateException if the package is already installed + */ + void installOverlayPackage(String packageName, String targetPackageName, int userId) { + if (mState.select(packageName, userId) != null) { + throw new IllegalStateException("package already installed"); + } + mState.addOverlay(packageName, targetPackageName, userId); + mImpl.onOverlayPackageAdded(packageName, userId); + } + + /** + * Begins upgrading the overlay package. + * + * This corresponds to when the OMS receives the + * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast with the + * {@link android.content.Intent#EXTRA_REPLACING} extra. + * + * @throws IllegalStateException if the package is not currently installed + */ + void beginUpgradeOverlayPackage(String packageName, int userId) { + if (mState.select(packageName, userId) == null) { + throw new IllegalStateException("package not installed, cannot upgrade"); + } + + mImpl.onOverlayPackageReplacing(packageName, userId); + } + + /** + * Ends upgrading the overlay package, potentially changing its target package. + * + * This corresponds to when the OMS receives the + * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the + * {@link android.content.Intent#EXTRA_REPLACING} extra. + * + * @throws IllegalStateException if the package is not currently installed + */ + void endUpgradeOverlayPackage(String packageName, String targetPackageName, int userId) { + if (mState.select(packageName, userId) == null) { + throw new IllegalStateException("package not installed, cannot upgrade"); + } + + mState.addOverlay(packageName, targetPackageName, userId); + mImpl.onOverlayPackageReplaced(packageName, userId); + } + + private static final class DummyDeviceState { + private List mPackages = new ArrayList<>(); + private OverlayConfig mOverlayConfig = mock(OverlayConfig.class); + + /** Adds a non-overlay to the device. */ + public void addTarget(String packageName, int userId) { + remove(packageName, userId); + mPackages.add(new Package(packageName, userId, null, false, false, 0)); + } + + /** Adds an overlay to the device. */ + public void addOverlay(String packageName, String targetPackageName, int userId) { + addOverlay(packageName, targetPackageName, userId, true, false, OverlayConfig.DEFAULT_PRIORITY); + } + + /** Adds a configured overlay to the device. */ + public void addOverlay(String packageName, String targetPackageName, int userId, + boolean mutable, boolean enabled, int priority) { + remove(packageName, userId); + mPackages.add(new Package(packageName, userId, targetPackageName, mutable, enabled, + priority)); + when(mOverlayConfig.getPriority(packageName)).thenReturn(priority); + when(mOverlayConfig.isEnabled(packageName)).thenReturn(enabled); + when(mOverlayConfig.isMutable(packageName)).thenReturn(mutable); + } + + /** Remove a package from the device. */ + public void remove(String packageName, int userId) { + final Iterator iter = mPackages.iterator(); + while (iter.hasNext()) { + final Package pkg = iter.next(); + if (pkg.packageName.equals(packageName) && pkg.userId == userId) { + iter.remove(); + return; + } + } + } + + /** Retrieves all packages on device for a particular user. */ + public List select(int userId) { + return mPackages.stream().filter(p -> p.userId == userId).collect(Collectors.toList()); + } + + /** Retrieves the package with the specified package name for a particular user. */ + public Package select(String packageName, int userId) { + return mPackages.stream().filter( + p -> p.packageName.equals(packageName) && p.userId == userId) + .findFirst().orElse(null); + } + + private static final class Package { + public final String packageName; + public final int userId; + public final String targetPackageName; + public final boolean mutable; + public final boolean enabled; + public final int priority; + + private Package(String packageName, int userId, String targetPackageName, + boolean mutable, boolean enabled, int priority) { + this.packageName = packageName; + this.userId = userId; + this.targetPackageName = targetPackageName; + this.mutable = mutable; + this.enabled = enabled; + this.priority = priority; + } + } + } + + static final class DummyPackageManagerHelper implements + OverlayManagerServiceImpl.PackageManagerHelper { + private final DummyDeviceState mState; + + private DummyPackageManagerHelper(DummyDeviceState state) { + mState = state; + } + + @Override + public PackageInfo getPackageInfo(@NonNull String packageName, int userId) { + final DummyDeviceState.Package pkg = mState.select(packageName, userId); + if (pkg == null) { + return null; + } + final ApplicationInfo ai = new ApplicationInfo(); + ai.sourceDir = String.format("%s/%s/base.apk", + pkg.targetPackageName == null ? "/system/app/" : "/vendor/overlay/", + pkg.packageName); + PackageInfo pi = new PackageInfo(); + pi.applicationInfo = ai; + pi.packageName = pkg.packageName; + pi.overlayTarget = pkg.targetPackageName; + pi.overlayCategory = "dummy-category-" + pkg.targetPackageName; + return pi; + } + + @Override + public boolean signaturesMatching(@NonNull String packageName1, + @NonNull String packageName2, int userId) { + return false; + } + + @Override + public List getOverlayPackages(int userId) { + return mState.select(userId).stream() + .filter(p -> p.targetPackageName != null) + .map(p -> getPackageInfo(p.packageName, p.userId)) + .collect(Collectors.toList()); + } + } + + static class DummyIdmapManager extends IdmapManager { + private final DummyDeviceState mState; + private Set mIdmapFiles = new ArraySet<>(); + + private DummyIdmapManager(DummyDeviceState state, + DummyPackageManagerHelper packageManagerHelper) { + super(packageManagerHelper); + mState = state; + } + + @Override + boolean createIdmap(@NonNull final PackageInfo targetPackage, + @NonNull final PackageInfo overlayPackage, int userId) { + final DummyDeviceState.Package t = mState.select(targetPackage.packageName, userId); + if (t == null) { + return false; + } + final DummyDeviceState.Package o = mState.select(overlayPackage.packageName, userId); + if (o == null) { + return false; + } + final String key = createKey(overlayPackage.packageName, userId); + mIdmapFiles.add(key); + return true; + } + + @Override + boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) { + final String key = createKey(oi.packageName, oi.userId); + if (!mIdmapFiles.contains(key)) { + return false; + } + mIdmapFiles.remove(key); + return true; + } + + @Override + boolean idmapExists(@NonNull final OverlayInfo oi) { + final String key = createKey(oi.packageName, oi.userId); + return mIdmapFiles.contains(key); + } + + @Override + boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) { + final String key = createKey(overlayPackage.packageName, userId); + return mIdmapFiles.contains(key); + } + + private String createKey(@NonNull final String packageName, final int userId) { + return String.format("%s:%d", packageName, userId); + } + } + + static class DummyListener implements OverlayManagerServiceImpl.OverlayChangeListener { + public int count; + + public void onOverlaysChanged(@NonNull String targetPackage, int userId) { + count++; + } + } +} 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 8ff8b6e4a9e0e..146f60aff7243 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java @@ -58,7 +58,7 @@ public class OverlayManagerSettingsTests { STATE_DISABLED, 0, 0, - false); + true); private static final OverlayInfo OVERLAY_B0 = new OverlayInfo( "com.dummy.overlay_b", @@ -69,7 +69,7 @@ public class OverlayManagerSettingsTests { STATE_DISABLED, 0, 0, - false); + true); private static final OverlayInfo OVERLAY_C0 = new OverlayInfo( "com.dummy.overlay_c", @@ -80,7 +80,7 @@ public class OverlayManagerSettingsTests { STATE_DISABLED, 0, 0, - false); + true); private static final OverlayInfo OVERLAY_A1 = new OverlayInfo( "com.dummy.overlay_a", @@ -91,7 +91,7 @@ public class OverlayManagerSettingsTests { STATE_DISABLED, 1, 0, - false); + true); private static final OverlayInfo OVERLAY_B1 = new OverlayInfo( "com.dummy.overlay_b", @@ -102,7 +102,7 @@ public class OverlayManagerSettingsTests { STATE_DISABLED, 1, 0, - false); + true); @Before public void setUp() throws Exception { @@ -238,7 +238,7 @@ public class OverlayManagerSettingsTests { STATE_DISABLED, 0, 0, - false); + true); insert(otherTarget); changed = mSettings.setPriority(OVERLAY_A0.packageName, otherTarget.packageName, OVERLAY_A0.userId); @@ -435,7 +435,7 @@ public class OverlayManagerSettingsTests { private void insert(OverlayInfo oi) throws Exception { mSettings.init(oi.packageName, oi.userId, oi.targetPackageName, null, oi.baseCodePath, - false, 0, oi.category); + true, false,0, oi.category); mSettings.setState(oi.packageName, oi.userId, oi.state); mSettings.setEnabled(oi.packageName, oi.userId, false); }