Allow system to retrieve permission grant state

To inform the user which apps were granted permissions by the admin,
the Settings app needs to access this information without being a DO/PO.

Bug: 32692748
Test: FrameworksServicesTests unit test

Change-Id: I3770ec6343b85be9c6f7655675ed6db5cb50612c
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c95e011..b8b9793 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6317,7 +6317,7 @@
      * @see #setPermissionGrantState(ComponentName, String, String, int)
      * @see PackageManager#checkPermission(String, String)
      */
-    public int getPermissionGrantState(@NonNull ComponentName admin, String packageName,
+    public int getPermissionGrantState(@Nullable ComponentName admin, String packageName,
             String permission) {
         throwIfParentInstance("getPermissionGrantState");
         try {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ec5491d..8d8934e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6783,6 +6783,18 @@
         enforceManageUsers();
     }
 
+    private void enforceProfileOwnerOrSystemUser(ComponentName admin) {
+        synchronized (this) {
+            if (getActiveAdminWithPolicyForUidLocked(admin,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid())
+                            != null) {
+                return;
+            }
+        }
+        Preconditions.checkState(isCallerWithSystemUid(),
+                "Only profile owner, device owner and system may call this method.");
+    }
+
     private void ensureCallerPackage(@Nullable String packageName) {
         if (packageName == null) {
             Preconditions.checkState(isCallerWithSystemUid(),
@@ -8913,8 +8925,8 @@
         PackageManager packageManager = mInjector.getPackageManager();
 
         UserHandle user = mInjector.binderGetCallingUserHandle();
+        enforceProfileOwnerOrSystemUser(admin);
         synchronized (this) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             long ident = mInjector.binderClearCallingIdentity();
             try {
                 int granted = mIPackageManager.checkPermission(permission,
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 c3eb09d..f1eaf9b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3207,6 +3207,48 @@
         }
     }
 
+    public void testGetPermissionGrantState() throws Exception {
+        final String permission = "some.permission";
+        final String app1 = "com.example.app1";
+        final String app2 = "com.example.app2";
+
+        when(mContext.ipackageManager.checkPermission(eq(permission), eq(app1), anyInt()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        doReturn(PackageManager.FLAG_PERMISSION_POLICY_FIXED).when(mContext.packageManager)
+                .getPermissionFlags(permission, app1, UserHandle.SYSTEM);
+        when(mContext.packageManager.getPermissionFlags(permission, app1,
+                UserHandle.of(DpmMockContext.CALLER_USER_HANDLE)))
+                .thenReturn(PackageManager.FLAG_PERMISSION_POLICY_FIXED);
+        when(mContext.ipackageManager.checkPermission(eq(permission), eq(app2), anyInt()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        doReturn(0).when(mContext.packageManager).getPermissionFlags(permission, app2,
+                UserHandle.SYSTEM);
+        when(mContext.packageManager.getPermissionFlags(permission, app2,
+                UserHandle.of(DpmMockContext.CALLER_USER_HANDLE))).thenReturn(0);
+
+        // System can retrieve permission grant state.
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED,
+                dpm.getPermissionGrantState(null, app1, permission));
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT,
+                dpm.getPermissionGrantState(null, app2, permission));
+
+        // A regular app cannot retrieve permission grant state.
+        mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        try {
+            dpm.getPermissionGrantState(null, app1, permission);
+            fail("Didn't throw IllegalStateException");
+        } catch (IllegalStateException expected) {
+        }
+
+        // Profile owner can retrieve permission grant state.
+        setAsProfileOwner(admin1);
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED,
+                dpm.getPermissionGrantState(admin1, app1, permission));
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT,
+                dpm.getPermissionGrantState(admin1, app2, permission));
+    }
+
     private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
         when(mContext.settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
                 userhandle)).thenReturn(isUserSetupComplete ? 1 : 0);