Loading core/java/android/content/pm/IPackageInstaller.aidl +0 −1 Original line number Diff line number Diff line Loading @@ -98,7 +98,6 @@ interface IPackageInstaller { @PermissionManuallyEnforced int getDeveloperVerificationPolicy(int userId); @PermissionManuallyEnforced boolean setDeveloperVerificationPolicy(int policy, int userId); ComponentName getDeveloperVerificationServiceProvider(); @EnforcePermission("DEVELOPER_VERIFICATION_AGENT") Loading services/core/java/com/android/server/pm/PackageInstallerService.java +55 −14 Original line number Diff line number Diff line Loading @@ -124,6 +124,7 @@ import com.android.internal.content.InstallLocationUtils; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.pm.parsing.PackageParser2; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ImageUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.modules.utils.TypedXmlPullParser; Loading Loading @@ -176,6 +177,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements /** XML constants used in {@link #mSessionsFile} */ private static final String TAG_SESSIONS = "sessions"; private static final String TAG_DEVELOPER_VERIFICATION_POLICY_PER_USER = "developerVerificationPolicyPerUser"; private static final String ATTR_USER_ID = "userId"; private static final String ATTR_DEVELOPER_VERIFICATION_POLICY = "developerVerificationPolicy"; /** Automatically destroy sessions older than this */ private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS; Loading Loading @@ -289,7 +294,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements /** * Default verification policy for incoming installation sessions, mapped from userId to policy. */ @GuardedBy("mVerificationPolicyPerUser") @GuardedBy("mDeveloperVerificationPolicyPerUser") private final SparseIntArray mDeveloperVerificationPolicyPerUser = new SparseIntArray(1); /** * Default developer verification policy for a new user. Loading Loading @@ -353,13 +358,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mPackageArchiver = new PackageArchiver(mContext, mPm); mDeveloperVerifierController = DeveloperVerifierController.getInstance(context, mInstallHandler, developerVerificationServiceProvider); synchronized (mDeveloperVerificationPolicyPerUser) { int[] users = mPm.mUserManager.getUserIds(); for (int i = 0; i < users.length; i++) { // TODO(b/360129657): preserve the overridden policy across reboots. mDeveloperVerificationPolicyPerUser.put(users[i], DEFAULT_VERIFICATION_POLICY); } } mInstallDependencyHelper = new InstallDependencyHelper(mContext, mPm.mInjector.getSharedLibrariesImpl(), this); Loading Loading @@ -404,12 +402,30 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements Slog.w(TAG, "Deleting orphan icon " + icon); icon.delete(); } } // Clean up the per-user developer verification policies in case some previous users have // been removed since the last reboot. final int[] users = mPm.mUserManager.getUserIds(); synchronized (mDeveloperVerificationPolicyPerUser) { int size = mDeveloperVerificationPolicyPerUser.size(); for (int i = size - 1; i >= 0; i--) { if (!ArrayUtils.contains(users, mDeveloperVerificationPolicyPerUser.keyAt(i))) { mDeveloperVerificationPolicyPerUser.removeAt(i); } } // If the per-user developer verification policy was never set before for any user, // add a default policy for the user. for (int i = 0; i < users.length; i++) { if (mDeveloperVerificationPolicyPerUser.indexOfKey(users[i]) < 0) { mDeveloperVerificationPolicyPerUser.put(users[i], DEFAULT_VERIFICATION_POLICY); } } } // Invalid sessions might have been marked while parsing. Re-write the database with // the updated information. mSettingsWriteRequest.runNow(); } } private void onBroadcastReady() { Loading Loading @@ -570,6 +586,19 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } mSessions.put(session.sessionId, session); mAllocatedSessions.put(session.sessionId, true); } else if (TAG_DEVELOPER_VERIFICATION_POLICY_PER_USER.equals(tag)) { int userId = in.getAttributeInt(null, ATTR_USER_ID, -1); if (userId == -1) { continue; } int policy = in.getAttributeInt(null, ATTR_DEVELOPER_VERIFICATION_POLICY, -1); if (policy == -1) { continue; } synchronized (mDeveloperVerificationPolicyPerUser) { mDeveloperVerificationPolicyPerUser.put(userId, policy); } } } } Loading Loading @@ -674,6 +703,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements session.write(out, mSessionsDir); } out.endTag(null, TAG_SESSIONS); // Preserve current developer verification policy per user to disk. synchronized (mDeveloperVerificationPolicyPerUser) { for (int i = 0; i < mDeveloperVerificationPolicyPerUser.size(); i++) { out.startTag(null, TAG_DEVELOPER_VERIFICATION_POLICY_PER_USER); out.attributeInt(null, ATTR_USER_ID, mDeveloperVerificationPolicyPerUser.keyAt(i)); out.attributeInt(null, ATTR_DEVELOPER_VERIFICATION_POLICY, mDeveloperVerificationPolicyPerUser.valueAt(i)); out.endTag(null, TAG_DEVELOPER_VERIFICATION_POLICY_PER_USER); } } out.endDocument(); mSessionsFile.finishWrite(fos); Loading Loading @@ -1993,8 +2034,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } @Override @RequiresPermission(value = Manifest.permission.DEVELOPER_VERIFICATION_AGENT, conditional = true) public boolean setDeveloperVerificationPolicy( @PackageInstaller.DeveloperVerificationPolicy int policy, int userId) { // Write is more restrictive than read. Having the permission granted is not enough. Loading @@ -2021,6 +2060,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } if (policy != mDeveloperVerificationPolicyPerUser.get(userId)) { mDeveloperVerificationPolicyPerUser.put(userId, policy); // Preserve the change to disk mSettingsWriteRequest.schedule(); } } return true; Loading services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +3 −1 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ import com.android.internal.pm.parsing.pkg.PackageImpl import com.android.internal.pm.parsing.pkg.ParsedPackage import com.android.internal.pm.pkg.parsing.ParsingPackage import com.android.internal.pm.pkg.parsing.ParsingPackageUtils import com.android.server.IoThread import com.android.server.LocalManagerRegistry import com.android.server.LocalServices import com.android.server.LockGuard Loading @@ -75,8 +76,8 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal import com.android.server.pm.pkg.AndroidPackage import com.android.server.pm.resolution.ComponentResolver import com.android.server.pm.snapshot.PackageDataSnapshot import com.android.server.pm.verify.domain.DomainVerificationManagerInternal import com.android.server.pm.verify.developer.DeveloperVerifierController import com.android.server.pm.verify.domain.DomainVerificationManagerInternal import com.android.server.sdksandbox.SdkSandboxManagerLocal import com.android.server.testutils.TestHandler import com.android.server.testutils.mock Loading Loading @@ -158,6 +159,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { .mockStatic(DeviceConfig::class.java, Mockito.CALLS_REAL_METHODS) .mockStatic(HexEncoding::class.java) .mockStatic(DeveloperVerifierController::class.java) .mockStatic(IoThread::class.java) .apply(withSession) session = apply.startMocking() whenever(mocks.settings.insertPackageSettingLPw( Loading services/tests/mockingservicestests/src/com/android/server/pm/PackageInstallerServiceTest.java +177 −11 Original line number Diff line number Diff line Loading @@ -28,38 +28,63 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageInstaller; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; import android.os.PermissionEnforcer; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemServiceManager; import com.android.server.pm.verify.developer.DeveloperVerifierController; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.File; @Presubmit @RunWith(JUnit4.class) @RunWith(AndroidJUnit4.class) public class PackageInstallerServiceTest { private @Mock PermissionEnforcer mMockPermissionEnforcer; private @Mock ActivityManager mMockActivityManager; private @Mock AppOpsManager mAppOpsManager; private @Mock SystemServiceManager mMockSystemServiceManager; private @Mock DeveloperVerifierController mMockDeveloperVerifierController; private @Mock File mMockeFile; private String mPackageName; private PackageManagerService mPms; private @Mock Computer mMockSnapshot; private @Mock Handler mMockHandler; private File mTestDir; private final PackageManagerServiceTestParams mTestParams = new PackageManagerServiceTestParams(); private static final int TEST_USER_ID = 10; private static final int TEST_USER_ID_2 = 20; private static final int TEST_USER_ID_3 = 30; private static final int TEST_USER_ID_4 = 40; @Rule public final MockSystemRule rule = new MockSystemRule(); Loading @@ -69,37 +94,72 @@ public class PackageInstallerServiceTest { MockitoAnnotations.initMocks(this); rule.system().stageNominalSystemState(); mPackageName = this.getClass().getPackageName(); PackageManagerServiceTestParams testParams = new PackageManagerServiceTestParams(); testParams.packages = new ArrayMap<>(); mTestParams.packages = new ArrayMap<>(); // Basic mocks to support session creation when(rule.mocks().getContext().getSystemService(Context.PERMISSION_ENFORCER_SERVICE)) .thenReturn(mMockPermissionEnforcer); when(rule.mocks().getContext().getSystemService(ActivityManager.class)) .thenReturn(mMockActivityManager); when(rule.mocks().getContext().getSystemService(AppOpsManager.class)) .thenReturn(mAppOpsManager); doReturn(mMockeFile).when(() -> Environment.getDataAppDirectory(nullable(String.class))); doReturn(mMockeFile).when( () -> Environment.getDataStagingDirectory(nullable(String.class))); doReturn(mMockSystemServiceManager).when( () -> LocalServices.getService(SystemServiceManager.class)); // Run scheduled write tasks right away doReturn(mMockHandler).when(IoThread::getHandler); when(mMockHandler.post(any(Runnable.class))).thenAnswer( i -> { ((Runnable) i.getArguments()[0]).run(); return true; }); doReturn(mMockDeveloperVerifierController).when( () -> DeveloperVerifierController.getInstance(any(), any(), argThat( componentName -> componentName.getPackageName().equals(mPackageName) () -> DeveloperVerifierController.getInstance(any(Context.class), any(Handler.class), argThat(componentName -> componentName.getPackageName().equals(mPackageName) )) ); doReturn(mMockSystemServiceManager).when( () -> LocalServices.getService(SystemServiceManager.class)); // Test specific environment setup doReturn(mPackageName).when(mMockDeveloperVerifierController).getVerifierPackageName(); mPms = spy(new PackageManagerService(rule.mocks().getInjector(), testParams)); mPms = spy(new PackageManagerService(rule.mocks().getInjector(), mTestParams)); doReturn(false).when(mPms).isUserRestricted(anyInt(), nullable(String.class)); doReturn(mMockSnapshot).when(mPms).snapshotComputer(); doReturn(myUid()).when(mMockSnapshot).getPackageUidInternal( eq(mPackageName), anyLong(), anyInt(), anyInt()); // Create a test file for read/write of mSessionsFile mTestDir = new File(InstrumentationRegistry.getInstrumentation().getTargetContext() .getFilesDir(), "testDir"); assertThat(mTestDir.mkdirs()).isTrue(); doReturn(mTestDir).when(Environment::getDataSystemDirectory); } @After public void tearDown() { if (mTestDir != null) { // Clean up test dir to remove persisted user files. FileUtils.deleteContentsAndDir(mTestDir); } } @Test public void testVerificationPolicyPerUser() { PackageInstallerService service = new PackageInstallerService( doReturn(new int[] {UserHandle.USER_SYSTEM, TEST_USER_ID, TEST_USER_ID_2}) .when(mPms.mUserManager).getUserIds(); final PackageInstallerService service = new PackageInstallerService( rule.mocks().getContext(), mPms, null, new ComponentName(mPackageName, this.getClass().getName())); service.systemReady(); final int defaultPolicy = service.getDeveloperVerificationPolicy( /* userId= */ UserHandle.USER_SYSTEM); assertThat(defaultPolicy).isAtLeast(PackageInstaller.DEVELOPER_VERIFICATION_POLICY_NONE); assertThat(service.getDeveloperVerificationPolicy(TEST_USER_ID)).isEqualTo(defaultPolicy); assertThat(service.getDeveloperVerificationPolicy(TEST_USER_ID_2)).isEqualTo(defaultPolicy); assertThat(service.setDeveloperVerificationPolicy( /* policy= */ PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED, /* userId= */ UserHandle.USER_SYSTEM)).isTrue(); // Test with a non-existing user final int newUserId = 1; final int newUserId = TEST_USER_ID_3; assertThrows(IllegalStateException.class, () -> service.getDeveloperVerificationPolicy( /* userId= */ newUserId)); assertThrows(IllegalStateException.class, Loading @@ -126,6 +186,112 @@ public class PackageInstallerServiceTest { /* userId= */ newUserId)); } @Test public void testWriteAndReadVerificationPolicyPerUser() { doReturn(new int[] {UserHandle.USER_SYSTEM, TEST_USER_ID, TEST_USER_ID_2}) .when(mPms.mUserManager).getUserIds(); final PackageInstallerService service = new PackageInstallerService( rule.mocks().getContext(), mPms, null, new ComponentName(mPackageName, this.getClass().getName())); service.systemReady(); final int defaultPolicy = service.getDeveloperVerificationPolicy( /* userId= */ UserHandle.USER_SYSTEM); // Modify the policy for each user final int policyForSystemUser = PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; final int policyForTestUser = PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_WARN; final int policyForTestUser2 = PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_OPEN; assertThat(service.setDeveloperVerificationPolicy( /* policy= */ policyForSystemUser, /* userId= */ UserHandle.USER_SYSTEM)).isTrue(); assertThat(service.setDeveloperVerificationPolicy( /* policy= */ policyForTestUser, /* userId= */ TEST_USER_ID)).isTrue(); assertThat(service.setDeveloperVerificationPolicy( /* policy= */ policyForTestUser2, /* userId= */ TEST_USER_ID_2)).isTrue(); // systemReady and Each policy change above should have triggered a write to the test file // but since the write in systemReady is done immediately in the main thread of // PackageInstallerService and doesn't go through the handler of the IO thread, we don't // have the chance to verify that here. verify(mMockHandler, times(3)).post(any(Runnable.class)); // Mimic a reboot by creating a new service instance with the latest policy for each user // loaded from the test file final PackageInstallerService service2 = new PackageInstallerService( rule.mocks().getContext(), mPms, null, new ComponentName(mPackageName, this.getClass().getName())); service2.systemReady(); assertThat(service2.getDeveloperVerificationPolicy(UserHandle.USER_SYSTEM)).isEqualTo( policyForSystemUser); assertThat(service2.getDeveloperVerificationPolicy(TEST_USER_ID)).isEqualTo( policyForTestUser); assertThat(service2.getDeveloperVerificationPolicy(TEST_USER_ID_2)).isEqualTo( policyForTestUser2); } // Mimic a system where it initially has 2 users, then 1 is removed and a new one is added // on reboot. Then all are removed and a different one is added on reboot. Test that the // per-user developer verification policy is correctly set. @Test public void testCleanUpVerificationPolicyPerUser() { doReturn(new int[] {UserHandle.USER_SYSTEM, TEST_USER_ID}) .when(mPms.mUserManager).getUserIds(); PackageInstallerService service = new PackageInstallerService( rule.mocks().getContext(), mPms, null, new ComponentName(mPackageName, this.getClass().getName())); service.systemReady(); final int defaultPolicy = service.getDeveloperVerificationPolicy( /* userId= */ UserHandle.USER_SYSTEM); // Modify the policy for each user final int policyForSystemUser = PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; final int policyForTestUser = PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_WARN; assertThat(service.setDeveloperVerificationPolicy( /* policy= */ policyForSystemUser, /* userId= */ UserHandle.USER_SYSTEM)).isTrue(); assertThat(service.setDeveloperVerificationPolicy( /* policy= */ policyForTestUser, /* userId= */ TEST_USER_ID)).isTrue(); // Mimic a reboot by creating a new service instance. The policy for the deleted user // should not exist, and the policy for the new user should be the default policy. // And previously set policy should be preserved on the remaining user. PackageManagerService mPms2 = spy(new PackageManagerService(rule.mocks().getInjector(), mTestParams)); doReturn(new int[] {UserHandle.USER_SYSTEM, TEST_USER_ID_2}) .when(mPms2.mUserManager).getUserIds(); final PackageInstallerService service2 = new PackageInstallerService( rule.mocks().getContext(), mPms2, null, new ComponentName(mPackageName, this.getClass().getName())); service2.systemReady(); assertThat(service2.getDeveloperVerificationPolicy(UserHandle.USER_SYSTEM)).isEqualTo( policyForSystemUser); assertThrows(IllegalStateException.class, () -> service2.getDeveloperVerificationPolicy( TEST_USER_ID)); assertThat(service2.getDeveloperVerificationPolicy(TEST_USER_ID_2)).isEqualTo( defaultPolicy); // Mimic another reboot by creating a new service instance. The policy for the deleted users // should not exist, and the policy for the new users should be the default policy. PackageManagerService mPms3 = spy(new PackageManagerService(rule.mocks().getInjector(), mTestParams)); doReturn(new int[] {TEST_USER_ID_3, TEST_USER_ID_4}) .when(mPms3.mUserManager).getUserIds(); final PackageInstallerService service3 = new PackageInstallerService( rule.mocks().getContext(), mPms3, null, new ComponentName(mPackageName, this.getClass().getName())); service3.systemReady(); assertThrows(IllegalStateException.class, () -> service3.getDeveloperVerificationPolicy( UserHandle.USER_SYSTEM)); assertThrows(IllegalStateException.class, () -> service3.getDeveloperVerificationPolicy( TEST_USER_ID_2)); assertThat(service3.getDeveloperVerificationPolicy(TEST_USER_ID_3)).isEqualTo( defaultPolicy); assertThat(service3.getDeveloperVerificationPolicy(TEST_USER_ID_4)).isEqualTo( defaultPolicy); } @Test public void testVerifierIsNullThrowsException() { doReturn(mMockDeveloperVerifierController).when( Loading Loading
core/java/android/content/pm/IPackageInstaller.aidl +0 −1 Original line number Diff line number Diff line Loading @@ -98,7 +98,6 @@ interface IPackageInstaller { @PermissionManuallyEnforced int getDeveloperVerificationPolicy(int userId); @PermissionManuallyEnforced boolean setDeveloperVerificationPolicy(int policy, int userId); ComponentName getDeveloperVerificationServiceProvider(); @EnforcePermission("DEVELOPER_VERIFICATION_AGENT") Loading
services/core/java/com/android/server/pm/PackageInstallerService.java +55 −14 Original line number Diff line number Diff line Loading @@ -124,6 +124,7 @@ import com.android.internal.content.InstallLocationUtils; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.pm.parsing.PackageParser2; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ImageUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.modules.utils.TypedXmlPullParser; Loading Loading @@ -176,6 +177,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements /** XML constants used in {@link #mSessionsFile} */ private static final String TAG_SESSIONS = "sessions"; private static final String TAG_DEVELOPER_VERIFICATION_POLICY_PER_USER = "developerVerificationPolicyPerUser"; private static final String ATTR_USER_ID = "userId"; private static final String ATTR_DEVELOPER_VERIFICATION_POLICY = "developerVerificationPolicy"; /** Automatically destroy sessions older than this */ private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS; Loading Loading @@ -289,7 +294,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements /** * Default verification policy for incoming installation sessions, mapped from userId to policy. */ @GuardedBy("mVerificationPolicyPerUser") @GuardedBy("mDeveloperVerificationPolicyPerUser") private final SparseIntArray mDeveloperVerificationPolicyPerUser = new SparseIntArray(1); /** * Default developer verification policy for a new user. Loading Loading @@ -353,13 +358,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mPackageArchiver = new PackageArchiver(mContext, mPm); mDeveloperVerifierController = DeveloperVerifierController.getInstance(context, mInstallHandler, developerVerificationServiceProvider); synchronized (mDeveloperVerificationPolicyPerUser) { int[] users = mPm.mUserManager.getUserIds(); for (int i = 0; i < users.length; i++) { // TODO(b/360129657): preserve the overridden policy across reboots. mDeveloperVerificationPolicyPerUser.put(users[i], DEFAULT_VERIFICATION_POLICY); } } mInstallDependencyHelper = new InstallDependencyHelper(mContext, mPm.mInjector.getSharedLibrariesImpl(), this); Loading Loading @@ -404,12 +402,30 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements Slog.w(TAG, "Deleting orphan icon " + icon); icon.delete(); } } // Clean up the per-user developer verification policies in case some previous users have // been removed since the last reboot. final int[] users = mPm.mUserManager.getUserIds(); synchronized (mDeveloperVerificationPolicyPerUser) { int size = mDeveloperVerificationPolicyPerUser.size(); for (int i = size - 1; i >= 0; i--) { if (!ArrayUtils.contains(users, mDeveloperVerificationPolicyPerUser.keyAt(i))) { mDeveloperVerificationPolicyPerUser.removeAt(i); } } // If the per-user developer verification policy was never set before for any user, // add a default policy for the user. for (int i = 0; i < users.length; i++) { if (mDeveloperVerificationPolicyPerUser.indexOfKey(users[i]) < 0) { mDeveloperVerificationPolicyPerUser.put(users[i], DEFAULT_VERIFICATION_POLICY); } } } // Invalid sessions might have been marked while parsing. Re-write the database with // the updated information. mSettingsWriteRequest.runNow(); } } private void onBroadcastReady() { Loading Loading @@ -570,6 +586,19 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } mSessions.put(session.sessionId, session); mAllocatedSessions.put(session.sessionId, true); } else if (TAG_DEVELOPER_VERIFICATION_POLICY_PER_USER.equals(tag)) { int userId = in.getAttributeInt(null, ATTR_USER_ID, -1); if (userId == -1) { continue; } int policy = in.getAttributeInt(null, ATTR_DEVELOPER_VERIFICATION_POLICY, -1); if (policy == -1) { continue; } synchronized (mDeveloperVerificationPolicyPerUser) { mDeveloperVerificationPolicyPerUser.put(userId, policy); } } } } Loading Loading @@ -674,6 +703,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements session.write(out, mSessionsDir); } out.endTag(null, TAG_SESSIONS); // Preserve current developer verification policy per user to disk. synchronized (mDeveloperVerificationPolicyPerUser) { for (int i = 0; i < mDeveloperVerificationPolicyPerUser.size(); i++) { out.startTag(null, TAG_DEVELOPER_VERIFICATION_POLICY_PER_USER); out.attributeInt(null, ATTR_USER_ID, mDeveloperVerificationPolicyPerUser.keyAt(i)); out.attributeInt(null, ATTR_DEVELOPER_VERIFICATION_POLICY, mDeveloperVerificationPolicyPerUser.valueAt(i)); out.endTag(null, TAG_DEVELOPER_VERIFICATION_POLICY_PER_USER); } } out.endDocument(); mSessionsFile.finishWrite(fos); Loading Loading @@ -1993,8 +2034,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } @Override @RequiresPermission(value = Manifest.permission.DEVELOPER_VERIFICATION_AGENT, conditional = true) public boolean setDeveloperVerificationPolicy( @PackageInstaller.DeveloperVerificationPolicy int policy, int userId) { // Write is more restrictive than read. Having the permission granted is not enough. Loading @@ -2021,6 +2060,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } if (policy != mDeveloperVerificationPolicyPerUser.get(userId)) { mDeveloperVerificationPolicyPerUser.put(userId, policy); // Preserve the change to disk mSettingsWriteRequest.schedule(); } } return true; Loading
services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +3 −1 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ import com.android.internal.pm.parsing.pkg.PackageImpl import com.android.internal.pm.parsing.pkg.ParsedPackage import com.android.internal.pm.pkg.parsing.ParsingPackage import com.android.internal.pm.pkg.parsing.ParsingPackageUtils import com.android.server.IoThread import com.android.server.LocalManagerRegistry import com.android.server.LocalServices import com.android.server.LockGuard Loading @@ -75,8 +76,8 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal import com.android.server.pm.pkg.AndroidPackage import com.android.server.pm.resolution.ComponentResolver import com.android.server.pm.snapshot.PackageDataSnapshot import com.android.server.pm.verify.domain.DomainVerificationManagerInternal import com.android.server.pm.verify.developer.DeveloperVerifierController import com.android.server.pm.verify.domain.DomainVerificationManagerInternal import com.android.server.sdksandbox.SdkSandboxManagerLocal import com.android.server.testutils.TestHandler import com.android.server.testutils.mock Loading Loading @@ -158,6 +159,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { .mockStatic(DeviceConfig::class.java, Mockito.CALLS_REAL_METHODS) .mockStatic(HexEncoding::class.java) .mockStatic(DeveloperVerifierController::class.java) .mockStatic(IoThread::class.java) .apply(withSession) session = apply.startMocking() whenever(mocks.settings.insertPackageSettingLPw( Loading
services/tests/mockingservicestests/src/com/android/server/pm/PackageInstallerServiceTest.java +177 −11 Original line number Diff line number Diff line Loading @@ -28,38 +28,63 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageInstaller; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; import android.os.PermissionEnforcer; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemServiceManager; import com.android.server.pm.verify.developer.DeveloperVerifierController; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.File; @Presubmit @RunWith(JUnit4.class) @RunWith(AndroidJUnit4.class) public class PackageInstallerServiceTest { private @Mock PermissionEnforcer mMockPermissionEnforcer; private @Mock ActivityManager mMockActivityManager; private @Mock AppOpsManager mAppOpsManager; private @Mock SystemServiceManager mMockSystemServiceManager; private @Mock DeveloperVerifierController mMockDeveloperVerifierController; private @Mock File mMockeFile; private String mPackageName; private PackageManagerService mPms; private @Mock Computer mMockSnapshot; private @Mock Handler mMockHandler; private File mTestDir; private final PackageManagerServiceTestParams mTestParams = new PackageManagerServiceTestParams(); private static final int TEST_USER_ID = 10; private static final int TEST_USER_ID_2 = 20; private static final int TEST_USER_ID_3 = 30; private static final int TEST_USER_ID_4 = 40; @Rule public final MockSystemRule rule = new MockSystemRule(); Loading @@ -69,37 +94,72 @@ public class PackageInstallerServiceTest { MockitoAnnotations.initMocks(this); rule.system().stageNominalSystemState(); mPackageName = this.getClass().getPackageName(); PackageManagerServiceTestParams testParams = new PackageManagerServiceTestParams(); testParams.packages = new ArrayMap<>(); mTestParams.packages = new ArrayMap<>(); // Basic mocks to support session creation when(rule.mocks().getContext().getSystemService(Context.PERMISSION_ENFORCER_SERVICE)) .thenReturn(mMockPermissionEnforcer); when(rule.mocks().getContext().getSystemService(ActivityManager.class)) .thenReturn(mMockActivityManager); when(rule.mocks().getContext().getSystemService(AppOpsManager.class)) .thenReturn(mAppOpsManager); doReturn(mMockeFile).when(() -> Environment.getDataAppDirectory(nullable(String.class))); doReturn(mMockeFile).when( () -> Environment.getDataStagingDirectory(nullable(String.class))); doReturn(mMockSystemServiceManager).when( () -> LocalServices.getService(SystemServiceManager.class)); // Run scheduled write tasks right away doReturn(mMockHandler).when(IoThread::getHandler); when(mMockHandler.post(any(Runnable.class))).thenAnswer( i -> { ((Runnable) i.getArguments()[0]).run(); return true; }); doReturn(mMockDeveloperVerifierController).when( () -> DeveloperVerifierController.getInstance(any(), any(), argThat( componentName -> componentName.getPackageName().equals(mPackageName) () -> DeveloperVerifierController.getInstance(any(Context.class), any(Handler.class), argThat(componentName -> componentName.getPackageName().equals(mPackageName) )) ); doReturn(mMockSystemServiceManager).when( () -> LocalServices.getService(SystemServiceManager.class)); // Test specific environment setup doReturn(mPackageName).when(mMockDeveloperVerifierController).getVerifierPackageName(); mPms = spy(new PackageManagerService(rule.mocks().getInjector(), testParams)); mPms = spy(new PackageManagerService(rule.mocks().getInjector(), mTestParams)); doReturn(false).when(mPms).isUserRestricted(anyInt(), nullable(String.class)); doReturn(mMockSnapshot).when(mPms).snapshotComputer(); doReturn(myUid()).when(mMockSnapshot).getPackageUidInternal( eq(mPackageName), anyLong(), anyInt(), anyInt()); // Create a test file for read/write of mSessionsFile mTestDir = new File(InstrumentationRegistry.getInstrumentation().getTargetContext() .getFilesDir(), "testDir"); assertThat(mTestDir.mkdirs()).isTrue(); doReturn(mTestDir).when(Environment::getDataSystemDirectory); } @After public void tearDown() { if (mTestDir != null) { // Clean up test dir to remove persisted user files. FileUtils.deleteContentsAndDir(mTestDir); } } @Test public void testVerificationPolicyPerUser() { PackageInstallerService service = new PackageInstallerService( doReturn(new int[] {UserHandle.USER_SYSTEM, TEST_USER_ID, TEST_USER_ID_2}) .when(mPms.mUserManager).getUserIds(); final PackageInstallerService service = new PackageInstallerService( rule.mocks().getContext(), mPms, null, new ComponentName(mPackageName, this.getClass().getName())); service.systemReady(); final int defaultPolicy = service.getDeveloperVerificationPolicy( /* userId= */ UserHandle.USER_SYSTEM); assertThat(defaultPolicy).isAtLeast(PackageInstaller.DEVELOPER_VERIFICATION_POLICY_NONE); assertThat(service.getDeveloperVerificationPolicy(TEST_USER_ID)).isEqualTo(defaultPolicy); assertThat(service.getDeveloperVerificationPolicy(TEST_USER_ID_2)).isEqualTo(defaultPolicy); assertThat(service.setDeveloperVerificationPolicy( /* policy= */ PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED, /* userId= */ UserHandle.USER_SYSTEM)).isTrue(); // Test with a non-existing user final int newUserId = 1; final int newUserId = TEST_USER_ID_3; assertThrows(IllegalStateException.class, () -> service.getDeveloperVerificationPolicy( /* userId= */ newUserId)); assertThrows(IllegalStateException.class, Loading @@ -126,6 +186,112 @@ public class PackageInstallerServiceTest { /* userId= */ newUserId)); } @Test public void testWriteAndReadVerificationPolicyPerUser() { doReturn(new int[] {UserHandle.USER_SYSTEM, TEST_USER_ID, TEST_USER_ID_2}) .when(mPms.mUserManager).getUserIds(); final PackageInstallerService service = new PackageInstallerService( rule.mocks().getContext(), mPms, null, new ComponentName(mPackageName, this.getClass().getName())); service.systemReady(); final int defaultPolicy = service.getDeveloperVerificationPolicy( /* userId= */ UserHandle.USER_SYSTEM); // Modify the policy for each user final int policyForSystemUser = PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; final int policyForTestUser = PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_WARN; final int policyForTestUser2 = PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_OPEN; assertThat(service.setDeveloperVerificationPolicy( /* policy= */ policyForSystemUser, /* userId= */ UserHandle.USER_SYSTEM)).isTrue(); assertThat(service.setDeveloperVerificationPolicy( /* policy= */ policyForTestUser, /* userId= */ TEST_USER_ID)).isTrue(); assertThat(service.setDeveloperVerificationPolicy( /* policy= */ policyForTestUser2, /* userId= */ TEST_USER_ID_2)).isTrue(); // systemReady and Each policy change above should have triggered a write to the test file // but since the write in systemReady is done immediately in the main thread of // PackageInstallerService and doesn't go through the handler of the IO thread, we don't // have the chance to verify that here. verify(mMockHandler, times(3)).post(any(Runnable.class)); // Mimic a reboot by creating a new service instance with the latest policy for each user // loaded from the test file final PackageInstallerService service2 = new PackageInstallerService( rule.mocks().getContext(), mPms, null, new ComponentName(mPackageName, this.getClass().getName())); service2.systemReady(); assertThat(service2.getDeveloperVerificationPolicy(UserHandle.USER_SYSTEM)).isEqualTo( policyForSystemUser); assertThat(service2.getDeveloperVerificationPolicy(TEST_USER_ID)).isEqualTo( policyForTestUser); assertThat(service2.getDeveloperVerificationPolicy(TEST_USER_ID_2)).isEqualTo( policyForTestUser2); } // Mimic a system where it initially has 2 users, then 1 is removed and a new one is added // on reboot. Then all are removed and a different one is added on reboot. Test that the // per-user developer verification policy is correctly set. @Test public void testCleanUpVerificationPolicyPerUser() { doReturn(new int[] {UserHandle.USER_SYSTEM, TEST_USER_ID}) .when(mPms.mUserManager).getUserIds(); PackageInstallerService service = new PackageInstallerService( rule.mocks().getContext(), mPms, null, new ComponentName(mPackageName, this.getClass().getName())); service.systemReady(); final int defaultPolicy = service.getDeveloperVerificationPolicy( /* userId= */ UserHandle.USER_SYSTEM); // Modify the policy for each user final int policyForSystemUser = PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED; final int policyForTestUser = PackageInstaller.DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_WARN; assertThat(service.setDeveloperVerificationPolicy( /* policy= */ policyForSystemUser, /* userId= */ UserHandle.USER_SYSTEM)).isTrue(); assertThat(service.setDeveloperVerificationPolicy( /* policy= */ policyForTestUser, /* userId= */ TEST_USER_ID)).isTrue(); // Mimic a reboot by creating a new service instance. The policy for the deleted user // should not exist, and the policy for the new user should be the default policy. // And previously set policy should be preserved on the remaining user. PackageManagerService mPms2 = spy(new PackageManagerService(rule.mocks().getInjector(), mTestParams)); doReturn(new int[] {UserHandle.USER_SYSTEM, TEST_USER_ID_2}) .when(mPms2.mUserManager).getUserIds(); final PackageInstallerService service2 = new PackageInstallerService( rule.mocks().getContext(), mPms2, null, new ComponentName(mPackageName, this.getClass().getName())); service2.systemReady(); assertThat(service2.getDeveloperVerificationPolicy(UserHandle.USER_SYSTEM)).isEqualTo( policyForSystemUser); assertThrows(IllegalStateException.class, () -> service2.getDeveloperVerificationPolicy( TEST_USER_ID)); assertThat(service2.getDeveloperVerificationPolicy(TEST_USER_ID_2)).isEqualTo( defaultPolicy); // Mimic another reboot by creating a new service instance. The policy for the deleted users // should not exist, and the policy for the new users should be the default policy. PackageManagerService mPms3 = spy(new PackageManagerService(rule.mocks().getInjector(), mTestParams)); doReturn(new int[] {TEST_USER_ID_3, TEST_USER_ID_4}) .when(mPms3.mUserManager).getUserIds(); final PackageInstallerService service3 = new PackageInstallerService( rule.mocks().getContext(), mPms3, null, new ComponentName(mPackageName, this.getClass().getName())); service3.systemReady(); assertThrows(IllegalStateException.class, () -> service3.getDeveloperVerificationPolicy( UserHandle.USER_SYSTEM)); assertThrows(IllegalStateException.class, () -> service3.getDeveloperVerificationPolicy( TEST_USER_ID_2)); assertThat(service3.getDeveloperVerificationPolicy(TEST_USER_ID_3)).isEqualTo( defaultPolicy); assertThat(service3.getDeveloperVerificationPolicy(TEST_USER_ID_4)).isEqualTo( defaultPolicy); } @Test public void testVerifierIsNullThrowsException() { doReturn(mMockDeveloperVerifierController).when( Loading