Restrict setting the profile/device owner with a signature-level permission.

Create the new permission MANAGE_PROFILE_OWNERS to restrict setting
the profile/device owner.

BUG:19838376

Change-Id: Ib55a2db85fcb6f34e3b88c398683bddb0ad66868
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a20aa668..47133d4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2651,14 +2651,12 @@
 
     /**
      * @hide
-     * Sets the given package as the device owner. The package must already be installed and there
-     * shouldn't be an existing device owner registered, for this call to succeed. Also, this
-     * method must be called before the device is provisioned.
+     * Sets the given package as the device owner.
+     * Same as {@link #setDeviceOwner(String, String)} but without setting a device owner name.
      * @param packageName the package name of the application to be registered as the device owner.
      * @return whether the package was successfully registered as the device owner.
      * @throws IllegalArgumentException if the package name is null or invalid
-     * @throws IllegalStateException if a device owner is already registered or the device has
-     *         already been provisioned.
+     * @throws IllegalStateException If the preconditions mentioned are not met.
      */
     public boolean setDeviceOwner(String packageName) throws IllegalArgumentException,
             IllegalStateException {
@@ -2667,15 +2665,17 @@
 
     /**
      * @hide
-     * Sets the given package as the device owner. The package must already be installed and there
-     * shouldn't be an existing device owner registered, for this call to succeed. Also, this
-     * method must be called before the device is provisioned.
+     * Sets the given package as the device owner. The package must already be installed. There
+     * must not already be a device owner.
+     * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call
+     * this method.
+     * Calling this after the setup phase of the primary user has completed is allowed only if
+     * the caller is the shell uid, and there are no additional users and no accounts.
      * @param packageName the package name of the application to be registered as the device owner.
      * @param ownerName the human readable name of the institution that owns this device.
      * @return whether the package was successfully registered as the device owner.
      * @throws IllegalArgumentException if the package name is null or invalid
-     * @throws IllegalStateException if a device owner is already registered or the device has
-     *         already been provisioned.
+     * @throws IllegalStateException If the preconditions mentioned are not met.
      */
     public boolean setDeviceOwner(String packageName, String ownerName)
             throws IllegalArgumentException, IllegalStateException {
@@ -2961,14 +2961,18 @@
     /**
      * @hide
      * Sets the given component as the profile owner of the given user profile. The package must
-     * already be installed and there shouldn't be an existing profile owner registered for this
-     * user. Only the system can call this API if the user has already completed setup.
+     * already be installed. There must not already be a profile owner for this user.
+     * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call
+     * this method.
+     * Calling this after the setup phase of the specified user has completed is allowed only if:
+     * - the caller is SYSTEM_UID.
+     * - or the caller is the shell uid, and there are no accounts on the specified user.
      * @param admin the component name to be registered as profile owner.
      * @param ownerName the human readable name of the organisation associated with this DPM.
      * @param userHandle the userId to set the profile owner for.
      * @return whether the component was successfully registered as the profile owner.
-     * @throws IllegalArgumentException if admin is null, the package isn't installed, or
-     *         the user has already been set up.
+     * @throws IllegalArgumentException if admin is null, the package isn't installed, or the
+     * preconditions mentioned are not met.
      */
     public boolean setProfileOwner(ComponentName admin, String ownerName, int userHandle)
             throws IllegalArgumentException {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 62685a1..dced051 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1309,6 +1309,14 @@
     <permission android:name="android.permission.MANAGE_USERS"
         android:protectionLevel="signature|system" />
 
+    <!-- @hide Allows an application to set the profile owners and the device owner.
+         This permission is not available to third party applications.-->
+    <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature"
+        android:label="@string/permlab_manageProfileAndDeviceOwners"
+        android:description="@string/permdesc_manageProfileAndDeviceOwners" />
+
     <!-- Allows an application to get full detailed information about
          recently running tasks, with full fidelity to the real state.
          @hide -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6f554f08..51c2062 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -699,6 +699,12 @@
        discover information about which applications are used on the device.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_manageProfileAndDeviceOwners">Manage profile and device owners</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to set the profile/device owners.
+     [CHAR LIMIT=NONE] -->
+    <string name="permdesc_manageProfileAndDeviceOwners">Allows apps to set the profile owners and the device owner.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_reorderTasks">reorder running apps</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_reorderTasks">Allows the app to move tasks to the
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index eb9234a..6fc3103 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3983,15 +3983,7 @@
                     + " for device owner");
         }
         synchronized (this) {
-            if (!allowedToSetDeviceOwnerOnDevice()) {
-                throw new IllegalStateException(
-                        "Trying to set device owner but device is already provisioned.");
-            }
-
-            if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()) {
-                throw new IllegalStateException(
-                        "Trying to set device owner but device owner is already set.");
-            }
+            enforceCanSetDeviceOwner();
 
             // Shutting down backup manager service permanently.
             long ident = Binder.clearCallingIdentity();
@@ -4009,7 +4001,7 @@
                 // Device owner is not set and does not exist, set it.
                 mDeviceOwner = DeviceOwner.createWithDeviceOwner(packageName, ownerName);
             } else {
-                // Device owner is not set but a profile owner exists, update Device owner state.
+                // Device owner state already exists, update it.
                 mDeviceOwner.setDeviceOwner(packageName, ownerName);
             }
             mDeviceOwner.writeOwnerFile();
@@ -4225,43 +4217,23 @@
         if (!mHasFeature) {
             return false;
         }
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
-
-        UserInfo info = mUserManager.getUserInfo(userHandle);
-        if (info == null) {
-            // User doesn't exist.
-            throw new IllegalArgumentException(
-                    "Attempted to set profile owner for invalid userId: " + userHandle);
-        }
-        if (info.isGuest()) {
-            throw new IllegalStateException("Cannot set a profile owner on a guest");
-        }
-
         if (who == null
                 || !DeviceOwner.isInstalledForUser(who.getPackageName(), userHandle)) {
             throw new IllegalArgumentException("Component " + who
                     + " not installed for userId:" + userHandle);
         }
         synchronized (this) {
-            // Only SYSTEM_UID can override the userSetupComplete
-            if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID
-                    && hasUserSetupCompleted(userHandle)) {
-                throw new IllegalStateException(
-                        "Trying to set profile owner but user is already set-up.");
-            }
-
+            enforceCanSetProfileOwner(userHandle);
             if (mDeviceOwner == null) {
                 // Device owner state does not exist, create it.
                 mDeviceOwner = DeviceOwner.createWithProfileOwner(who, ownerName,
                         userHandle);
-                mDeviceOwner.writeOwnerFile();
-                return true;
             } else {
-                // Device owner already exists, update it.
+                // Device owner state already exists, update it.
                 mDeviceOwner.setProfileOwner(who, ownerName, userHandle);
-                mDeviceOwner.writeOwnerFile();
-                return true;
             }
+            mDeviceOwner.writeOwnerFile();
+            return true;
         }
     }
 
@@ -4451,18 +4423,77 @@
     }
 
     /**
-     * Device owner can only be set on an unprovisioned device. However, if initiated via "adb",
-     * we also allow it if no accounts or additional users are present on the device.
+     * The profile owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
+     * permission.
+     * The profile owner can only be set before the user setup phase has completed,
+     * except for:
+     * - SYSTEM_UID
+     * - adb if there are not accounts.
      */
-    private boolean allowedToSetDeviceOwnerOnDevice() {
-        if (!hasUserSetupCompleted(UserHandle.USER_OWNER)) {
-            return true;
+    private void enforceCanSetProfileOwner(int userHandle) {
+        UserInfo info = mUserManager.getUserInfo(userHandle);
+        if (info == null) {
+            // User doesn't exist.
+            throw new IllegalArgumentException(
+                    "Attempted to set profile owner for invalid userId: " + userHandle);
         }
+        if (info.isGuest()) {
+            throw new IllegalStateException("Cannot set a profile owner on a guest");
+        }
+        if (getProfileOwner(userHandle) != null) {
+            throw new IllegalStateException("Trying to set the profile owner, but profile owner "
+                    + "is already set.");
+        }
+        int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+            if (hasUserSetupCompleted(userHandle) &&
+                    AccountManager.get(mContext).getAccountsAsUser(userHandle).length > 0) {
+                throw new IllegalStateException("Not allowed to set the profile owner because "
+                        + "there are already some accounts on the profile");
+            }
+            return;
+        }
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+        if (hasUserSetupCompleted(userHandle)
+                && UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
+            throw new IllegalStateException("Cannot set the profile owner on a user which is "
+                    + "already set-up");
+        }
+    }
 
-        int callingId = Binder.getCallingUid();
-        return (callingId == Process.SHELL_UID || callingId == Process.ROOT_UID)
-                && mUserManager.getUserCount() == 1
-                && AccountManager.get(mContext).getAccounts().length == 0;
+    /**
+     * The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
+     * permission.
+     * The device owner can only be set before the setup phase of the primary user has completed,
+     * except for adb if no accounts or additional users are present on the device.
+     */
+    private void enforceCanSetDeviceOwner() {
+        if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()) {
+            throw new IllegalStateException("Trying to set the device owner, but device owner "
+                    + "is already set.");
+        }
+        int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+            if (!hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+                return;
+            }
+            if (mUserManager.getUserCount() > 1) {
+                throw new IllegalStateException("Not allowed to set the device owner because there "
+                        + "are already several users on the device");
+            }
+            if (AccountManager.get(mContext).getAccounts().length > 0) {
+                throw new IllegalStateException("Not allowed to set the device owner because there "
+                        + "are already some accounts on the device");
+            }
+            return;
+        }
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+        if (hasUserSetupCompleted(UserHandle.USER_OWNER)) {
+            throw new IllegalStateException("Cannot set the device owner if the device is "
+                    + "already set-up");
+        }
     }
 
     private void enforceCrossUserPermission(int userHandle) {