Merge "Add overlayable configurator resources" into rvc-dev am: 4515014469

Change-Id: If2913c9b26ff73fd8d0e4914736d11e8d46396dd
This commit is contained in:
Ryan Mitchell
2020-05-26 18:03:12 +00:00
committed by Automerger Merge Worker
10 changed files with 471 additions and 315 deletions

View File

@@ -4451,4 +4451,11 @@
<bool name="config_pdp_reject_enable_retry">false</bool> <bool name="config_pdp_reject_enable_retry">false</bool>
<!-- pdp data reject retry delay in ms --> <!-- pdp data reject retry delay in ms -->
<integer name="config_pdp_reject_retry_delay_ms">-1</integer> <integer name="config_pdp_reject_retry_delay_ms">-1</integer>
<!-- Package name that is recognized as an actor for the packages listed in
@array/config_overlayableConfiguratorTargets. If an overlay targeting one of the listed
targets is signed with the same signature as the configurator, the overlay will be granted
the "actor" policy. -->
<string name="config_overlayableConfigurator" translatable="false" />
<string-array name="config_overlayableConfiguratorTargets" translatable="false" />
</resources> </resources>

View File

@@ -4025,4 +4025,6 @@
<java-symbol type="string" name="config_pdp_reject_service_not_subscribed" /> <java-symbol type="string" name="config_pdp_reject_service_not_subscribed" />
<java-symbol type="string" name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" /> <java-symbol type="string" name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" />
<java-symbol type="string" name="config_overlayableConfigurator" />
<java-symbol type="array" name="config_overlayableConfiguratorTargets" />
</resources> </resources>

View File

@@ -30,6 +30,7 @@ import android.util.Slog;
import com.android.server.FgThread; import com.android.server.FgThread;
import java.io.File;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger; 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()) { 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;
} }
} }

View File

@@ -27,10 +27,10 @@ import android.content.pm.PackageInfo;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.os.OverlayablePolicy; import android.os.OverlayablePolicy;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Slog; import android.util.Slog;
import java.io.File; import com.android.internal.util.ArrayUtils;
import java.io.IOException; import java.io.IOException;
/** /**
@@ -55,12 +55,16 @@ class IdmapManager {
VENDOR_IS_Q_OR_LATER = isQOrLater; VENDOR_IS_Q_OR_LATER = isQOrLater;
} }
private final OverlayableInfoCallback mOverlayableCallback;
private final IdmapDaemon mIdmapDaemon; 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; 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) { 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) { boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath(), userId)) return mIdmapDaemon.idmapExists(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 "";
}
} }
/** /**
@@ -198,9 +190,17 @@ class IdmapManager {
String targetOverlayableName = overlayPackage.targetOverlayableName; String targetOverlayableName = overlayPackage.targetOverlayableName;
if (targetOverlayableName != null) { if (targetOverlayableName != null) {
try { try {
if (!mOverlayableConfigurator.isEmpty()
&& ArrayUtils.contains(mOverlayableConfiguratorTargets,
targetPackage.packageName)
&& mOverlayableCallback.signaturesMatching(mOverlayableConfigurator,
overlayPackage.packageName, userId)) {
return true;
}
OverlayableInfo overlayableInfo = mOverlayableCallback.getOverlayableForTarget( OverlayableInfo overlayableInfo = mOverlayableCallback.getOverlayableForTarget(
targetPackage.packageName, targetOverlayableName, userId); targetPackage.packageName, targetOverlayableName, userId);
if (overlayableInfo != null) { if (overlayableInfo != null && overlayableInfo.actor != null) {
String actorPackageName = OverlayActorEnforcer.getPackageNameForActor( String actorPackageName = OverlayActorEnforcer.getPackageNameForActor(
overlayableInfo.actor, mOverlayableCallback.getNamedActors()).first; overlayableInfo.actor, mOverlayableCallback.getNamedActors()).first;
if (mOverlayableCallback.signaturesMatching(actorPackageName, if (mOverlayableCallback.signaturesMatching(actorPackageName,

View File

@@ -50,8 +50,8 @@ public class OverlayActorEnforcer {
/** /**
* @return nullable actor result with {@link ActorState} failure status * @return nullable actor result with {@link ActorState} failure status
*/ */
static Pair<String, ActorState> getPackageNameForActor(String actorUriString, static Pair<String, ActorState> getPackageNameForActor(@NonNull String actorUriString,
Map<String, Map<String, String>> namedActors) { @NonNull Map<String, Map<String, String>> namedActors) {
Uri actorUri = Uri.parse(actorUriString); Uri actorUri = Uri.parse(actorUriString);
String actorScheme = actorUri.getScheme(); String actorScheme = actorUri.getScheme();

View File

@@ -45,6 +45,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.content.res.ApkAssets; import android.content.res.ApkAssets;
import android.content.res.Resources;
import android.net.Uri; import android.net.Uri;
import android.os.Binder; import android.os.Binder;
import android.os.Environment; import android.os.Environment;
@@ -62,6 +63,7 @@ import android.util.AtomicFile;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.content.om.OverlayConfig; import com.android.internal.content.om.OverlayConfig;
import com.android.server.FgThread; import com.android.server.FgThread;
import com.android.server.IoThread; import com.android.server.IoThread;
@@ -246,7 +248,7 @@ public final class OverlayManagerService extends SystemService {
new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays"); new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
mPackageManager = new PackageManagerHelperImpl(context); mPackageManager = new PackageManagerHelperImpl(context);
mUserManager = UserManagerService.getInstance(); mUserManager = UserManagerService.getInstance();
IdmapManager im = new IdmapManager(mPackageManager); IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager);
mSettings = new OverlayManagerSettings(); mSettings = new OverlayManagerSettings();
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings, mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(), OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
@@ -1118,6 +1120,17 @@ public final class OverlayManagerService extends SystemService {
return false; 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 @Override
public List<PackageInfo> getOverlayPackages(final int userId) { public List<PackageInfo> getOverlayPackages(final int userId) {
final List<PackageInfo> overlays = mPackageManagerInternal.getOverlayPackages(userId); final List<PackageInfo> overlays = mPackageManagerInternal.getOverlayPackages(userId);

View File

@@ -80,4 +80,24 @@ public interface OverlayableInfoCallback {
* in the system returns {@link PackageManager#SIGNATURE_MATCH} * in the system returns {@link PackageManager#SIGNATURE_MATCH}
*/ */
boolean signaturesMatching(@NonNull String pkgName1, @NonNull String pkgName2, int userId); 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];
}
} }

View File

@@ -44,9 +44,9 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
@Test @Test
public void testUpdateOverlaysForUser() { public void testUpdateOverlaysForUser() {
final OverlayManagerServiceImpl impl = getImpl(); final OverlayManagerServiceImpl impl = getImpl();
installTargetPackage(TARGET, USER); addSystemPackage(target(TARGET), USER);
installTargetPackage("some.other.target", USER); addSystemPackage(target("some.other.target"), USER);;
installOverlayPackage(OVERLAY, TARGET, USER); addSystemPackage(overlay(OVERLAY, TARGET), USER);
// do nothing, expect no change // do nothing, expect no change
final List<String> a = impl.updateOverlaysForUser(USER); final List<String> a = impl.updateOverlaysForUser(USER);
@@ -54,8 +54,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
assertTrue(a.contains(TARGET)); assertTrue(a.contains(TARGET));
// upgrade overlay, keep target // upgrade overlay, keep target
beginUpgradeOverlayPackage(OVERLAY, USER); addSystemPackage(overlay(OVERLAY, TARGET), USER);
endUpgradeOverlayPackage(OVERLAY, TARGET, USER);
final List<String> b = impl.updateOverlaysForUser(USER); final List<String> b = impl.updateOverlaysForUser(USER);
assertEquals(1, b.size()); assertEquals(1, b.size());
@@ -67,7 +66,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
assertTrue(c.contains(TARGET)); assertTrue(c.contains(TARGET));
// upgrade overlay, switch to new 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<String> d = impl.updateOverlaysForUser(USER); final List<String> d = impl.updateOverlaysForUser(USER);
assertEquals(2, d.size()); assertEquals(2, d.size());
assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target"))); assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target")));
@@ -81,23 +80,24 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
@Test @Test
public void testImmutableEnabledChange() { public void testImmutableEnabledChange() {
final OverlayManagerServiceImpl impl = getImpl(); 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); impl.updateOverlaysForUser(USER);
final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(o1); assertNotNull(o1);
assertFalse(o1.isEnabled()); assertFalse(o1.isEnabled());
assertFalse(o1.isMutable); assertFalse(o1.isMutable);
addOverlayPackage(OVERLAY, TARGET, USER, false, true, 0); configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER); impl.updateOverlaysForUser(USER);
final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(o2); assertNotNull(o2);
assertTrue(o2.isEnabled()); assertTrue(o2.isEnabled());
assertFalse(o2.isMutable); assertFalse(o2.isMutable);
addOverlayPackage(OVERLAY, TARGET, USER, false, false, 0); configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER); impl.updateOverlaysForUser(USER);
final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(o3); assertNotNull(o3);
@@ -108,23 +108,24 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
@Test @Test
public void testMutableEnabledChangeHasNoEffect() { public void testMutableEnabledChangeHasNoEffect() {
final OverlayManagerServiceImpl impl = getImpl(); 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); impl.updateOverlaysForUser(USER);
final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(o1); assertNotNull(o1);
assertFalse(o1.isEnabled()); assertFalse(o1.isEnabled());
assertTrue(o1.isMutable); assertTrue(o1.isMutable);
addOverlayPackage(OVERLAY, TARGET, USER, true, true, 0); configureSystemOverlay(OVERLAY, true /* mutable */, true /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER); impl.updateOverlaysForUser(USER);
final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(o2); assertNotNull(o2);
assertFalse(o2.isEnabled()); assertFalse(o2.isEnabled());
assertTrue(o2.isMutable); assertTrue(o2.isMutable);
addOverlayPackage(OVERLAY, TARGET, USER, true, false, 0); configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER); impl.updateOverlaysForUser(USER);
final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(o3); assertNotNull(o3);
@@ -135,15 +136,16 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
@Test @Test
public void testMutableEnabledToImmutableEnabled() { public void testMutableEnabledToImmutableEnabled() {
final OverlayManagerServiceImpl impl = getImpl(); final OverlayManagerServiceImpl impl = getImpl();
installTargetPackage(TARGET, USER); installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
final BiConsumer<Boolean, Boolean> setOverlay = (mutable, enabled) -> { final BiConsumer<Boolean, Boolean> setOverlay = (mutable, enabled) -> {
addOverlayPackage(OVERLAY, TARGET, USER, mutable, enabled, 0); configureSystemOverlay(OVERLAY, mutable, enabled, 0 /* priority */);
impl.updateOverlaysForUser(USER); impl.updateOverlaysForUser(USER);
final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(o1); assertNotNull(o);
assertEquals(enabled, o1.isEnabled()); assertEquals(enabled, o.isEnabled());
assertEquals(mutable, o1.isMutable); assertEquals(mutable, o.isMutable);
}; };
// Immutable/enabled -> mutable/enabled // Immutable/enabled -> mutable/enabled
@@ -178,70 +180,76 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
@Test @Test
public void testMutablePriorityChange() { public void testMutablePriorityChange() {
final OverlayManagerServiceImpl impl = getImpl(); final OverlayManagerServiceImpl impl = getImpl();
installTargetPackage(TARGET, USER); installNewPackage(target(TARGET), USER);
addOverlayPackage(OVERLAY, TARGET, USER, true, true, 0); installNewPackage(overlay(OVERLAY, TARGET), USER);
addOverlayPackage(OVERLAY2, TARGET, USER, true, true, 1); installNewPackage(overlay(OVERLAY2, TARGET), USER);
configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */);
configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 1 /* priority */);
impl.updateOverlaysForUser(USER); impl.updateOverlaysForUser(USER);
final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(o1); assertNotNull(o1);
assertEquals(0, o1.priority); assertEquals(0, o1.priority);
assertFalse(o1.isEnabled());
final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
assertNotNull(o2); assertNotNull(o2);
assertEquals(1, o2.priority); assertEquals(1, o2.priority);
assertFalse(o2.isEnabled());
// Overlay priority changing between reboots should not affect enable state of mutable // Overlay priority changing between reboots should not affect enable state of mutable
// overlays // overlays.
impl.setEnabled(OVERLAY, true, USER); impl.setEnabled(OVERLAY, true, USER);
// Reorder the overlays // Reorder the overlays
addOverlayPackage(OVERLAY, TARGET, USER, true, true, 1); configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 1 /* priority */);
addOverlayPackage(OVERLAY2, TARGET, USER, true, true, 0); configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER); impl.updateOverlaysForUser(USER);
final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(o3); assertNotNull(o3);
assertEquals(1, o3.priority); assertEquals(1, o3.priority);
assertTrue(o3.isEnabled());
final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER); final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER);
assertNotNull(o4); assertNotNull(o4);
assertEquals(0, o4.priority); assertEquals(0, o4.priority);
assertTrue(o1.isEnabled()); assertFalse(o4.isEnabled());
} }
@Test @Test
public void testImmutablePriorityChange() { public void testImmutablePriorityChange() {
final OverlayManagerServiceImpl impl = getImpl(); final OverlayManagerServiceImpl impl = getImpl();
installTargetPackage(TARGET, USER); installNewPackage(target(TARGET), USER);
addOverlayPackage(OVERLAY, TARGET, USER, false, true, 0); installNewPackage(overlay(OVERLAY, TARGET), USER);
addOverlayPackage(OVERLAY2, TARGET, USER, false, true, 1); installNewPackage(overlay(OVERLAY2, TARGET), USER);
configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */);
configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 1 /* priority */);
impl.updateOverlaysForUser(USER); impl.updateOverlaysForUser(USER);
final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(o1); assertNotNull(o1);
assertEquals(0, o1.priority); assertEquals(0, o1.priority);
assertTrue(o1.isEnabled());
final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
assertNotNull(o2); assertNotNull(o2);
assertEquals(1, o2.priority); assertEquals(1, o2.priority);
assertTrue(o2.isEnabled());
// Overlay priority changing between reboots should not affect enable state of mutable
// overlays
impl.setEnabled(OVERLAY, true, USER);
// Reorder the overlays // Reorder the overlays
addOverlayPackage(OVERLAY, TARGET, USER, false, true, 1); configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 1 /* priority */);
addOverlayPackage(OVERLAY2, TARGET, USER, false, true, 0); configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 0 /* priority */);
impl.updateOverlaysForUser(USER); impl.updateOverlaysForUser(USER);
final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
assertNotNull(o3); assertNotNull(o3);
assertEquals(1, o3.priority); assertEquals(1, o3.priority);
assertTrue(o3.isEnabled());
final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER); final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER);
assertNotNull(o4); assertNotNull(o4);
assertEquals(0, o4.priority); assertEquals(0, o4.priority);
assertTrue(o1.isEnabled()); assertTrue(o4.isEnabled());
} }
} }

View File

@@ -26,6 +26,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import android.content.om.OverlayInfo; import android.content.om.OverlayInfo;
import android.os.OverlayablePolicy;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
@@ -49,11 +50,9 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
private static final String OVERLAY3 = OVERLAY + "3"; private static final String OVERLAY3 = OVERLAY + "3";
private static final int USER3 = USER2 + 1; private static final int USER3 = USER2 + 1;
// tests: basics
@Test @Test
public void testGetOverlayInfo() throws Exception { public void testGetOverlayInfo() {
installOverlayPackage(OVERLAY, TARGET, USER); installNewPackage(overlay(OVERLAY, TARGET), USER);
final OverlayManagerServiceImpl impl = getImpl(); final OverlayManagerServiceImpl impl = getImpl();
final OverlayInfo oi = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo oi = impl.getOverlayInfo(OVERLAY, USER);
@@ -64,10 +63,10 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
} }
@Test @Test
public void testGetOverlayInfosForTarget() throws Exception { public void testGetOverlayInfosForTarget() {
installOverlayPackage(OVERLAY, TARGET, USER); installNewPackage(overlay(OVERLAY, TARGET), USER);
installOverlayPackage(OVERLAY2, TARGET, USER); installNewPackage(overlay(OVERLAY2, TARGET), USER);
installOverlayPackage(OVERLAY3, TARGET, USER2); installNewPackage(overlay(OVERLAY3, TARGET), USER2);
final OverlayManagerServiceImpl impl = getImpl(); final OverlayManagerServiceImpl impl = getImpl();
final List<OverlayInfo> ois = impl.getOverlayInfosForTarget(TARGET, USER); final List<OverlayInfo> ois = impl.getOverlayInfosForTarget(TARGET, USER);
@@ -89,11 +88,11 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
} }
@Test @Test
public void testGetOverlayInfosForUser() throws Exception { public void testGetOverlayInfosForUser() {
installTargetPackage(TARGET, USER); installNewPackage(target(TARGET), USER);
installOverlayPackage(OVERLAY, TARGET, USER); installNewPackage(overlay(OVERLAY, TARGET), USER);
installOverlayPackage(OVERLAY2, TARGET, USER); installNewPackage(overlay(OVERLAY2, TARGET), USER);
installOverlayPackage(OVERLAY3, TARGET2, USER); installNewPackage(overlay(OVERLAY3, TARGET2), USER);
final OverlayManagerServiceImpl impl = getImpl(); final OverlayManagerServiceImpl impl = getImpl();
final Map<String, List<OverlayInfo>> everything = impl.getOverlaysForUser(USER); final Map<String, List<OverlayInfo>> everything = impl.getOverlaysForUser(USER);
@@ -116,91 +115,86 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
} }
@Test @Test
public void testPriority() throws Exception { public void testPriority() {
installOverlayPackage(OVERLAY, TARGET, USER); installNewPackage(overlay(OVERLAY, TARGET), USER);
installOverlayPackage(OVERLAY2, TARGET, USER); installNewPackage(overlay(OVERLAY2, TARGET), USER);
installOverlayPackage(OVERLAY3, TARGET, USER); installNewPackage(overlay(OVERLAY3, TARGET), USER);
final OverlayManagerServiceImpl impl = getImpl(); final OverlayManagerServiceImpl impl = getImpl();
final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER); final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER); final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY3, 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)); assertTrue(impl.setLowestPriority(OVERLAY3, USER));
assertOverlayInfoList(TARGET, USER, o3, o1, o2); assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2);
assertTrue(impl.setHighestPriority(OVERLAY3, USER)); assertTrue(impl.setHighestPriority(OVERLAY3, USER));
assertOverlayInfoList(TARGET, USER, o1, o2, o3); assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER)); assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER));
assertOverlayInfoList(TARGET, USER, o2, o1, o3); assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3);
} }
@Test @Test
public void testOverlayInfoStateTransitions() throws Exception { public void testOverlayInfoStateTransitions() {
final OverlayManagerServiceImpl impl = getImpl(); final OverlayManagerServiceImpl impl = getImpl();
assertNull(impl.getOverlayInfo(OVERLAY, USER)); assertNull(impl.getOverlayInfo(OVERLAY, USER));
installOverlayPackage(OVERLAY, TARGET, USER); installNewPackage(overlay(OVERLAY, TARGET), USER);
assertState(STATE_MISSING_TARGET, OVERLAY, USER); assertState(STATE_MISSING_TARGET, OVERLAY, USER);
installTargetPackage(TARGET, USER); final DummyDeviceState.PackageBuilder target = target(TARGET);
installNewPackage(target, USER);
assertState(STATE_DISABLED, OVERLAY, USER); assertState(STATE_DISABLED, OVERLAY, USER);
impl.setEnabled(OVERLAY, true, USER); impl.setEnabled(OVERLAY, true, USER);
assertState(STATE_ENABLED, OVERLAY, USER); assertState(STATE_ENABLED, OVERLAY, USER);
// target upgrades do not change the state of the overlay // target upgrades do not change the state of the overlay
beginUpgradeTargetPackage(TARGET, USER); upgradePackage(target, USER);
assertState(STATE_ENABLED, OVERLAY, USER); assertState(STATE_ENABLED, OVERLAY, USER);
endUpgradeTargetPackage(TARGET, USER); uninstallPackage(TARGET, USER);
assertState(STATE_ENABLED, OVERLAY, USER);
uninstallTargetPackage(TARGET, USER);
assertState(STATE_MISSING_TARGET, OVERLAY, USER); assertState(STATE_MISSING_TARGET, OVERLAY, USER);
installTargetPackage(TARGET, USER); installNewPackage(target, USER);
assertState(STATE_ENABLED, OVERLAY, USER); assertState(STATE_ENABLED, OVERLAY, USER);
} }
@Test @Test
public void testOnOverlayPackageUpgraded() throws Exception { public void testOnOverlayPackageUpgraded() {
final OverlayManagerServiceImpl impl = getImpl();
final DummyListener listener = getListener(); final DummyListener listener = getListener();
installTargetPackage(TARGET, USER); final DummyDeviceState.PackageBuilder target = target(TARGET);
installOverlayPackage(OVERLAY, TARGET, USER); final DummyDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
impl.onOverlayPackageReplacing(OVERLAY, USER); installNewPackage(target, USER);
installNewPackage(overlay, USER);
listener.count = 0; listener.count = 0;
impl.onOverlayPackageReplaced(OVERLAY, USER); upgradePackage(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
assertEquals(2, listener.count); 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; listener.count = 0;
endUpgradeOverlayPackage(OVERLAY, "some.other.target", USER); final DummyDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
assertEquals(1, listener.count); upgradePackage(overlay2, USER);
assertEquals(3, listener.count);
listener.count = 0;
upgradePackage(overlay2, USER);
assertEquals(2, listener.count);
} }
// tests: listener interface
@Test @Test
public void testListener() throws Exception { public void testListener() {
final OverlayManagerServiceImpl impl = getImpl(); final OverlayManagerServiceImpl impl = getImpl();
final DummyListener listener = getListener(); final DummyListener listener = getListener();
installOverlayPackage(OVERLAY, TARGET, USER); installNewPackage(overlay(OVERLAY, TARGET), USER);
assertEquals(1, listener.count); assertEquals(1, listener.count);
listener.count = 0; listener.count = 0;
installTargetPackage(TARGET, USER); installNewPackage(target(TARGET), USER);
assertEquals(1, listener.count); assertEquals(1, listener.count);
listener.count = 0; listener.count = 0;
@@ -211,4 +205,49 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
impl.setEnabled(OVERLAY, true, USER); impl.setEnabled(OVERLAY, true, USER);
assertEquals(0, listener.count); 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);
}
} }

View File

@@ -17,6 +17,7 @@
package com.android.server.om; package com.android.server.om;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -26,17 +27,19 @@ import android.content.om.OverlayInfo.State;
import android.content.om.OverlayableInfo; import android.content.om.OverlayableInfo;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.internal.content.om.OverlayConfig; import com.android.internal.content.om.OverlayConfig;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -47,29 +50,48 @@ class OverlayManagerServiceImplTestsBase {
private OverlayManagerServiceImpl mImpl; private OverlayManagerServiceImpl mImpl;
private DummyDeviceState mState; private DummyDeviceState mState;
private DummyListener mListener; private DummyListener mListener;
private DummyPackageManagerHelper mPackageManager;
private DummyIdmapDaemon mIdmapDaemon;
private OverlayConfig mOverlayConfig;
@Before @Before
public void setUp() { public void setUp() {
mState = new DummyDeviceState(); mState = new DummyDeviceState();
mListener = new DummyListener(); 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, void reinitializeImpl() {
new DummyIdmapManager(mState, pmh), mImpl = new OverlayManagerServiceImpl(mPackageManager,
new IdmapManager(mIdmapDaemon, mPackageManager),
new OverlayManagerSettings(), new OverlayManagerSettings(),
mState.mOverlayConfig, mOverlayConfig,
new String[0], new String[0],
mListener); mListener);
} }
public OverlayManagerServiceImpl getImpl() { OverlayManagerServiceImpl getImpl() {
return mImpl; return mImpl;
} }
public DummyListener getListener() { DummyListener getListener() {
return mListener; return mListener;
} }
DummyPackageManagerHelper getPackageManager() {
return mPackageManager;
}
DummyIdmapDaemon getIdmapDaemon() {
return mIdmapDaemon;
}
void assertState(@State int expected, final String overlayPackageName, int userId) { void assertState(@State int expected, final String overlayPackageName, int userId) {
final OverlayInfo info = mImpl.getOverlayInfo(overlayPackageName, userId); final OverlayInfo info = mImpl.getOverlayInfo(overlayPackageName, userId);
if (info == null) { if (info == null) {
@@ -81,7 +103,7 @@ class OverlayManagerServiceImplTestsBase {
assertEquals(msg, expected, info.state); assertEquals(msg, expected, info.state);
} }
void assertOverlayInfoList(final String targetPackageName, int userId, void assertOverlayInfoForTarget(final String targetPackageName, int userId,
OverlayInfo... overlayInfos) { OverlayInfo... overlayInfos) {
final List<OverlayInfo> expected = final List<OverlayInfo> expected =
mImpl.getOverlayInfosForTarget(targetPackageName, userId); mImpl.getOverlayInfosForTarget(targetPackageName, userId);
@@ -89,198 +111,202 @@ class OverlayManagerServiceImplTestsBase {
assertEquals(expected, actual); assertEquals(expected, actual);
} }
/** DummyDeviceState.PackageBuilder target(String packageName) {
* Creates an overlay configured through {@link OverlayConfig}. return new DummyDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
* null /* targetOverlayableName */);
* @throws IllegalStateException if the package is already installed }
*/
void addOverlayPackage(String packageName, String targetPackageName, int userId, DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) {
boolean mutable, boolean enabled, int priority) { return overlay(packageName, targetPackageName, null /* targetOverlayableName */);
mState.addOverlay(packageName, targetPackageName, userId, mutable, enabled, priority); }
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 * This corresponds to when the OMS receives the
* {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast. * {@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) { void installNewPackage(DummyDeviceState.PackageBuilder pkg, int userId) {
if (mState.select(packageName, userId) != null) { if (mState.select(pkg.packageName, userId) != null) {
throw new IllegalStateException("package already installed"); 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 * This corresponds to when the OMS receives the
* {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast with the * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast with the
* {@link android.content.Intent#EXTRA_REPLACING} extra. * {@link android.content.Intent#EXTRA_REPLACING} extra and then receives the
*
* @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#ACTION_PACKAGE_ADDED} broadcast with the
* {@link android.content.Intent#EXTRA_REPLACING} extra. * {@link android.content.Intent#EXTRA_REPLACING} extra.
* *
* @throws IllegalStateException if the package is not currently installed * @throws IllegalStateException if the package is not currently installed
*/ */
void endUpgradeTargetPackage(String packageName, int userId) { void upgradePackage(DummyDeviceState.PackageBuilder pkg, int userId) {
if (mState.select(packageName, userId) == null) { final DummyDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
throw new IllegalStateException("package not installed"); 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 * This corresponds to when the OMS receives the
* {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast. * {@link android.content.Intent#ACTION_PACKAGE_REMOVED} broadcast.
* *
* @throws IllegalStateException if the package is not currently installed * @throws IllegalStateException if the package is not currently installed
*/ */
void uninstallTargetPackage(String packageName, int userId) { void uninstallPackage(String packageName, int userId) {
if (mState.select(packageName, userId) == null) { final DummyDeviceState.Package pkg = mState.select(packageName, userId);
throw new IllegalStateException("package not installed"); 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);
} }
/** /** Represents the state of packages installed on a fake device. */
* Adds the overlay package to the device. static class DummyDeviceState {
* private ArrayMap<String, Package> mPackages = new ArrayMap<>();
* 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);
}
/** void add(PackageBuilder pkgBuilder, int userId) {
* Begins upgrading the overlay package. final Package pkg = pkgBuilder.build();
* final Package previousPkg = select(pkg.packageName, userId);
* This corresponds to when the OMS receives the mPackages.put(pkg.packageName, pkg);
* {@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); pkg.installedUserIds.add(userId);
} if (previousPkg != null) {
pkg.installedUserIds.addAll(previousPkg.installedUserIds);
/**
* 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<Package> 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<Package> 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. */ void remove(String packageName) {
public List<Package> select(int userId) { mPackages.remove(packageName);
return mPackages.stream().filter(p -> p.userId == userId).collect(Collectors.toList());
} }
/** Retrieves the package with the specified package name for a particular user. */ void uninstall(String packageName, int userId) {
public Package select(String packageName, int userId) { final Package pkg = mPackages.get(packageName);
return mPackages.stream().filter( if (pkg != null) {
p -> p.packageName.equals(packageName) && p.userId == userId) pkg.installedUserIds.remove(userId);
.findFirst().orElse(null); }
} }
private static final class Package { List<Package> select(int userId) {
public final String packageName; return mPackages.values().stream().filter(p -> p.installedUserIds.contains(userId))
public final int userId; .collect(Collectors.toList());
public final String targetPackageName; }
public final boolean mutable;
public final boolean enabled;
public final int priority;
private Package(String packageName, int userId, String targetPackageName, Package select(String packageName, int userId) {
boolean mutable, boolean enabled, int priority) { 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<String> 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<String> overlayableNames = new ArrayList<>();
private final ArraySet<Integer> installedUserIds = new ArraySet<>();
private Package(String packageName, String targetPackageName,
String targetOverlayableName, int versionCode, String apkPath,
String certificate) {
this.packageName = packageName; this.packageName = packageName;
this.userId = userId;
this.targetPackageName = targetPackageName; this.targetPackageName = targetPackageName;
this.mutable = mutable; this.targetOverlayableName = targetOverlayableName;
this.enabled = enabled; this.versionCode = versionCode;
this.priority = priority; this.apkPath = apkPath;
this.certificate = certificate;
} }
} }
} }
@@ -288,6 +314,8 @@ class OverlayManagerServiceImplTestsBase {
static final class DummyPackageManagerHelper implements PackageManagerHelper, static final class DummyPackageManagerHelper implements PackageManagerHelper,
OverlayableInfoCallback { OverlayableInfoCallback {
private final DummyDeviceState mState; private final DummyDeviceState mState;
String[] overlayableConfiguratorTargets = new String[0];
String overlayableConfigurator = "";
private DummyPackageManagerHelper(DummyDeviceState state) { private DummyPackageManagerHelper(DummyDeviceState state) {
mState = state; mState = state;
@@ -300,13 +328,12 @@ class OverlayManagerServiceImplTestsBase {
return null; return null;
} }
final ApplicationInfo ai = new ApplicationInfo(); final ApplicationInfo ai = new ApplicationInfo();
ai.sourceDir = String.format("%s/%s/base.apk", ai.sourceDir = pkg.apkPath;
pkg.targetPackageName == null ? "/system/app/" : "/vendor/overlay/",
pkg.packageName);
PackageInfo pi = new PackageInfo(); PackageInfo pi = new PackageInfo();
pi.applicationInfo = ai; pi.applicationInfo = ai;
pi.packageName = pkg.packageName; pi.packageName = pkg.packageName;
pi.overlayTarget = pkg.targetPackageName; pi.overlayTarget = pkg.targetPackageName;
pi.targetOverlayableName = pkg.targetOverlayableName;
pi.overlayCategory = "dummy-category-" + pkg.targetPackageName; pi.overlayCategory = "dummy-category-" + pkg.targetPackageName;
return pi; return pi;
} }
@@ -314,14 +341,16 @@ class OverlayManagerServiceImplTestsBase {
@Override @Override
public boolean signaturesMatching(@NonNull String packageName1, public boolean signaturesMatching(@NonNull String packageName1,
@NonNull String packageName2, int userId) { @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 @Override
public List<PackageInfo> getOverlayPackages(int userId) { public List<PackageInfo> getOverlayPackages(int userId) {
return mState.select(userId).stream() return mState.select(userId).stream()
.filter(p -> p.targetPackageName != null) .filter(p -> p.targetPackageName != null)
.map(p -> getPackageInfo(p.packageName, p.userId)) .map(p -> getPackageInfo(p.packageName, userId))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@@ -329,7 +358,11 @@ class OverlayManagerServiceImplTestsBase {
@Override @Override
public OverlayableInfo getOverlayableForTarget(@NonNull String packageName, public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
@NonNull String targetOverlayableName, int userId) { @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 @Nullable
@@ -341,69 +374,98 @@ class OverlayManagerServiceImplTestsBase {
@NonNull @NonNull
@Override @Override
public Map<String, Map<String, String>> getNamedActors() { public Map<String, Map<String, String>> getNamedActors() {
throw new UnsupportedOperationException(); return Collections.emptyMap();
} }
@Override @Override
public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) { 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 @Override
public void enforcePermission(String permission, String message) throws SecurityException { public void enforcePermission(String permission, String message) throws SecurityException {
throw new UnsupportedOperationException(); 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 final DummyDeviceState mState;
private Set<String> mIdmapFiles = new ArraySet<>(); private final ArrayMap<String, IdmapHeader> mIdmapFiles = new ArrayMap<>();
private DummyIdmapManager(DummyDeviceState state, DummyIdmapDaemon(DummyDeviceState state) {
DummyPackageManagerHelper packageManagerHelper) { this.mState = state;
super(packageManagerHelper); }
mState = state;
private int getCrc(@NonNull final String path) {
final DummyDeviceState.Package pkg = mState.selectFromPath(path);
Assert.assertNotNull(pkg);
return pkg.versionCode;
} }
@Override @Override
boolean createIdmap(@NonNull final PackageInfo targetPackage, String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
@NonNull final PackageInfo overlayPackage, int userId) { int userId) {
final DummyDeviceState.Package t = mState.select(targetPackage.packageName, userId); mIdmapFiles.put(overlayPath, new IdmapHeader(getCrc(targetPath),
if (t == null) { 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; return false;
} }
final DummyDeviceState.Package o = mState.select(overlayPackage.packageName, userId); return idmap.isUpToDate(getCrc(targetPath), getCrc(overlayPath), targetPath);
if (o == null) { }
return false;
@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 private boolean isUpToDate(int expectedTargetCrc, int expectedOverlayCrc,
boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) { String expectedTargetPath) {
final String key = createKey(oi.packageName, oi.userId); return expectedTargetCrc == targetCrc && expectedOverlayCrc == overlayCrc
if (!mIdmapFiles.contains(key)) { && expectedTargetPath.equals(targetPath);
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);
} }
} }