diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a205fcf9520b6..2dc3996e3d7bb 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4451,4 +4451,11 @@ false -1 + + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 758a4f7baffc0..6ce25d45b16c9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4025,4 +4025,6 @@ + + diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java index 910ed44df0f87..19fa9204935d3 100644 --- a/services/core/java/com/android/server/om/IdmapDaemon.java +++ b/services/core/java/com/android/server/om/IdmapDaemon.java @@ -30,6 +30,7 @@ import android.util.Slog; import com.android.server.FgThread; +import java.io.File; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; @@ -124,9 +125,13 @@ class IdmapDaemon { } } - String getIdmapPath(String overlayPath, int userId) throws TimeoutException, RemoteException { + boolean idmapExists(String overlayPath, int userId) { try (Connection c = connect()) { - return mService.getIdmapPath(overlayPath, userId); + return new File(mService.getIdmapPath(overlayPath, userId)).isFile(); + } catch (Exception e) { + Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath + ": " + + e.getMessage()); + return false; } } diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java index 90c85ada9defc..735d669835208 100644 --- a/services/core/java/com/android/server/om/IdmapManager.java +++ b/services/core/java/com/android/server/om/IdmapManager.java @@ -27,10 +27,10 @@ import android.content.pm.PackageInfo; import android.os.Build.VERSION_CODES; import android.os.OverlayablePolicy; import android.os.SystemProperties; -import android.os.UserHandle; import android.util.Slog; -import java.io.File; +import com.android.internal.util.ArrayUtils; + import java.io.IOException; /** @@ -55,12 +55,16 @@ class IdmapManager { VENDOR_IS_Q_OR_LATER = isQOrLater; } - private final OverlayableInfoCallback mOverlayableCallback; private final IdmapDaemon mIdmapDaemon; + private final OverlayableInfoCallback mOverlayableCallback; + private final String mOverlayableConfigurator; + private final String[] mOverlayableConfiguratorTargets; - IdmapManager(final OverlayableInfoCallback verifyCallback) { + IdmapManager(final IdmapDaemon idmapDaemon, final OverlayableInfoCallback verifyCallback) { mOverlayableCallback = verifyCallback; - mIdmapDaemon = IdmapDaemon.getInstance(); + mIdmapDaemon = idmapDaemon; + mOverlayableConfigurator = verifyCallback.getOverlayableConfigurator(); + mOverlayableConfiguratorTargets = verifyCallback.getOverlayableConfiguratorTargets() ; } /** @@ -103,23 +107,11 @@ class IdmapManager { } boolean idmapExists(@NonNull final OverlayInfo oi) { - return new File(getIdmapPath(oi.baseCodePath, oi.userId)).isFile(); + return mIdmapDaemon.idmapExists(oi.baseCodePath, oi.userId); } boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) { - return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath(), userId)) - .isFile(); - } - - private @NonNull String getIdmapPath(@NonNull final String overlayPackagePath, - final int userId) { - try { - return mIdmapDaemon.getIdmapPath(overlayPackagePath, userId); - } catch (Exception e) { - Slog.w(TAG, "failed to get idmap path for " + overlayPackagePath + ": " - + e.getMessage()); - return ""; - } + return mIdmapDaemon.idmapExists(overlayPackage.applicationInfo.getBaseCodePath(), userId); } /** @@ -198,9 +190,17 @@ class IdmapManager { String targetOverlayableName = overlayPackage.targetOverlayableName; if (targetOverlayableName != null) { try { + if (!mOverlayableConfigurator.isEmpty() + && ArrayUtils.contains(mOverlayableConfiguratorTargets, + targetPackage.packageName) + && mOverlayableCallback.signaturesMatching(mOverlayableConfigurator, + overlayPackage.packageName, userId)) { + return true; + } + OverlayableInfo overlayableInfo = mOverlayableCallback.getOverlayableForTarget( targetPackage.packageName, targetOverlayableName, userId); - if (overlayableInfo != null) { + if (overlayableInfo != null && overlayableInfo.actor != null) { String actorPackageName = OverlayActorEnforcer.getPackageNameForActor( overlayableInfo.actor, mOverlayableCallback.getNamedActors()).first; if (mOverlayableCallback.signaturesMatching(actorPackageName, diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java index ef6655dd224ed..2bc34998785b3 100644 --- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java +++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java @@ -50,8 +50,8 @@ public class OverlayActorEnforcer { /** * @return nullable actor result with {@link ActorState} failure status */ - static Pair getPackageNameForActor(String actorUriString, - Map> namedActors) { + static Pair getPackageNameForActor(@NonNull String actorUriString, + @NonNull Map> namedActors) { Uri actorUri = Uri.parse(actorUriString); String actorScheme = actorUri.getScheme(); diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 6a8e465529d78..086ab81832544 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -45,6 +45,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; import android.content.res.ApkAssets; +import android.content.res.Resources; import android.net.Uri; import android.os.Binder; import android.os.Environment; @@ -62,6 +63,7 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.R; import com.android.internal.content.om.OverlayConfig; import com.android.server.FgThread; import com.android.server.IoThread; @@ -246,7 +248,7 @@ public final class OverlayManagerService extends SystemService { new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays"); mPackageManager = new PackageManagerHelperImpl(context); mUserManager = UserManagerService.getInstance(); - IdmapManager im = new IdmapManager(mPackageManager); + IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager); mSettings = new OverlayManagerSettings(); mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings, OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(), @@ -1118,6 +1120,17 @@ public final class OverlayManagerService extends SystemService { return false; } + @Override + public String getOverlayableConfigurator() { + return Resources.getSystem().getString(R.string.config_overlayableConfigurator); + } + + @Override + public String[] getOverlayableConfiguratorTargets() { + return Resources.getSystem().getStringArray( + R.array.config_overlayableConfiguratorTargets); + } + @Override public List getOverlayPackages(final int userId) { final List overlays = mPackageManagerInternal.getOverlayPackages(userId); diff --git a/services/core/java/com/android/server/om/OverlayableInfoCallback.java b/services/core/java/com/android/server/om/OverlayableInfoCallback.java index 5066ecdd6316f..41c341adf1bc0 100644 --- a/services/core/java/com/android/server/om/OverlayableInfoCallback.java +++ b/services/core/java/com/android/server/om/OverlayableInfoCallback.java @@ -80,4 +80,24 @@ public interface OverlayableInfoCallback { * in the system returns {@link PackageManager#SIGNATURE_MATCH} */ boolean signaturesMatching(@NonNull String pkgName1, @NonNull String pkgName2, int userId); + + /** + * Retrieves the package name that is recognized as an actor for the packages specified by + * {@link #getOverlayableConfiguratorTargets()}. + */ + @NonNull + default String getOverlayableConfigurator() { + return ""; + } + + /** + * Retrieves the target packages that recognize the {@link #getOverlayableConfigurator} as an + * actor for its overlayable declarations. Overlays targeting one of the specified targets that + * are signed with the same signature as the overlayable configurator will be granted the + * "actor" policy. + */ + @NonNull + default String[] getOverlayableConfiguratorTargets() { + return new String[0]; + } } 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 f35eecf05f320..b7692f912e393 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java @@ -44,9 +44,9 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testUpdateOverlaysForUser() { final OverlayManagerServiceImpl impl = getImpl(); - installTargetPackage(TARGET, USER); - installTargetPackage("some.other.target", USER); - installOverlayPackage(OVERLAY, TARGET, USER); + addSystemPackage(target(TARGET), USER); + addSystemPackage(target("some.other.target"), USER);; + addSystemPackage(overlay(OVERLAY, TARGET), USER); // do nothing, expect no change final List a = impl.updateOverlaysForUser(USER); @@ -54,8 +54,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI assertTrue(a.contains(TARGET)); // upgrade overlay, keep target - beginUpgradeOverlayPackage(OVERLAY, USER); - endUpgradeOverlayPackage(OVERLAY, TARGET, USER); + addSystemPackage(overlay(OVERLAY, TARGET), USER); final List b = impl.updateOverlaysForUser(USER); assertEquals(1, b.size()); @@ -67,7 +66,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI assertTrue(c.contains(TARGET)); // upgrade overlay, switch to new target - addOverlayPackage(OVERLAY, "some.other.target", USER, true, false, 0); + addSystemPackage(overlay(OVERLAY, "some.other.target"), USER); final List d = impl.updateOverlaysForUser(USER); assertEquals(2, d.size()); assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target"))); @@ -81,23 +80,24 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testImmutableEnabledChange() { final OverlayManagerServiceImpl impl = getImpl(); - installTargetPackage(TARGET, USER); + installNewPackage(target(TARGET), USER); + installNewPackage(overlay(OVERLAY, TARGET), USER); - addOverlayPackage(OVERLAY, TARGET, USER, false, false, 0); + configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */); 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); + configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */); 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); + configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); assertNotNull(o3); @@ -108,23 +108,24 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testMutableEnabledChangeHasNoEffect() { final OverlayManagerServiceImpl impl = getImpl(); - installTargetPackage(TARGET, USER); + installNewPackage(target(TARGET), USER); + installNewPackage(overlay(OVERLAY, TARGET), USER); + configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */); - 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); + configureSystemOverlay(OVERLAY, true /* mutable */, true /* enabled */, 0 /* priority */); 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); + configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); assertNotNull(o3); @@ -135,15 +136,16 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testMutableEnabledToImmutableEnabled() { final OverlayManagerServiceImpl impl = getImpl(); - installTargetPackage(TARGET, USER); + installNewPackage(target(TARGET), USER); + installNewPackage(overlay(OVERLAY, TARGET), USER); final BiConsumer setOverlay = (mutable, enabled) -> { - addOverlayPackage(OVERLAY, TARGET, USER, mutable, enabled, 0); + configureSystemOverlay(OVERLAY, mutable, enabled, 0 /* priority */); impl.updateOverlaysForUser(USER); - final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); - assertNotNull(o1); - assertEquals(enabled, o1.isEnabled()); - assertEquals(mutable, o1.isMutable); + final OverlayInfo o = impl.getOverlayInfo(OVERLAY, USER); + assertNotNull(o); + assertEquals(enabled, o.isEnabled()); + assertEquals(mutable, o.isMutable); }; // Immutable/enabled -> mutable/enabled @@ -178,70 +180,76 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI @Test public void testMutablePriorityChange() { final OverlayManagerServiceImpl impl = getImpl(); - installTargetPackage(TARGET, USER); - addOverlayPackage(OVERLAY, TARGET, USER, true, true, 0); - addOverlayPackage(OVERLAY2, TARGET, USER, true, true, 1); + installNewPackage(target(TARGET), USER); + installNewPackage(overlay(OVERLAY, TARGET), USER); + installNewPackage(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); assertNotNull(o1); assertEquals(0, o1.priority); + assertFalse(o1.isEnabled()); final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); assertNotNull(o2); assertEquals(1, o2.priority); + assertFalse(o2.isEnabled()); // Overlay priority changing between reboots should not affect enable state of mutable - // overlays + // overlays. impl.setEnabled(OVERLAY, true, USER); // Reorder the overlays - addOverlayPackage(OVERLAY, TARGET, USER, true, true, 1); - addOverlayPackage(OVERLAY2, TARGET, USER, true, true, 0); + 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); assertNotNull(o3); assertEquals(1, o3.priority); + assertTrue(o3.isEnabled()); final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER); assertNotNull(o4); assertEquals(0, o4.priority); - assertTrue(o1.isEnabled()); + assertFalse(o4.isEnabled()); } @Test public void testImmutablePriorityChange() { final OverlayManagerServiceImpl impl = getImpl(); - installTargetPackage(TARGET, USER); - addOverlayPackage(OVERLAY, TARGET, USER, false, true, 0); - addOverlayPackage(OVERLAY2, TARGET, USER, false, true, 1); + installNewPackage(target(TARGET), USER); + installNewPackage(overlay(OVERLAY, TARGET), USER); + installNewPackage(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); assertNotNull(o1); assertEquals(0, o1.priority); + assertTrue(o1.isEnabled()); final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); assertNotNull(o2); assertEquals(1, o2.priority); - - // Overlay priority changing between reboots should not affect enable state of mutable - // overlays - impl.setEnabled(OVERLAY, true, USER); + assertTrue(o2.isEnabled()); // Reorder the overlays - addOverlayPackage(OVERLAY, TARGET, USER, false, true, 1); - addOverlayPackage(OVERLAY2, TARGET, USER, false, true, 0); + configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 1 /* priority */); + configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 0 /* priority */); impl.updateOverlaysForUser(USER); final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); assertNotNull(o3); assertEquals(1, o3.priority); + assertTrue(o3.isEnabled()); final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER); assertNotNull(o4); assertEquals(0, o4.priority); - assertTrue(o1.isEnabled()); + 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 cd73432357507..b25af05438294 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java @@ -26,6 +26,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.content.om.OverlayInfo; +import android.os.OverlayablePolicy; import androidx.test.runner.AndroidJUnit4; @@ -49,11 +50,9 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes private static final String OVERLAY3 = OVERLAY + "3"; private static final int USER3 = USER2 + 1; - // tests: basics - @Test - public void testGetOverlayInfo() throws Exception { - installOverlayPackage(OVERLAY, TARGET, USER); + public void testGetOverlayInfo() { + installNewPackage(overlay(OVERLAY, TARGET), USER); final OverlayManagerServiceImpl impl = getImpl(); final OverlayInfo oi = impl.getOverlayInfo(OVERLAY, USER); @@ -64,10 +63,10 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes } @Test - public void testGetOverlayInfosForTarget() throws Exception { - installOverlayPackage(OVERLAY, TARGET, USER); - installOverlayPackage(OVERLAY2, TARGET, USER); - installOverlayPackage(OVERLAY3, TARGET, USER2); + public void testGetOverlayInfosForTarget() { + installNewPackage(overlay(OVERLAY, TARGET), USER); + installNewPackage(overlay(OVERLAY2, TARGET), USER); + installNewPackage(overlay(OVERLAY3, TARGET), USER2); final OverlayManagerServiceImpl impl = getImpl(); final List ois = impl.getOverlayInfosForTarget(TARGET, USER); @@ -89,11 +88,11 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes } @Test - public void testGetOverlayInfosForUser() throws Exception { - installTargetPackage(TARGET, USER); - installOverlayPackage(OVERLAY, TARGET, USER); - installOverlayPackage(OVERLAY2, TARGET, USER); - installOverlayPackage(OVERLAY3, TARGET2, USER); + public void testGetOverlayInfosForUser() { + installNewPackage(target(TARGET), USER); + installNewPackage(overlay(OVERLAY, TARGET), USER); + installNewPackage(overlay(OVERLAY2, TARGET), USER); + installNewPackage(overlay(OVERLAY3, TARGET2), USER); final OverlayManagerServiceImpl impl = getImpl(); final Map> everything = impl.getOverlaysForUser(USER); @@ -116,91 +115,86 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes } @Test - public void testPriority() throws Exception { - installOverlayPackage(OVERLAY, TARGET, USER); - installOverlayPackage(OVERLAY2, TARGET, USER); - installOverlayPackage(OVERLAY3, TARGET, USER); + public void testPriority() { + installNewPackage(overlay(OVERLAY, TARGET), USER); + installNewPackage(overlay(OVERLAY2, TARGET), USER); + installNewPackage(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); - assertOverlayInfoList(TARGET, USER, o1, o2, o3); + assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3); assertTrue(impl.setLowestPriority(OVERLAY3, USER)); - assertOverlayInfoList(TARGET, USER, o3, o1, o2); + assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2); assertTrue(impl.setHighestPriority(OVERLAY3, USER)); - assertOverlayInfoList(TARGET, USER, o1, o2, o3); + assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3); assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER)); - assertOverlayInfoList(TARGET, USER, o2, o1, o3); + assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3); } @Test - public void testOverlayInfoStateTransitions() throws Exception { + public void testOverlayInfoStateTransitions() { final OverlayManagerServiceImpl impl = getImpl(); assertNull(impl.getOverlayInfo(OVERLAY, USER)); - installOverlayPackage(OVERLAY, TARGET, USER); + installNewPackage(overlay(OVERLAY, TARGET), USER); assertState(STATE_MISSING_TARGET, OVERLAY, USER); - installTargetPackage(TARGET, USER); + final DummyDeviceState.PackageBuilder target = target(TARGET); + installNewPackage(target, USER); assertState(STATE_DISABLED, OVERLAY, USER); impl.setEnabled(OVERLAY, true, USER); assertState(STATE_ENABLED, OVERLAY, USER); // target upgrades do not change the state of the overlay - beginUpgradeTargetPackage(TARGET, USER); + upgradePackage(target, USER); assertState(STATE_ENABLED, OVERLAY, USER); - endUpgradeTargetPackage(TARGET, USER); - assertState(STATE_ENABLED, OVERLAY, USER); - - uninstallTargetPackage(TARGET, USER); + uninstallPackage(TARGET, USER); assertState(STATE_MISSING_TARGET, OVERLAY, USER); - installTargetPackage(TARGET, USER); + installNewPackage(target, USER); assertState(STATE_ENABLED, OVERLAY, USER); } @Test - public void testOnOverlayPackageUpgraded() throws Exception { - final OverlayManagerServiceImpl impl = getImpl(); + public void testOnOverlayPackageUpgraded() { final DummyListener listener = getListener(); - installTargetPackage(TARGET, USER); - installOverlayPackage(OVERLAY, TARGET, USER); - impl.onOverlayPackageReplacing(OVERLAY, USER); + final DummyDeviceState.PackageBuilder target = target(TARGET); + final DummyDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET); + installNewPackage(target, USER); + installNewPackage(overlay, USER); listener.count = 0; - impl.onOverlayPackageReplaced(OVERLAY, USER); - assertEquals(1, listener.count); - - // upgrade to a version where the overlay has changed its target - 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 + upgradePackage(overlay, USER); assertEquals(2, listener.count); - beginUpgradeOverlayPackage(OVERLAY, USER); + // upgrade to a version where the overlay has changed its target + // expect once for the old target package, once for the new target package listener.count = 0; - endUpgradeOverlayPackage(OVERLAY, "some.other.target", USER); - assertEquals(1, listener.count); + final DummyDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target"); + upgradePackage(overlay2, USER); + assertEquals(3, listener.count); + + listener.count = 0; + upgradePackage(overlay2, USER); + assertEquals(2, listener.count); } - // tests: listener interface - @Test - public void testListener() throws Exception { + public void testListener() { final OverlayManagerServiceImpl impl = getImpl(); final DummyListener listener = getListener(); - installOverlayPackage(OVERLAY, TARGET, USER); + installNewPackage(overlay(OVERLAY, TARGET), USER); assertEquals(1, listener.count); listener.count = 0; - installTargetPackage(TARGET, USER); + installNewPackage(target(TARGET), USER); assertEquals(1, listener.count); listener.count = 0; @@ -211,4 +205,49 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes impl.setEnabled(OVERLAY, true, USER); assertEquals(0, listener.count); } + + @Test + public void testConfigurator() { + final DummyPackageManagerHelper packageManager = getPackageManager(); + packageManager.overlayableConfigurator = "actor"; + packageManager.overlayableConfiguratorTargets = new String[]{TARGET}; + reinitializeImpl(); + + installNewPackage(target("actor").setCertificate("one"), USER); + installNewPackage(target(TARGET) + .addOverlayable("TestResources") + .setCertificate("two"), USER); + + final DummyDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET, "TestResources") + .setCertificate("one"); + installNewPackage(overlay, USER); + + final DummyIdmapDaemon idmapDaemon = getIdmapDaemon(); + final DummyIdmapDaemon.IdmapHeader idmap = idmapDaemon.getIdmap(overlay.build().apkPath); + assertNotNull(idmap); + assertEquals(OverlayablePolicy.ACTOR_SIGNATURE, + idmap.policies & OverlayablePolicy.ACTOR_SIGNATURE); + } + + @Test + public void testConfiguratorDifferentSignatures() { + final DummyPackageManagerHelper packageManager = getPackageManager(); + packageManager.overlayableConfigurator = "actor"; + packageManager.overlayableConfiguratorTargets = new String[]{TARGET}; + reinitializeImpl(); + + installNewPackage(target("actor").setCertificate("one"), USER); + installNewPackage(target(TARGET) + .addOverlayable("TestResources") + .setCertificate("two"), USER); + + final DummyDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET, "TestResources") + .setCertificate("two"); + installNewPackage(overlay, USER); + + final DummyIdmapDaemon idmapDaemon = getIdmapDaemon(); + final DummyIdmapDaemon.IdmapHeader idmap = idmapDaemon.getIdmap(overlay.build().apkPath); + assertNotNull(idmap); + assertEquals(0, idmap.policies & OverlayablePolicy.ACTOR_SIGNATURE); + } } 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 9eda718ed922b..ec6a48182a252 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java @@ -17,6 +17,7 @@ package com.android.server.om; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -26,17 +27,19 @@ import android.content.om.OverlayInfo.State; import android.content.om.OverlayableInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; +import android.util.ArrayMap; import android.util.ArraySet; import androidx.annotation.Nullable; import com.android.internal.content.om.OverlayConfig; +import org.junit.Assert; import org.junit.Before; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -47,29 +50,48 @@ class OverlayManagerServiceImplTestsBase { private OverlayManagerServiceImpl mImpl; private DummyDeviceState mState; private DummyListener mListener; + private DummyPackageManagerHelper mPackageManager; + private DummyIdmapDaemon mIdmapDaemon; + private OverlayConfig mOverlayConfig; @Before public void setUp() { mState = new DummyDeviceState(); mListener = new DummyListener(); - final DummyPackageManagerHelper pmh = new DummyPackageManagerHelper(mState); + mPackageManager = new DummyPackageManagerHelper(mState); + mIdmapDaemon = new DummyIdmapDaemon(mState); + mOverlayConfig = mock(OverlayConfig.class); + when(mOverlayConfig.getPriority(any())).thenReturn(OverlayConfig.DEFAULT_PRIORITY); + when(mOverlayConfig.isEnabled(any())).thenReturn(false); + when(mOverlayConfig.isMutable(any())).thenReturn(true); + reinitializeImpl(); + } - mImpl = new OverlayManagerServiceImpl(pmh, - new DummyIdmapManager(mState, pmh), + void reinitializeImpl() { + mImpl = new OverlayManagerServiceImpl(mPackageManager, + new IdmapManager(mIdmapDaemon, mPackageManager), new OverlayManagerSettings(), - mState.mOverlayConfig, + mOverlayConfig, new String[0], mListener); } - public OverlayManagerServiceImpl getImpl() { + OverlayManagerServiceImpl getImpl() { return mImpl; } - public DummyListener getListener() { + DummyListener getListener() { return mListener; } + DummyPackageManagerHelper getPackageManager() { + return mPackageManager; + } + + DummyIdmapDaemon getIdmapDaemon() { + return mIdmapDaemon; + } + void assertState(@State int expected, final String overlayPackageName, int userId) { final OverlayInfo info = mImpl.getOverlayInfo(overlayPackageName, userId); if (info == null) { @@ -81,7 +103,7 @@ class OverlayManagerServiceImplTestsBase { assertEquals(msg, expected, info.state); } - void assertOverlayInfoList(final String targetPackageName, int userId, + void assertOverlayInfoForTarget(final String targetPackageName, int userId, OverlayInfo... overlayInfos) { final List expected = mImpl.getOverlayInfosForTarget(targetPackageName, userId); @@ -89,198 +111,202 @@ class OverlayManagerServiceImplTestsBase { 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); + DummyDeviceState.PackageBuilder target(String packageName) { + return new DummyDeviceState.PackageBuilder(packageName, null /* targetPackageName */, + null /* targetOverlayableName */); + } + + DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) { + return overlay(packageName, targetPackageName, null /* targetOverlayableName */); + } + + DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName, + String targetOverlayableName) { + return new DummyDeviceState.PackageBuilder(packageName, targetPackageName, + targetOverlayableName); + } + + void addSystemPackage(DummyDeviceState.PackageBuilder pkg, int userId) { + mState.add(pkg, userId); + } + + void configureSystemOverlay(String packageName, boolean mutable, boolean enabled, + int priority) { + when(mOverlayConfig.getPriority(packageName)).thenReturn(priority); + when(mOverlayConfig.isEnabled(packageName)).thenReturn(enabled); + when(mOverlayConfig.isMutable(packageName)).thenReturn(mutable); } /** - * Adds the target package to the device. + * Adds the 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 + * @throws IllegalStateException if the package is currently installed */ - void installTargetPackage(String packageName, int userId) { - if (mState.select(packageName, userId) != null) { - throw new IllegalStateException("package already installed"); + void installNewPackage(DummyDeviceState.PackageBuilder pkg, int userId) { + 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); } - mState.addTarget(packageName, userId); - mImpl.onTargetPackageAdded(packageName, userId); } /** - * Begins upgrading the target package. + * Begins upgrading the 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#EXTRA_REPLACING} extra and then 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"); + void upgradePackage(DummyDeviceState.PackageBuilder pkg, int userId) { + final DummyDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId); + if (replacedPackage == null) { + throw new IllegalStateException("package " + pkg.packageName + " not installed"); + } + if (replacedPackage.targetPackageName != null) { + mImpl.onOverlayPackageReplacing(pkg.packageName, userId); + } + + mState.add(pkg, userId); + if (pkg.targetPackage == null) { + mImpl.onTargetPackageReplaced(pkg.packageName, userId); + } else { + mImpl.onOverlayPackageReplaced(pkg.packageName, userId); } - mState.addTarget(packageName, userId); - mImpl.onTargetPackageReplaced(packageName, userId); } /** - * Removes the target package from the device. + * Removes the 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"); + void uninstallPackage(String packageName, int userId) { + final DummyDeviceState.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); } - 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); - } + /** Represents the state of packages installed on a fake device. */ + static class DummyDeviceState { + private ArrayMap mPackages = new ArrayMap<>(); - /** - * 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"); - } + void add(PackageBuilder pkgBuilder, int userId) { + final Package pkg = pkgBuilder.build(); + final Package previousPkg = select(pkg.packageName, userId); + mPackages.put(pkg.packageName, pkg); - 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; - } + pkg.installedUserIds.add(userId); + if (previousPkg != null) { + pkg.installedUserIds.addAll(previousPkg.installedUserIds); } } - /** 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()); + void remove(String packageName) { + mPackages.remove(packageName); } - /** 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); + void uninstall(String packageName, int userId) { + final Package pkg = mPackages.get(packageName); + if (pkg != null) { + pkg.installedUserIds.remove(userId); + } } - 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; + List select(int userId) { + return mPackages.values().stream().filter(p -> p.installedUserIds.contains(userId)) + .collect(Collectors.toList()); + } - private Package(String packageName, int userId, String targetPackageName, - boolean mutable, boolean enabled, int priority) { + Package select(String packageName, int userId) { + final Package pkg = mPackages.get(packageName); + return pkg != null && pkg.installedUserIds.contains(userId) ? pkg : null; + } + + private Package selectFromPath(String path) { + return mPackages.values().stream() + .filter(p -> p.apkPath.equals(path)).findFirst().orElse(null); + } + + static final class PackageBuilder { + private String packageName; + private String targetPackage; + private String certificate = "[default]"; + private int version = 0; + private ArrayList overlayableNames = new ArrayList<>(); + private String targetOverlayableName; + + private PackageBuilder(String packageName, String targetPackage, + String targetOverlayableName) { + this.packageName = packageName; + this.targetPackage = targetPackage; + this.targetOverlayableName = targetOverlayableName; + } + + PackageBuilder setCertificate(String certificate) { + this.certificate = certificate; + return this; + } + + PackageBuilder addOverlayable(String overlayableName) { + overlayableNames.add(overlayableName); + return this; + } + + PackageBuilder setVersion(int version) { + this.version = version; + return this; + } + + Package build() { + final String apkPath = String.format("%s/%s/base.apk", + targetPackage == null ? "/system/app/:" : "/vendor/overlay/:", + packageName); + final Package newPackage = new Package(packageName, targetPackage, + targetOverlayableName, version, apkPath, certificate); + newPackage.overlayableNames.addAll(overlayableNames); + return newPackage; + } + } + + static final class Package { + final String packageName; + final String targetPackageName; + final String targetOverlayableName; + final int versionCode; + final String apkPath; + final String certificate; + final ArrayList overlayableNames = new ArrayList<>(); + private final ArraySet installedUserIds = new ArraySet<>(); + + private Package(String packageName, String targetPackageName, + String targetOverlayableName, int versionCode, String apkPath, + String certificate) { this.packageName = packageName; - this.userId = userId; this.targetPackageName = targetPackageName; - this.mutable = mutable; - this.enabled = enabled; - this.priority = priority; + this.targetOverlayableName = targetOverlayableName; + this.versionCode = versionCode; + this.apkPath = apkPath; + this.certificate = certificate; } } } @@ -288,6 +314,8 @@ class OverlayManagerServiceImplTestsBase { static final class DummyPackageManagerHelper implements PackageManagerHelper, OverlayableInfoCallback { private final DummyDeviceState mState; + String[] overlayableConfiguratorTargets = new String[0]; + String overlayableConfigurator = ""; private DummyPackageManagerHelper(DummyDeviceState state) { mState = state; @@ -300,13 +328,12 @@ class OverlayManagerServiceImplTestsBase { return null; } final ApplicationInfo ai = new ApplicationInfo(); - ai.sourceDir = String.format("%s/%s/base.apk", - pkg.targetPackageName == null ? "/system/app/" : "/vendor/overlay/", - pkg.packageName); + 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 = "dummy-category-" + pkg.targetPackageName; return pi; } @@ -314,14 +341,16 @@ class OverlayManagerServiceImplTestsBase { @Override public boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2, int userId) { - return false; + final DummyDeviceState.Package pkg1 = mState.select(packageName1, userId); + final DummyDeviceState.Package pkg2 = mState.select(packageName2, userId); + return pkg1 != null && pkg2 != null && pkg1.certificate.equals(pkg2.certificate); } @Override public List getOverlayPackages(int userId) { return mState.select(userId).stream() .filter(p -> p.targetPackageName != null) - .map(p -> getPackageInfo(p.packageName, p.userId)) + .map(p -> getPackageInfo(p.packageName, userId)) .collect(Collectors.toList()); } @@ -329,7 +358,11 @@ class OverlayManagerServiceImplTestsBase { @Override public OverlayableInfo getOverlayableForTarget(@NonNull String packageName, @NonNull String targetOverlayableName, int userId) { - throw new UnsupportedOperationException(); + final DummyDeviceState.Package pkg = mState.select(packageName, userId); + if (pkg == null || !pkg.overlayableNames.contains(targetOverlayableName)) { + return null; + } + return new OverlayableInfo(targetOverlayableName, null /* actor */); } @Nullable @@ -341,69 +374,98 @@ class OverlayManagerServiceImplTestsBase { @NonNull @Override public Map> getNamedActors() { - throw new UnsupportedOperationException(); + return Collections.emptyMap(); } @Override public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) { - throw new UnsupportedOperationException(); + final DummyDeviceState.Package pkg = mState.select(targetPackageName, userId); + return pkg != null && pkg.overlayableNames.contains(targetPackageName); } @Override public void enforcePermission(String permission, String message) throws SecurityException { throw new UnsupportedOperationException(); } + + @Override + public String[] getOverlayableConfiguratorTargets() { + return overlayableConfiguratorTargets; + } + + @Override + public String getOverlayableConfigurator() { + return overlayableConfigurator; + } } - static class DummyIdmapManager extends IdmapManager { + static class DummyIdmapDaemon extends IdmapDaemon { private final DummyDeviceState mState; - private Set mIdmapFiles = new ArraySet<>(); + private final ArrayMap mIdmapFiles = new ArrayMap<>(); - private DummyIdmapManager(DummyDeviceState state, - DummyPackageManagerHelper packageManagerHelper) { - super(packageManagerHelper); - mState = state; + DummyIdmapDaemon(DummyDeviceState state) { + this.mState = state; + } + + private int getCrc(@NonNull final String path) { + final DummyDeviceState.Package pkg = mState.selectFromPath(path); + Assert.assertNotNull(pkg); + return pkg.versionCode; } @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) { + String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce, + int userId) { + mIdmapFiles.put(overlayPath, new IdmapHeader(getCrc(targetPath), + getCrc(overlayPath), targetPath, policies, enforce)); + return overlayPath; + } + + @Override + boolean removeIdmap(String overlayPath, int userId) { + return mIdmapFiles.remove(overlayPath) != null; + } + + @Override + boolean verifyIdmap(String targetPath, String overlayPath, int policies, boolean enforce, + int userId) { + final IdmapHeader idmap = mIdmapFiles.get(overlayPath); + if (idmap == null) { return false; } - final DummyDeviceState.Package o = mState.select(overlayPackage.packageName, userId); - if (o == null) { - return false; + return idmap.isUpToDate(getCrc(targetPath), getCrc(overlayPath), targetPath); + } + + @Override + boolean idmapExists(String overlayPath, int userId) { + return mIdmapFiles.containsKey(overlayPath); + } + + IdmapHeader getIdmap(String overlayPath) { + return mIdmapFiles.get(overlayPath); + } + + static class IdmapHeader { + private final int targetCrc; + private final int overlayCrc; + final String targetPath; + final int policies; + final boolean enforceOverlayable; + + private IdmapHeader(int targetCrc, int overlayCrc, String targetPath, int policies, + boolean enforceOverlayable) { + this.targetCrc = targetCrc; + this.overlayCrc = overlayCrc; + this.targetPath = targetPath; + this.policies = policies; + this.enforceOverlayable = enforceOverlayable; } - final String key = createKey(overlayPackage.packageName, userId); - return mIdmapFiles.add(key); - } - @Override - boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) { - final String key = createKey(oi.packageName, oi.userId); - if (!mIdmapFiles.contains(key)) { - return false; + private boolean isUpToDate(int expectedTargetCrc, int expectedOverlayCrc, + String expectedTargetPath) { + return expectedTargetCrc == targetCrc && expectedOverlayCrc == overlayCrc + && expectedTargetPath.equals(targetPath); } - 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); } }