Add method to tell the dpc if provisioning is allowed.

The DPC can use it to tell if provisioning a managed profile or for
device owner would work or not.

BUG:25338478
Change-Id: I09ea6a9f23a8e88e4ed37c048170b2a68213086e
diff --git a/api/current.txt b/api/current.txt
index 62f0113..6c64e28 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5741,6 +5741,7 @@
     method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
     method public boolean isProfileOwnerApp(java.lang.String);
+    method public boolean isProvisioningAllowed(java.lang.String);
     method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
     method public void lockNow();
     method public void removeActiveAdmin(android.content.ComponentName);
diff --git a/api/system-current.txt b/api/system-current.txt
index 4118f33..df63ecf 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5870,6 +5870,7 @@
     method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
     method public boolean isProfileOwnerApp(java.lang.String);
+    method public boolean isProvisioningAllowed(java.lang.String);
     method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
     method public void lockNow();
     method public void notifyPendingSystemUpdate(long);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0fdf3d3..6a3f8e6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4299,4 +4299,23 @@
             return PERMISSION_GRANT_STATE_DEFAULT;
         }
     }
+
+    /**
+     * Returns if provisioning a managed profile or device is possible or not.
+     * @param action One of {@link #ACTION_PROVISION_MANAGED_DEVICE},
+     * {@link #ACTION_PROVISION_MANAGED_PROFILE}.
+     * Note that even if this method returns true, there is a slight possibility that the
+     * provisioning will not be allowed when it is actually initiated because some event has
+     * happened in between.
+     * @return if provisioning a managed profile or device is possible or not.
+     * @throws IllegalArgumentException if the supplied action is not valid.
+     */
+    public boolean isProvisioningAllowed(String action) {
+        try {
+            return mService.isProvisioningAllowed(action);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Failed talking with device policy service", re);
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ccaa8cb..15e8837 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -227,4 +227,5 @@
     boolean setPermissionGrantState(in ComponentName admin, String packageName,
             String permission, int grantState);
     int getPermissionGrantState(in ComponentName admin, String packageName, String permission);
+    boolean isProvisioningAllowed(String action);
 }
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 12cac85..b5bbbbb 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -44,7 +44,7 @@
     UserInfo getPrimaryUser();
     List<UserInfo> getUsers(boolean excludeDying);
     List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
-    boolean canAddMoreManagedProfiles(int userId);
+    boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne);
     UserInfo getProfileParent(int userHandle);
     boolean isSameProfileGroup(int userId, int otherUserId);
     UserInfo getUserInfo(int userHandle);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index e892349..c191b23 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1077,13 +1077,15 @@
     /**
      * Checks whether it's possible to add more managed profiles. Caller must hold the MANAGE_USERS
      * permission.
+     * if allowedToRemoveOne is true and if the user already has a managed profile, then return if
+     * we could add a new managed profile to this user after removing the existing one.
      *
      * @return true if more managed profiles can be added, false if limit has been reached.
      * @hide
      */
-    public boolean canAddMoreManagedProfiles(int userId) {
+    public boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne) {
         try {
-            return mService.canAddMoreManagedProfiles(userId);
+            return mService.canAddMoreManagedProfiles(userId, allowedToRemoveOne);
         } catch (RemoteException re) {
             Log.w(TAG, "Could not check if we can add more managed profiles", re);
             return false;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c41d493..ebb69bf 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -853,7 +853,7 @@
     }
 
     @Override
-    public boolean canAddMoreManagedProfiles(int userId) {
+    public boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne) {
         checkManageUsersPermission("check if more managed profiles can be added.");
         if (ActivityManager.isLowRamDeviceStatic()) {
             return false;
@@ -863,8 +863,9 @@
             return false;
         }
         // Limit number of managed profiles that can be created
-        int managedProfilesCount = getProfiles(userId, true).size() - 1;
-        if (managedProfilesCount >= MAX_MANAGED_PROFILES) {
+        final int managedProfilesCount = getProfiles(userId, true).size() - 1;
+        final int profilesRemovedCount = managedProfilesCount > 0 && allowedToRemoveOne ? 1 : 0;
+        if (managedProfilesCount - profilesRemovedCount >= MAX_MANAGED_PROFILES) {
             return false;
         }
         synchronized(mUsersLock) {
@@ -872,9 +873,11 @@
             if (!userInfo.canHaveProfile()) {
                 return false;
             }
-            int usersCount = getAliveUsersExcludingGuestsCountLU();
+            int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
+                    - profilesRemovedCount;
             // We allow creating a managed profile in the special case where there is only one user.
-            return usersCount == 1 || usersCount < UserManager.getMaxSupportedUsers();
+            return usersCountAfterRemoving  == 1
+                    || usersCountAfterRemoving < UserManager.getMaxSupportedUsers();
         }
     }
 
@@ -1450,7 +1453,7 @@
                         }
                         if (parent == null) return null;
                     }
-                    if (isManagedProfile && !canAddMoreManagedProfiles(parentId)) {
+                    if (isManagedProfile && !canAddMoreManagedProfiles(parentId, false)) {
                         Log.e(LOG_TAG, "Cannot add more managed profiles for user " + parentId);
                         return null;
                     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index aea2ecf..cabebe7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1220,6 +1220,10 @@
             Settings.Secure.putInt(mContext.getContentResolver(), name, value);
         }
 
+        int settingsGlobalGetInt(String name, int def) {
+            return Settings.Global.getInt(mContext.getContentResolver(), name, def);
+        }
+
         void settingsGlobalPutInt(String name, int value) {
             Settings.Global.putInt(mContext.getContentResolver(), name, value);
         }
@@ -6622,4 +6626,43 @@
             throw new RuntimeException("Package manager has died", re);
         }
     }
+
+    @Override
+    public boolean isProvisioningAllowed(String action) {
+        if (mOwners.hasDeviceOwner()) {
+            return false;
+        }
+        final int callingUserId = mInjector.userHandleGetCallingUserId();
+        if (DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE.equals(action)) {
+            try {
+                if (!mIPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)) {
+                    return false;
+                }
+            } catch (RemoteException e) {
+                return false;
+            }
+            final long ident = mInjector.binderClearCallingIdentity();
+            try {
+                if (!mUserManager.canAddMoreManagedProfiles(callingUserId, true)) {
+                    return false;
+                }
+            } finally {
+                mInjector.binderRestoreCallingIdentity(ident);
+            }
+            return true;
+        } else if (DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE.equals(action)) {
+            if (getProfileOwner(callingUserId) != null) {
+                return false;
+            }
+            if (mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
+                return false;
+            }
+            if (callingUserId != UserHandle.USER_SYSTEM) {
+                // Device owner provisioning can only be initiated from system user.
+                return false;
+            }
+            return true;
+        }
+        throw new IllegalArgumentException("Unknown provisioning action " + action);
+    }
 }