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/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8aef9bd..33224a0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -300,7 +300,8 @@
 
     /**
      * Sets all the user-wide restrictions for this user.
-     * Requires the MANAGE_USERS permission.
+     * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission or profile/device owner
+     * privileges.
      * @param restrictions the Bundle containing all the restrictions.
      */
     public void setUserRestrictions(Bundle restrictions) {
@@ -309,7 +310,8 @@
 
     /**
      * Sets all the user-wide restrictions for the specified user.
-     * Requires the MANAGE_USERS permission.
+     * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission or profile/device owner
+     * privileges.
      * @param restrictions the Bundle containing all the restrictions.
      * @param userHandle the UserHandle of the user for whom to set the restrictions.
      */
@@ -323,7 +325,8 @@
 
     /**
      * Sets the value of a specific restriction.
-     * Requires the MANAGE_USERS permission.
+     * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission or profile/device owner
+     * privileges.
      * @param key the key of the restriction
      * @param value the value for the restriction
      */
@@ -336,7 +339,8 @@
     /**
      * @hide
      * Sets the value of a specific restriction on a specific user.
-     * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission or profile/device owner
+     * privileges.
      * @param key the key of the restriction
      * @param value the value for the restriction
      * @param userHandle the user whose restriction is to be changed.
@@ -548,7 +552,7 @@
     /**
      * Returns information for all users on this device. Requires
      * {@link android.Manifest.permission#MANAGE_USERS} permission.
-     * 
+     *
      * @param excludeDying specify if the list should exclude users being
      *            removed.
      * @return the list of users that were created.
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);
     }