Loading services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +133 −99 Original line number Diff line number Diff line Loading @@ -157,7 +157,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { super.setUp(); mContext = getContext(); when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN))) .thenReturn(true); Loading @@ -174,6 +173,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { setUpUserManager(); } @Override protected void tearDown() throws Exception { flushTasks(); super.tearDown(); } private void initializeDpms() { // Need clearCallingIdentity() to pass permission checks. final long ident = mContext.binder.clearCallingIdentity(); Loading Loading @@ -1246,7 +1251,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.applicationInfo = new ApplicationInfo(); mContext.callerPermissions.add(permission.MANAGE_USERS); mContext.packageName = "com.android.frameworks.servicestests"; mContext.userContexts.put(user, mContext); mContext.addPackageContext(user, mContext); when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); StringParceledListSlice oneCert = asSlice(new String[] {"1"}); Loading Loading @@ -3966,121 +3971,168 @@ public class DevicePolicyManagerTest extends DpmTestBase { } public void testGetOwnerInstalledCaCertsForDeviceOwner() throws Exception { mContext.packageName = mRealTestContext.getPackageName(); setDeviceOwner(); mContext.packageName = admin1.getPackageName(); mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; verifyCanGetOwnerInstalledCaCerts(admin1); final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller"); caller.packageName = admin1.getPackageName(); caller.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; verifyCanGetOwnerInstalledCaCerts(admin1, caller); } public void testGetOwnerInstalledCaCertsForProfileOwner() throws Exception { mContext.packageName = mRealTestContext.getPackageName(); setAsProfileOwner(admin1); mContext.packageName = admin1.getPackageName(); verifyCanGetOwnerInstalledCaCerts(admin1); verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(admin1); final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller"); caller.packageName = admin1.getPackageName(); caller.binder.callingUid = DpmMockContext.CALLER_UID; verifyCanGetOwnerInstalledCaCerts(admin1, caller); verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(admin1, caller); } public void testGetOwnerInstalledCaCertsForDelegate() throws Exception { mContext.packageName = mRealTestContext.getPackageName(); setAsProfileOwner(admin1); final String delegate = "com.example.delegate"; final int delegateUid = setupPackageInPackageManager(delegate, 20988); dpm.setCertInstallerPackage(admin1, delegate); mContext.packageName = delegate; mContext.binder.callingUid = delegateUid; verifyCanGetOwnerInstalledCaCerts(null); verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null); final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller"); caller.packageName = delegate; caller.binder.callingUid = delegateUid; verifyCanGetOwnerInstalledCaCerts(null, caller); verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null, caller); } private void verifyCanGetOwnerInstalledCaCerts(ComponentName caller) throws Exception { final UserHandle user = UserHandle.getUserHandleForUid(mContext.binder.callingUid); final int ownerUid = user.equals(UserHandle.SYSTEM) ? DpmMockContext.CALLER_SYSTEM_USER_UID : DpmMockContext.CALLER_UID; private void verifyCanGetOwnerInstalledCaCerts( final ComponentName caller, final DpmMockContext callerContext) throws Exception { final String alias = "cert"; final byte[] caCert = TEST_CA.getBytes(); mContext.applicationInfo = new ApplicationInfo(); mContext.userContexts.put(user, mContext); when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); // device admin (used for posting the tls notification) final DpmMockContext admin1Context; if (admin1.getPackageName().equals(callerContext.getPackageName())) { admin1Context = callerContext; } else { admin1Context = new DpmMockContext(mRealTestContext, "test-admin"); admin1Context.packageName = admin1.getPackageName(); admin1Context.applicationInfo = new ApplicationInfo(); } when(admin1Context.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); // caller: device admin or delegated certificate installer callerContext.applicationInfo = new ApplicationInfo(); final UserHandle callerUser = callerContext.binder.getCallingUserHandle(); // system_server final DpmMockContext serviceContext = mContext; serviceContext.binder.callingUid = DpmMockContext.SYSTEM_UID; serviceContext.addPackageContext(callerUser, admin1Context); serviceContext.addPackageContext(callerUser, callerContext); // Install a CA cert. final String alias = "cert"; final byte[] caCert = TEST_CA.getBytes(); runAsCaller(callerContext, dpms, (dpm) -> { when(mContext.keyChainConnection.getService().installCaCertificate(caCert)) .thenReturn(alias); assertTrue(dpm.installCaCert(caller, caCert)); when(mContext.keyChainConnection.getService().getUserCaAliases()) .thenReturn(asSlice(new String[] {alias})); mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)); }); serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier())); flushTasks(); final List<String> ownerInstalledCaCerts = new ArrayList<>(); // Device Owner / Profile Owner can find out which CA certs were installed by itself. final String packageName = mContext.packageName; mContext.packageName = admin1.getPackageName(); final long callerIdentity = mContext.binder.clearCallingIdentity(); mContext.binder.callingUid = ownerUid; List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user); assertNotNull(ownerInstalledCaCerts); assertEquals(1, ownerInstalledCaCerts.size()); assertTrue(ownerInstalledCaCerts.contains(alias)); runAsCaller(admin1Context, dpms, (dpm) -> { final List<String> installedCaCerts = dpm.getOwnerInstalledCaCerts(callerUser); assertEquals(Arrays.asList(alias), installedCaCerts); ownerInstalledCaCerts.addAll(installedCaCerts); }); // Restarting the DPMS should not lose information. initializeDpms(); assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user)); runAsCaller(admin1Context, dpms, (dpm) -> { assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(callerUser)); }); // System can find out which CA certs were installed by the Device Owner / Profile Owner. mContext.packageName = "com.android.frameworks.servicestests"; mContext.binder.clearCallingIdentity(); assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user)); runAsCaller(serviceContext, dpms, (dpm) -> { assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(callerUser)); // Remove the CA cert. mContext.packageName = packageName; mContext.binder.restoreCallingIdentity(callerIdentity); reset(mContext.keyChainConnection.getService()); mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)); }); serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier())); flushTasks(); // Verify that the CA cert is no longer reported as installed by the Device Owner / Profile // Owner. mContext.packageName = admin1.getPackageName(); mContext.binder.callingUid = ownerUid; ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user); assertNotNull(ownerInstalledCaCerts); assertTrue(ownerInstalledCaCerts.isEmpty()); runAsCaller(admin1Context, dpms, (dpm) -> { MoreAsserts.assertEmpty(dpm.getOwnerInstalledCaCerts(callerUser)); }); } mContext.packageName = packageName; mContext.binder.restoreCallingIdentity(callerIdentity); private void verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval( final ComponentName callerName, final DpmMockContext callerContext) throws Exception { final String alias = "cert"; final byte[] caCert = TEST_CA.getBytes(); // device admin (used for posting the tls notification) final DpmMockContext admin1Context; if (admin1.getPackageName().equals(callerContext.getPackageName())) { admin1Context = callerContext; } else { admin1Context = new DpmMockContext(mRealTestContext, "test-admin"); admin1Context.packageName = admin1.getPackageName(); admin1Context.applicationInfo = new ApplicationInfo(); } when(admin1Context.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); private void verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(ComponentName caller) throws Exception { final UserHandle user = UserHandle.of(DpmMockContext.CALLER_USER_HANDLE); // caller: device admin or delegated certificate installer callerContext.applicationInfo = new ApplicationInfo(); final UserHandle callerUser = callerContext.binder.getCallingUserHandle(); mContext.applicationInfo = new ApplicationInfo(); mContext.userContexts.put(user, mContext); when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); // system_server final DpmMockContext serviceContext = mContext; serviceContext.binder.callingUid = DpmMockContext.SYSTEM_UID; serviceContext.addPackageContext(callerUser, admin1Context); serviceContext.addPackageContext(callerUser, callerContext); // Install a CA cert. final String alias = "cert"; final byte[] caCert = TEST_CA.getBytes(); // Install a CA cert as caller runAsCaller(callerContext, dpms, (dpm) -> { when(mContext.keyChainConnection.getService().installCaCertificate(caCert)) .thenReturn(alias); assertTrue(dpm.installCaCert(caller, caCert)); when(mContext.keyChainConnection.getService().getUserCaAliases()) assertTrue(dpm.installCaCert(callerName, caCert)); }); // Fake the CA cert as having been installed when(serviceContext.keyChainConnection.getService().getUserCaAliases()) .thenReturn(asSlice(new String[] {alias})); mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)); serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier())); flushTasks(); // Removing the Profile Owner should clear the information which CA certs were installed // by it. mContext.packageName = admin1.getPackageName(); mContext.binder.callingUid = DpmMockContext.CALLER_UID; // Removing the Profile Owner should clear the information on which CA certs were installed runAsCaller(admin1Context, dpms, (dpm) -> { dpm.clearProfileOwner(admin1); mContext.packageName = "com.android.frameworks.servicestests"; mContext.binder.clearCallingIdentity(); final List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user); }); runAsCaller(serviceContext, dpms, (dpm) -> { final List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(callerUser); assertNotNull(ownerInstalledCaCerts); assertTrue(ownerInstalledCaCerts.isEmpty()); }); } private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) { Loading Loading @@ -4147,29 +4199,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void flushTasks() throws Exception { Boolean tasksFlushed[] = new Boolean[] {false}; final Runnable tasksFlushedNotifier = () -> { synchronized (tasksFlushed) { tasksFlushed[0] = true; tasksFlushed.notify(); } }; dpms.mHandler.runWithScissors(() -> {}, 0 /*now*/); dpms.mBackgroundHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush main thread handler. dpms.mHandler.post(tasksFlushedNotifier); synchronized (tasksFlushed) { if (!tasksFlushed[0]) { tasksFlushed.wait(); } } // Flush background thread handler. tasksFlushed[0] = false; dpms.mBackgroundHandler.post(tasksFlushedNotifier); synchronized (tasksFlushed) { if (!tasksFlushed[0]) { tasksFlushed.wait(); } } // We can't let exceptions happen on the background thread. Throw them here if they happen // so they still cause the test to fail despite being suppressed. mContext.rethrowBackgroundBroadcastExceptions(); } } services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +57 −14 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.telephony.TelephonyManager; import android.test.mock.MockContentResolver; import android.test.mock.MockContext; import android.util.ArrayMap; import android.util.Pair; import android.view.IWindowManager; import com.android.internal.widget.LockPatternUtils; Loading @@ -61,6 +62,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; Loading Loading @@ -111,6 +113,7 @@ public class DpmMockContext extends MockContext { public static class MockBinder { public int callingUid = CALLER_UID; public int callingPid = CALLER_PID; public final Map<Integer, List<String>> callingPermissions = new ArrayMap<>(); public long clearCallingIdentity() { final long token = (((long) callingUid) << 32) | (callingPid); Loading Loading @@ -303,14 +306,19 @@ public class DpmMockContext extends MockContext { /** Note this is a partial mock, not a real mock. */ public final PackageManager packageManager; /** TODO: Migrate everything to use {@link #permissions} to avoid confusion. */ @Deprecated public final List<String> callerPermissions = new ArrayList<>(); /** Less confusing alias for {@link #callerPermissions}. */ public final List<String> permissions = callerPermissions; private final ArrayList<UserInfo> mUserInfos = new ArrayList<>(); public final BuildMock buildMock = new BuildMock(); /** Optional mapping of other user contexts for {@link #createPackageContextAsUser} to return */ public final Map<UserHandle, Context> userContexts = new ArrayMap<>(); public final Map<Pair<UserHandle, String>, Context> userPackageContexts = new ArrayMap<>(); public String packageName = null; Loading @@ -324,6 +332,9 @@ public class DpmMockContext extends MockContext { public final IntentFilter filter; public final Handler scheduler; // Exceptions thrown in a background thread kill the whole test. Save them instead. public final AtomicReference<Exception> backgroundException = new AtomicReference<>(); public BroadcastReceiverRegistration(BroadcastReceiver receiver, IntentFilter filter, Handler scheduler) { this.receiver = receiver; Loading @@ -337,20 +348,30 @@ public class DpmMockContext extends MockContext { 0 /* type */, false /* ordered */, false /* sticky */, null /* token */, userId, 0 /* flags */); if (filter.match(null, intent, false, "DpmMockContext") > 0) { if (scheduler != null) { scheduler.post(() -> { final Runnable send = () -> { receiver.setPendingResult(result); receiver.onReceive(DpmMockContext.this, intent); }; if (scheduler != null) { scheduler.post(() -> { try { send.run(); } catch (Exception e) { backgroundException.compareAndSet(null, e); } }); } else { receiver.setPendingResult(result); receiver.onReceive(DpmMockContext.this, intent); send.run(); } } } } private List<BroadcastReceiverRegistration> mBroadcastReceivers = new ArrayList<>(); public DpmMockContext(Context realTestContext, String name) { this(realTestContext, new File(realTestContext.getCacheDir(), name)); } public DpmMockContext(Context context, File dataDir) { realTestContext = context; Loading Loading @@ -511,13 +532,29 @@ public class DpmMockContext extends MockContext { .thenReturn(isRunning); } public void injectBroadcast(Intent intent) { public void injectBroadcast(final Intent intent) { final int userId = UserHandle.getUserId(binder.getCallingUid()); for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) { receiver.sendBroadcastIfApplicable(userId, intent); } } public void rethrowBackgroundBroadcastExceptions() throws Exception { for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) { final Exception e = receiver.backgroundException.getAndSet(null); if (e != null) { throw e; } } } public void addPackageContext(UserHandle user, Context context) { if (context.getPackageName() == null) { throw new NullPointerException("getPackageName() == null"); } userPackageContexts.put(new Pair<>(user, context.getPackageName()), context); } @Override public Resources getResources() { return resources; Loading Loading @@ -576,7 +613,16 @@ public class DpmMockContext extends MockContext { if (binder.getCallingUid() == SYSTEM_UID) { return; // Assume system has all permissions. } if (!callerPermissions.contains(permission)) { List<String> permissions = binder.callingPermissions.get(binder.getCallingUid()); if (permissions == null) { // TODO: delete the following line. to do this without breaking any tests, first it's // necessary to remove all tests that set it directly. permissions = callerPermissions; // throw new UnsupportedOperationException( // "Caller UID " + binder.getCallingUid() + " doesn't exist"); } if (!permissions.contains(permission)) { throw new SecurityException("Caller doesn't have " + permission + " : " + message); } } Loading Loading @@ -751,14 +797,11 @@ public class DpmMockContext extends MockContext { @Override public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) throws PackageManager.NameNotFoundException { if (!userContexts.containsKey(user)) { return super.createPackageContextAsUser(packageName, flags, user); } if (!getPackageName().equals(packageName)) { throw new UnsupportedOperationException( "Creating a context as another package is not implemented"); final Pair<UserHandle, String> key = new Pair<>(user, packageName); if (userPackageContexts.containsKey(key)) { return userPackageContexts.get(key); } return userContexts.get(user); throw new UnsupportedOperationException("No package " + packageName + " for user " + user); } @Override Loading services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java +29 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.devicepolicy; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading Loading @@ -70,6 +71,34 @@ public abstract class DpmTestBase extends AndroidTestCase { return mMockContext; } protected interface DpmRunnable { public void run(DevicePolicyManager dpm) throws Exception; } /** * Simulate an RPC from {@param caller} to the service context ({@link #mContext}). * * The caller sees its own context. The server also sees its own separate context, with the * appropriate calling UID and calling permissions fields already set up. */ protected void runAsCaller(DpmMockContext caller, DevicePolicyManagerServiceTestable dpms, DpmRunnable action) { final DpmMockContext serviceContext = mMockContext; final long origId = serviceContext.binder.clearCallingIdentity(); try { serviceContext.binder.callingUid = caller.binder.callingUid; serviceContext.binder.callingPid = caller.binder.callingPid; serviceContext.binder.callingPermissions.put(caller.binder.callingUid, caller.permissions); action.run(new DevicePolicyManagerTestable(caller, dpms)); } catch (Exception e) { throw new AssertionError(e); } finally { serviceContext.binder.restoreCallingIdentity(origId); } } protected void markPackageAsInstalled(String packageName, ApplicationInfo ai, int userId) throws Exception { final PackageInfo pi = DpmTestUtils.cloneParcelable( Loading Loading
services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +133 −99 Original line number Diff line number Diff line Loading @@ -157,7 +157,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { super.setUp(); mContext = getContext(); when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN))) .thenReturn(true); Loading @@ -174,6 +173,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { setUpUserManager(); } @Override protected void tearDown() throws Exception { flushTasks(); super.tearDown(); } private void initializeDpms() { // Need clearCallingIdentity() to pass permission checks. final long ident = mContext.binder.clearCallingIdentity(); Loading Loading @@ -1246,7 +1251,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.applicationInfo = new ApplicationInfo(); mContext.callerPermissions.add(permission.MANAGE_USERS); mContext.packageName = "com.android.frameworks.servicestests"; mContext.userContexts.put(user, mContext); mContext.addPackageContext(user, mContext); when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); StringParceledListSlice oneCert = asSlice(new String[] {"1"}); Loading Loading @@ -3966,121 +3971,168 @@ public class DevicePolicyManagerTest extends DpmTestBase { } public void testGetOwnerInstalledCaCertsForDeviceOwner() throws Exception { mContext.packageName = mRealTestContext.getPackageName(); setDeviceOwner(); mContext.packageName = admin1.getPackageName(); mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; verifyCanGetOwnerInstalledCaCerts(admin1); final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller"); caller.packageName = admin1.getPackageName(); caller.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; verifyCanGetOwnerInstalledCaCerts(admin1, caller); } public void testGetOwnerInstalledCaCertsForProfileOwner() throws Exception { mContext.packageName = mRealTestContext.getPackageName(); setAsProfileOwner(admin1); mContext.packageName = admin1.getPackageName(); verifyCanGetOwnerInstalledCaCerts(admin1); verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(admin1); final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller"); caller.packageName = admin1.getPackageName(); caller.binder.callingUid = DpmMockContext.CALLER_UID; verifyCanGetOwnerInstalledCaCerts(admin1, caller); verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(admin1, caller); } public void testGetOwnerInstalledCaCertsForDelegate() throws Exception { mContext.packageName = mRealTestContext.getPackageName(); setAsProfileOwner(admin1); final String delegate = "com.example.delegate"; final int delegateUid = setupPackageInPackageManager(delegate, 20988); dpm.setCertInstallerPackage(admin1, delegate); mContext.packageName = delegate; mContext.binder.callingUid = delegateUid; verifyCanGetOwnerInstalledCaCerts(null); verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null); final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller"); caller.packageName = delegate; caller.binder.callingUid = delegateUid; verifyCanGetOwnerInstalledCaCerts(null, caller); verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null, caller); } private void verifyCanGetOwnerInstalledCaCerts(ComponentName caller) throws Exception { final UserHandle user = UserHandle.getUserHandleForUid(mContext.binder.callingUid); final int ownerUid = user.equals(UserHandle.SYSTEM) ? DpmMockContext.CALLER_SYSTEM_USER_UID : DpmMockContext.CALLER_UID; private void verifyCanGetOwnerInstalledCaCerts( final ComponentName caller, final DpmMockContext callerContext) throws Exception { final String alias = "cert"; final byte[] caCert = TEST_CA.getBytes(); mContext.applicationInfo = new ApplicationInfo(); mContext.userContexts.put(user, mContext); when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); // device admin (used for posting the tls notification) final DpmMockContext admin1Context; if (admin1.getPackageName().equals(callerContext.getPackageName())) { admin1Context = callerContext; } else { admin1Context = new DpmMockContext(mRealTestContext, "test-admin"); admin1Context.packageName = admin1.getPackageName(); admin1Context.applicationInfo = new ApplicationInfo(); } when(admin1Context.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); // caller: device admin or delegated certificate installer callerContext.applicationInfo = new ApplicationInfo(); final UserHandle callerUser = callerContext.binder.getCallingUserHandle(); // system_server final DpmMockContext serviceContext = mContext; serviceContext.binder.callingUid = DpmMockContext.SYSTEM_UID; serviceContext.addPackageContext(callerUser, admin1Context); serviceContext.addPackageContext(callerUser, callerContext); // Install a CA cert. final String alias = "cert"; final byte[] caCert = TEST_CA.getBytes(); runAsCaller(callerContext, dpms, (dpm) -> { when(mContext.keyChainConnection.getService().installCaCertificate(caCert)) .thenReturn(alias); assertTrue(dpm.installCaCert(caller, caCert)); when(mContext.keyChainConnection.getService().getUserCaAliases()) .thenReturn(asSlice(new String[] {alias})); mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)); }); serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier())); flushTasks(); final List<String> ownerInstalledCaCerts = new ArrayList<>(); // Device Owner / Profile Owner can find out which CA certs were installed by itself. final String packageName = mContext.packageName; mContext.packageName = admin1.getPackageName(); final long callerIdentity = mContext.binder.clearCallingIdentity(); mContext.binder.callingUid = ownerUid; List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user); assertNotNull(ownerInstalledCaCerts); assertEquals(1, ownerInstalledCaCerts.size()); assertTrue(ownerInstalledCaCerts.contains(alias)); runAsCaller(admin1Context, dpms, (dpm) -> { final List<String> installedCaCerts = dpm.getOwnerInstalledCaCerts(callerUser); assertEquals(Arrays.asList(alias), installedCaCerts); ownerInstalledCaCerts.addAll(installedCaCerts); }); // Restarting the DPMS should not lose information. initializeDpms(); assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user)); runAsCaller(admin1Context, dpms, (dpm) -> { assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(callerUser)); }); // System can find out which CA certs were installed by the Device Owner / Profile Owner. mContext.packageName = "com.android.frameworks.servicestests"; mContext.binder.clearCallingIdentity(); assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user)); runAsCaller(serviceContext, dpms, (dpm) -> { assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(callerUser)); // Remove the CA cert. mContext.packageName = packageName; mContext.binder.restoreCallingIdentity(callerIdentity); reset(mContext.keyChainConnection.getService()); mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)); }); serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier())); flushTasks(); // Verify that the CA cert is no longer reported as installed by the Device Owner / Profile // Owner. mContext.packageName = admin1.getPackageName(); mContext.binder.callingUid = ownerUid; ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user); assertNotNull(ownerInstalledCaCerts); assertTrue(ownerInstalledCaCerts.isEmpty()); runAsCaller(admin1Context, dpms, (dpm) -> { MoreAsserts.assertEmpty(dpm.getOwnerInstalledCaCerts(callerUser)); }); } mContext.packageName = packageName; mContext.binder.restoreCallingIdentity(callerIdentity); private void verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval( final ComponentName callerName, final DpmMockContext callerContext) throws Exception { final String alias = "cert"; final byte[] caCert = TEST_CA.getBytes(); // device admin (used for posting the tls notification) final DpmMockContext admin1Context; if (admin1.getPackageName().equals(callerContext.getPackageName())) { admin1Context = callerContext; } else { admin1Context = new DpmMockContext(mRealTestContext, "test-admin"); admin1Context.packageName = admin1.getPackageName(); admin1Context.applicationInfo = new ApplicationInfo(); } when(admin1Context.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); private void verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(ComponentName caller) throws Exception { final UserHandle user = UserHandle.of(DpmMockContext.CALLER_USER_HANDLE); // caller: device admin or delegated certificate installer callerContext.applicationInfo = new ApplicationInfo(); final UserHandle callerUser = callerContext.binder.getCallingUserHandle(); mContext.applicationInfo = new ApplicationInfo(); mContext.userContexts.put(user, mContext); when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); // system_server final DpmMockContext serviceContext = mContext; serviceContext.binder.callingUid = DpmMockContext.SYSTEM_UID; serviceContext.addPackageContext(callerUser, admin1Context); serviceContext.addPackageContext(callerUser, callerContext); // Install a CA cert. final String alias = "cert"; final byte[] caCert = TEST_CA.getBytes(); // Install a CA cert as caller runAsCaller(callerContext, dpms, (dpm) -> { when(mContext.keyChainConnection.getService().installCaCertificate(caCert)) .thenReturn(alias); assertTrue(dpm.installCaCert(caller, caCert)); when(mContext.keyChainConnection.getService().getUserCaAliases()) assertTrue(dpm.installCaCert(callerName, caCert)); }); // Fake the CA cert as having been installed when(serviceContext.keyChainConnection.getService().getUserCaAliases()) .thenReturn(asSlice(new String[] {alias})); mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)); serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED) .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier())); flushTasks(); // Removing the Profile Owner should clear the information which CA certs were installed // by it. mContext.packageName = admin1.getPackageName(); mContext.binder.callingUid = DpmMockContext.CALLER_UID; // Removing the Profile Owner should clear the information on which CA certs were installed runAsCaller(admin1Context, dpms, (dpm) -> { dpm.clearProfileOwner(admin1); mContext.packageName = "com.android.frameworks.servicestests"; mContext.binder.clearCallingIdentity(); final List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user); }); runAsCaller(serviceContext, dpms, (dpm) -> { final List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(callerUser); assertNotNull(ownerInstalledCaCerts); assertTrue(ownerInstalledCaCerts.isEmpty()); }); } private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) { Loading Loading @@ -4147,29 +4199,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { } private void flushTasks() throws Exception { Boolean tasksFlushed[] = new Boolean[] {false}; final Runnable tasksFlushedNotifier = () -> { synchronized (tasksFlushed) { tasksFlushed[0] = true; tasksFlushed.notify(); } }; dpms.mHandler.runWithScissors(() -> {}, 0 /*now*/); dpms.mBackgroundHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush main thread handler. dpms.mHandler.post(tasksFlushedNotifier); synchronized (tasksFlushed) { if (!tasksFlushed[0]) { tasksFlushed.wait(); } } // Flush background thread handler. tasksFlushed[0] = false; dpms.mBackgroundHandler.post(tasksFlushedNotifier); synchronized (tasksFlushed) { if (!tasksFlushed[0]) { tasksFlushed.wait(); } } // We can't let exceptions happen on the background thread. Throw them here if they happen // so they still cause the test to fail despite being suppressed. mContext.rethrowBackgroundBroadcastExceptions(); } }
services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +57 −14 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.telephony.TelephonyManager; import android.test.mock.MockContentResolver; import android.test.mock.MockContext; import android.util.ArrayMap; import android.util.Pair; import android.view.IWindowManager; import com.android.internal.widget.LockPatternUtils; Loading @@ -61,6 +62,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; Loading Loading @@ -111,6 +113,7 @@ public class DpmMockContext extends MockContext { public static class MockBinder { public int callingUid = CALLER_UID; public int callingPid = CALLER_PID; public final Map<Integer, List<String>> callingPermissions = new ArrayMap<>(); public long clearCallingIdentity() { final long token = (((long) callingUid) << 32) | (callingPid); Loading Loading @@ -303,14 +306,19 @@ public class DpmMockContext extends MockContext { /** Note this is a partial mock, not a real mock. */ public final PackageManager packageManager; /** TODO: Migrate everything to use {@link #permissions} to avoid confusion. */ @Deprecated public final List<String> callerPermissions = new ArrayList<>(); /** Less confusing alias for {@link #callerPermissions}. */ public final List<String> permissions = callerPermissions; private final ArrayList<UserInfo> mUserInfos = new ArrayList<>(); public final BuildMock buildMock = new BuildMock(); /** Optional mapping of other user contexts for {@link #createPackageContextAsUser} to return */ public final Map<UserHandle, Context> userContexts = new ArrayMap<>(); public final Map<Pair<UserHandle, String>, Context> userPackageContexts = new ArrayMap<>(); public String packageName = null; Loading @@ -324,6 +332,9 @@ public class DpmMockContext extends MockContext { public final IntentFilter filter; public final Handler scheduler; // Exceptions thrown in a background thread kill the whole test. Save them instead. public final AtomicReference<Exception> backgroundException = new AtomicReference<>(); public BroadcastReceiverRegistration(BroadcastReceiver receiver, IntentFilter filter, Handler scheduler) { this.receiver = receiver; Loading @@ -337,20 +348,30 @@ public class DpmMockContext extends MockContext { 0 /* type */, false /* ordered */, false /* sticky */, null /* token */, userId, 0 /* flags */); if (filter.match(null, intent, false, "DpmMockContext") > 0) { if (scheduler != null) { scheduler.post(() -> { final Runnable send = () -> { receiver.setPendingResult(result); receiver.onReceive(DpmMockContext.this, intent); }; if (scheduler != null) { scheduler.post(() -> { try { send.run(); } catch (Exception e) { backgroundException.compareAndSet(null, e); } }); } else { receiver.setPendingResult(result); receiver.onReceive(DpmMockContext.this, intent); send.run(); } } } } private List<BroadcastReceiverRegistration> mBroadcastReceivers = new ArrayList<>(); public DpmMockContext(Context realTestContext, String name) { this(realTestContext, new File(realTestContext.getCacheDir(), name)); } public DpmMockContext(Context context, File dataDir) { realTestContext = context; Loading Loading @@ -511,13 +532,29 @@ public class DpmMockContext extends MockContext { .thenReturn(isRunning); } public void injectBroadcast(Intent intent) { public void injectBroadcast(final Intent intent) { final int userId = UserHandle.getUserId(binder.getCallingUid()); for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) { receiver.sendBroadcastIfApplicable(userId, intent); } } public void rethrowBackgroundBroadcastExceptions() throws Exception { for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) { final Exception e = receiver.backgroundException.getAndSet(null); if (e != null) { throw e; } } } public void addPackageContext(UserHandle user, Context context) { if (context.getPackageName() == null) { throw new NullPointerException("getPackageName() == null"); } userPackageContexts.put(new Pair<>(user, context.getPackageName()), context); } @Override public Resources getResources() { return resources; Loading Loading @@ -576,7 +613,16 @@ public class DpmMockContext extends MockContext { if (binder.getCallingUid() == SYSTEM_UID) { return; // Assume system has all permissions. } if (!callerPermissions.contains(permission)) { List<String> permissions = binder.callingPermissions.get(binder.getCallingUid()); if (permissions == null) { // TODO: delete the following line. to do this without breaking any tests, first it's // necessary to remove all tests that set it directly. permissions = callerPermissions; // throw new UnsupportedOperationException( // "Caller UID " + binder.getCallingUid() + " doesn't exist"); } if (!permissions.contains(permission)) { throw new SecurityException("Caller doesn't have " + permission + " : " + message); } } Loading Loading @@ -751,14 +797,11 @@ public class DpmMockContext extends MockContext { @Override public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) throws PackageManager.NameNotFoundException { if (!userContexts.containsKey(user)) { return super.createPackageContextAsUser(packageName, flags, user); } if (!getPackageName().equals(packageName)) { throw new UnsupportedOperationException( "Creating a context as another package is not implemented"); final Pair<UserHandle, String> key = new Pair<>(user, packageName); if (userPackageContexts.containsKey(key)) { return userPackageContexts.get(key); } return userContexts.get(user); throw new UnsupportedOperationException("No package " + packageName + " for user " + user); } @Override Loading
services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java +29 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.devicepolicy; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading Loading @@ -70,6 +71,34 @@ public abstract class DpmTestBase extends AndroidTestCase { return mMockContext; } protected interface DpmRunnable { public void run(DevicePolicyManager dpm) throws Exception; } /** * Simulate an RPC from {@param caller} to the service context ({@link #mContext}). * * The caller sees its own context. The server also sees its own separate context, with the * appropriate calling UID and calling permissions fields already set up. */ protected void runAsCaller(DpmMockContext caller, DevicePolicyManagerServiceTestable dpms, DpmRunnable action) { final DpmMockContext serviceContext = mMockContext; final long origId = serviceContext.binder.clearCallingIdentity(); try { serviceContext.binder.callingUid = caller.binder.callingUid; serviceContext.binder.callingPid = caller.binder.callingPid; serviceContext.binder.callingPermissions.put(caller.binder.callingUid, caller.permissions); action.run(new DevicePolicyManagerTestable(caller, dpms)); } catch (Exception e) { throw new AssertionError(e); } finally { serviceContext.binder.restoreCallingIdentity(origId); } } protected void markPackageAsInstalled(String packageName, ApplicationInfo ai, int userId) throws Exception { final PackageInfo pi = DpmTestUtils.cloneParcelable( Loading