Device or profile owner can let another app manage app restrictions

The device or profile owner can allow another
package to set app restrictions for any app in that user

Similar to the way it can give permission to access
CA certificate related APIs from M.

Bug: 22541936
Change-Id: I0c1b0804ad300dfa4fbdc1c7721c5d8653d77861
diff --git a/api/current.txt b/api/current.txt
index ec38436..d7493e9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5740,6 +5740,7 @@
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
+    method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
     method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
     method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5783,6 +5784,7 @@
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
+    method public boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5798,6 +5800,7 @@
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
+    method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
     method public void setAutoTimeRequired(android.content.ComponentName, boolean);
     method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index d89e2ca..f6e427a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5865,6 +5865,7 @@
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
+    method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
     method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
     method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5915,6 +5916,7 @@
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
+    method public boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5932,6 +5934,7 @@
     method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
+    method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
     method public void setAutoTimeRequired(android.content.ComponentName, boolean);
     method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
diff --git a/api/test-current.txt b/api/test-current.txt
index 7cc7cb6..25bd3ad 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5740,6 +5740,7 @@
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
+    method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
     method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
     method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5783,6 +5784,7 @@
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
+    method public boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
@@ -5798,6 +5800,7 @@
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
+    method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
     method public void setAutoTimeRequired(android.content.ComponentName, boolean);
     method public void setBluetoothContactSharingDisabled(android.content.ComponentName, boolean);
     method public void setCameraDisabled(android.content.ComponentName, boolean);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 660ce3b..d9cd3cc 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3312,8 +3312,69 @@
     }
 
     /**
-     * Called by a profile or device owner to set the application restrictions for a given target
-     * application running in the profile.
+     * Called by a profile owner or device owner to grant permission to a package to manage
+     * application restrictions for the calling user via {@link #setApplicationRestrictions} and
+     * {@link #getApplicationRestrictions}.
+     * <p>
+     * This permission is persistent until it is later cleared by calling this method with a
+     * {@code null} value or uninstalling the managing package.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName The package name which will be given access to application restrictions
+     * APIs. If {@code null} is given the current package will be cleared.
+     */
+    public void setApplicationRestrictionsManagingPackage(@NonNull ComponentName admin,
+            @Nullable String packageName) {
+        if (mService != null) {
+            try {
+                mService.setApplicationRestrictionsManagingPackage(admin, packageName);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+    }
+
+    /**
+     * Called by a profile owner or device owner to retrieve the application restrictions managing
+     * package for the current user, or {@code null} if none is set.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @return The package name allowed to manage application restrictions on the current user, or
+     * {@code null} if none is set.
+     */
+    public String getApplicationRestrictionsManagingPackage(@NonNull ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getApplicationRestrictionsManagingPackage(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns {@code true} if the calling package has been granted permission via
+     * {@link #setApplicationRestrictionsManagingPackage} to manage application
+     * restrictions for the calling user.
+     */
+    public boolean isCallerApplicationRestrictionsManagingPackage() {
+        if (mService != null) {
+            try {
+                return mService.isCallerApplicationRestrictionsManagingPackage();
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sets the application restrictions for a given target application running in the calling user.
+     *
+     * <p>The caller must be a profile or device owner on that user, or the package allowed to
+     * manage application restrictions via {@link #setApplicationRestrictionsManagingPackage};
+     * otherwise a security exception will be thrown.
      *
      * <p>The provided {@link Bundle} consists of key-value pairs, where the types of values may be:
      * <ul>
@@ -3323,24 +3384,25 @@
      * <li>From {@link android.os.Build.VERSION_CODES#M}, {@code Bundle} or {@code Bundle[]}
      * </ul>
      *
-     * <p>The application restrictions are only made visible to the target application and the
-     * profile or device owner.
-     *
      * <p>If the restrictions are not available yet, but may be applied in the near future,
-     * the admin can notify the target application of that by adding
+     * the caller can notify the target application of that by adding
      * {@link UserManager#KEY_RESTRICTIONS_PENDING} to the settings parameter.
      *
-     * <p>The calling device admin must be a profile or device owner; if it is not, a security
-     * exception will be thrown.
+     * <p>The application restrictions are only made visible to the target application via
+     * {@link UserManager#getApplicationRestrictions(String)}, in addition to the profile or
+     * device owner, and the application restrictions managing package via
+     * {@link #getApplicationRestrictions}.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     * {@code null} if called by the application restrictions managing package.
      * @param packageName The name of the package to update restricted settings for.
      * @param settings A {@link Bundle} to be parsed by the receiving application, conveying a new
      * set of active restrictions.
      *
+     * @see #setApplicationRestrictionsManagingPackage
      * @see UserManager#KEY_RESTRICTIONS_PENDING
      */
-    public void setApplicationRestrictions(@NonNull ComponentName admin, String packageName,
+    public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
             Bundle settings) {
         if (mService != null) {
             try {
@@ -3896,19 +3958,23 @@
     }
 
     /**
-     * Called by a profile or device owner to get the application restrictions for a given target
-     * application running in the profile.
+     * Retrieves the application restrictions for a given target application running in the calling
+     * user.
      *
-     * <p>The calling device admin must be a profile or device owner; if it is not, a security
-     * exception will be thrown.
+     * <p>The caller must be a profile or device owner on that user, or the package allowed to
+     * manage application restrictions via {@link #setApplicationRestrictionsManagingPackage};
+     * otherwise a security exception will be thrown.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     * {@code null} if called by the application restrictions managing package.
      * @param packageName The name of the package to fetch restricted settings of.
      * @return {@link Bundle} of settings corresponding to what was set last time
      * {@link DevicePolicyManager#setApplicationRestrictions} was called, or an empty {@link Bundle}
      * if no restrictions have been set.
+     *
+     * @see {@link #setApplicationRestrictionsManagingPackage}
      */
-    public Bundle getApplicationRestrictions(@NonNull ComponentName admin, String packageName) {
+    public Bundle getApplicationRestrictions(@Nullable ComponentName admin, String packageName) {
         if (mService != null) {
             try {
                 return mService.getApplicationRestrictions(admin, packageName);
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d8cc2ea..ac5a303 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -149,6 +149,9 @@
 
     void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings);
     Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
+    void setApplicationRestrictionsManagingPackage(in ComponentName admin, in String packageName);
+    String getApplicationRestrictionsManagingPackage(in ComponentName admin);
+    boolean isCallerApplicationRestrictionsManagingPackage();
 
     void setRestrictionsProvider(in ComponentName who, in ComponentName provider);
     ComponentName getRestrictionsProvider(int userHandle);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 810ee6b..c9810fc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -193,6 +193,8 @@
     private static final String ATTR_PERMISSION_POLICY = "permission-policy";
 
     private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
+    private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
+            = "application-restrictions-manager";
 
     private static final int STATUS_BAR_DISABLE_MASK =
             StatusBarManager.DISABLE_EXPAND |
@@ -322,6 +324,8 @@
 
         boolean doNotAskCredentialsOnBoot = false;
 
+        String mApplicationRestrictionsManagingPackage;
+
         public DevicePolicyData(int userHandle) {
             mUserHandle = userHandle;
         }
@@ -1035,19 +1039,15 @@
                 saveSettingsLocked(policy.mUserHandle);
             }
 
-            if (policy.mDelegatedCertInstallerPackage != null &&
-                    (packageName == null
-                    || packageName.equals(policy.mDelegatedCertInstallerPackage))) {
-                try {
-                    // Check if delegated cert installer package is removed.
-                    if (mIPackageManager.getPackageInfo(
-                            policy.mDelegatedCertInstallerPackage, 0, userHandle) == null) {
-                        policy.mDelegatedCertInstallerPackage = null;
-                        saveSettingsLocked(policy.mUserHandle);
-                    }
-                } catch (RemoteException e) {
-                    // Shouldn't happen
-                }
+            // Check if delegated cert installer or app restrictions managing packages are removed.
+            if (isRemovedPackage(packageName, policy.mDelegatedCertInstallerPackage, userHandle)) {
+                policy.mDelegatedCertInstallerPackage = null;
+                saveSettingsLocked(policy.mUserHandle);
+            }
+            if (isRemovedPackage(
+                    packageName, policy.mApplicationRestrictionsManagingPackage, userHandle)) {
+                policy.mApplicationRestrictionsManagingPackage = null;
+                saveSettingsLocked(policy.mUserHandle);
             }
         }
         if (removed) {
@@ -1056,6 +1056,18 @@
         }
     }
 
+    private boolean isRemovedPackage(String changedPackage, String targetPackage, int userHandle) {
+        try {
+            return targetPackage != null
+                    && (changedPackage == null || changedPackage.equals(targetPackage))
+                    && mIPackageManager.getPackageInfo(targetPackage, 0, userHandle) == null;
+        } catch (RemoteException e) {
+            // Shouldn't happen
+        }
+
+        return false;
+    }
+
     /**
      * Unit test will subclass it to inject mocks.
      */
@@ -1795,6 +1807,10 @@
                 out.attribute(null, ATTR_DELEGATED_CERT_INSTALLER,
                         policy.mDelegatedCertInstallerPackage);
             }
+            if (policy.mApplicationRestrictionsManagingPackage != null) {
+                out.attribute(null, ATTR_APPLICATION_RESTRICTIONS_MANAGER,
+                        policy.mApplicationRestrictionsManagingPackage);
+            }
 
             final int N = policy.mAdminList.size();
             for (int i=0; i<N; i++) {
@@ -1920,6 +1936,8 @@
             }
             policy.mDelegatedCertInstallerPackage = parser.getAttributeValue(null,
                     ATTR_DELEGATED_CERT_INSTALLER);
+            policy.mApplicationRestrictionsManagingPackage = parser.getAttributeValue(null,
+                    ATTR_APPLICATION_RESTRICTIONS_MANAGER);
 
             type = parser.next();
             int outerDepth = parser.getDepth();
@@ -4815,6 +4833,7 @@
         DevicePolicyData policy = getUserData(userId);
         policy.mPermissionPolicy = DevicePolicyManager.PERMISSION_POLICY_PROMPT;
         policy.mDelegatedCertInstallerPackage = null;
+        policy.mApplicationRestrictionsManagingPackage = null;
         policy.mStatusBarDisabled = false;
         saveSettingsLocked(userId);
 
@@ -5187,18 +5206,68 @@
     }
 
     @Override
-    public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
-        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+    public void setApplicationRestrictionsManagingPackage(ComponentName admin, String packageName) {
+        final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            DevicePolicyData policy = getUserData(userHandle);
+            policy.mApplicationRestrictionsManagingPackage = packageName;
+            saveSettingsLocked(userHandle);
+        }
+    }
 
-            long id = mInjector.binderClearCallingIdentity();
-            try {
-                mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
-            } finally {
-                mInjector.binderRestoreCallingIdentity(id);
+    @Override
+    public String getApplicationRestrictionsManagingPackage(ComponentName admin) {
+        final int userHandle = mInjector.userHandleGetCallingUserId();
+        synchronized (this) {
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            DevicePolicyData policy = getUserData(userHandle);
+            return policy.mApplicationRestrictionsManagingPackage;
+        }
+    }
+
+    @Override
+    public boolean isCallerApplicationRestrictionsManagingPackage() {
+        final int callingUid = mInjector.binderGetCallingUid();
+        final int userHandle = UserHandle.getUserId(callingUid);
+        synchronized (this) {
+            final DevicePolicyData policy = getUserData(userHandle);
+            if (policy.mApplicationRestrictionsManagingPackage == null) {
+                return false;
             }
+
+            try {
+                int uid = mContext.getPackageManager().getPackageUid(
+                        policy.mApplicationRestrictionsManagingPackage, userHandle);
+                return uid == callingUid;
+            } catch (NameNotFoundException e) {
+                return false;
+            }
+        }
+    }
+
+    private void enforceCanManageApplicationRestrictions(ComponentName who) {
+        if (who != null) {
+            synchronized (this) {
+                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            }
+        } else if (!isCallerApplicationRestrictionsManagingPackage()) {
+            throw new SecurityException(
+                    "No admin component given, and caller cannot manage application restrictions "
+                    + "for other apps.");
+        }
+    }
+
+    @Override
+    public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
+        enforceCanManageApplicationRestrictions(who);
+
+        final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
@@ -5764,21 +5833,17 @@
 
     @Override
     public Bundle getApplicationRestrictions(ComponentName who, String packageName) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
-        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+        enforceCanManageApplicationRestrictions(who);
 
-        synchronized (this) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
-            long id = mInjector.binderClearCallingIdentity();
-            try {
-                Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle);
-                // if no restrictions were saved, mUserManager.getApplicationRestrictions
-                // returns null, but DPM method should return an empty Bundle as per JavaDoc
-                return bundle != null ? bundle : Bundle.EMPTY;
-            } finally {
-                mInjector.binderRestoreCallingIdentity(id);
-            }
+        final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+           Bundle bundle = mUserManager.getApplicationRestrictions(packageName, userHandle);
+           // if no restrictions were saved, mUserManager.getApplicationRestrictions
+           // returns null, but DPM method should return an empty Bundle as per JavaDoc
+           return bundle != null ? bundle : Bundle.EMPTY;
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
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 565ef4b..7747fd9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -944,6 +944,88 @@
         assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size());
     }
 
+    public void testApplicationRestrictionsManagingApp() throws Exception {
+        setAsProfileOwner(admin1);
+
+        final String appRestrictionsManagerPackage = "com.google.app.restrictions.manager";
+        final int appRestrictionsManagerAppId = 20987;
+        final int appRestrictionsManagerUid = UserHandle.getUid(
+                DpmMockContext.CALLER_USER_HANDLE, appRestrictionsManagerAppId);
+        doReturn(appRestrictionsManagerUid).when(mContext.packageManager).getPackageUid(
+                eq(appRestrictionsManagerPackage),
+                eq(DpmMockContext.CALLER_USER_HANDLE));
+        mContext.binder.callingUid = appRestrictionsManagerUid;
+
+        // appRestrictionsManager package shouldn't be able to manage restrictions as the PO hasn't
+        // delegated that permission yet.
+        assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
+        Bundle rest = new Bundle();
+        rest.putString("KEY_STRING", "Foo1");
+        try {
+            dpm.setApplicationRestrictions(null, "pkg1", rest);
+            fail("Didn't throw expected SecurityException");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "caller cannot manage application restrictions", expected.getMessage());
+        }
+        try {
+            dpm.getApplicationRestrictions(null, "pkg1");
+            fail("Didn't throw expected SecurityException");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "caller cannot manage application restrictions", expected.getMessage());
+        }
+
+        // Check via the profile owner that no restrictions were set.
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
+
+        // Let appRestrictionsManagerPackage manage app restrictions
+        dpm.setApplicationRestrictionsManagingPackage(admin1, appRestrictionsManagerPackage);
+        assertEquals(appRestrictionsManagerPackage,
+                dpm.getApplicationRestrictionsManagingPackage(admin1));
+
+        // Now that package should be able to set and retrieve app restrictions.
+        mContext.binder.callingUid = appRestrictionsManagerUid;
+        assertTrue(dpm.isCallerApplicationRestrictionsManagingPackage());
+        dpm.setApplicationRestrictions(null, "pkg1", rest);
+        Bundle returned = dpm.getApplicationRestrictions(null, "pkg1");
+        assertEquals(1, returned.size(), 1);
+        assertEquals("Foo1", returned.get("KEY_STRING"));
+
+        // The same app running on a separate user shouldn't be able to manage app restrictions.
+        mContext.binder.callingUid = UserHandle.getUid(
+                UserHandle.USER_SYSTEM, appRestrictionsManagerAppId);
+        assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
+        try {
+            dpm.setApplicationRestrictions(null, "pkg1", rest);
+            fail("Didn't throw expected SecurityException");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "caller cannot manage application restrictions", expected.getMessage());
+        }
+
+        // The DPM is still able to manage app restrictions, even if it allowed another app to do it
+        // too.
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        assertEquals(returned, dpm.getApplicationRestrictions(admin1, "pkg1"));
+        dpm.setApplicationRestrictions(admin1, "pkg1", null);
+        assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
+
+        // Removing the ability for the package to manage app restrictions.
+        dpm.setApplicationRestrictionsManagingPackage(admin1, null);
+        assertNull(dpm.getApplicationRestrictionsManagingPackage(admin1));
+        mContext.binder.callingUid = appRestrictionsManagerUid;
+        assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
+        try {
+            dpm.setApplicationRestrictions(null, "pkg1", null);
+            fail("Didn't throw expected SecurityException");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "caller cannot manage application restrictions", expected.getMessage());
+        }
+    }
+
     public void testSetUserRestriction_asDo() throws Exception {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         mContext.callerPermissions.add(permission.MANAGE_USERS);