Allow device or profile owner app to modify user restrictions.

Currently this is gated on being a system or root app with the
MANAGE_USERS permission; third-party MDM apps set as device or profile
owner should have this ability as well.

Bug: 13585295

Change-Id: I61d21b13b9ec66fc0cb497ec2007ee732461d448
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a6e83a7..53db9ef 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -22,6 +22,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.ActivityThread;
+import android.app.admin.DevicePolicyManager;
 import android.app.IStopUserCallback;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -437,7 +438,7 @@
 
     @Override
     public void setUserRestrictions(Bundle restrictions, int userId) {
-        checkManageUsersPermission("setUserRestrictions");
+        checkProfileOwnerOrManageUsersPermission("setUserRestrictions");
         if (restrictions == null) return;
 
         synchronized (mPackagesLock) {
@@ -463,16 +464,53 @@
      * @param message used as message if SecurityException is thrown
      * @throws SecurityException if the caller is not system or root
      */
-    private static final void checkManageUsersPermission(String message) {
+    private final void checkManageUsersPermission(String message) {
         final int uid = Binder.getCallingUid();
-        if (uid != Process.SYSTEM_UID && uid != 0
-                && ActivityManager.checkComponentPermission(
-                        android.Manifest.permission.MANAGE_USERS,
-                        uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+
+        if (missingManageUsersPermission(uid)) {
             throw new SecurityException("You need MANAGE_USERS permission to: " + message);
         }
     }
 
+    /**
+     * Enforces that only the system UID, root's UID, apps that have the
+     * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}
+     * permission, the profile owner, or the device owner can make certain calls to the
+     * UserManager.
+     *
+     * @param message used as message if SecurityException is thrown
+     * @throws SecurityException if the caller is not system, root, or device
+     * owner
+     */
+    private final void checkProfileOwnerOrManageUsersPermission(String message) {
+        final int uid = Binder.getCallingUid();
+        boolean isProfileOwner = false;
+        if (mContext != null && mContext.getPackageManager() != null) {
+            String[] pkgs = mContext.getPackageManager().getPackagesForUid(uid);
+            DevicePolicyManager dpm =
+                    (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+            if (dpm != null) {
+                for (String pkg : pkgs) {
+                    if (dpm.isDeviceOwnerApp(pkg) || dpm.isProfileOwnerApp(pkg)) {
+                        isProfileOwner = true;
+                    }
+                }
+            }
+        }
+
+        if (missingManageUsersPermission(uid) && !isProfileOwner) {
+            throw new SecurityException(
+                    "You need MANAGE_USERS permission or device owner privileges to: " + message);
+        }
+    }
+
+    private boolean missingManageUsersPermission(int uid) {
+        return uid != Process.SYSTEM_UID && uid != 0
+                && ActivityManager.checkComponentPermission(
+                        android.Manifest.permission.MANAGE_USERS,
+                        uid, -1, true) != PackageManager.PERMISSION_GRANTED;
+    }
+
     private void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
         try {
             File dir = new File(mUsersDir, Integer.toString(info.id));
@@ -1175,7 +1213,8 @@
     public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
         if (UserHandle.getCallingUserId() != userId
                 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
-            checkManageUsersPermission("Only system can get restrictions for other users/apps");
+            checkProfileOwnerOrManageUsersPermission(
+                    "Only system or device owner can get restrictions for other users/apps");
         }
         synchronized (mPackagesLock) {
             // Read the restrictions from XML
@@ -1188,7 +1227,8 @@
             int userId) {
         if (UserHandle.getCallingUserId() != userId
                 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
-            checkManageUsersPermission("Only system can set restrictions for other users/apps");
+            checkProfileOwnerOrManageUsersPermission(
+                    "Only system or device owner can set restrictions for other users/apps");
         }
         synchronized (mPackagesLock) {
             // Write the restrictions to XML
@@ -1290,7 +1330,8 @@
 
     @Override
     public void removeRestrictions() {
-        checkManageUsersPermission("Only system can remove restrictions");
+        checkProfileOwnerOrManageUsersPermission(
+                "Only system or device owner can remove restrictions");
         final int userHandle = UserHandle.getCallingUserId();
         removeRestrictionsForUser(userHandle, true);
     }