Update DPMS to push active admins info to UsageStatsService.

Bug: 71710099
Test: atest services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
Test: atest services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
Change-Id: Ia46be9008470b0228978306b9992560fc4f2c586
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 0650acb..f5894e0 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -20,6 +20,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.backup.IBackupManager;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.IPackageManager;
@@ -152,6 +153,11 @@
         }
 
         @Override
+        UsageStatsManagerInternal getUsageStatsManagerInternal() {
+            return services.usageStatsManagerInternal;
+        }
+
+        @Override
         PackageManagerInternal getPackageManagerInternal() {
             return services.packageManagerInternal;
         }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 58ac7d2..57030e4 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -44,6 +44,7 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
@@ -90,6 +91,7 @@
 
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
+import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
@@ -182,6 +184,7 @@
 
         initializeDpms();
 
+        Mockito.reset(getServices().usageStatsManagerInternal);
         setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
         setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
         setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
@@ -207,6 +210,7 @@
         LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
 
         dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
+        dpms.handleStart();
         dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
         dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
 
@@ -278,6 +282,32 @@
         assertNull(LocalServices.getService(DevicePolicyManagerInternal.class));
     }
 
+    public void testHandleStart() throws Exception {
+        // Device owner in SYSTEM_USER
+        setDeviceOwner();
+        // Profile owner in CALLER_USER_HANDLE
+        setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
+        setAsProfileOwner(admin2);
+        // Active admin in CALLER_USER_HANDLE
+        final int ANOTHER_UID = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, 1306);
+        setUpPackageManagerForFakeAdmin(adminAnotherPackage, ANOTHER_UID, admin2);
+        dpm.setActiveAdmin(adminAnotherPackage, /* replace =*/ false,
+                DpmMockContext.CALLER_USER_HANDLE);
+        assertTrue(dpm.isAdminActiveAsUser(adminAnotherPackage,
+                DpmMockContext.CALLER_USER_HANDLE));
+
+        initializeDpms();
+
+        // Verify
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                MockUtils.checkAdminApps(admin1.getPackageName()),
+                eq(UserHandle.USER_SYSTEM));
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                MockUtils.checkAdminApps(admin2.getPackageName(),
+                        adminAnotherPackage.getPackageName()),
+                eq(DpmMockContext.CALLER_USER_HANDLE));
+    }
+
     /**
      * Caller doesn't have proper permissions.
      */
@@ -330,6 +360,9 @@
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 anyString());
 
+        verify(getServices().usageStatsManagerInternal).onActiveAdminAdded(
+                admin1.getPackageName(), DpmMockContext.CALLER_USER_HANDLE);
+
         // TODO Verify other calls too.
 
         // Make sure it's active admin1.
@@ -369,6 +402,11 @@
                 eq(DpmMockContext.CALLER_USER_HANDLE),
                 anyString());
 
+        // times(2) because it was previously called for admin1 which is in the same package
+        // as admin2.
+        verify(getServices().usageStatsManagerInternal, times(2)).onActiveAdminAdded(
+                admin2.getPackageName(), DpmMockContext.CALLER_USER_HANDLE);
+
         // 4. Add the same admin1 again without replace, which should throw.
         assertExpectException(IllegalArgumentException.class, /* messageRegex= */ null,
                 () -> dpm.setActiveAdmin(admin1, /* replace =*/ false));
@@ -384,6 +422,10 @@
         assertEquals(admin1, admins.get(0));
         assertEquals(admin2, admins.get(1));
 
+        // There shouldn't be any callback to UsageStatsManagerInternal when the admin is being
+        // replaced
+        verifyNoMoreInteractions(getServices().usageStatsManagerInternal);
+
         // Another user has no admins.
         mContext.callerPermissions.add("android.permission.INTERACT_ACROSS_USERS_FULL");
 
@@ -516,6 +558,8 @@
                 () -> dpm.removeActiveAdmin(admin1));
 
         assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+        verify(getServices().usageStatsManagerInternal, times(0)).setActiveAdminApps(
+                null, DpmMockContext.CALLER_USER_HANDLE);
 
         // 2. User unlocked.
         when(getServices().userManager.isUserUnlocked(eq(DpmMockContext.CALLER_USER_HANDLE)))
@@ -523,6 +567,8 @@
 
         dpm.removeActiveAdmin(admin1);
         assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                null, DpmMockContext.CALLER_USER_HANDLE);
     }
 
     /**
@@ -547,6 +593,8 @@
 
         dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
         assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                null, DpmMockContext.CALLER_USER_HANDLE);
 
         // TODO DO Still can't be removed in this case.
     }
@@ -588,6 +636,8 @@
                 isNull(Bundle.class));
 
         assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                null, DpmMockContext.CALLER_USER_HANDLE);
 
         // Again broadcast from saveSettingsLocked().
         verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
@@ -598,6 +648,86 @@
         // TODO Check other internal calls.
     }
 
+    public void testRemoveActiveAdmin_multipleAdminsInUser() {
+        // Need MANAGE_DEVICE_ADMINS for setActiveAdmin.  We'll remove it later.
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+        // Add admin1.
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+        assertTrue(dpm.isAdminActive(admin1));
+        assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+        // Add admin2.
+        dpm.setActiveAdmin(admin2, /* replace =*/ false);
+
+        assertTrue(dpm.isAdminActive(admin2));
+        assertFalse(dpm.isRemovingAdmin(admin2, DpmMockContext.CALLER_USER_HANDLE));
+
+        // Broadcast from saveSettingsLocked().
+        verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+
+        // Remove.  No permissions, but same user, so it'll work.
+        mContext.callerPermissions.clear();
+        dpm.removeActiveAdmin(admin1);
+
+        verify(mContext.spiedContext).sendOrderedBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE),
+                isNull(String.class),
+                any(BroadcastReceiver.class),
+                eq(dpms.mHandler),
+                eq(Activity.RESULT_OK),
+                isNull(String.class),
+                isNull(Bundle.class));
+
+        assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                MockUtils.checkAdminApps(admin2.getPackageName()),
+                eq(DpmMockContext.CALLER_USER_HANDLE));
+
+        // Again broadcast from saveSettingsLocked().
+        verify(mContext.spiedContext, times(3)).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(
+                        DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+    }
+
+    /**
+     * Test for:
+     * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)}
+     */
+    public void testForceRemoveActiveAdmin() throws Exception {
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+        // Add admin.
+        setupPackageInPackageManager(admin1.getPackageName(),
+                /* userId= */ DpmMockContext.CALLER_USER_HANDLE,
+                /* appId= */ 10138,
+                /* flags= */ ApplicationInfo.FLAG_TEST_ONLY);
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+        assertTrue(dpm.isAdminActive(admin1));
+
+        // Calling from a non-shell uid should fail with a SecurityException
+        mContext.binder.callingUid = 123456;
+        assertExpectException(SecurityException.class,
+                /* messageRegex =*/ "Non-shell user attempted to call",
+                () -> dpms.forceRemoveActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
+
+        mContext.binder.callingUid = Process.SHELL_UID;
+        dpms.forceRemoveActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+        mContext.callerPermissions.add(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        // Verify
+        assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                null, DpmMockContext.CALLER_USER_HANDLE);
+    }
+
     /**
      * Test for: @{link DevicePolicyManager#setActivePasswordState}
      *
@@ -954,6 +1084,9 @@
                 eq(null),
                 eq(true), eq(CAMERA_NOT_DISABLED));
 
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                null, UserHandle.USER_SYSTEM);
+
         assertFalse(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM));
 
         // ACTION_DEVICE_OWNER_CHANGED should be sent twice, once for setting the device owner
@@ -1044,6 +1177,8 @@
         // Check
         assertFalse(dpm.isProfileOwnerApp(admin1.getPackageName()));
         assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                null, DpmMockContext.CALLER_USER_HANDLE);
     }
 
     public void testSetProfileOwner_failures() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 4232c44..11e32f8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -29,6 +29,7 @@
 import android.app.IActivityManager;
 import android.app.NotificationManager;
 import android.app.backup.IBackupManager;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -73,6 +74,7 @@
     public final SystemPropertiesForMock systemProperties;
     public final UserManager userManager;
     public final UserManagerInternal userManagerInternal;
+    public final UsageStatsManagerInternal usageStatsManagerInternal;
     public final PackageManagerInternal packageManagerInternal;
     public final UserManagerForMock userManagerForMock;
     public final PowerManagerForMock powerManager;
@@ -108,6 +110,7 @@
         systemProperties = mock(SystemPropertiesForMock.class);
         userManager = mock(UserManager.class);
         userManagerInternal = mock(UserManagerInternal.class);
+        usageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
         userManagerForMock = mock(UserManagerForMock.class);
         packageManagerInternal = mock(PackageManagerInternal.class);
         powerManager = mock(PowerManagerForMock.class);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
index e43786c..288a8be 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -24,13 +24,16 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.util.ArraySet;
 
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
-import org.mockito.Mockito;
 import org.mockito.hamcrest.MockitoHamcrest;
 
+import java.util.Arrays;
+import java.util.Set;
+
 public class MockUtils {
     private MockUtils() {
     }
@@ -115,6 +118,30 @@
         return MockitoHamcrest.argThat(m);
     }
 
+    public static Set<String> checkAdminApps(String... adminApps) {
+        final Matcher<Set<String>> m = new BaseMatcher<Set<String>>() {
+            @Override
+            public boolean matches(Object item) {
+                if (item == null) return false;
+                final Set<String> actualAdminApps = (Set<String>) item;
+                if (adminApps.length != actualAdminApps.size()) {
+                    return false;
+                }
+                final Set<String> copyOfAdmins = new ArraySet<>(actualAdminApps);
+                for (String adminApp : adminApps) {
+                    copyOfAdmins.remove(adminApp);
+                }
+                return copyOfAdmins.isEmpty();
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("Admin apps=" + Arrays.toString(adminApps));
+            }
+        };
+        return MockitoHamcrest.argThat(m);
+    }
+
     private static String getRestrictionsAsString(Bundle b) {
         final StringBuilder sb = new StringBuilder();
         sb.append("[");
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 725fb21..40964c0 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -34,13 +34,13 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
+import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
 import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManager;
 import android.appwidget.AppWidgetManager;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -55,6 +55,7 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
 import android.view.Display;
 
 import com.android.server.SystemService;
@@ -65,7 +66,9 @@
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Unit test for AppStandbyController.
@@ -78,6 +81,11 @@
     private static final String PACKAGE_1 = "com.example.foo";
     private static final int UID_1 = 10000;
     private static final int USER_ID = 0;
+    private static final int USER_ID2 = 10;
+
+    private static final String ADMIN_PKG = "com.android.admin";
+    private static final String ADMIN_PKG2 = "com.android.admin2";
+    private static final String ADMIN_PKG3 = "com.android.admin3";
 
     private static final long MINUTE_MS = 60 * 1000;
     private static final long HOUR_MS = 60 * MINUTE_MS;
@@ -454,6 +462,105 @@
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
                 REASON_PREDICTED, mInjector.mElapsedRealtime);
         assertBucket(STANDBY_BUCKET_FREQUENT);
+    }
 
+    @Test
+    public void testAddActiveDeviceAdmin() {
+        assertActiveAdmins(USER_ID, (String[]) null);
+        assertActiveAdmins(USER_ID2, (String[]) null);
+
+        mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
+        assertActiveAdmins(USER_ID, ADMIN_PKG);
+        assertActiveAdmins(USER_ID2, (String[]) null);
+
+        mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
+        assertActiveAdmins(USER_ID, ADMIN_PKG);
+        assertActiveAdmins(USER_ID2, (String[]) null);
+
+        mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
+        assertActiveAdmins(USER_ID, ADMIN_PKG);
+        assertActiveAdmins(USER_ID2, ADMIN_PKG2);
+    }
+
+    @Test
+    public void testSetActiveAdminApps() {
+        assertActiveAdmins(USER_ID, (String[]) null);
+        assertActiveAdmins(USER_ID2, (String[]) null);
+
+        setActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
+        assertActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
+        assertActiveAdmins(USER_ID2, (String[]) null);
+
+        mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
+        setActiveAdmins(USER_ID2, ADMIN_PKG);
+        assertActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
+        assertActiveAdmins(USER_ID2, ADMIN_PKG);
+
+        mController.setActiveAdminApps(null, USER_ID);
+        assertActiveAdmins(USER_ID, (String[]) null);
+    }
+
+    @Test
+    public void isActiveDeviceAdmin() {
+        assertActiveAdmins(USER_ID, (String[]) null);
+        assertActiveAdmins(USER_ID2, (String[]) null);
+
+        mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
+        assertIsActiveAdmin(ADMIN_PKG, USER_ID);
+        assertIsNotActiveAdmin(ADMIN_PKG, USER_ID2);
+
+        mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
+        mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID2);
+        assertIsActiveAdmin(ADMIN_PKG, USER_ID);
+        assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID);
+        assertIsActiveAdmin(ADMIN_PKG, USER_ID2);
+        assertIsActiveAdmin(ADMIN_PKG2, USER_ID2);
+
+        setActiveAdmins(USER_ID2, ADMIN_PKG2);
+        assertIsActiveAdmin(ADMIN_PKG2, USER_ID2);
+        assertIsNotActiveAdmin(ADMIN_PKG, USER_ID2);
+        assertIsActiveAdmin(ADMIN_PKG, USER_ID);
+        assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID);
+    }
+
+    private String getAdminAppsStr(int userId) {
+        return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
+    }
+
+    private String getAdminAppsStr(int userId, Set<String> adminApps) {
+        return "admin apps for u" + userId + ": "
+                + (adminApps == null ? "null" : Arrays.toString(adminApps.toArray()));
+    }
+
+    private void assertIsActiveAdmin(String adminApp, int userId) {
+        assertTrue(adminApp + " should be an active admin; " + getAdminAppsStr(userId),
+                mController.isActiveDeviceAdmin(adminApp, userId));
+    }
+
+    private void assertIsNotActiveAdmin(String adminApp, int userId) {
+        assertFalse(adminApp + " shouldn't be an active admin; " + getAdminAppsStr(userId),
+                mController.isActiveDeviceAdmin(adminApp, userId));
+    }
+
+    private void assertActiveAdmins(int userId, String... admins) {
+        final Set<String> actualAdminApps = mController.getActiveAdminAppsForTest(userId);
+        if (admins == null) {
+            if (actualAdminApps != null && !actualAdminApps.isEmpty()) {
+                fail("Admin apps should be null; " + getAdminAppsStr(userId, actualAdminApps));
+            }
+            return;
+        }
+        assertEquals("No. of admin apps not equal; " + getAdminAppsStr(userId, actualAdminApps)
+                + "; expected=" + Arrays.toString(admins), admins.length, actualAdminApps.size());
+        final Set<String> adminAppsCopy = new ArraySet<>(actualAdminApps);
+        for (String admin : admins) {
+            adminAppsCopy.remove(admin);
+        }
+        assertTrue("Unexpected admin apps; " + getAdminAppsStr(userId, actualAdminApps)
+                + "; expected=" + Arrays.toString(admins), adminAppsCopy.isEmpty());
+    }
+
+    private void setActiveAdmins(int userId, String... admins) {
+        mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId);
     }
 }