diff --git a/core/res/res/xml/config_user_types.xml b/core/res/res/xml/config_user_types.xml
index 71dfc551ccc14..766315029f3d1 100644
--- a/core/res/res/xml/config_user_types.xml
+++ b/core/res/res/xml/config_user_types.xml
@@ -62,6 +62,10 @@ and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROF
+
+
@@ -78,6 +82,7 @@ Mandatory attributes:
Supported optional properties (to be used as shown in the example above) are as follows.
For profile and full users:
default-restrictions (with values defined in UserRestrictionUtils.USER_RESTRICTIONS)
+ enabled
For profile users only:
max-allowed-per-parent
icon-badge
@@ -98,6 +103,9 @@ If this file is updated, the properties of any pre-existing user types will be u
Note, however, that default-restrictions refers to the restrictions applied at the time of user
creation; therefore, the active restrictions of any pre-existing users will not be updated.
+If a user type is disabled, by setting enabled='0', then no further users of that type may be
+created; however, any pre-existing users of that type will remain.
+
The 'change-user-type' tag should be used in conjunction with the 'version' property of
'user-types'. It defines a type change for all pre-existing users of 'from' type to the new 'to'
type, if the former 'user-type's version of device is less than or equal to 'whenVersionLeq'.
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 89f2bfe4c64d7..11219c75a77db 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2352,6 +2352,9 @@ public class UserManagerService extends IUserManager.Stub {
* {@link #canAddMoreProfilesToUser}.
*/
private boolean canAddMoreUsersOfType(UserTypeDetails userTypeDetails) {
+ if (!userTypeDetails.isEnabled()) {
+ return false;
+ }
final int max = userTypeDetails.getMaxAllowed();
if (max == UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
return true; // Indicates that there is no max.
@@ -2392,7 +2395,7 @@ public class UserManagerService extends IUserManager.Stub {
boolean allowedToRemoveOne) {
checkManageUsersPermission("check if more profiles can be added.");
final UserTypeDetails type = mUserTypes.get(userType);
- if (type == null) {
+ if (type == null || !type.isEnabled()) {
return false;
}
// Managed profiles have their own specific rules.
@@ -3549,6 +3552,12 @@ public class UserManagerService extends IUserManager.Stub {
+ ") indicated SYSTEM user, which cannot be created.");
return null;
}
+ if (!userTypeDetails.isEnabled()) {
+ throwCheckedUserOperationException(
+ "Cannot add a user of disabled type " + userType + ".",
+ UserManager.USER_OPERATION_ERROR_MAX_USERS);
+ }
+
synchronized (mUsersLock) {
if (mForceEphemeralUsers) {
flags |= UserInfo.FLAG_EPHEMERAL;
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 92ec31b21e07a..24dab9ed73b99 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -50,7 +50,7 @@ public final class UserTypeDetails {
/** Name of the user type, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}. */
private final @NonNull String mName;
- // TODO(b/142482943): Currently unused. Hook this up.
+ /** Whether users of this type can be created. */
private final boolean mEnabled;
// TODO(b/142482943): Currently unused and not set. Hook this up.
@@ -195,7 +195,10 @@ public final class UserTypeDetails {
return mName;
}
- // TODO(b/142482943) Hook this up or delete it.
+ /**
+ * Returns whether this user type is enabled.
+ * If it is not enabled, all future attempts to create users of this type will be rejected.
+ */
public boolean isEnabled() {
return mEnabled;
}
@@ -390,7 +393,7 @@ public final class UserTypeDetails {
private @Nullable Bundle mDefaultSecureSettings = null;
private @Nullable List mDefaultCrossProfileIntentFilters =
null;
- private boolean mEnabled = true;
+ private int mEnabled = 1;
private int mLabel = Resources.ID_NULL;
private @Nullable int[] mBadgeLabels = null;
private @Nullable int[] mBadgeColors = null;
@@ -405,7 +408,7 @@ public final class UserTypeDetails {
return this;
}
- public Builder setEnabled(boolean enabled) {
+ public Builder setEnabled(int enabled) {
mEnabled = enabled;
return this;
}
@@ -522,12 +525,25 @@ public final class UserTypeDetails {
"UserTypeDetails %s has a non empty "
+ "defaultCrossProfileIntentFilters", mName);
}
- return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
- mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
- mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
+ return new UserTypeDetails(
+ mName,
+ mEnabled != 0,
+ mMaxAllowed,
+ mBaseType,
+ mDefaultUserInfoPropertyFlags,
+ mLabel,
+ mMaxAllowedPerParent,
+ mIconBadge,
+ mBadgePlain,
+ mBadgeNoBackground,
+ mBadgeLabels,
+ mBadgeColors,
mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
- mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
- mDefaultCrossProfileIntentFilters, mIsMediaSharedWithParent);
+ mDefaultRestrictions,
+ mDefaultSystemSettings,
+ mDefaultSecureSettings,
+ mDefaultCrossProfileIntentFilters,
+ mIsMediaSharedWithParent);
}
private boolean hasBadge() {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 30cb40c342035..5fcb843a26994 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -376,6 +376,8 @@ public final class UserTypeFactory {
setResAttribute(parser, "badge-no-background", builder::setBadgeNoBackground);
}
+ setIntAttribute(parser, "enabled", builder::setEnabled);
+
// Process child elements.
final int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index cd98d44075ca4..f1acc66798777 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -81,7 +81,7 @@ public class UserManagerServiceUserTypeTest {
/* letsPersonalDataIntoProfile= */false).build());
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
- .setEnabled(true)
+ .setEnabled(1)
.setMaxAllowed(21)
.setBaseType(FLAG_PROFILE)
.setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
@@ -316,6 +316,7 @@ public class UserManagerServiceUserTypeTest {
builders.put(userTypeFull, new UserTypeDetails.Builder()
.setName(userTypeFull)
.setBaseType(FLAG_FULL)
+ .setEnabled(0)
.setDefaultRestrictions(restrictions));
final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_full);
@@ -323,6 +324,7 @@ public class UserManagerServiceUserTypeTest {
UserTypeDetails details = builders.get(userTypeFull).createUserTypeDetails();
assertEquals(UNLIMITED_NUMBER_OF_USERS, details.getMaxAllowedPerParent());
+ assertFalse(details.isEnabled());
assertTrue(UserRestrictionsUtils.areEqual(
makeRestrictionsBundle("no_remove_user", "no_bluetooth"),
details.getDefaultRestrictions()));