diff --git a/core/api/system-current.txt b/core/api/system-current.txt index f9d802b16a791..947f00e897893 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -9637,6 +9637,8 @@ package android.os { method @NonNull public java.util.List getAllProfiles(); method @NonNull public java.util.List getEnabledProfiles(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableProfileCount(@NonNull String, boolean); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableUserCount(@NonNull String); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getRestrictedProfileParent(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions(); diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index fe86874da3da3..d9c9a2b55abd5 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -62,6 +62,8 @@ interface IUserManager { int[] getProfileIds(int userId, boolean enabledOnly); boolean isUserTypeEnabled(in String userType); boolean canAddMoreUsersOfType(in String userType); + int getRemainingCreatableUserCount(in String userType); + int getRemainingCreatableProfileCount(in String userType, int userId, boolean allowedToRemoveOne); boolean canAddMoreProfilesToUser(in String userType, int userId, boolean allowedToRemoveOne); boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne); UserInfo getProfileParent(int userId); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index f18c9c92889ed..37e7809b4bc80 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -4028,6 +4028,58 @@ public class UserManager { } } + /** + * Returns the remaining number of users of the given type that can be created. + * + * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_SECONDARY}. + * @return how many additional users can be created. + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS, + android.Manifest.permission.QUERY_USERS + }) + public int getRemainingCreatableUserCount(@NonNull String userType) { + Objects.requireNonNull(userType, "userType must not be null"); + try { + return mService.getRemainingCreatableUserCount(userType); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Returns the remaining number of profiles that can be added to the context user. + *

Note that is applicable to any profile type (currently not including Restricted profiles). + * + * @param userType the type of profile, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}. + * @param allowedToRemoveOne whether removing an existing profile of given type -if there is- + * from the context user to make up space should be taken into account + * for the calculation. + * @return how many additional profiles can be created. + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS, + android.Manifest.permission.QUERY_USERS + }) + @UserHandleAware + public int getRemainingCreatableProfileCount(@NonNull String userType, + boolean allowedToRemoveOne) { + Objects.requireNonNull(userType, "userType must not be null"); + try { + // TODO(b/142482943): Perhaps let the following code apply to restricted users too. + return mService.getRemainingCreatableProfileCount(userType, mUserId, + allowedToRemoveOne); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + /** * Checks whether it's possible to add more managed profiles. * if allowedToRemoveOne is true and if the user already has a managed profile, then return if @@ -4038,6 +4090,7 @@ public class UserManager { */ @RequiresPermission(anyOf = { android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS }) public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) { @@ -4057,6 +4110,7 @@ public class UserManager { */ @RequiresPermission(anyOf = { android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS }) public boolean canAddMoreProfilesToUser(@NonNull String userType, @UserIdInt int userId) { @@ -4931,8 +4985,8 @@ public class UserManager { public static int getMaxSupportedUsers() { // Don't allow multiple users on certain builds if (android.os.Build.ID.startsWith("JVP")) return 1; - return SystemProperties.getInt("fw.max_users", - Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers)); + return Math.max(1, SystemProperties.getInt("fw.max_users", + Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers))); } /** diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d6e88f40d05d1..b07cd1063ed2a 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2417,6 +2417,51 @@ public class UserManagerService extends IUserManager.Stub { return getNumberOfUsersOfType(userTypeDetails.getName()) < max; } + /** + * Returns the remaining number of users of the given type that can be created. (taking into + * account the total number of users on the device as well as how many exist of that type) + */ + @Override + public int getRemainingCreatableUserCount(String userType) { + checkQueryOrCreateUsersPermission("get the remaining number of users that can be added."); + final UserTypeDetails type = mUserTypes.get(userType); + if (type == null || !type.isEnabled()) { + return 0; + } + synchronized (mUsersLock) { + final int userCount = getAliveUsersExcludingGuestsCountLU(); + + // Limit total number of users that can be created (except for guest and demo) + int result = + UserManager.isUserTypeGuest(userType) || UserManager.isUserTypeDemo(userType) + ? Integer.MAX_VALUE + : (UserManager.getMaxSupportedUsers() - userCount); + + // Managed profiles have their own specific rules. + if (type.isManagedProfile()) { + if (!mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_MANAGED_USERS)) { + return 0; + } + // Special case: Allow creating a managed profile anyway if there's only 1 user + if (result <= 0 & userCount == 1) { + result = 1; + } + } + if (result <= 0) { + return 0; + } + + // Limit against max allowed for type + result = Math.min(result, + type.getMaxAllowed() == UserTypeDetails.UNLIMITED_NUMBER_OF_USERS + ? Integer.MAX_VALUE + : (type.getMaxAllowed() - getNumberOfUsersOfType(userType))); + + return Math.max(0, result); + } + } + /** * Gets the number of users of the given user type. * Does not include users that are about to die. @@ -2467,24 +2512,36 @@ public class UserManagerService extends IUserManager.Stub { @Override public boolean canAddMoreProfilesToUser(String userType, @UserIdInt int userId, boolean allowedToRemoveOne) { - checkQueryUsersPermission("check if more profiles can be added."); + return 0 < getRemainingCreatableProfileCount(userType, userId, allowedToRemoveOne); + } + + /** + * Returns the remaining number of profiles of the given type that can be added to the given + * user. (taking into account the total number of users on the device as well as how many + * profiles exist of that type both in general and for the given user) + */ + @Override + public int getRemainingCreatableProfileCount(@NonNull String userType, @UserIdInt int userId, + boolean allowedToRemoveOne) { + checkQueryOrCreateUsersPermission( + "get the remaining number of profiles that can be added to the given user."); final UserTypeDetails type = mUserTypes.get(userType); if (type == null || !type.isEnabled()) { - return false; + return 0; } // Managed profiles have their own specific rules. final boolean isManagedProfile = type.isManagedProfile(); if (isManagedProfile) { if (!mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_MANAGED_USERS)) { - return false; + return 0; } } synchronized (mUsersLock) { // Check if the parent exists and its type is even allowed to have a profile. UserInfo userInfo = getUserInfoLU(userId); if (userInfo == null || !userInfo.canHaveProfile()) { - return false; + return 0; } final int userTypeCount = getProfileIds(userId, userType, false).length; @@ -2493,23 +2550,30 @@ public class UserManagerService extends IUserManager.Stub { - profilesRemovedCount; // Limit total number of users that can be created - if (usersCountAfterRemoving >= UserManager.getMaxSupportedUsers()) { - // Special case: Allow creating a managed profile anyway if there's only 1 user - // Otherwise, disallow. - if (!(isManagedProfile && usersCountAfterRemoving == 1)) { - return false; - } + int result = UserManager.getMaxSupportedUsers() - usersCountAfterRemoving; + + // Special case: Allow creating a managed profile anyway if there's only 1 user + if (result <= 0 && isManagedProfile && usersCountAfterRemoving == 1) { + result = 1; } // Limit the number of profiles of this type that can be created. final int maxUsersOfType = getMaxUsersOfTypePerParent(type); if (maxUsersOfType != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) { - if (userTypeCount - profilesRemovedCount >= maxUsersOfType) { - return false; - } + result = Math.min(result, maxUsersOfType - (userTypeCount - profilesRemovedCount)); } + if (result <= 0) { + return 0; + } + + // Limit against max allowed for type (beyond max allowed per parent) + if (type.getMaxAllowed() != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) { + result = Math.min(result, type.getMaxAllowed() + - (getNumberOfUsersOfType(userType) - profilesRemovedCount)); + } + + return Math.max(0, result); } - return true; } @GuardedBy("mUsersLock") @@ -3791,6 +3855,7 @@ public class UserManagerService extends IUserManager.Stub { + ". Maximum number of that type already exists.", UserManager.USER_OPERATION_ERROR_MAX_USERS); } + // Keep logic in sync with getRemainingCreatableUserCount() if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) { // If the user limit has been reached, we cannot add a user (except guest/demo). // Note that managed profiles can bypass it in certain circumstances (taken