Allow profile owners to set user restrictions

Pass the setting along to UserManager.

Fixes a security exception when fetching the profile's enabled state.

Change-Id: If71698cf32c52cce1158cf2027443a339bc58488
diff --git a/api/current.txt b/api/current.txt
index aa1b6f0..63be1ce 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4947,7 +4947,9 @@
 
   public class DevicePolicyManager {
     method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
+    method public void addUserRestriction(android.content.ComponentName, java.lang.String);
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
+    method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
     method public boolean getCameraDisabled(android.content.ComponentName);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d7170e8..d173f19 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1957,4 +1957,48 @@
         }
         return null;
     }
+
+    /**
+     * Called by a profile or device owner to set a user restriction specified
+     * by the key.
+     * <p>
+     * The calling device admin must be a profile or device owner; if it is not,
+     * a security exception will be thrown.
+     * 
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated
+     *            with.
+     * @param key The key of the restriction. See the constants in
+     *            {@link android.os.UserManager} for the list of keys.
+     */
+    public void addUserRestriction(ComponentName admin, String key) {
+        if (mService != null) {
+            try {
+                mService.setUserRestriction(admin, key, true);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Called by a profile or device owner to clear a user restriction specified
+     * by the key.
+     * <p>
+     * The calling device admin must be a profile or device owner; if it is not,
+     * a security exception will be thrown.
+     * 
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated
+     *            with.
+     * @param key The key of the restriction. See the constants in
+     *            {@link android.os.UserManager} for the list of keys.
+     */
+    public void clearUserRestriction(ComponentName admin, String key) {
+        if (mService != null) {
+            try {
+                mService.setUserRestriction(admin, key, false);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 85ba58b..72b3c20 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -119,4 +119,6 @@
 
     void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings);
     Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
+
+    void setUserRestriction(in ComponentName who, in String key, boolean enable);
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 63de9a0..59b918b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -300,8 +300,7 @@
 
     /**
      * Sets all the user-wide restrictions for this user.
-     * Requires the MANAGE_USERS permission or profile/device owner
-     * privileges.
+     * Requires the MANAGE_USERS permission.
      * @param restrictions the Bundle containing all the restrictions.
      */
     public void setUserRestrictions(Bundle restrictions) {
@@ -310,8 +309,7 @@
 
     /**
      * Sets all the user-wide restrictions for the specified user.
-     * Requires the MANAGE_USERS permission or profile/device owner
-     * privileges.
+     * Requires the MANAGE_USERS permission.
      * @param restrictions the Bundle containing all the restrictions.
      * @param userHandle the UserHandle of the user for whom to set the restrictions.
      */
@@ -325,8 +323,7 @@
 
     /**
      * Sets the value of a specific restriction.
-     * Requires the MANAGE_USERS permission or profile/device owner
-     * privileges.
+     * Requires the MANAGE_USERS permission.
      * @param key the key of the restriction
      * @param value the value for the restriction
      */
@@ -339,8 +336,7 @@
     /**
      * @hide
      * Sets the value of a specific restriction on a specific user.
-     * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission or profile/device owner
-     * privileges.
+     * Requires the MANAGE_USERS permission.
      * @param key the key of the restriction
      * @param value the value for the restriction
      * @param userHandle the user whose restriction is to be changed.
@@ -560,7 +556,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 210e151b..f8103de 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -22,7 +22,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.ActivityThread;
-import android.app.admin.DevicePolicyManager;
 import android.app.IStopUserCallback;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
@@ -263,46 +262,58 @@
         if (userId != UserHandle.getCallingUserId()) {
             checkManageUsersPermission("getting profiles related to user " + userId);
         }
-        synchronized (mPackagesLock) {
-            // Getting the service here is not good for testing purposes. However, this service
-            // is not available when UserManagerService starts up so we need a lazy load.
-
-            DevicePolicyManager dpm = null;
-            if (enabledOnly) {
-                dpm = (DevicePolicyManager)
-                        mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mPackagesLock) {
+                return getProfilesLocked(userId, enabledOnly);
             }
-
-            UserInfo user = getUserInfoLocked(userId);
-            ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
-            for (int i = 0; i < mUsers.size(); i++) {
-                UserInfo profile = mUsers.valueAt(i);
-                if (!isProfileOf(user, profile)) {
-                    continue;
-                }
-
-                if (enabledOnly && profile.isManagedProfile()) {
-                    if (dpm != null) {
-                        if(!dpm.isProfileEnabled(profile.id)) {
-                            continue;
-                        }
-                    } else {
-                        Log.w(LOG_TAG,
-                                "Attempting to reach DevicePolicyManager before it was started");
-                        // TODO: There might be system apps that need to call this. Make sure that
-                        // DevicePolicyManagerService is ready at that time (otherwise, any default
-                        // value is a bad one).
-                        throw new IllegalArgumentException(String.format(
-                                "Attempting to get enabled profiles for %d before "
-                                + "DevicePolicyManagerService has been started.", userId));
-                    }
-                }
-                users.add(profile);
-            }
-            return users;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
+    /** Assume permissions already checked and caller's identity cleared */
+    private List<UserInfo> getProfilesLocked(int userId, boolean enabledOnly) {
+        // Getting the service here is not good for testing purposes.
+        // However, this service is not available when UserManagerService starts
+        // up so we need a lazy load.
+
+        DevicePolicyManager dpm = null;
+        if (enabledOnly) {
+            dpm = (DevicePolicyManager)
+                    mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        }
+
+        UserInfo user = getUserInfoLocked(userId);
+        ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+        for (int i = 0; i < mUsers.size(); i++) {
+            UserInfo profile = mUsers.valueAt(i);
+            if (!isProfileOf(user, profile)) {
+                continue;
+            }
+
+            if (enabledOnly && profile.isManagedProfile()) {
+                if (dpm != null) {
+                    if (!dpm.isProfileEnabled(profile.id)) {
+                        continue;
+                    }
+                } else {
+                    Log.w(LOG_TAG,
+                            "Attempting to reach DevicePolicyManager before it is started");
+                    // TODO: There might be system apps that need to call this.
+                    // Make sure that DevicePolicyManagerService is ready at that
+                    // time (otherwise, any default value is a bad one).
+                    throw new IllegalArgumentException(String.format(
+                            "Attempting to get enabled profiles for %d before "
+                                    + "DevicePolicyManagerService has been started.",
+                            userId));
+                }
+            }
+            users.add(profile);
+        }
+        return users;
+    }
+
     private boolean isProfileOf(UserInfo user, UserInfo profile) {
         return user.id == profile.id ||
                 (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
@@ -459,13 +470,13 @@
 
         synchronized (mPackagesLock) {
             Bundle restrictions = mUserRestrictions.get(userId);
-            return restrictions != null ? restrictions : Bundle.EMPTY;
+            return restrictions != null ? new Bundle(restrictions) : new Bundle();
         }
     }
 
     @Override
     public void setUserRestrictions(Bundle restrictions, int userId) {
-        checkProfileOwnerOrManageUsersPermission("setUserRestrictions");
+        checkManageUsersPermission("setUserRestrictions");
         if (restrictions == null) return;
 
         synchronized (mPackagesLock) {
@@ -491,51 +502,14 @@
      * @param message used as message if SecurityException is thrown
      * @throws SecurityException if the caller is not system or root
      */
-    private final void checkManageUsersPermission(String message) {
+    private static final void checkManageUsersPermission(String message) {
         final int uid = Binder.getCallingUid();
-
-        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
+        if (uid != Process.SYSTEM_UID && uid != 0
                 && ActivityManager.checkComponentPermission(
                         android.Manifest.permission.MANAGE_USERS,
-                        uid, -1, true) != PackageManager.PERMISSION_GRANTED;
+                        uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("You need MANAGE_USERS permission to: " + message);
+        }
     }
 
     private void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
@@ -1240,8 +1214,7 @@
     public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
         if (UserHandle.getCallingUserId() != userId
                 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
-            checkProfileOwnerOrManageUsersPermission(
-                    "Only system or device owner can get restrictions for other users/apps");
+            checkManageUsersPermission("Only system can get restrictions for other users/apps");
         }
         synchronized (mPackagesLock) {
             // Read the restrictions from XML
@@ -1254,8 +1227,7 @@
             int userId) {
         if (UserHandle.getCallingUserId() != userId
                 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
-            checkProfileOwnerOrManageUsersPermission(
-                    "Only system or device owner can set restrictions for other users/apps");
+            checkManageUsersPermission("Only system can set restrictions for other users/apps");
         }
         synchronized (mPackagesLock) {
             // Write the restrictions to XML
@@ -1357,8 +1329,7 @@
 
     @Override
     public void removeRestrictions() {
-        checkProfileOwnerOrManageUsersPermission(
-                "Only system or device owner can remove restrictions");
+        checkManageUsersPermission("Only system can remove restrictions");
         final int userHandle = UserHandle.getCallingUserId();
         removeRestrictionsForUser(userHandle, true);
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b82a126..f1ee280 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3118,4 +3118,24 @@
             }
         }
     }
+
+    @Override
+    public void setUserRestriction(ComponentName who, String key, boolean enabled) {
+        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+            UserManager um = UserManager.get(mContext);
+            long id = Binder.clearCallingIdentity();
+            try {
+                um.setUserRestriction(key, enabled, userHandle);
+            } finally {
+                restoreCallingIdentity(id);
+            }
+        }
+    }
 }