Introduce user types

Introduces the notion of 'user types' to UserManager.
Previously, users only had properties, set by UserInfo flags. We now
solidify some of these as formal user types. Each user must be of
precisely one user type. The configuration of each type is defined
in a UserTypeDetails object.

Prior 'types' - namely Guest, Restricted, ManagedProfiles, Demo, and
'normal' - that were set via UserInfo flags are now controlled via
UserType. Previous UserInfo flag methods for categorizing these are
still supported as long as no user of of more than one type.

If a user was (e.g.) previously both Guest and Demo, it is now
invalid and will be prevented from upgrading. This should not be a
problem in AOSP, where these 'types' were assumed mutually exclusive.

UserTypeDetails is sufficiently general to support future non-managed
profiles. To this end, the logic for specifying badges and their
colors/labels is contained by UserTypeDetails, and therefore
UserManager is responsible for delivering the corresponding badge
information.

Bug: 142151520

Test: added new test class UserManagerServiceUserTypeTest
Test: added additional tests in UserManagerServiceUserInfoTest
Test: added additional tests in UserManagerTest
Test: added additional tests in UserManagerServiceCreateProfileTest
Test: created UserTests to run the various pm.User*Test's

Test: atest \
  com.android.server.pm.UserTests \
  UserLifecycleTests \
  ManagedUserContentResolverTest \
  CtsDevicePolicyManagerTestCases \
  CtsMultiUserHostTestCases \
  com.android.server.devicepolicy.DevicePolicyManagerTest \
  com.android.server.am.UserControllerTest \
  CreateManagedProfileTaskTest

Test: atest com.android.server.pm.UserManagerServiceCreateProfileTest
   using adb shell getprop persist.sys.max_profiles # for 5 and -1

Change-Id: Id1183a76055e9a7610965cf40b256d1392afe6f8
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index e74e4a9..278a786 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -516,7 +516,7 @@
     /** Creates a managed (work) profile under the current user, returning its userId. */
     private int createManagedProfile() {
         final UserInfo userInfo = mUm.createProfileForUser("TestProfile",
-                UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
+                UserManager.USER_TYPE_PROFILE_MANAGED, /* flags */ 0, mAm.getCurrentUser());
         if (userInfo == null) {
             throw new IllegalStateException("Creating managed profile failed. Most likely there is "
                     + "already a pre-existing profile on the device.");
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 03ef286..9165a95 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -92,7 +92,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
-import android.util.IconDrawableFactory;
 import android.util.LauncherIcons;
 import android.util.Log;
 import android.view.Display;
@@ -1474,11 +1473,11 @@
 
     @Override
     public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) {
-        if (!isManagedProfile(user.getIdentifier())) {
+        if (!hasUserBadge(user.getIdentifier())) {
             return icon;
         }
         Drawable badge = new LauncherIcons(mContext).getBadgeDrawable(
-                com.android.internal.R.drawable.ic_corp_icon_badge_case,
+                getUserManager().getUserIconBadgeResId(user.getIdentifier()),
                 getUserBadgeColor(user));
         return getBadgedDrawable(icon, badge, null, true);
     }
@@ -1493,26 +1492,21 @@
         return getBadgedDrawable(drawable, badgeDrawable, badgeLocation, true);
     }
 
-    @VisibleForTesting
-    public static final int[] CORP_BADGE_LABEL_RES_ID = new int[] {
-        com.android.internal.R.string.managed_profile_label_badge,
-        com.android.internal.R.string.managed_profile_label_badge_2,
-        com.android.internal.R.string.managed_profile_label_badge_3
-    };
-
+    /** Returns the color of the user's actual badge (not the badge's shadow). */
     private int getUserBadgeColor(UserHandle user) {
-        return IconDrawableFactory.getUserBadgeColor(getUserManager(), user.getIdentifier());
+        return getUserManager().getUserBadgeColor(user.getIdentifier());
     }
 
     @Override
     public Drawable getUserBadgeForDensity(UserHandle user, int density) {
-        Drawable badgeColor = getManagedProfileIconForDensity(user,
+        // This is part of the shadow, not the main color, and is not actually corp-specific.
+        Drawable badgeColor = getProfileIconForDensity(user,
                 com.android.internal.R.drawable.ic_corp_badge_color, density);
         if (badgeColor == null) {
             return null;
         }
         Drawable badgeForeground = getDrawableForDensity(
-                com.android.internal.R.drawable.ic_corp_badge_case, density);
+                getUserManager().getUserBadgeResId(user.getIdentifier()), density);
         badgeForeground.setTint(getUserBadgeColor(user));
         Drawable badge = new LayerDrawable(new Drawable[] {badgeColor, badgeForeground });
         return badge;
@@ -1520,8 +1514,8 @@
 
     @Override
     public Drawable getUserBadgeForDensityNoBackground(UserHandle user, int density) {
-        Drawable badge = getManagedProfileIconForDensity(user,
-                com.android.internal.R.drawable.ic_corp_badge_no_background, density);
+        Drawable badge = getProfileIconForDensity(user,
+                getUserManager().getUserBadgeNoBackgroundResId(user.getIdentifier()), density);
         if (badge != null) {
             badge.setTint(getUserBadgeColor(user));
         }
@@ -1535,8 +1529,8 @@
         return mContext.getResources().getDrawableForDensity(drawableId, density);
     }
 
-    private Drawable getManagedProfileIconForDensity(UserHandle user, int drawableId, int density) {
-        if (isManagedProfile(user.getIdentifier())) {
+    private Drawable getProfileIconForDensity(UserHandle user, int drawableId, int density) {
+        if (hasUserBadge(user.getIdentifier())) {
             return getDrawableForDensity(drawableId, density);
         }
         return null;
@@ -1544,12 +1538,7 @@
 
     @Override
     public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) {
-        if (isManagedProfile(user.getIdentifier())) {
-            int badge = getUserManager().getManagedProfileBadge(user.getIdentifier());
-            int resourceId = CORP_BADGE_LABEL_RES_ID[badge % CORP_BADGE_LABEL_RES_ID.length];
-            return Resources.getSystem().getString(resourceId, label);
-        }
-        return label;
+        return getUserManager().getBadgedLabelForUser(label, user);
     }
 
     @Override
@@ -2865,8 +2854,8 @@
         return drawable;
     }
 
-    private boolean isManagedProfile(int userId) {
-        return getUserManager().isManagedProfile(userId);
+    private boolean hasUserBadge(int userId) {
+        return getUserManager().hasBadge(userId);
     }
 
     /**
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 1e88ce7..42d64d8 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -17,6 +17,7 @@
 package android.content.pm;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.UserIdInt;
 import android.os.Parcel;
@@ -25,6 +26,8 @@
 import android.os.UserManager;
 import android.util.DebugUtils;
 
+import com.android.server.pm.UserTypeDetails;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -32,13 +35,13 @@
  * Per-user information.
  *
  * <p>There are 3 base properties of users: {@link #FLAG_SYSTEM}, {@link #FLAG_FULL}, and
- * {@link #FLAG_MANAGED_PROFILE}. Every user must have one of the following combination of these
+ * {@link #FLAG_PROFILE}. Every user must have one of the following combination of these
  * flags:
  * <ul>
  *    <li>FLAG_SYSTEM (user {@link UserHandle#USER_SYSTEM} on a headless-user-0 device)</li>
  *    <li>FLAG_SYSTEM and FLAG_FULL (user {@link UserHandle#USER_SYSTEM} on a regular device)</li>
  *    <li>FLAG_FULL (non-profile secondary user)</li>
- *    <li>FLAG_MANAGED_PROFILE (profile users)</li>
+ *    <li>FLAG_PROFILE (profile users)</li>
  * </ul>
  * Users can have also have additional flags (such as FLAG_GUEST) as appropriate.
  *
@@ -70,13 +73,17 @@
 
     /**
      * Indicates a guest user that may be transient.
+     * @deprecated Use {@link UserManager#USER_TYPE_FULL_GUEST} instead.
      */
+    @Deprecated
     public static final int FLAG_GUEST   = 0x00000004;
 
     /**
      * Indicates the user has restrictions in privileges, in addition to those for normal users.
      * Exact meaning TBD. For instance, maybe they can't install apps or administer WiFi access pts.
+     * @deprecated Use {@link UserManager#USER_TYPE_FULL_RESTRICTED} instead.
      */
+    @Deprecated
     public static final int FLAG_RESTRICTED = 0x00000008;
 
     /**
@@ -87,7 +94,9 @@
     /**
      * Indicates that this user is a profile of another user, for example holding a users
      * corporate data.
+     * @deprecated Use {@link UserManager#USER_TYPE_PROFILE_MANAGED} instead.
      */
+    @Deprecated
     public static final int FLAG_MANAGED_PROFILE = 0x00000020;
 
     /**
@@ -108,14 +117,16 @@
 
     /**
      * User is for demo purposes only and can be removed at any time.
+     * @deprecated Use {@link UserManager#USER_TYPE_FULL_DEMO} instead.
      */
+    @Deprecated
     public static final int FLAG_DEMO = 0x00000200;
 
     /**
      * Indicates that this user is a non-profile human user.
      *
      * <p>When creating a new (non-system) user, this flag will always be forced true unless the
-     * user is a {@link #FLAG_MANAGED_PROFILE}. If user {@link UserHandle#USER_SYSTEM} is also a
+     * user is a {@link #FLAG_PROFILE}. If user {@link UserHandle#USER_SYSTEM} is also a
      * human user, it must also be flagged as FULL.
      */
     public static final int FLAG_FULL = 0x00000400;
@@ -126,11 +137,10 @@
     public static final int FLAG_SYSTEM = 0x00000800;
 
     /**
-     * Indicates that this user is some sort of profile. Right now, the only profile type is
-     * {@link #FLAG_MANAGED_PROFILE}, but this can include other types of profiles too if any
-     * are created in the future. This is therefore not a flag, but an OR of several flags.
+     * Indicates that this user is a profile human user, such as a managed profile.
+     * Mutually exclusive with {@link #FLAG_FULL}.
      */
-    public static final int PROFILE_FLAGS_MASK = FLAG_MANAGED_PROFILE;
+    public static final int FLAG_PROFILE = 0x00001000;
 
     /**
      * @hide
@@ -147,7 +157,8 @@
             FLAG_EPHEMERAL,
             FLAG_DEMO,
             FLAG_FULL,
-            FLAG_SYSTEM
+            FLAG_SYSTEM,
+            FLAG_PROFILE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UserInfoFlag {
@@ -170,6 +181,13 @@
     @UnsupportedAppUsage
     public long lastLoggedInTime;
     public String lastLoggedInFingerprint;
+
+    /**
+     * Type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}, corresponding to
+     * {@link UserTypeDetails#getName()}.
+     */
+    public String userType;
+
     /**
      * If this user is a parent user, it would be its own user id.
      * If this user is a child user, it would be its parent user id.
@@ -178,7 +196,12 @@
     @UnsupportedAppUsage
     public int profileGroupId;
     public int restrictedProfileParentId;
-    /** Which profile badge color/label to use. */
+
+    /**
+     * Which badge color/label to use within a particular {@link UserTypeDetails}, i.e.
+     * the badgeIndex.
+     * This is an index for distinguishing different profiles with the same parent and user type.
+     */
     public int profileBadge;
 
     /** User is only partially created. */
@@ -199,21 +222,68 @@
      */
     public boolean preCreated;
 
+    /**
+     * Creates a UserInfo whose user type is determined automatically by the flags according to
+     * {@link #getDefaultUserType}; can only be used for user types handled there.
+     */
     @UnsupportedAppUsage
     public UserInfo(int id, String name, int flags) {
         this(id, name, null, flags);
     }
 
+    /**
+     * Creates a UserInfo whose user type is determined automatically by the flags according to
+     * {@link #getDefaultUserType}; can only be used for user types handled there.
+     */
     @UnsupportedAppUsage
     public UserInfo(int id, String name, String iconPath, int flags) {
+        this(id, name, iconPath, flags, getDefaultUserType(flags));
+    }
+
+    public UserInfo(int id, String name, String iconPath, int flags, String userType) {
         this.id = id;
         this.name = name;
         this.flags = flags;
+        this.userType = userType;
         this.iconPath = iconPath;
         this.profileGroupId = NO_PROFILE_GROUP_ID;
         this.restrictedProfileParentId = NO_PROFILE_GROUP_ID;
     }
 
+    /**
+     * Get the user type (such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}) that corresponds to
+     * the given {@link UserInfoFlag}s.
+
+     * <p>The userInfoFlag can contain GUEST, RESTRICTED, MANAGED_PROFILE, DEMO, or else be
+     * interpreted as a regular "secondary" user. It cannot contain more than one of these.
+     * It can contain other UserInfoFlag properties (like EPHEMERAL), which will be ignored here.
+     *
+     * @throws IllegalArgumentException if userInfoFlag is more than one type of user or if it
+     *                                  is a SYSTEM user.
+     *
+     * @hide
+     */
+    public static @NonNull String getDefaultUserType(@UserInfoFlag int userInfoFlag) {
+        if ((userInfoFlag & FLAG_SYSTEM) != 0) {
+            throw new IllegalArgumentException("Cannot getDefaultUserType for flags "
+                    + Integer.toHexString(userInfoFlag) + " because it corresponds to a "
+                    + "SYSTEM user type.");
+        }
+        final int supportedFlagTypes =
+                FLAG_GUEST | FLAG_RESTRICTED | FLAG_MANAGED_PROFILE | FLAG_DEMO;
+        switch (userInfoFlag & supportedFlagTypes) {
+            case 0 :                   return UserManager.USER_TYPE_FULL_SECONDARY;
+            case FLAG_GUEST:           return UserManager.USER_TYPE_FULL_GUEST;
+            case FLAG_RESTRICTED:      return UserManager.USER_TYPE_FULL_RESTRICTED;
+            case FLAG_MANAGED_PROFILE: return UserManager.USER_TYPE_PROFILE_MANAGED;
+            case FLAG_DEMO:            return UserManager.USER_TYPE_FULL_DEMO;
+            default:
+                throw new IllegalArgumentException("Cannot getDefaultUserType for flags "
+                        + Integer.toHexString(userInfoFlag) + " because it doesn't correspond to a "
+                        + "valid user type.");
+        }
+    }
+
     @UnsupportedAppUsage
     public boolean isPrimary() {
         return (flags & FLAG_PRIMARY) == FLAG_PRIMARY;
@@ -226,31 +296,21 @@
 
     @UnsupportedAppUsage
     public boolean isGuest() {
-        return isGuest(flags);
-    }
-
-    /**
-     * Checks if the flag denotes a guest user.
-     */
-    public static boolean isGuest(@UserInfoFlag int flags) {
-        return (flags & FLAG_GUEST) == FLAG_GUEST;
+        return UserManager.isUserTypeGuest(userType);
     }
 
     @UnsupportedAppUsage
     public boolean isRestricted() {
-        return (flags & FLAG_RESTRICTED) == FLAG_RESTRICTED;
+        return UserManager.isUserTypeRestricted(userType);
+    }
+
+    public boolean isProfile() {
+        return (flags & FLAG_PROFILE) != 0;
     }
 
     @UnsupportedAppUsage
     public boolean isManagedProfile() {
-        return isManagedProfile(flags);
-    }
-
-    /**
-     * Checks if the flag denotes a managed profile.
-     */
-    public static boolean isManagedProfile(@UserInfoFlag int flags) {
-        return (flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE;
+        return UserManager.isUserTypeManagedProfile(userType);
     }
 
     @UnsupportedAppUsage
@@ -271,7 +331,7 @@
     }
 
     public boolean isDemo() {
-        return (flags & FLAG_DEMO) == FLAG_DEMO;
+        return UserManager.isUserTypeDemo(userType);
     }
 
     public boolean isFull() {
@@ -304,7 +364,7 @@
             // Don't support switching to an ephemeral user with removal in progress.
             return false;
         }
-        return !isManagedProfile();
+        return !isProfile();
     }
 
     /**
@@ -316,9 +376,10 @@
         return (!hideSystemUser || id != UserHandle.USER_SYSTEM) && supportsSwitchTo();
     }
 
+    // TODO(b/142482943): Make this logic more specific and customizable. (canHaveProfile(userType))
     /* @hide */
     public boolean canHaveProfile() {
-        if (isManagedProfile() || isGuest() || isRestricted()) {
+        if (isProfile() || isGuest() || isRestricted()) {
             return false;
         }
         if (UserManager.isSplitSystemUser() || UserManager.isHeadlessSystemUserMode()) {
@@ -336,6 +397,7 @@
         iconPath = orig.iconPath;
         id = orig.id;
         flags = orig.flags;
+        userType = orig.userType;
         serialNumber = orig.serialNumber;
         creationTime = orig.creationTime;
         lastLoggedInTime = orig.lastLoggedInTime;
@@ -353,6 +415,7 @@
         return UserHandle.of(id);
     }
 
+    // TODO(b/142482943): Probably include mUserType here, which means updating TestDevice, etc.
     @Override
     public String toString() {
         // NOTE:  do not change this string, it's used by 'pm list users', which in turn is
@@ -365,6 +428,7 @@
     public String toFullString() {
         return "UserInfo[id=" + id
                 + ", name=" + name
+                + ", type=" + userType
                 + ", flags=" + flagsToString(flags)
                 + (preCreated ? " (pre-created)" : "")
                 + (partial ? " (partial)" : "")
@@ -387,6 +451,7 @@
         dest.writeString(name);
         dest.writeString(iconPath);
         dest.writeInt(flags);
+        dest.writeString(userType);
         dest.writeInt(serialNumber);
         dest.writeLong(creationTime);
         dest.writeLong(lastLoggedInTime);
@@ -415,6 +480,7 @@
         name = source.readString();
         iconPath = source.readString();
         flags = source.readInt();
+        userType = source.readString();
         serialNumber = source.readInt();
         creationTime = source.readLong();
         lastLoggedInTime = source.readLong();
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index e8cc73f..40048d9 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -35,77 +35,84 @@
     /*
      * DO NOT MOVE - UserManager.h depends on the ordering of this function.
      */
-    int getCredentialOwnerProfile(int userHandle);
-    int getProfileParentId(int userHandle);
+    int getCredentialOwnerProfile(int userId);
+    int getProfileParentId(int userId);
     /*
      * END OF DO NOT MOVE
      */
 
-    UserInfo createUser(in String name, int flags);
-    UserInfo preCreateUser(int flags);
-    UserInfo createProfileForUser(in String name, int flags, int userHandle,
+    UserInfo createUser(in String name, in String userType, int flags);
+    UserInfo preCreateUser(in String userType);
+    UserInfo createProfileForUser(in String name, in String userType, int flags, int userId,
             in String[] disallowedPackages);
     UserInfo createRestrictedProfile(String name, int parentUserHandle);
-    void setUserEnabled(int userHandle);
+    void setUserEnabled(int userId);
     void setUserAdmin(int userId);
-    void evictCredentialEncryptionKey(int userHandle);
-    boolean removeUser(int userHandle);
-    boolean removeUserEvenWhenDisallowed(int userHandle);
-    void setUserName(int userHandle, String name);
-    void setUserIcon(int userHandle, in Bitmap icon);
-    ParcelFileDescriptor getUserIcon(int userHandle);
+    void evictCredentialEncryptionKey(int userId);
+    boolean removeUser(int userId);
+    boolean removeUserEvenWhenDisallowed(int userId);
+    void setUserName(int userId, String name);
+    void setUserIcon(int userId, in Bitmap icon);
+    ParcelFileDescriptor getUserIcon(int userId);
     UserInfo getPrimaryUser();
     List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated);
-    List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
+    List<UserInfo> getProfiles(int userId, boolean enabledOnly);
     int[] getProfileIds(int userId, boolean enabledOnly);
-    boolean canAddMoreManagedProfiles(int userHandle, boolean allowedToRemoveOne);
-    UserInfo getProfileParent(int userHandle);
-    boolean isSameProfileGroup(int userHandle, int otherUserHandle);
+    boolean canAddMoreProfilesToUser(in String userType, int userId, boolean allowedToRemoveOne);
+    boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne);
+    UserInfo getProfileParent(int userId);
+    boolean isSameProfileGroup(int userId, int otherUserHandle);
+    String getUserTypeForUser(int userId);
     @UnsupportedAppUsage
-    UserInfo getUserInfo(int userHandle);
-    String getUserAccount(int userHandle);
-    void setUserAccount(int userHandle, String accountName);
-    long getUserCreationTime(int userHandle);
+    UserInfo getUserInfo(int userId);
+    String getUserAccount(int userId);
+    void setUserAccount(int userId, String accountName);
+    long getUserCreationTime(int userId);
     boolean isRestricted();
-    boolean canHaveRestrictedProfile(int userHandle);
-    int getUserSerialNumber(int userHandle);
+    boolean canHaveRestrictedProfile(int userId);
+    int getUserSerialNumber(int userId);
     int getUserHandle(int userSerialNumber);
-    int getUserRestrictionSource(String restrictionKey, int userHandle);
-    List<UserManager.EnforcingUser> getUserRestrictionSources(String restrictionKey, int userHandle);
-    Bundle getUserRestrictions(int userHandle);
-    boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
-    boolean hasUserRestriction(in String restrictionKey, int userHandle);
+    int getUserRestrictionSource(String restrictionKey, int userId);
+    List<UserManager.EnforcingUser> getUserRestrictionSources(String restrictionKey, int userId);
+    Bundle getUserRestrictions(int userId);
+    boolean hasBaseUserRestriction(String restrictionKey, int userId);
+    boolean hasUserRestriction(in String restrictionKey, int userId);
     boolean hasUserRestrictionOnAnyUser(in String restrictionKey);
     boolean isSettingRestrictedForUser(in String setting, int userId, in String value, int callingUid);
     void addUserRestrictionsListener(IUserRestrictionsListener listener);
-    void setUserRestriction(String key, boolean value, int userHandle);
-    void setApplicationRestrictions(in String packageName, in Bundle restrictions,
-            int userHandle);
+    void setUserRestriction(String key, boolean value, int userId);
+    void setApplicationRestrictions(in String packageName, in Bundle restrictions, int userId);
     Bundle getApplicationRestrictions(in String packageName);
-    Bundle getApplicationRestrictionsForUser(in String packageName, int userHandle);
+    Bundle getApplicationRestrictionsForUser(in String packageName, int userId);
     void setDefaultGuestRestrictions(in Bundle restrictions);
     Bundle getDefaultGuestRestrictions();
-    boolean markGuestForDeletion(int userHandle);
-    boolean isQuietModeEnabled(int userHandle);
-    void setSeedAccountData(int userHandle, in String accountName,
+    boolean markGuestForDeletion(int userId);
+    boolean isQuietModeEnabled(int userId);
+    void setSeedAccountData(int userId, in String accountName,
             in String accountType, in PersistableBundle accountOptions, boolean persist);
     String getSeedAccountName();
     String getSeedAccountType();
     PersistableBundle getSeedAccountOptions();
     void clearSeedAccountData();
     boolean someUserHasSeedAccount(in String accountName, in String accountType);
+    boolean isProfile(int userId);
     boolean isManagedProfile(int userId);
     boolean isDemoUser(int userId);
     boolean isPreCreated(int userId);
-    UserInfo createProfileForUserEvenWhenDisallowed(in String name, int flags, int userHandle,
-            in String[] disallowedPackages);
+    UserInfo createProfileForUserEvenWhenDisallowed(in String name, in String userType, int flags,
+            int userId, in String[] disallowedPackages);
     boolean isUserUnlockingOrUnlocked(int userId);
-    int getManagedProfileBadge(int userId);
+    int getUserIconBadgeResId(int userId);
+    int getUserBadgeResId(int userId);
+    int getUserBadgeNoBackgroundResId(int userId);
+    int getUserBadgeLabelResId(int userId);
+    int getUserBadgeColorResId(int userId);
+    boolean hasBadge(int userId);
     boolean isUserUnlocked(int userId);
     boolean isUserRunning(int userId);
-    boolean isUserNameSet(int userHandle);
+    boolean isUserNameSet(int userId);
     boolean hasRestrictedProfiles();
-    boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userHandle, in IntentSender target);
+    boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target);
     String getUserName();
     long getUserStartRealtime();
     long getUserUnlockRealtime();
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 71b94ed..79e6ac2 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -18,6 +18,8 @@
 
 import android.Manifest;
 import android.accounts.AccountManager;
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -76,6 +78,57 @@
     private final Context mContext;
 
     private Boolean mIsManagedProfileCached;
+    private Boolean mIsProfileCached;
+
+    /**
+     * User type representing a {@link UserHandle#USER_SYSTEM system} user that is a human user.
+     * This type of user cannot be created; it can only pre-exist on first boot.
+     * @hide
+     */
+    public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
+
+    /**
+     * User type representing a regular non-profile non-{@link UserHandle#USER_SYSTEM system} human
+     * user.
+     * This is sometimes called an ordinary 'secondary user'.
+     * @hide
+     */
+    public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY";
+
+    /**
+     * User type representing a guest user that may be transient.
+     * @hide
+     */
+    public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST";
+
+    /**
+     * User type representing a user for demo purposes only, which can be removed at any time.
+     * @hide
+     */
+    public static final String USER_TYPE_FULL_DEMO = "android.os.usertype.full.DEMO";
+
+    /**
+     * User type representing a "restricted profile" user, which is a full user that is subject to
+     * certain restrictions from a parent user. Note, however, that it is NOT technically a profile.
+     * @hide
+     */
+    public static final String USER_TYPE_FULL_RESTRICTED = "android.os.usertype.full.RESTRICTED";
+
+    /**
+     * User type representing a managed profile, which is a profile that is to be managed by a
+     * device policy controller (DPC).
+     * The intended purpose is for work profiles, which are managed by a corporate entity.
+     * @hide
+     */
+    public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED";
+
+    /**
+     * User type representing a {@link UserHandle#USER_SYSTEM system} user that is <b>not</b> a
+     * human user.
+     * This type of user cannot be created; it can only pre-exist on first boot.
+     * @hide
+     */
+    public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS";
 
     /**
      * @hide
@@ -1454,6 +1507,79 @@
     }
 
     /**
+     * Returns the calling user's user type.
+     *
+     * // TODO(b/142482943): Decide on the appropriate permission requirements.
+     *
+     * @return the name of the user type, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @hide
+     */
+    public @NonNull String getUserType() {
+        try {
+            return mService.getUserTypeForUser(UserHandle.myUserId());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the given user's user type.
+     *
+     * // TODO(b/142482943): Decide on the appropriate permission requirements.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
+     * must be in the same profile group of specified user.
+     *
+     * @param userHandle the user handle of the user whose type is being requested.
+     * @return the name of the user's user type, e.g. {@link UserManager#USER_TYPE_PROFILE_MANAGED},
+     *         or {@code null} if there is no such user.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public @Nullable String getUserTypeForUser(@NonNull UserHandle userHandle) {
+        try {
+            return mService.getUserTypeForUser(userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the user type is a
+     * {@link UserManager#USER_TYPE_PROFILE_MANAGED managed profile}.
+     * @hide
+     */
+    public static boolean isUserTypeManagedProfile(String userType) {
+        return USER_TYPE_PROFILE_MANAGED.equals(userType);
+    }
+
+    /**
+     * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_GUEST guest user}.
+     * @hide
+     */
+    public static boolean isUserTypeGuest(String userType) {
+        return USER_TYPE_FULL_GUEST.equals(userType);
+    }
+
+    /**
+     * Returns whether the user type is a
+     * {@link UserManager#USER_TYPE_FULL_RESTRICTED restricted user}.
+     * @hide
+     */
+    public static boolean isUserTypeRestricted(String userType) {
+        return USER_TYPE_FULL_RESTRICTED.equals(userType);
+    }
+
+    /**
+     * Returns whether the user type is a {@link UserManager#USER_TYPE_FULL_DEMO demo user}.
+     * @hide
+     */
+    public static boolean isUserTypeDemo(String userType) {
+        return USER_TYPE_FULL_DEMO.equals(userType);
+    }
+
+    /**
      * @hide
      * @deprecated Use {@link #isRestrictedProfile()}
      */
@@ -1564,6 +1690,48 @@
     }
 
     /**
+     * Checks if the calling app is running in a profile.
+     *
+     * @return whether the caller is in a profile.
+     * @hide
+     */
+    public boolean isProfile() {
+        // No need for synchronization.  Once it becomes non-null, it'll be non-null forever.
+        // Worst case we might end up calling the AIDL method multiple times but that's fine.
+        if (mIsProfileCached != null) {
+            return mIsProfileCached;
+        }
+        try {
+            mIsProfileCached = mService.isProfile(UserHandle.myUserId());
+            return mIsProfileCached;
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Checks if the specified user is a profile.
+     *
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
+     * must be in the same profile group of specified user.
+     *
+     * @return whether the specified user is a profile.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+    public boolean isProfile(@UserIdInt int userId) {
+        if (userId == UserHandle.myUserId()) {
+            return isProfile();
+        }
+        try {
+            return mService.isProfile(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+    /**
      * Checks if the calling app is running in a managed profile.
      *
      * @return whether the caller is in a managed profile.
@@ -1587,7 +1755,8 @@
 
     /**
      * Checks if the specified user is a managed profile.
-     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission, otherwise the caller
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
      * must be in the same profile group of specified user.
      *
      * @return whether the specified user is a managed profile.
@@ -1607,23 +1776,6 @@
     }
 
     /**
-     * Gets badge for a managed profile.
-     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission, otherwise the caller
-     * must be in the same profile group of specified user.
-     *
-     * @return which badge to use for the managed profile badge id will be less than
-     *         UserManagerService.getMaxManagedProfiles()
-     * @hide
-     */
-    public int getManagedProfileBadge(@UserIdInt int userId) {
-        try {
-            return mService.getManagedProfileBadge(userId);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Checks if the calling app is running as an ephemeral user.
      *
      * @return whether the caller is an ephemeral user.
@@ -2068,23 +2220,44 @@
 
     /**
      * Creates a user with the specified name and options. For non-admin users, default user
+     * restrictions are going to be applied.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @param name the user's name
+     * @param flags UserInfo flags that identify the type of user and other properties.
+     * @see UserInfo
+     *
+     * @return the UserInfo object for the created user, or null if the user could not be created.
+     * @throws IllegalArgumentException if flags do not correspond to a valid user type.
+     * @deprecated Use {@link #createUser(String, String, int)} instead.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
+        return createUser(name, UserInfo.getDefaultUserType(flags), flags);
+    }
+
+    /**
+     * Creates a user with the specified name and options. For non-admin users, default user
      * restrictions will be applied.
      *
      * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
      * @param name the user's name
-     * @param flags UserInfo flags that identify the type of user and other properties.
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
+     * @param flags UserInfo flags that specify user properties.
      * @see UserInfo
      *
      * @return the UserInfo object for the created user, or {@code null} if the user could not be
      * created.
      * @hide
      */
-    @UnsupportedAppUsage
-    public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
+    public @Nullable UserInfo createUser(@Nullable String name, @NonNull String userType,
+            @UserInfoFlag int flags) {
         UserInfo user = null;
         try {
-            user = mService.createUser(name, flags);
+            user = mService.createUser(name, userType, flags);
             // TODO: Keep this in sync with
             // UserManagerService.LocalService.createUserEvenWhenDisallowed
             if (user != null && !user.isAdmin() && !user.isDemo()) {
@@ -2098,19 +2271,17 @@
     }
 
     /**
-     * Pre-creates a user with the specified name and options. For non-admin users, default user
+     * Pre-creates a user of the specified type. For non-admin users, default user
      * restrictions will be applied.
      *
      * <p>This method can be used by OEMs to "warm" up the user creation by pre-creating some users
      * at the first boot, so they when the "real" user is created (for example,
-     * by {@link #createUser(String, int)} or {@link #createGuest(Context, String)}), it takes
-     * less time.
+     * by {@link #createUser(String, String, int)} or {@link #createGuest(Context, String)}), it
+     * takes less time.
      *
      * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
-     * @param flags UserInfo flags that identify the type of user and other properties.
-     * @see UserInfo
-     *
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
      * @return the UserInfo object for the created user, or {@code null} if the user could not be
      * created.
      *
@@ -2119,9 +2290,9 @@
      *
      * @hide
      */
-    public @Nullable UserInfo preCreateUser(@UserInfoFlag int flags) {
+    public @Nullable UserInfo preCreateUser(@NonNull String userType) {
         try {
-            return mService.preCreateUser(flags);
+            return mService.preCreateUser(userType);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -2136,7 +2307,7 @@
     public UserInfo createGuest(Context context, String name) {
         UserInfo guest = null;
         try {
-            guest = mService.createUser(name, UserInfo.FLAG_GUEST);
+            guest = mService.createUser(name, USER_TYPE_FULL_GUEST, 0);
             if (guest != null) {
                 Settings.Secure.putStringForUser(context.getContentResolver(),
                         Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
@@ -2150,6 +2321,7 @@
     /**
      * Creates a user with the specified name and options as a profile of another user.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * The type of profile must be specified using the given flags.
      *
      * @param name the user's name
      * @param flags flags that identify the type of user and other properties.
@@ -2157,20 +2329,44 @@
      *
      * @return the {@link UserInfo} object for the created user, or null if the user
      *         could not be created.
+     * @throws IllegalArgumentException if flags do not correspond to a valid user type.
+     * @deprecated Use {@link #createProfileForUser(String, String, int, int)} instead.
      * @hide
      */
     @UnsupportedAppUsage
-    public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userId) {
-        return createProfileForUser(name, flags, userId, null);
+    @Deprecated
+    public UserInfo createProfileForUser(String name, @UserInfoFlag int flags,
+            @UserIdInt int userId) {
+        return createProfileForUser(name, UserInfo.getDefaultUserType(flags), flags,
+                userId, null);
     }
 
     /**
-     * Version of {@link #createProfileForUser(String, int, int)} that allows you to specify
+     * Creates a user with the specified name and options as a profile of another user.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @param name the user's name
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @param flags UserInfo flags that specify user properties.
+     * @param userId new user will be a profile of this user.
+     *
+     * @return the {@link UserInfo} object for the created user, or null if the user
+     *         could not be created.
+     * @hide
+     */
+    public UserInfo createProfileForUser(String name, @NonNull String userType,
+            @UserInfoFlag int flags, @UserIdInt int userId) {
+        return createProfileForUser(name, userType, flags, userId, null);
+    }
+
+    /**
+     * Version of {@link #createProfileForUser(String, String, int, int)} that allows you to specify
      * any packages that should not be installed in the new profile by default, these packages can
      * still be installed later by the user if needed.
      *
      * @param name the user's name
-     * @param flags flags that identify the type of user and other properties.
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @param flags UserInfo flags that specify user properties.
      * @param userId new user will be a profile of this user.
      * @param disallowedPackages packages that will not be installed in the profile being created.
      *
@@ -2178,28 +2374,29 @@
      *         could not be created.
      * @hide
      */
-    public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userId,
-            String[] disallowedPackages) {
+    public UserInfo createProfileForUser(String name, @NonNull String userType,
+            @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages) {
         try {
-            return mService.createProfileForUser(name, flags, userId, disallowedPackages);
+            return mService.createProfileForUser(name, userType, flags, userId, disallowedPackages);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Similar to {@link #createProfileForUser(String, int, int, String[])}
+     * Similar to {@link #createProfileForUser(String, String, int, int, String[])}
      * except bypassing the checking of {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
-     * @see #createProfileForUser(String, int, int, String[])
+     * @see #createProfileForUser(String, String, int, int, String[])
      * @hide
      */
-    public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags,
-            @UserIdInt int userId, String[] disallowedPackages) {
+    public UserInfo createProfileForUserEvenWhenDisallowed(String name,
+            @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId,
+            String[] disallowedPackages) {
         try {
-            return mService.createProfileForUserEvenWhenDisallowed(name, flags, userId,
-                    disallowedPackages);
+            return mService.createProfileForUserEvenWhenDisallowed(name, userType, flags,
+                    userId, disallowedPackages);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -2541,6 +2738,8 @@
      * @hide
      */
     public boolean canAddMoreUsers() {
+        // TODO(b/142482943): UMS has different logic, excluding Demo and Profile from counting. Why
+        //                    not here? The logic is inconsistent. See UMS.canAddMoreManagedProfiles
         final List<UserInfo> users = getUsers(true);
         final int totalUserCount = users.size();
         int aliveUserCount = 0;
@@ -2571,6 +2770,22 @@
     }
 
     /**
+     * Checks whether it's possible to add more profiles of the given type to the given user.
+     *
+     * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @return true if more profiles can be added, false if limit has been reached.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public boolean canAddMoreProfilesToUser(@NonNull String userType, @UserIdInt int userId) {
+        try {
+            return mService.canAddMoreProfilesToUser(userType, userId, false);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns list of the profiles of userId including userId itself.
      * Note that this returns both enabled and not enabled profiles. See
      * {@link #getEnabledProfiles(int)} if you need only the enabled ones.
@@ -2804,8 +3019,112 @@
     }
 
     /**
-     * If the target user is a managed profile of the calling user or the caller
-     * is itself a managed profile, then this returns a badged copy of the given
+     * Returns whether the given user has a badge (generally to put on profiles' icons).
+     *
+     * @param userId userId of the user in question
+     * @return true if the user's icons should display a badge; false otherwise.
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public boolean hasBadge(@UserIdInt int userId) {
+        if (!isProfile(userId)) {
+            // Since currently only profiles actually have badges, we can do this optimization.
+            return false;
+        }
+        try {
+            return mService.hasBadge(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the calling app's user has a badge (generally to put on profiles' icons).
+     *
+     * @return true if the user's icons should display a badge; false otherwise.
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public boolean hasBadge() {
+        return hasBadge(UserHandle.myUserId());
+    }
+
+    /**
+     * Returns the badge color for the given user (generally to color a profile's icon's badge).
+     *
+     * <p>To check whether a badge color is expected for the user, first call {@link #hasBadge}.
+     *
+     * @return the color (not the resource ID) to be used for the user's badge
+     * @throws Resources.NotFoundException if no valid badge color exists for this user
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public @ColorInt int getUserBadgeColor(@UserIdInt int userId) {
+        try {
+            final int resourceId = mService.getUserBadgeColorResId(userId);
+            return Resources.getSystem().getColor(resourceId, null);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the Resource ID of the user's icon badge.
+     *
+     * @return the Resource ID of the user's icon badge if it has one; otherwise
+     *         {@link Resources#ID_NULL}.
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public @DrawableRes int getUserIconBadgeResId(@UserIdInt int userId) {
+        try {
+            return mService.getUserIconBadgeResId(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the Resource ID of the user's badge.
+     *
+     * @return the Resource ID of the user's badge if it has one; otherwise
+     *         {@link Resources#ID_NULL}.
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public @DrawableRes int getUserBadgeResId(@UserIdInt int userId) {
+        try {
+            return mService.getUserBadgeResId(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the Resource ID of the user's badge without a background.
+     *
+     * @return the Resource ID of the user's no-background badge if it has one; otherwise
+     *         {@link Resources#ID_NULL}.
+     *
+     * @see #getBadgedIconForUser more information about badging in general
+     * @hide
+     */
+    public @DrawableRes int getUserBadgeNoBackgroundResId(@UserIdInt int userId) {
+        try {
+            return mService.getUserBadgeNoBackgroundResId(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * If the target user is a profile of the calling user or the caller
+     * is itself a profile, then this returns a badged copy of the given
      * icon to be able to distinguish it from the original icon. For badging an
      * arbitrary drawable use {@link #getBadgedDrawableForUser(
      * android.graphics.drawable.Drawable, UserHandle, android.graphics.Rect, int)}.
@@ -2826,8 +3145,8 @@
     }
 
     /**
-     * If the target user is a managed profile of the calling user or the caller
-     * is itself a managed profile, then this returns a badged copy of the given
+     * If the target user is a profile of the calling user or the caller
+     * is itself a profile, then this returns a badged copy of the given
      * drawable allowing the user to distinguish it from the original drawable.
      * The caller can specify the location in the bounds of the drawable to be
      * badged where the badge should be applied as well as the density of the
@@ -2857,11 +3176,15 @@
     }
 
     /**
-     * If the target user is a managed profile of the calling user or the caller
-     * is itself a managed profile, then this returns a copy of the label with
+     * If the target user is a profile of the calling user or the caller
+     * is itself a profile, then this returns a copy of the label with
      * badging for accessibility services like talkback. E.g. passing in "Email"
      * and it might return "Work Email" for Email in the work profile.
      *
+     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
+     * must be in the same profile group of specified user.
+     *
      * @param label The label to change.
      * @param user The target user.
      * @return A label that combines the original label and a badge as
@@ -2869,7 +3192,16 @@
      * @removed
      */
     public CharSequence getBadgedLabelForUser(CharSequence label, UserHandle user) {
-        return mContext.getPackageManager().getUserBadgedLabel(label, user);
+        final int userId = user.getIdentifier();
+        if (!hasBadge(userId)) {
+            return label;
+        }
+        try {
+            final int resourceId = mService.getUserBadgeLabelResId(userId);
+            return Resources.getSystem().getString(resourceId, label);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
     }
 
     /**
diff --git a/core/java/android/util/IconDrawableFactory.java b/core/java/android/util/IconDrawableFactory.java
index d90b65e..d86ebf3 100644
--- a/core/java/android/util/IconDrawableFactory.java
+++ b/core/java/android/util/IconDrawableFactory.java
@@ -26,8 +26,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 /**
  * Utility class to load app drawables with appropriate badging.
  *
@@ -78,10 +76,10 @@
                     com.android.internal.R.drawable.ic_instant_icon_badge_bolt,
                     badgeColor);
         }
-        if (mUm.isManagedProfile(userId)) {
+        if (mUm.hasBadge(userId)) {
             icon = mLauncherIcons.getBadgedDrawable(icon,
-                    com.android.internal.R.drawable.ic_corp_icon_badge_case,
-                    getUserBadgeColor(mUm, userId));
+                    mUm.getUserIconBadgeResId(userId),
+                    mUm.getUserBadgeColor(userId));
         }
         return icon;
     }
@@ -93,23 +91,6 @@
         return mLauncherIcons.wrapIconDrawableWithShadow(icon);
     }
 
-    // Should have enough colors to cope with UserManagerService.getMaxManagedProfiles()
-    @VisibleForTesting
-    public static final int[] CORP_BADGE_COLORS = new int[] {
-            com.android.internal.R.color.profile_badge_1,
-            com.android.internal.R.color.profile_badge_2,
-            com.android.internal.R.color.profile_badge_3
-    };
-
-    public static int getUserBadgeColor(UserManager um, @UserIdInt int userId) {
-        int badge = um.getManagedProfileBadge(userId);
-        if (badge < 0) {
-            badge = 0;
-        }
-        int resourceId = CORP_BADGE_COLORS[badge % CORP_BADGE_COLORS.length];
-        return Resources.getSystem().getColor(resourceId, null);
-    }
-
     @UnsupportedAppUsage
     public static IconDrawableFactory newInstance(Context context) {
         return new IconDrawableFactory(context, true);
diff --git a/core/java/android/util/LauncherIcons.java b/core/java/android/util/LauncherIcons.java
index 8501eb5..e652e17 100644
--- a/core/java/android/util/LauncherIcons.java
+++ b/core/java/android/util/LauncherIcons.java
@@ -106,9 +106,11 @@
         Resources overlayableRes =
                 ActivityThread.currentActivityThread().getApplication().getResources();
 
+        // ic_corp_icon_badge_shadow is not work-profile-specific.
         Drawable badgeShadow = overlayableRes.getDrawable(
                 com.android.internal.R.drawable.ic_corp_icon_badge_shadow);
 
+        // ic_corp_icon_badge_color is not work-profile-specific.
         Drawable badgeColor = overlayableRes.getDrawable(
                 com.android.internal.R.drawable.ic_corp_icon_badge_color)
                 .getConstantState().newDrawable().mutate();
diff --git a/core/java/com/android/server/pm/UserTypeDetails.java b/core/java/com/android/server/pm/UserTypeDetails.java
new file mode 100644
index 0000000..5fc3ba1
--- /dev/null
+++ b/core/java/com/android/server/pm/UserTypeDetails.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.ColorRes;
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.StringRes;
+import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
+import android.content.res.Resources;
+import android.os.UserManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Contains the details about a multiuser "user type", such as a
+ * {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ *
+ * Tests are located in UserManagerServiceUserTypeTest.java.
+ * @hide
+ */
+public final class UserTypeDetails {
+
+    /** Indicates that there is no limit to the number of users allowed. */
+    public static final int UNLIMITED_NUMBER_OF_USERS = -1;
+
+    /** 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.
+    private final boolean mEnabled;
+
+    // TODO(b/142482943): Currently unused and not set. Hook this up.
+    private final int mLabel;
+
+    /**
+     * Maximum number of this user type allowed on the device.
+     * Use {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
+     */
+    private final int mMaxAllowed;
+
+    /**
+     * Maximum number of this user type allowed per parent (for user types, like profiles, that
+     * have parents).
+     * Use {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
+     */
+    // TODO(b/142482943): Should this also apply to restricted profiles?
+    private final int mMaxAllowedPerParent;
+
+    // TODO(b/143784345): Update doc when we clean up UserInfo.
+    /** The {@link UserInfo.UserInfoFlag} representing the base type of this user. */
+    private final @UserInfoFlag int mBaseType;
+
+    // TODO(b/143784345): Update doc/name when we clean up UserInfo.
+    /** The {@link UserInfo.UserInfoFlag}s that all users of this type will automatically have. */
+    private final @UserInfoFlag int mDefaultUserInfoPropertyFlags;
+
+    // TODO(b/142482943): Hook these up to something and set them for each type.
+    private final List<String> mDefaultRestrictions;
+
+
+    // Fields for profiles only, controlling the nature of their badges.
+    // All badge information should be set if {@link #hasBadge()} is true.
+
+    /** Resource ID of the badge put on icons. */
+    private @DrawableRes final int mIconBadge;
+    /** Resource ID of the badge. Should be set if mIconBadge is set. */
+    private @DrawableRes final int mBadgePlain;
+    /** Resource ID of the badge without a background. Should be set if mIconBadge is set. */
+    private @DrawableRes final int mBadgeNoBackground;
+
+    /**
+     * Resource ID ({@link StringRes}) of the of the labels to describe badged apps; should be the
+     * same format as com.android.internal.R.color.profile_badge_1. These are used for accessibility
+     * services.
+     *
+     * <p>This is an array because, in general, there may be multiple users of the same user type.
+     * In this case, the user is indexed according to its {@link UserInfo#profileBadge}.
+     *
+     * <p>Must be set if mIconBadge is set.
+     */
+    private final int[] mBadgeLabels;
+
+    /**
+     * Resource ID ({@link ColorRes}) of the colors badge put on icons.
+     * (The value is a resource ID referring to the color; it is not the color value itself).
+     *
+     * <p>This is an array because, in general, there may be multiple users of the same user type.
+     * In this case, the user is indexed according to its {@link UserInfo#profileBadge}.
+     *
+     * <p>Must be set if mIconBadge is set.
+     */
+    private final int[] mBadgeColors;
+
+    private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
+            @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
+            int maxAllowedPerParent,
+            int iconBadge, int badgePlain, int badgeNoBackground,
+            int[] badgeLabels, int[] badgeColors,
+            ArrayList<String> defaultRestrictions) {
+        this.mName = name;
+        this.mEnabled = enabled;
+        this.mMaxAllowed = maxAllowed;
+        this.mMaxAllowedPerParent = maxAllowedPerParent;
+        this.mBaseType = baseType;
+        this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags;
+        this.mDefaultRestrictions =
+                Collections.unmodifiableList(new ArrayList<>(defaultRestrictions));
+
+        this.mIconBadge = iconBadge;
+        this.mBadgePlain = badgePlain;
+        this.mBadgeNoBackground = badgeNoBackground;
+        this.mLabel = label;
+        this.mBadgeLabels = badgeLabels;
+        this.mBadgeColors = badgeColors;
+    }
+
+    /**
+     * Returns the name of the user type, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    // TODO(b/142482943) Hook this up or delete it.
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    /**
+     * Returns the maximum number of this user type allowed on the device.
+     * <p>Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
+     */
+    public int getMaxAllowed() {
+        return mMaxAllowed;
+    }
+
+    /**
+     * Returns the maximum number of this user type allowed per parent (for user types, like
+     * profiles, that have parents).
+     * <p>Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
+     */
+    public int getMaxAllowedPerParent() {
+        return mMaxAllowedPerParent;
+    }
+
+    // TODO(b/143784345): Update comment when UserInfo is reorganized.
+    /** The {@link UserInfo.UserInfoFlag}s that all users of this type will automatically have. */
+    public int getDefaultUserInfoFlags() {
+        return mDefaultUserInfoPropertyFlags | mBaseType;
+    }
+
+    // TODO(b/142482943) Hook this up; it is currently unused.
+    public int getLabel() {
+        return mLabel;
+    }
+
+    /** Returns whether users of this user type should be badged. */
+    public boolean hasBadge() {
+        return mIconBadge != Resources.ID_NULL;
+    }
+
+    /** Resource ID of the badge put on icons. */
+    public @DrawableRes int getIconBadge() {
+        return mIconBadge;
+    }
+
+    /** Resource ID of the badge. Used for {@link UserManager#getUserBadgeResId(int)}. */
+    public @DrawableRes int getBadgePlain() {
+        return mBadgePlain;
+    }
+
+    /** Resource ID of the badge without a background. */
+    public @DrawableRes int getBadgeNoBackground() {
+        return mBadgeNoBackground;
+    }
+
+    /**
+     * Returns the Resource ID of the badgeIndexth badge label, where the badgeIndex is expected
+     * to be the {@link UserInfo#profileBadge} of the user.
+     * If badgeIndex exceeds the number of labels, returns the label for the highest index.
+     */
+    public @StringRes int getBadgeLabel(int badgeIndex) {
+        if (mBadgeLabels == null || mBadgeLabels.length == 0 || badgeIndex < 0) {
+            return Resources.ID_NULL;
+        }
+        return mBadgeLabels[Math.min(badgeIndex, mBadgeLabels.length - 1)];
+    }
+
+    /**
+     * Returns the Resource ID of the badgeIndexth badge color, where the badgeIndex is expected
+     * to be the {@link UserInfo#profileBadge} of the user.
+     * If badgeIndex exceeds the number of colors, returns the color for the highest index.
+     */
+    public @ColorRes int getBadgeColor(int badgeIndex) {
+        if (mBadgeColors == null || mBadgeColors.length == 0 || badgeIndex < 0) {
+            return Resources.ID_NULL;
+        }
+        return mBadgeColors[Math.min(badgeIndex, mBadgeColors.length - 1)];
+    }
+
+    public boolean isProfile() {
+        return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
+    }
+
+    // TODO(b/142482943): Hook this up and don't return the original.
+    public List<String> getDefaultRestrictions() {
+        return mDefaultRestrictions;
+    }
+
+    /** Dumps details of the UserTypeDetails. Do not parse this. */
+    public void dump(PrintWriter pw) {
+        final String prefix = "        ";
+        pw.print(prefix); pw.print("mName: "); pw.println(mName);
+        pw.print(prefix); pw.print("mBaseType: "); pw.println(UserInfo.flagsToString(mBaseType));
+        pw.print(prefix); pw.print("mEnabled: "); pw.println(mEnabled);
+        pw.print(prefix); pw.print("mMaxAllowed: "); pw.println(mMaxAllowed);
+        pw.print(prefix); pw.print("mMaxAllowedPerParent: "); pw.println(mMaxAllowedPerParent);
+        pw.print(prefix); pw.print("mDefaultUserInfoFlags: ");
+        pw.println(UserInfo.flagsToString(mDefaultUserInfoPropertyFlags));
+        pw.print(prefix); pw.print("mLabel: "); pw.println(mLabel);
+        pw.print(prefix); pw.print("mDefaultRestrictions: "); pw.println(mDefaultRestrictions);
+        pw.print(prefix); pw.print("mIconBadge: "); pw.println(mIconBadge);
+        pw.print(prefix); pw.print("mBadgePlain: "); pw.println(mBadgePlain);
+        pw.print(prefix); pw.print("mBadgeNoBackground: "); pw.println(mBadgeNoBackground);
+        pw.print(prefix); pw.print("mBadgeLabels.length: ");
+        pw.println(mBadgeLabels != null ? mBadgeLabels.length : "0(null)");
+        pw.print(prefix); pw.print("mBadgeColors.length: ");
+        pw.println(mBadgeColors != null ? mBadgeColors.length : "0(null)");
+    }
+
+    /** Builder for a {@link UserTypeDetails}; see that class for documentation. */
+    public static final class Builder {
+        // UserTypeDetails properties and their default values.
+        private String mName; // This MUST be explicitly set.
+        private int mBaseType; // This MUST be explicitly set.
+        private int mMaxAllowed = UNLIMITED_NUMBER_OF_USERS;
+        private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS;
+        private int mDefaultUserInfoPropertyFlags = 0;
+        private ArrayList<String> mDefaultRestrictions = new ArrayList<>();
+        private boolean mEnabled = true;
+        private int mLabel = Resources.ID_NULL;
+        private int[] mBadgeLabels = null;
+        private int[] mBadgeColors = null;
+        private int mIconBadge = Resources.ID_NULL;
+        private int mBadgePlain = Resources.ID_NULL;
+        private int mBadgeNoBackground = Resources.ID_NULL;
+
+        public Builder setName(String name) {
+            mName = name;
+            return this;
+        }
+
+        public Builder setEnabled(boolean enabled) {
+            mEnabled = enabled;
+            return this;
+        }
+
+        public Builder setMaxAllowed(int maxAllowed) {
+            mMaxAllowed = maxAllowed;
+            return this;
+        }
+
+        public Builder setMaxAllowedPerParent(int maxAllowedPerParent) {
+            mMaxAllowedPerParent = maxAllowedPerParent;
+            return this;
+        }
+
+        public Builder setBaseType(@UserInfoFlag int baseType) {
+            mBaseType = baseType;
+            return this;
+        }
+
+        public Builder setDefaultUserInfoPropertyFlags(@UserInfoFlag int flags) {
+            mDefaultUserInfoPropertyFlags = flags;
+            return this;
+        }
+
+        public Builder setBadgeLabels(int ... badgeLabels) {
+            mBadgeLabels = badgeLabels;
+            return this;
+        }
+
+        public Builder setBadgeColors(int ... badgeColors) {
+            mBadgeColors = badgeColors;
+            return this;
+        }
+
+        public Builder setIconBadge(int badgeIcon) {
+            mIconBadge = badgeIcon;
+            return this;
+        }
+
+        public Builder setBadgePlain(int badgePlain) {
+            mBadgePlain = badgePlain;
+            return this;
+        }
+
+        public Builder setBadgeNoBackground(int badgeNoBackground) {
+            mBadgeNoBackground = badgeNoBackground;
+            return this;
+        }
+
+        public Builder setLabel(int label) {
+            mLabel = label;
+            return this;
+        }
+
+        public Builder setDefaultRestrictions(ArrayList<String> restrictions) {
+            mDefaultRestrictions = restrictions;
+            return this;
+        }
+
+        public UserTypeDetails createUserTypeDetails() {
+            Preconditions.checkArgument(mName != null,
+                    "Cannot create a UserTypeDetails with no name.");
+            Preconditions.checkArgument(hasValidBaseType(),
+                    "UserTypeDetails " + mName + " has invalid baseType: " + mBaseType);
+            Preconditions.checkArgument(hasValidPropertyFlags(),
+                    "UserTypeDetails " + mName + " has invalid flags: "
+                            + Integer.toHexString(mDefaultUserInfoPropertyFlags));
+            if (hasBadge()) {
+                Preconditions.checkArgument(mBadgeLabels != null && mBadgeLabels.length != 0,
+                        "UserTypeDetails " + mName + " has badge but no badgeLabels.");
+                Preconditions.checkArgument(mBadgeColors != null && mBadgeColors.length != 0,
+                        "UserTypeDetails " + mName + " has badge but no badgeColors.");
+            }
+
+            return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
+                    mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
+                    mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
+                    mDefaultRestrictions);
+        }
+
+        private boolean hasBadge() {
+            return mIconBadge != Resources.ID_NULL;
+        }
+
+        // TODO(b/143784345): Refactor this when we clean up UserInfo.
+        private boolean hasValidBaseType() {
+            return mBaseType == UserInfo.FLAG_FULL
+                    || mBaseType == UserInfo.FLAG_PROFILE
+                    || mBaseType == UserInfo.FLAG_SYSTEM
+                    || mBaseType == (UserInfo.FLAG_FULL | UserInfo.FLAG_SYSTEM);
+        }
+
+        // TODO(b/143784345): Refactor this when we clean up UserInfo.
+        private boolean hasValidPropertyFlags() {
+            final int forbiddenMask =
+                    UserInfo.FLAG_PRIMARY |
+                    UserInfo.FLAG_ADMIN |
+                    UserInfo.FLAG_INITIALIZED |
+                    UserInfo.FLAG_QUIET_MODE |
+                    UserInfo.FLAG_FULL |
+                    UserInfo.FLAG_SYSTEM |
+                    UserInfo.FLAG_PROFILE;
+            return (mDefaultUserInfoPropertyFlags & forbiddenMask) == 0;
+        }
+    }
+
+    /**
+     * Returns whether the user type is a managed profile
+     * (i.e. {@link UserManager#USER_TYPE_PROFILE_MANAGED}).
+     */
+    public boolean isManagedProfile() {
+        return UserManager.isUserTypeManagedProfile(mName);
+    }
+}
diff --git a/core/java/com/android/server/pm/UserTypeFactory.java b/core/java/com/android/server/pm/UserTypeFactory.java
new file mode 100644
index 0000000..43bbab1
--- /dev/null
+++ b/core/java/com/android/server/pm/UserTypeFactory.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.UserInfo.FLAG_DEMO;
+import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
+import static android.content.pm.UserInfo.FLAG_FULL;
+import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.content.pm.UserInfo.FLAG_PROFILE;
+import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static android.content.pm.UserInfo.FLAG_SYSTEM;
+import static android.os.UserManager.USER_TYPE_FULL_DEMO;
+import static android.os.UserManager.USER_TYPE_FULL_GUEST;
+import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED;
+import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
+import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
+import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
+import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
+
+import static com.android.server.pm.UserTypeDetails.UNLIMITED_NUMBER_OF_USERS;
+
+import android.content.res.Resources;
+import android.os.UserManager;
+import android.util.ArrayMap;
+
+/**
+ * Class for creating all {@link UserTypeDetails} on the device.
+ *
+ * Tests are located in UserManagerServiceUserTypeTest.java.
+ * @hide
+ */
+public final class UserTypeFactory {
+
+    /** This is a utility class, so no instantiable constructor. */
+    private UserTypeFactory() {}
+
+    /**
+     * Obtains the user types (built-in and customized) for this device.
+     *
+     * @return mapping from the name of each user type to its {@link UserTypeDetails} object
+     */
+    public static ArrayMap<String, UserTypeDetails> getUserTypes() {
+        final ArrayMap<String, UserTypeDetails> map = new ArrayMap<>();
+        // TODO(b/142482943): Read an xml file for OEM customized types.
+        //                    Remember to disallow "android." namespace
+        // TODO(b/142482943): Read an xml file to get any overrides for the built-in types.
+        final int maxManagedProfiles = 1;
+        map.put(USER_TYPE_PROFILE_MANAGED,
+                getDefaultTypeProfileManaged().setMaxAllowedPerParent(maxManagedProfiles)
+                        .createUserTypeDetails());
+        map.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeSystemFull().createUserTypeDetails());
+        map.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary().createUserTypeDetails());
+        map.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest().createUserTypeDetails());
+        map.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo().createUserTypeDetails());
+        map.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted().createUserTypeDetails());
+        map.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless().createUserTypeDetails());
+        return map;
+    }
+
+    /**
+     * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_MANAGED}
+     * configuration.
+     */
+    private static UserTypeDetails.Builder getDefaultTypeProfileManaged() {
+        return new UserTypeDetails.Builder()
+                .setName(USER_TYPE_PROFILE_MANAGED)
+                .setBaseType(FLAG_PROFILE)
+                .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE)
+                .setMaxAllowedPerParent(1)
+                .setLabel(0)
+                .setIconBadge(com.android.internal.R.drawable.ic_corp_icon_badge_case)
+                .setBadgePlain(com.android.internal.R.drawable.ic_corp_badge_case)
+                .setBadgeNoBackground(com.android.internal.R.drawable.ic_corp_badge_no_background)
+                .setBadgeLabels(
+                        com.android.internal.R.string.managed_profile_label_badge,
+                        com.android.internal.R.string.managed_profile_label_badge_2,
+                        com.android.internal.R.string.managed_profile_label_badge_3)
+                .setBadgeColors(
+                        com.android.internal.R.color.profile_badge_1,
+                        com.android.internal.R.color.profile_badge_2,
+                        com.android.internal.R.color.profile_badge_3);
+    }
+
+    /**
+     * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_SECONDARY}
+     * configuration.
+     */
+    private static UserTypeDetails.Builder getDefaultTypeFullSecondary() {
+        return new UserTypeDetails.Builder()
+                .setName(USER_TYPE_FULL_SECONDARY)
+                .setBaseType(FLAG_FULL)
+                .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS);
+    }
+
+    /**
+     * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_GUEST} configuration.
+     */
+    private static UserTypeDetails.Builder getDefaultTypeFullGuest() {
+        final boolean ephemeralGuests = Resources.getSystem()
+                .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
+        final int flags = FLAG_GUEST | (ephemeralGuests ? FLAG_EPHEMERAL : 0);
+
+        // TODO(b/142482943): Put UMS.initDefaultGuestRestrictions() here; then fetch them from here
+
+        return new UserTypeDetails.Builder()
+                .setName(USER_TYPE_FULL_GUEST)
+                .setBaseType(FLAG_FULL)
+                .setDefaultUserInfoPropertyFlags(flags)
+                .setMaxAllowed(1);
+    }
+
+    /**
+     * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_DEMO} configuration.
+     */
+    private static UserTypeDetails.Builder getDefaultTypeFullDemo() {
+        return new UserTypeDetails.Builder()
+                .setName(USER_TYPE_FULL_DEMO)
+                .setBaseType(FLAG_FULL)
+                .setDefaultUserInfoPropertyFlags(FLAG_DEMO)
+                .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS);
+    }
+
+    /**
+     * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_RESTRICTED}
+     * configuration.
+     */
+    private static UserTypeDetails.Builder getDefaultTypeFullRestricted() {
+        return new UserTypeDetails.Builder()
+                .setName(USER_TYPE_FULL_RESTRICTED)
+                .setBaseType(FLAG_FULL)
+                .setDefaultUserInfoPropertyFlags(FLAG_RESTRICTED)
+                .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS);
+    }
+
+    /**
+     * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_SYSTEM} configuration.
+     */
+    private static UserTypeDetails.Builder getDefaultTypeSystemFull() {
+        return new UserTypeDetails.Builder()
+                .setName(USER_TYPE_FULL_SYSTEM)
+                .setBaseType(FLAG_SYSTEM | FLAG_FULL);
+    }
+
+    /**
+     * Returns the Builder for the default {@link UserManager#USER_TYPE_SYSTEM_HEADLESS}
+     * configuration.
+     */
+    private static UserTypeDetails.Builder getDefaultTypeSystemHeadless() {
+        return new UserTypeDetails.Builder()
+                .setName(USER_TYPE_SYSTEM_HEADLESS)
+                .setBaseType(FLAG_SYSTEM);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
index 22b2314..1bc46a7 100644
--- a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java
@@ -19,6 +19,7 @@
 import android.content.pm.UserInfo;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.UserManager;
 
 import androidx.test.filters.LargeTest;
 
@@ -40,6 +41,6 @@
     @Override
     protected UserInfo createUser() throws RemoteException {
         return mUm.createProfileForUser("Managed user",
-                UserInfo.FLAG_MANAGED_PROFILE, UserHandle.myUserId());
+                UserManager.USER_TYPE_PROFILE_MANAGED, /* flags */ 0, UserHandle.myUserId());
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 274696b..69487d5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -39,6 +39,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
+import android.os.UserManager;
 
 import com.android.settingslib.R;
 
@@ -176,8 +177,8 @@
             boolean isManaged = context.getSystemService(DevicePolicyManager.class)
                     .getProfileOwnerAsUser(userId) != null;
             if (isManaged) {
-                badge = getDrawableForDisplayDensity(
-                        context, com.android.internal.R.drawable.ic_corp_badge_case);
+                badge = getDrawableForDisplayDensity(context,
+                        context.getSystemService(UserManager.class).getUserBadgeResId(userId));
             }
         }
         return setBadge(badge);
diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java
index e5f8b49..9a7cb3f 100644
--- a/services/core/java/android/os/UserManagerInternal.java
+++ b/services/core/java/android/os/UserManagerInternal.java
@@ -143,8 +143,8 @@
      * <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
      * createAndManageUser is called by the device owner.
      */
-    public abstract UserInfo createUserEvenWhenDisallowed(String name, int flags,
-            String[] disallowedPackages);
+    public abstract UserInfo createUserEvenWhenDisallowed(String name, String userType,
+            int flags, String[] disallowedPackages);
 
     /**
      * Same as {@link UserManager#removeUser(int userId)}, but bypasses the check for
@@ -202,8 +202,7 @@
 
     /**
      * Checks if the {@code callingUserId} and {@code targetUserId} are same or in same group
-     * and that the {@code callingUserId} is not a managed profile and
-     * {@code targetUserId} is enabled.
+     * and that the {@code callingUserId} is not a profile and {@code targetUserId} is enabled.
      *
      * @return TRUE if the {@code callingUserId} can access {@code targetUserId}. FALSE
      * otherwise
@@ -215,8 +214,7 @@
             String debugMsg, boolean throwSecurityException);
 
     /**
-     * If {@code userId} is of a managed profile, return the parent user ID. Otherwise return
-     * itself.
+     * If {@code userId} is of a profile, return the parent user ID. Otherwise return itself.
      */
     public abstract int getProfileParentId(int userId);
 
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 0ea913f..83e3da5 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -384,7 +384,7 @@
 
         // We need to delay unlocking managed profiles until the parent user
         // is also unlocked.
-        if (mInjector.getUserManager().isManagedProfile(userId)) {
+        if (mInjector.getUserManager().isProfile(userId)) {
             final UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
             if (parent != null
                     && isUserRunning(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 2b6c347..bd95667 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -304,7 +304,7 @@
             long ident = injectClearCallingIdentity();
             try {
                 final UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
-                if (callingUserInfo != null && callingUserInfo.isManagedProfile()) {
+                if (callingUserInfo != null && callingUserInfo.isProfile()) {
                     Slog.w(TAG, message + " for another profile "
                             + targetUserId + " from " + callingUserId + " not allowed");
                     return false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index f1c84b8..7e7da4c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2414,27 +2414,40 @@
         String name;
         int userId = -1;
         int flags = 0;
+        String userType = null;
         String opt;
         boolean preCreateOnly = false;
         while ((opt = getNextOption()) != null) {
+            String newUserType = null;
             if ("--profileOf".equals(opt)) {
                 userId = UserHandle.parseUserArg(getNextArgRequired());
             } else if ("--managed".equals(opt)) {
-                flags |= UserInfo.FLAG_MANAGED_PROFILE;
+                newUserType = UserManager.USER_TYPE_PROFILE_MANAGED;
             } else if ("--restricted".equals(opt)) {
-                flags |= UserInfo.FLAG_RESTRICTED;
+                newUserType = UserManager.USER_TYPE_FULL_RESTRICTED;
+            } else if ("--guest".equals(opt)) {
+                newUserType = UserManager.USER_TYPE_FULL_GUEST;
+            } else if ("--demo".equals(opt)) {
+                newUserType = UserManager.USER_TYPE_FULL_DEMO;
             } else if ("--ephemeral".equals(opt)) {
                 flags |= UserInfo.FLAG_EPHEMERAL;
-            } else if ("--guest".equals(opt)) {
-                flags |= UserInfo.FLAG_GUEST;
-            } else if ("--demo".equals(opt)) {
-                flags |= UserInfo.FLAG_DEMO;
             } else if ("--pre-create-only".equals(opt)) {
                 preCreateOnly = true;
+            } else if ("--user-type".equals(opt)) {
+                newUserType = getNextArgRequired();
             } else {
                 getErrPrintWriter().println("Error: unknown option " + opt);
                 return 1;
             }
+            // Ensure only one user-type was specified.
+            if (newUserType != null) {
+                if (userType != null && !userType.equals(newUserType)) {
+                    getErrPrintWriter().println("Error: more than one user type was specified ("
+                            + userType + " and " + newUserType + ")");
+                    return 1;
+                }
+                userType = newUserType;
+            }
         }
         String arg = getNextArg();
         if (arg == null && !preCreateOnly) {
@@ -2451,16 +2464,20 @@
                 ServiceManager.getService(Context.USER_SERVICE));
         IAccountManager accm = IAccountManager.Stub.asInterface(
                 ServiceManager.getService(Context.ACCOUNT_SERVICE));
-        if ((flags & UserInfo.FLAG_RESTRICTED) != 0) {
+        if (userType == null) {
+            userType = UserInfo.getDefaultUserType(flags);
+        }
+        if (UserManager.isUserTypeRestricted(userType)) {
             // In non-split user mode, userId can only be SYSTEM
             int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
             info = um.createRestrictedProfile(name, parentUserId);
             accm.addSharedAccountsFromParentUser(parentUserId, userId,
                     (Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
         } else if (userId < 0) {
-            info = preCreateOnly ? um.preCreateUser(flags) : um.createUser(name, flags);
+            info = preCreateOnly ?
+                    um.preCreateUser(userType) : um.createUser(name, userType, flags);
         } else {
-            info = um.createProfileForUser(name, flags, userId, null);
+            info = um.createProfileForUser(name, userType, flags, userId, null);
         }
 
         if (info != null) {
@@ -3375,9 +3392,15 @@
         pw.println("    Lists the current users.");
         pw.println("");
         pw.println("  create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]");
-        pw.println("      [--guest] [--pre-create-only] USER_NAME");
+        pw.println("      [--guest] [--pre-create-only] [--user-type USER_TYPE] USER_NAME");
         pw.println("    Create a new user with the given USER_NAME, printing the new user identifier");
         pw.println("    of the user.");
+        // TODO(b/142482943): Consider fetching the list of user types from UMS.
+        pw.println("    USER_TYPE is the name of a user type, e.g. android.os.usertype.profile.MANAGED.");
+        pw.println("      If not specified, the default user type is android.os.usertype.full.SECONDARY.");
+        pw.println("      --managed is shorthand for '--user-type android.os.usertype.profile.MANAGED'.");
+        pw.println("      --restricted is shorthand for '--user-type android.os.usertype.full.RESTRICTED'.");
+        pw.println("      --guest is shorthand for '--user-type android.os.usertype.full.GUEST'.");
         pw.println("");
         pw.println("  remove-user USER_ID");
         pw.println("    Remove the user with the given USER_IDENTIFIER, deleting all data");
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4d2512c..4b04726 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -20,8 +20,11 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
 import android.Manifest;
+import android.annotation.ColorRes;
+import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -77,6 +80,7 @@
 import android.security.GateKeeper;
 import android.service.gatekeeper.IGateKeeperService;
 import android.stats.devicepolicy.DevicePolicyEnums;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.IntArray;
@@ -152,6 +156,7 @@
     private static final String TAG_NAME = "name";
     private static final String TAG_ACCOUNT = "account";
     private static final String ATTR_FLAGS = "flags";
+    private static final String ATTR_TYPE = "type";
     private static final String ATTR_ICON_PATH = "icon";
     private static final String ATTR_ID = "id";
     private static final String ATTR_CREATION_TIME = "created";
@@ -220,15 +225,10 @@
     @VisibleForTesting
     static final int MAX_RECENTLY_REMOVED_IDS_SIZE = 100;
 
-    private static final int USER_VERSION = 8;
+    private static final int USER_VERSION = 9;
 
     private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
 
-    // Maximum number of managed profiles permitted per user is 1. This cannot be increased
-    // without first making sure that the rest of the framework is prepared for it.
-    @VisibleForTesting
-    static final int MAX_MANAGED_PROFILES = 1;
-
     static final int WRITE_USER_MSG = 1;
     static final int WRITE_USER_DELAY = 2*1000;  // 2 seconds
 
@@ -304,6 +304,12 @@
     private final SparseArray<UserData> mUsers = new SparseArray<>();
 
     /**
+     * Map of user type names to their corresponding {@link UserTypeDetails}.
+     * Should not be modified after UserManagerService constructor finishes.
+     */
+    private final ArrayMap<String, UserTypeDetails> mUserTypes;
+
+    /**
      * User restrictions set via UserManager.  This doesn't include restrictions set by
      * device owner / profile owners. Only non-empty restriction bundles are stored.
      *
@@ -543,6 +549,7 @@
         mPackagesLock = packagesLock;
         mHandler = new MainHandler();
         mUserDataPreparer = userDataPreparer;
+        mUserTypes = UserTypeFactory.getUserTypes();
         synchronized (mPackagesLock) {
             mUsersDir = new File(dataDir, USER_INFO_DIR);
             mUsersDir.mkdirs();
@@ -700,22 +707,37 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mUsersLock) {
-                return getProfilesLU(userId, enabledOnly, returnFullInfo);
+                return getProfilesLU(userId, /* userType */ null, enabledOnly, returnFullInfo);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 
+    // TODO(b/142482943): Will probably need a getProfiles(userType). But permissions may vary.
+
     @Override
     public int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
+        return getProfileIds(userId, null, enabledOnly);
+    }
+
+    // TODO(b/142482943): Probably @Override and make this accessible in UserManager.
+    /**
+     * Returns all the users of type userType that are in the same profile group as userId
+     * (including userId itself, if it is of the appropriate user type).
+     *
+     * <p>If userType is non-{@code null}, only returns users that are of type userType.
+     * If enabledOnly, only returns users that are not {@link UserInfo#FLAG_DISABLED}.
+     */
+    public int[] getProfileIds(@UserIdInt int userId, @Nullable String userType,
+            boolean enabledOnly) {
         if (userId != UserHandle.getCallingUserId()) {
             checkManageOrCreateUsersPermission("getting profiles related to user " + userId);
         }
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mUsersLock) {
-                return getProfileIdsLU(userId, enabledOnly).toArray();
+                return getProfileIdsLU(userId, userType, enabledOnly).toArray();
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -724,9 +746,9 @@
 
     /** Assume permissions already checked and caller's identity cleared */
     @GuardedBy("mUsersLock")
-    private List<UserInfo> getProfilesLU(@UserIdInt int userId, boolean enabledOnly,
-            boolean fullInfo) {
-        IntArray profileIds = getProfileIdsLU(userId, enabledOnly);
+    private List<UserInfo> getProfilesLU(@UserIdInt int userId, @Nullable String userType,
+            boolean enabledOnly, boolean fullInfo) {
+        IntArray profileIds = getProfileIdsLU(userId, userType, enabledOnly);
         ArrayList<UserInfo> users = new ArrayList<>(profileIds.size());
         for (int i = 0; i < profileIds.size(); i++) {
             int profileId = profileIds.get(i);
@@ -746,9 +768,12 @@
 
     /**
      *  Assume permissions already checked and caller's identity cleared
+     *  <p>If userType is {@code null}, returns all profiles for user; else, only returns
+     *  profiles of that type.
      */
     @GuardedBy("mUsersLock")
-    private IntArray getProfileIdsLU(@UserIdInt int userId, boolean enabledOnly) {
+    private IntArray getProfileIdsLU(@UserIdInt int userId, @Nullable String userType,
+            boolean enabledOnly) {
         UserInfo user = getUserInfoLU(userId);
         IntArray result = new IntArray(mUsers.size());
         if (user == null) {
@@ -770,6 +795,9 @@
             if (profile.partial) {
                 continue;
             }
+            if (userType != null && !userType.equals(profile.userType)) {
+                continue;
+            }
             result.add(profile.id);
         }
         return result;
@@ -1110,6 +1138,45 @@
         }
     }
 
+    /**
+     * Returns the user type, e.g. {@link UserManager#USER_TYPE_FULL_GUEST}, of the given userId,
+     * or null if the user doesn't exist.
+     */
+    @Override
+    public @Nullable String getUserTypeForUser(@UserIdInt int userId) {
+        // TODO(b/142482943): Decide on the appropriate permission requirements.
+        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserTypeForUser");
+        return getUserTypeNoChecks(userId);
+    }
+
+    /**
+     * Returns the user type of the given userId, or null if the user doesn't exist.
+     * <p>No permissions checks are made (but userId checks may be made).
+     */
+    private @Nullable String getUserTypeNoChecks(@UserIdInt int userId) {
+        synchronized (mUsersLock) {
+            final UserInfo userInfo = getUserInfoLU(userId);
+            return userInfo != null ? userInfo.userType : null;
+        }
+    }
+
+    /**
+     * Returns the UserTypeDetails of the given userId's user type, or null if the no such user.
+     * <p>No permissions checks are made (but userId checks may be made).
+     */
+    private @Nullable UserTypeDetails getUserTypeDetailsNoChecks(@UserIdInt int userId) {
+        final String typeStr = getUserTypeNoChecks(userId);
+        return typeStr != null ? mUserTypes.get(typeStr) : null;
+    }
+
+    /**
+     * Returns the UserTypeDetails of the given userInfo's user type (or null for a null userInfo).
+     */
+    private @Nullable UserTypeDetails getUserTypeDetails(@Nullable UserInfo userInfo) {
+        final String typeStr = userInfo != null ? userInfo.userType : null;
+        return typeStr != null ? mUserTypes.get(typeStr) : null;
+    }
+
     @Override
     public UserInfo getUserInfo(@UserIdInt int userId) {
         checkManageOrCreateUsersPermission("query user");
@@ -1133,11 +1200,78 @@
     }
 
     @Override
-    public int getManagedProfileBadge(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getManagedProfileBadge");
+    public boolean hasBadge(@UserIdInt int userId) {
+        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "hasBadge");
+        final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+        return userTypeDetails != null && userTypeDetails.hasBadge();
+    }
+
+    @Override
+    public @StringRes int getUserBadgeLabelResId(@UserIdInt int userId) {
+        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeLabelResId");
+        final UserInfo userInfo = getUserInfoNoChecks(userId);
+        final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
+        if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
+            Slog.e(LOG_TAG, "Requested badge label for non-badged user " + userId);
+            return Resources.ID_NULL;
+        }
+        final int badgeIndex = userInfo.profileBadge;
+        return userTypeDetails.getBadgeLabel(badgeIndex);
+    }
+
+    @Override
+    public @ColorRes int getUserBadgeColorResId(@UserIdInt int userId) {
+        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeColorResId");
+        final UserInfo userInfo = getUserInfoNoChecks(userId);
+        final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
+        if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
+            Slog.e(LOG_TAG, "Requested badge color for non-badged user " + userId);
+            return Resources.ID_NULL;
+        }
+        final int badgeIndex = userInfo.profileBadge;
+        return userTypeDetails.getBadgeColor(badgeIndex);
+    }
+
+    @Override
+    public @DrawableRes int getUserIconBadgeResId(@UserIdInt int userId) {
+        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserIconBadgeResId");
+        final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+        if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
+            Slog.e(LOG_TAG, "Requested icon badge for non-badged user " + userId);
+            return Resources.ID_NULL;
+        }
+        return userTypeDetails.getIconBadge();
+    }
+
+    @Override
+    public @DrawableRes int getUserBadgeResId(@UserIdInt int userId) {
+        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeResId");
+        final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+        if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
+            Slog.e(LOG_TAG, "Requested badge for non-badged user " + userId);
+            return Resources.ID_NULL;
+        }
+        return userTypeDetails.getBadgePlain();
+    }
+
+    @Override
+    public @DrawableRes int getUserBadgeNoBackgroundResId(@UserIdInt int userId) {
+        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId,
+                "getUserBadgeNoBackgroundResId");
+        final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+        if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
+            Slog.e(LOG_TAG, "Requested badge (no background) for non-badged user " + userId);
+            return Resources.ID_NULL;
+        }
+        return userTypeDetails.getBadgeNoBackground();
+    }
+
+    @Override
+    public boolean isProfile(@UserIdInt int userId) {
+        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isProfile");
         synchronized (mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
-            return userInfo != null ? userInfo.profileBadge : 0;
+            return userInfo != null && userInfo.isProfile();
         }
     }
 
@@ -1888,33 +2022,93 @@
         return count >= UserManager.getMaxSupportedUsers();
     }
 
+    /**
+     * Returns whether more users of the given type can be added (based on how many users of that
+     * type already exist).
+     *
+     * <p>For checking whether more profiles can be added to a particular parent use
+     * {@link #canAddMoreProfilesToUser}.
+     */
+    private boolean canAddMoreUsersOfType(UserTypeDetails userTypeDetails) {
+        final int max = userTypeDetails.getMaxAllowed();
+        if (max == UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+            return true; // Indicates that there is no max.
+        }
+        return getNumberOfUsersOfType(userTypeDetails.getName()) < max;
+    }
+
+    /**
+     * Gets the number of users of the given user type.
+     * Does not include users that are about to die.
+     */
+    private int getNumberOfUsersOfType(String userType) {
+        int count = 0;
+        synchronized (mUsersLock) {
+            final int size = mUsers.size();
+            for (int i = 0; i < size; i++) {
+                final UserInfo user = mUsers.valueAt(i).info;
+                if (user.userType.equals(userType)
+                        && !user.guestToRemove
+                        && !mRemovingUserIds.get(user.id)
+                        && !user.preCreated) {
+                    count++;
+                }
+            }
+        }
+        return count;
+    }
+
     @Override
     public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) {
-        checkManageUsersPermission("check if more managed profiles can be added.");
-        if (ActivityManager.isLowRamDeviceStatic()) {
+        return canAddMoreProfilesToUser(UserManager.USER_TYPE_PROFILE_MANAGED, userId,
+                allowedToRemoveOne);
+    }
+
+    /** Returns whether more profiles of the given type can be added to the given parent userId. */
+    @Override
+    public boolean canAddMoreProfilesToUser(String userType, @UserIdInt int userId,
+            boolean allowedToRemoveOne) {
+        checkManageUsersPermission("check if more profiles can be added.");
+        final UserTypeDetails type = mUserTypes.get(userType);
+        if (type == null) {
             return false;
         }
-        if (!mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_MANAGED_USERS)) {
-            return false;
+        // Managed profiles have their own specific rules.
+        final boolean isManagedProfile = type.isManagedProfile();
+        if (isManagedProfile) {
+            if (ActivityManager.isLowRamDeviceStatic()) {
+                return false;
+            }
+            if (!mContext.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_MANAGED_USERS)) {
+                return false;
+            }
         }
-        // Limit number of managed profiles that can be created
-        final int managedProfilesCount = getProfiles(userId, false).size() - 1;
-        final int profilesRemovedCount = managedProfilesCount > 0 && allowedToRemoveOne ? 1 : 0;
-        if (managedProfilesCount - profilesRemovedCount >= getMaxManagedProfiles()) {
-            return false;
-        }
-        synchronized(mUsersLock) {
+        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;
             }
-            int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
-                    - profilesRemovedCount;
-            // We allow creating a managed profile in the special case where there is only one user.
-            return usersCountAfterRemoving  == 1
-                    || usersCountAfterRemoving < UserManager.getMaxSupportedUsers();
+
+            // Limit the number of profiles that can be created
+            final int maxUsersOfType = getMaxUsersOfTypePerParent(type);
+            if (maxUsersOfType != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+                final int userTypeCount = getProfileIds(userId, userType, false).length;
+                final int profilesRemovedCount = userTypeCount > 0 && allowedToRemoveOne ? 1 : 0;
+                if (userTypeCount - profilesRemovedCount >= maxUsersOfType) {
+                    return false;
+                }
+                // Allow creating a managed profile in the special case where there is only one user
+                if (isManagedProfile) {
+                    int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
+                            - profilesRemovedCount;
+                    return usersCountAfterRemoving == 1
+                            || usersCountAfterRemoving < UserManager.getMaxSupportedUsers();
+                }
+            }
         }
+        return true;
     }
 
     @GuardedBy("mUsersLock")
@@ -2199,9 +2393,18 @@
      */
     @GuardedBy({"mRestrictionsLock", "mPackagesLock"})
     private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) {
+        upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion);
+    }
+
+    /**
+     * Version of {@link #upgradeIfNecessaryLP(Bundle)} that takes in the userVersion for testing
+     * purposes. For non-tests, use {@link #upgradeIfNecessaryLP(Bundle)}.
+     */
+    @GuardedBy({"mRestrictionsLock", "mPackagesLock"})
+    @VisibleForTesting
+    void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion) {
         Set<Integer> userIdsToWrite = new ArraySet<>();
         final int originalVersion = mUserVersion;
-        int userVersion = mUserVersion;
         if (userVersion < 1) {
             // Assign a proper name for the owner, if not initialized correctly before
             UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM);
@@ -2290,6 +2493,44 @@
             userVersion = 8;
         }
 
+        if (userVersion < 9) {
+            // Convert from UserInfo flags to UserTypes. Apply FLAG_PROFILE to FLAG_MANAGED_PROFILE.
+            synchronized (mUsersLock) {
+                for (int i = 0; i < mUsers.size(); i++) {
+                    UserData userData = mUsers.valueAt(i);
+                    final int flags = userData.info.flags;
+                    if ((flags & UserInfo.FLAG_SYSTEM) != 0) {
+                        if ((flags & UserInfo.FLAG_FULL) != 0) {
+                            userData.info.userType = UserManager.USER_TYPE_FULL_SYSTEM;
+                        } else {
+                            userData.info.userType = UserManager.USER_TYPE_SYSTEM_HEADLESS;
+                        }
+                    } else {
+                        try {
+                            userData.info.userType = UserInfo.getDefaultUserType(flags);
+                        } catch (IllegalArgumentException e) {
+                            // TODO(b/142482943): What should we do here? Delete user? Crashloop?
+                            throw new IllegalStateException("Cannot upgrade user with flags "
+                                    + Integer.toHexString(flags) + " because it doesn't correspond "
+                                    + "to a valid user type.", e);
+                        }
+                    }
+                    // OEMs are responsible for their own custom upgrade logic here.
+
+                    final UserTypeDetails userTypeDetails = mUserTypes.get(userData.info.userType);
+                    if (userTypeDetails == null) {
+                        throw new IllegalStateException(
+                                "Cannot upgrade user with flags " + Integer.toHexString(flags)
+                                        + " because " + userData.info.userType + " isn't defined"
+                                        + " on this device!");
+                    }
+                    userData.info.flags |= userTypeDetails.getDefaultUserInfoFlags();
+                    userIdsToWrite.add(userData.info.id);
+                }
+            }
+            userVersion = 9;
+        }
+
         if (userVersion < USER_VERSION) {
             Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
                     + USER_VERSION);
@@ -2312,12 +2553,11 @@
     private void fallbackToSingleUserLP() {
         int flags = UserInfo.FLAG_SYSTEM | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN
                 | UserInfo.FLAG_PRIMARY;
-        // In headless system user mode, headless system user is not a full user.
-        if (!UserManager.isHeadlessSystemUserMode()) {
-            flags |= UserInfo.FLAG_FULL;
-        }
         // Create the system user
-        UserInfo system = new UserInfo(UserHandle.USER_SYSTEM, null, null, flags);
+        String systemUserType = UserManager.isHeadlessSystemUserMode() ?
+                UserManager.USER_TYPE_SYSTEM_HEADLESS : UserManager.USER_TYPE_FULL_SYSTEM;
+        flags |= mUserTypes.get(systemUserType).getDefaultUserInfoFlags();
+        UserInfo system = new UserInfo(UserHandle.USER_SYSTEM, null, null, flags, systemUserType);
         UserData userData = putUserInfo(system);
         mNextSerialNumber = MIN_USER_ID;
         mUserVersion = USER_VERSION;
@@ -2402,6 +2642,7 @@
         serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
         serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));
         serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
+        serializer.attribute(null, ATTR_TYPE, userInfo.userType);
         serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime));
         serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,
                 Long.toString(userInfo.lastLoggedInTime));
@@ -2562,6 +2803,7 @@
     UserData readUserLP(int id, InputStream is) throws IOException,
             XmlPullParserException {
         int flags = 0;
+        String userType = null;
         int serialNumber = id;
         String name = null;
         String account = null;
@@ -2605,6 +2847,8 @@
             }
             serialNumber = readIntAttribute(parser, ATTR_SERIAL_NO, id);
             flags = readIntAttribute(parser, ATTR_FLAGS, 0);
+            userType = parser.getAttributeValue(null, ATTR_TYPE);
+            userType = userType != null ? userType.intern() : null;
             iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
             creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0);
             lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0);
@@ -2670,7 +2914,7 @@
         }
 
         // Create the UserInfo object that gets passed around
-        UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
+        UserInfo userInfo = new UserInfo(id, name, iconPath, flags, userType);
         userInfo.serialNumber = serialNumber;
         userInfo.creationTime = creationTime;
         userInfo.lastLoggedInTime = lastLoggedInTime;
@@ -2737,108 +2981,135 @@
         }
     }
 
+    /**
+     * Creates a profile user. Used for actual profiles, like
+     * {@link UserManager#USER_TYPE_PROFILE_MANAGED}, as well as for
+     * {@link UserManager#USER_TYPE_FULL_RESTRICTED}.
+     */
     @Override
-    public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userId,
-            String[] disallowedPackages) {
+    public UserInfo createProfileForUser(String name, @NonNull String userType,
+            @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) {
         checkManageOrCreateUsersPermission(flags);
-        return createUserInternal(name, flags, userId, disallowedPackages);
+        return createUserInternal(name, userType, flags, userId, disallowedPackages);
+    }
+
+    /** @see #createProfileForUser */
+    @Override
+    public UserInfo createProfileForUserEvenWhenDisallowed(String name, @NonNull String userType,
+            @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) {
+        checkManageOrCreateUsersPermission(flags);
+        return createUserInternalUnchecked(name, userType, flags, userId,
+                /* preCreate= */ false, disallowedPackages);
     }
 
     @Override
-    public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags,
-            @UserIdInt int userId, String[] disallowedPackages) {
+    public UserInfo createUser(String name, @NonNull String userType, @UserInfoFlag int flags) {
         checkManageOrCreateUsersPermission(flags);
-        return createUserInternalUnchecked(name, flags, userId, /* preCreate= */ false,
-                disallowedPackages);
+        return createUserInternal(name, userType, flags, UserHandle.USER_NULL,
+                /* disallowedPackages= */ null);
     }
 
     @Override
-    public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
-        checkManageOrCreateUsersPermission("Only the system can remove users");
-        return removeUserUnchecked(userId);
-    }
+    public UserInfo preCreateUser(String userType) {
+        final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
+        final int flags = userTypeDetails != null ? userTypeDetails.getDefaultUserInfoFlags() : 0;
 
-    @Override
-    public UserInfo createUser(String name, int flags) {
-        checkManageOrCreateUsersPermission(flags);
-        return createUserInternal(name, flags, UserHandle.USER_NULL);
-    }
-
-    @Override
-    public UserInfo preCreateUser(int flags) {
         checkManageOrCreateUsersPermission(flags);
 
-        Preconditions.checkArgument(!UserInfo.isManagedProfile(flags),
-                "cannot pre-create managed profiles");
+        Preconditions.checkArgument(isUserTypeEligibleForPreCreation(userTypeDetails),
+                "cannot pre-create user of type " + userType);
+        Slog.i(LOG_TAG, "Pre-creating user of type " + userType);
 
-        Slog.i(LOG_TAG, "Pre-creating user with flags " + UserInfo.flagsToString(flags));
-
-        return createUserInternalUnchecked(/* name= */ null, flags,
+        return createUserInternalUnchecked(/* name= */ null, userType, flags,
                 /* parentId= */ UserHandle.USER_NULL, /* preCreate= */ true,
                 /* disallowedPackages= */ null);
     }
 
-    private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
-            @UserIdInt int parentId) {
-        return createUserInternal(name, flags, parentId, null);
-    }
-
-    private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
-            @UserIdInt int parentId, @Nullable String[] disallowedPackages) {
-        String restriction = ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0)
+    private UserInfo createUserInternal(@Nullable String name, @NonNull String userType,
+            @UserInfoFlag int flags, @UserIdInt int parentId,
+            @Nullable String[] disallowedPackages) {
+        String restriction = (UserManager.isUserTypeManagedProfile(userType))
                 ? UserManager.DISALLOW_ADD_MANAGED_PROFILE
                 : UserManager.DISALLOW_ADD_USER;
         if (hasUserRestriction(restriction, UserHandle.getCallingUserId())) {
             Log.w(LOG_TAG, "Cannot add user. " + restriction + " is enabled.");
             return null;
         }
-        return createUserInternalUnchecked(name, flags, parentId, /* preCreate= */ false,
-                disallowedPackages);
+        return createUserInternalUnchecked(name, userType, flags, parentId,
+                /* preCreate= */ false, disallowedPackages);
     }
 
-    private UserInfo createUserInternalUnchecked(@Nullable String name, @UserInfoFlag int flags,
-            @UserIdInt int parentId, boolean preCreate,
-            @Nullable String[] disallowedPackages) {
+    private UserInfo createUserInternalUnchecked(@Nullable String name,
+            @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
+            boolean preCreate, @Nullable String[] disallowedPackages) {
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         t.traceBegin("createUser-" + flags);
         try {
-            return createUserInternalUncheckedNoTracing(name, flags, parentId, preCreate,
-                disallowedPackages, t);
+            return createUserInternalUncheckedNoTracing(name, userType, flags, parentId,
+                    preCreate, disallowedPackages, t);
         } finally {
             t.traceEnd();
         }
     }
 
     private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name,
-            @UserInfoFlag int flags, @UserIdInt int parentId, boolean preCreate,
-            @Nullable String[] disallowedPackages, @NonNull TimingsTraceAndSlog t) {
+            @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
+            boolean preCreate, @Nullable String[] disallowedPackages,
+            @NonNull TimingsTraceAndSlog t) {
+
+        final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
+        if (userTypeDetails == null) {
+            Slog.e(LOG_TAG, "Cannot create user of invalid user type: " + userType);
+            return null;
+        }
+        userType = userType.intern(); // Now that we know it's valid, we can intern it.
+        flags |= userTypeDetails.getDefaultUserInfoFlags();
+        if (!checkUserTypeConsistency(flags)) {
+            Slog.e(LOG_TAG, "Cannot add user. Flags (" + Integer.toHexString(flags)
+                    + ") and userTypeDetails (" + userType +  ") are inconsistent.");
+            return null;
+        }
+        if ((flags & UserInfo.FLAG_SYSTEM) != 0) {
+            Slog.e(LOG_TAG, "Cannot add user. Flags (" + Integer.toHexString(flags)
+                    + ") indicated SYSTEM user, which cannot be created.");
+            return null;
+        }
+        synchronized (mUsersLock) {
+            if (mForceEphemeralUsers) {
+                flags |= UserInfo.FLAG_EPHEMERAL;
+            }
+        }
 
         // First try to use a pre-created user (if available).
-        // NOTE: currently we don't support pre-created managed profiles
-        if (!preCreate && (parentId < 0 && !UserInfo.isManagedProfile(flags))) {
+        // TODO(b/142482943): Move this to its own function later.
+        if (!preCreate
+                && (parentId < 0 && isUserTypeEligibleForPreCreation(userTypeDetails))) {
             final UserData preCreatedUserData;
             synchronized (mUsersLock) {
-                preCreatedUserData = getPreCreatedUserLU(flags);
+                preCreatedUserData = getPreCreatedUserLU(userType);
             }
             if (preCreatedUserData != null) {
                 final UserInfo preCreatedUser = preCreatedUserData.info;
-                Log.i(LOG_TAG, "Reusing pre-created user " + preCreatedUser.id + " for flags + "
-                        + UserInfo.flagsToString(flags));
-                if (DBG) {
-                    Log.d(LOG_TAG, "pre-created user flags: "
-                            + UserInfo.flagsToString(preCreatedUser.flags)
-                            + " new-user flags: " + UserInfo.flagsToString(flags));
+                final int newFlags = preCreatedUser.flags | flags;
+                if (!checkUserTypeConsistency(newFlags)) {
+                    Slog.wtf(LOG_TAG, "Cannot reuse pre-created user " + preCreatedUser.id
+                            + " of type " + userType + " because flags are inconsistent. "
+                            + "Flags (" + Integer.toHexString(flags) + "); preCreatedUserFlags ( "
+                            + Integer.toHexString(preCreatedUser.flags) + ").");
+                } else {
+                    Log.i(LOG_TAG, "Reusing pre-created user " + preCreatedUser.id + " of type "
+                            + userType + " and bestowing on it flags "
+                            + UserInfo.flagsToString(flags));
+                    preCreatedUser.name = name;
+                    preCreatedUser.flags = newFlags;
+                    preCreatedUser.preCreated = false;
+                    preCreatedUser.creationTime = getCreationTime();
+
+                    dispatchUserAddedIntent(preCreatedUser);
+                    writeUserLP(preCreatedUserData);
+                    writeUserListLP();
+                    return preCreatedUser;
                 }
-                preCreatedUser.name = name;
-                preCreatedUser.preCreated = false;
-                preCreatedUser.creationTime = getCreationTime();
-
-                dispatchUserAddedIntent(preCreatedUser);
-
-                writeUserLP(preCreatedUserData);
-                writeUserListLP();
-
-                return preCreatedUser;
             }
         }
 
@@ -2849,10 +3120,11 @@
             return null;
         }
 
-        final boolean isGuest = UserInfo.isGuest(flags);
-        final boolean isManagedProfile = UserInfo.isManagedProfile(flags);
-        final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
-        final boolean isDemo = (flags & UserInfo.FLAG_DEMO) != 0;
+        final boolean isProfile = userTypeDetails.isProfile();
+        final boolean isGuest = UserManager.isUserTypeGuest(userType);
+        final boolean isRestricted = UserManager.isUserTypeRestricted(userType);
+        final boolean isDemo = UserManager.isUserTypeDemo(userType);
+
         final long ident = Binder.clearCallingIdentity();
         UserInfo userInfo;
         UserData userData;
@@ -2866,21 +3138,23 @@
                     }
                     if (parent == null) return null;
                 }
-                if (isManagedProfile && !canAddMoreManagedProfiles(parentId, false)) {
-                    Log.e(LOG_TAG, "Cannot add more managed profiles for user " + parentId);
+                if (!preCreate && !canAddMoreUsersOfType(userTypeDetails)) {
+                    Log.e(LOG_TAG, "Cannot add more users of type " + userType
+                            + ". Maximum number of that type already exists.");
                     return null;
                 }
-                if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
-                    // If we're not adding a guest/demo user or a managed profile,
-                    // and the limit has been reached, cannot add a user.
+                // TODO(b/142482943): Perhaps let the following code apply to restricted users too.
+                if (isProfile && !canAddMoreProfilesToUser(userType, parentId, false)) {
+                    Log.e(LOG_TAG, "Cannot add more profiles of type " + userType
+                            + " for user " + parentId);
+                    return null;
+                }
+                if (!isGuest && !isProfile && !isDemo && isUserLimitReached()) {
+                    // If we're not adding a guest/demo user or a profile and the 'user limit' has
+                    // been reached, cannot add a user.
                     Log.e(LOG_TAG, "Cannot add user. Maximum user limit is reached.");
                     return null;
                 }
-                // If we're adding a guest and there already exists one, bail.
-                if (isGuest && !preCreate && findCurrentGuestUser() != null) {
-                    Log.e(LOG_TAG, "Cannot add guest user. Guest user already exists.");
-                    return null;
-                }
                 // In legacy mode, restricted profile's parent can only be the owner user
                 if (isRestricted && !UserManager.isSplitSystemUser()
                         && (parentId != UserHandle.USER_SYSTEM)) {
@@ -2900,30 +3174,23 @@
                     }
                 }
 
-                if (!isManagedProfile) {
-                    // New users cannot be system, and it's not a profile, so per-force it's FULL.
-                    flags |= UserInfo.FLAG_FULL;
-                }
-
                 userId = getNextAvailableId();
                 Environment.getUserSystemDirectory(userId).mkdirs();
-                boolean ephemeralGuests = areGuestUsersEphemeral();
 
                 synchronized (mUsersLock) {
-                    // Add ephemeral flag to guests/users if required. Also inherit it from parent.
-                    if ((isGuest && ephemeralGuests) || mForceEphemeralUsers
-                            || (parent != null && parent.info.isEphemeral())) {
+                    // Inherit ephemeral flag from parent.
+                    if (parent != null && parent.info.isEphemeral()) {
                         flags |= UserInfo.FLAG_EPHEMERAL;
                     }
 
-                    userInfo = new UserInfo(userId, name, null, flags);
+                    userInfo = new UserInfo(userId, name, null, flags, userType);
                     userInfo.serialNumber = mNextSerialNumber++;
                     userInfo.creationTime = getCreationTime();
                     userInfo.partial = true;
                     userInfo.preCreated = preCreate;
                     userInfo.lastLoggedInFingerprint = Build.FINGERPRINT;
-                    if (isManagedProfile && parentId != UserHandle.USER_NULL) {
-                        userInfo.profileBadge = getFreeProfileBadgeLU(parentId);
+                    if (userTypeDetails.hasBadge() && parentId != UserHandle.USER_NULL) {
+                        userInfo.profileBadge = getFreeProfileBadgeLU(parentId, userType);
                     }
                     userData = new UserData();
                     userData.info = userInfo;
@@ -2932,7 +3199,7 @@
                 writeUserLP(userData);
                 writeUserListLP();
                 if (parent != null) {
-                    if (isManagedProfile) {
+                    if (isProfile) {
                         if (parent.info.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
                             parent.info.profileGroupId = parent.info.id;
                             writeUserLP(parent);
@@ -2958,7 +3225,7 @@
                     StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
             t.traceEnd();
 
-            final Set<String> installablePackages =
+            final Set<String> installablePackages = // TODO(b/142482943): use userType
                     mSystemPackageInstaller.getInstallablePackagesForUserType(flags);
             t.traceBegin("PM.createNewUser");
             mPm.createNewUser(userId, installablePackages, disallowedPackages);
@@ -2970,6 +3237,7 @@
             }
             updateUserIds();
             Bundle restrictions = new Bundle();
+            // TODO(b/142482943): Generalize this using UserTypeDetails default restrictions.
             if (isGuest) {
                 synchronized (mGuestRestrictions) {
                     restrictions.putAll(mGuestRestrictions);
@@ -3018,6 +3286,22 @@
         return userInfo;
     }
 
+    /** Checks that the flags do not contain mutually exclusive types/properties. */
+    static boolean checkUserTypeConsistency(@UserInfoFlag int flags) {
+        // Mask to check that flags don't refer to multiple user types.
+        final int userTypeFlagMask = UserInfo.FLAG_GUEST | UserInfo.FLAG_DEMO
+                | UserInfo.FLAG_RESTRICTED | UserInfo.FLAG_PROFILE;
+        return isAtMostOneFlag(flags & userTypeFlagMask)
+                && isAtMostOneFlag(flags & (UserInfo.FLAG_PROFILE | UserInfo.FLAG_FULL))
+                && isAtMostOneFlag(flags & (UserInfo.FLAG_PROFILE | UserInfo.FLAG_SYSTEM));
+    }
+
+    /** Returns whether the given flags contains at most one 1. */
+    private static boolean isAtMostOneFlag(int flags) {
+        return (flags & (flags - 1)) == 0;
+        // If !=0, this means that flags is not a power of 2, and therefore is multiple types.
+    }
+
     /** Install/uninstall system packages for all users based on their user-type, as applicable. */
     boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade) {
         return mSystemPackageInstaller.installWhitelistedSystemPackages(isFirstBoot, isUpgrade);
@@ -3037,39 +3321,23 @@
                 : (userInfo.isDemo() ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);
     }
 
-    private boolean areGuestUsersEphemeral() {
-        return Resources.getSystem()
-                .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
-    }
-
     /**
-     * Gets a pre-created user for the given flag.
+     * Gets a pre-created user for the given user type.
      *
      * <p>Should be used only during user creation, so the pre-created user can be used (instead of
      * creating and initializing a new user from scratch).
      */
     // TODO(b/143092698): add unit test
     @GuardedBy("mUsersLock")
-    private @Nullable UserData getPreCreatedUserLU(@UserInfoFlag int flags) {
-        if (DBG) {
-            Slog.d(LOG_TAG, "getPreCreatedUser(): initialFlags= " + UserInfo.flagsToString(flags));
-        }
-        flags |= UserInfo.FLAG_FULL;
-        if (UserInfo.isGuest(flags) && areGuestUsersEphemeral()) {
-            flags |= UserInfo.FLAG_EPHEMERAL;
-        }
-        if (DBG) {
-            Slog.d(LOG_TAG, "getPreCreatedUser(): targetFlags= " + UserInfo.flagsToString(flags));
-        }
+    private @Nullable UserData getPreCreatedUserLU(String userType) {
+        if (DBG) Slog.d(LOG_TAG, "getPreCreatedUser(): userType= " + userType);
         final int userSize = mUsers.size();
         for (int i = 0; i < userSize; i++) {
             final UserData user = mUsers.valueAt(i);
             if (DBG) Slog.d(LOG_TAG, i + ":" + user.info.toFullString());
-            if (user.info.preCreated
-                    && (user.info.flags & ~UserInfo.FLAG_INITIALIZED) == flags) {
+            if (user.info.preCreated && user.info.userType.equals(userType)) {
                 if (!user.info.isInitialized()) {
-                    Slog.w(LOG_TAG, "found pre-created user for flags "
-                            + "" + UserInfo.flagsToString(flags)
+                    Slog.w(LOG_TAG, "found pre-created user of type " + userType
                             + ", but it's not initialized yet: " + user.info.toFullString());
                     continue;
                 }
@@ -3079,6 +3347,18 @@
         return null;
     }
 
+    /**
+     * Returns whether a user with the given userTypeDetails is eligible to be
+     * {@link UserInfo#preCreated}.
+     */
+    private static boolean isUserTypeEligibleForPreCreation(UserTypeDetails userTypeDetails) {
+        if (userTypeDetails == null) {
+            return false;
+        }
+        return !userTypeDetails.isProfile()
+                && !userTypeDetails.getName().equals(UserManager.USER_TYPE_FULL_RESTRICTED);
+    }
+
     @VisibleForTesting
     UserData putUserInfo(UserInfo userInfo) {
         final UserData userData = new UserData();
@@ -3103,7 +3383,7 @@
     public UserInfo createRestrictedProfile(String name, int parentUserId) {
         checkManageOrCreateUsersPermission("setupRestrictedProfile");
         final UserInfo user = createProfileForUser(
-                name, UserInfo.FLAG_RESTRICTED, parentUserId, null);
+                name, UserManager.USER_TYPE_FULL_RESTRICTED, 0, parentUserId, null);
         if (user == null) {
             return null;
         }
@@ -3209,6 +3489,12 @@
         return removeUserUnchecked(userId);
     }
 
+    @Override
+    public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
+        checkManageOrCreateUsersPermission("Only the system can remove users");
+        return removeUserUnchecked(userId);
+    }
+
     private boolean removeUserUnchecked(@UserIdInt int userId) {
         long ident = Binder.clearCallingIdentity();
         try {
@@ -3256,6 +3542,7 @@
                 Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user.", e);
             }
 
+            // TODO(b/142482943): Send some sort of broadcast for profiles even if non-managed?
             if (userData.info.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
                     && userData.info.isManagedProfile()) {
                 // Send broadcast to notify system that the user removed was a
@@ -4022,6 +4309,7 @@
                         pw.print(" <pre-created>");
                     }
                     pw.println();
+                    pw.print("    Type: "); pw.println(userInfo.userType);
                     pw.print("    Flags: "); pw.print(userInfo.flags); pw.print(" (");
                     pw.print(UserInfo.flagsToString(userInfo.flags)); pw.println(")");
                     pw.print("    State: ");
@@ -4106,11 +4394,20 @@
         pw.print("  Max users: " + UserManager.getMaxSupportedUsers());
         pw.println(" (limit reached: " + isUserLimitReached() + ")");
         pw.println("  Supports switchable users: " + UserManager.supportsMultipleUsers());
-        pw.println("  All guests ephemeral: " + areGuestUsersEphemeral());
+        pw.println("  All guests ephemeral: " + Resources.getSystem().getBoolean(
+                com.android.internal.R.bool.config_guestUserEphemeral));
         pw.println("  Is split-system user: " + UserManager.isSplitSystemUser());
         pw.println("  Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
         pw.println("  User version: " + mUserVersion);
 
+        // Dump UserTypes
+        pw.println();
+        pw.println("  User types (" + mUserTypes.size() + " types):");
+        for (int i = 0; i < mUserTypes.size(); i++) {
+            pw.println("    " + mUserTypes.keyAt(i) + ": ");
+            mUserTypes.valueAt(i).dump(pw);
+        }
+
         // Dump package whitelist
         pw.println();
         mSystemPackageInstaller.dump(pw);
@@ -4309,10 +4606,10 @@
         }
 
         @Override
-        public UserInfo createUserEvenWhenDisallowed(String name, int flags,
-                String[] disallowedPackages) {
-            UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL,
-                    /* preCreated= */ false, disallowedPackages);
+        public UserInfo createUserEvenWhenDisallowed(String name, @NonNull String userType,
+                @UserInfoFlag int flags, String[] disallowedPackages) {
+            UserInfo user = createUserInternalUnchecked(name, userType, flags,
+                    UserHandle.USER_NULL, /* preCreated= */ false, disallowedPackages);
             // Keep this in sync with UserManager.createUser
             if (user != null && !user.isAdmin() && !user.isDemo()) {
                 setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
@@ -4397,7 +4694,7 @@
             }
             synchronized (mUsersLock) {
                 UserInfo callingUserInfo = getUserInfoLU(callingUserId);
-                if (callingUserInfo == null || callingUserInfo.isManagedProfile()) {
+                if (callingUserInfo == null || callingUserInfo.isProfile()) {
                     if (throwSecurityException) {
                         throw new SecurityException(
                                 debugMsg + " for another profile "
@@ -4516,36 +4813,53 @@
                 (DBG_WITH_STACKTRACE ? " called at\n" + Debug.getCallers(10, "  ") : ""));
     }
 
+    /** @see #getMaxUsersOfTypePerParent(UserTypeDetails) */
     @VisibleForTesting
-    static int getMaxManagedProfiles() {
-        // Allow overriding max managed profiles on debuggable builds for testing
-        // of multiple profiles.
-        if (!Build.IS_DEBUGGABLE) {
-            return MAX_MANAGED_PROFILES;
-        } else {
-            return SystemProperties.getInt("persist.sys.max_profiles",
-                    MAX_MANAGED_PROFILES);
+    int getMaxUsersOfTypePerParent(String userType) {
+        final UserTypeDetails type = mUserTypes.get(userType);
+        if (type == null) {
+            return 0;
         }
+        return getMaxUsersOfTypePerParent(type);
+    }
+
+    /**
+     * Returns the maximum number of users allowed for the given userTypeDetails per parent user.
+     * This is applicable for user types that are {@link UserTypeDetails#isProfile()}.
+     * If there is no maximum, {@link UserTypeDetails#UNLIMITED_NUMBER_OF_USERS} is returned.
+     */
+    private static int getMaxUsersOfTypePerParent(UserTypeDetails userTypeDetails) {
+        final int defaultMax = userTypeDetails.getMaxAllowedPerParent();
+        if (!Build.IS_DEBUGGABLE) {
+            return defaultMax;
+        } else {
+            if (userTypeDetails.isManagedProfile()) {
+                return SystemProperties.getInt("persist.sys.max_profiles", defaultMax);
+            }
+        }
+        return defaultMax;
     }
 
     @GuardedBy("mUsersLock")
     @VisibleForTesting
-    int getFreeProfileBadgeLU(int parentUserId) {
-        int maxManagedProfiles = getMaxManagedProfiles();
-        boolean[] usedBadges = new boolean[maxManagedProfiles];
+    int getFreeProfileBadgeLU(int parentUserId, String userType) {
+        Set<Integer> usedBadges = new ArraySet<>();
         final int userSize = mUsers.size();
         for (int i = 0; i < userSize; i++) {
             UserInfo ui = mUsers.valueAt(i).info;
             // Check which badge indexes are already used by this profile group.
-            if (ui.isManagedProfile()
+            if (ui.userType.equals(userType)
                     && ui.profileGroupId == parentUserId
-                    && !mRemovingUserIds.get(ui.id)
-                    && ui.profileBadge < maxManagedProfiles) {
-                usedBadges[ui.profileBadge] = true;
+                    && !mRemovingUserIds.get(ui.id)) {
+                usedBadges.add(ui.profileBadge);
             }
         }
-        for (int i = 0; i < maxManagedProfiles; i++) {
-            if (!usedBadges[i]) {
+        int maxUsersOfType = getMaxUsersOfTypePerParent(userType);
+        if (maxUsersOfType == UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+            maxUsersOfType = Integer.MAX_VALUE;
+        }
+        for (int i = 0; i < maxUsersOfType; i++) {
+            if (!usedBadges.contains(i)) {
                 return i;
             }
         }
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 323c957..31ea01e 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -404,12 +404,13 @@
         }
         // Regardless of the whitelists/blacklists, ensure mandatory packages.
         result.put("android",
-                UserInfo.FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.PROFILE_FLAGS_MASK);
+                UserInfo.FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.FLAG_PROFILE);
         return result;
     }
 
     /** Converts a user types, as used in SystemConfig, to a UserInfo flag. */
     private static int getFlagsFromUserTypes(Iterable<String> userTypes) {
+        // TODO(b/142482943): Update all this for the new UserTypes.
         int flags = 0;
         for (String type : userTypes) {
             switch (type) {
@@ -435,7 +436,7 @@
                     flags |= UserInfo.FLAG_SYSTEM;
                     break;
                 case "PROFILE":
-                    flags |= UserInfo.PROFILE_FLAGS_MASK;
+                    flags |= UserInfo.FLAG_PROFILE;
                     break;
                 default:
                     Slog.w(TAG, "SystemConfig contained an invalid user type: " + type);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5e49c7a..75cb372 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9727,13 +9727,9 @@
                     }
                 }
 
-                int userInfoFlags = 0;
-                if (ephemeral) {
-                    userInfoFlags |= UserInfo.FLAG_EPHEMERAL;
-                }
-                if (demo) {
-                    userInfoFlags |= UserInfo.FLAG_DEMO;
-                }
+                int userInfoFlags = ephemeral ? UserInfo.FLAG_EPHEMERAL : 0;
+                String userType = demo ? UserManager.USER_TYPE_FULL_DEMO
+                        : UserManager.USER_TYPE_FULL_SECONDARY;
                 String[] disallowedPackages = null;
                 if (!leaveAllSystemAppsEnabled) {
                     disallowedPackages = mOverlayPackagesProvider.getNonRequiredApps(admin,
@@ -9741,7 +9737,7 @@
                             new String[0]);
                 }
                 UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name,
-                        userInfoFlags, disallowedPackages);
+                        userType, userInfoFlags, disallowedPackages);
                 if (userInfo != null) {
                     user = userInfo.getUserHandle();
                 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
index d6f7e37..7916bd3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
@@ -71,7 +71,7 @@
             throws IOException, RemoteException, InterruptedException {
         for (int i = 0; i < NUM_ITERATIONS_STOP_USER; i++) {
             final UserInfo userInfo = mUserManager.createProfileForUser("TestUser",
-                    UserInfo.FLAG_MANAGED_PROFILE, mActivityManager.getCurrentUser());
+                    UserManager.USER_TYPE_PROFILE_MANAGED, 0, mActivityManager.getCurrentUser());
             assertNotNull(userInfo);
             try {
                 assertTrue(
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
index 8dd8967..e375aef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
@@ -16,15 +16,15 @@
 
 package com.android.server.pm;
 
+import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.app.ApplicationPackageManager;
 import android.content.pm.UserInfo;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
-import android.util.IconDrawableFactory;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
@@ -71,6 +71,7 @@
         removeUsers();
     }
 
+    /** Tests UMS.getProfileIds() when no specific userType is specified. */
     @Test
     public void testGetProfiles() {
         // Pretend we have a secondary user with a profile.
@@ -93,38 +94,73 @@
                 || users.get(1).id == profile.id);
     }
 
+    /** Tests UMS.getProfileIds() when a specific userType is specified. */
+    @Test
+    public void testGetProfileIds_specifyType() {
+        // Pretend we have a secondary user with a profile.
+        UserInfo secondaryUser = addUser();
+        UserInfo profile = addProfile(secondaryUser);
+
+        // TODO: When there are multiple profiles types, ensure correct output for mixed types.
+        final String userType1 = USER_TYPE_PROFILE_MANAGED;
+
+        // System user should still have no userType1 profile so getProfileIds should be empty.
+        int[] users = mUserManagerService.getProfileIds(UserHandle.USER_SYSTEM, userType1, false);
+        assertEquals("System user should have no managed profiles", 0, users.length);
+
+        // Secondary user should have one userType1 profile, so return just that.
+        users = mUserManagerService.getProfileIds(secondaryUser.id, userType1, false);
+        assertEquals("Wrong number of profiles", 1, users.length);
+        assertEquals("Wrong profile id", profile.id, users[0]);
+
+        // The profile itself is a userType1 profile, so it should return just itself.
+        users = mUserManagerService.getProfileIds(profile.id, userType1, false);
+        assertEquals("Wrong number of profiles", 1, users.length);
+        assertEquals("Wrong profile id", profile.id, users[0]);
+    }
+
     @Test
     public void testProfileBadge() {
         // First profile for system user should get badge 0
         assertEquals("First profile isn't given badge index 0", 0,
-                mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM));
+                mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM,
+                        USER_TYPE_PROFILE_MANAGED));
 
         // Pretend we have a secondary user.
         UserInfo secondaryUser = addUser();
 
         // Check first profile badge for secondary user is also 0.
         assertEquals("First profile for secondary user isn't given badge index 0", 0,
-                mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id));
+                mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id,
+                        USER_TYPE_PROFILE_MANAGED));
 
         // Shouldn't impact the badge for profile in system user
         assertEquals("First profile isn't given badge index 0 with secondary user", 0,
-                mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM));
+                mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM,
+                        USER_TYPE_PROFILE_MANAGED));
 
         // Pretend a secondary user has a profile.
         addProfile(secondaryUser);
 
         // Shouldn't have impacted the badge for the system user
         assertEquals("First profile isn't given badge index 0 in secondary user", 0,
-                mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM));
+                mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM,
+                        USER_TYPE_PROFILE_MANAGED));
     }
 
     @Test
     public void testProfileBadgeUnique() {
         List<UserInfo> users = mUserManagerService.getUsers(/* excludeDying */ false);
         UserInfo system = users.get(0);
+        int max = mUserManagerService.getMaxUsersOfTypePerParent(USER_TYPE_PROFILE_MANAGED);
+        if (max < 0) {
+            // Indicates no max. Instead of infinite, we'll just do 10.
+            max = 10;
+        }
         // Badges should get allocated 0 -> max
-        for (int i = 0; i < UserManagerService.getMaxManagedProfiles(); ++i) {
-            int nextBadge = mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM);
+        for (int i = 0; i < max; ++i) {
+            int nextBadge = mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM,
+                    USER_TYPE_PROFILE_MANAGED);
             assertEquals("Wrong badge allocated", i, nextBadge);
             UserInfo profile = addProfile(system);
             profile.profileBadge = nextBadge;
@@ -140,30 +176,23 @@
         mUserManagerService.addRemovingUserIdLocked(profile.id);
         // We should reuse the badge from the profile being removed.
         assertEquals("Badge index not reused while removing a user", 0,
-                mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id));
+                mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id,
+                        USER_TYPE_PROFILE_MANAGED));
 
         // Edge case of reuse that only applies if we ever support 3 managed profiles
         // We should prioritise using lower badge indexes
-        if (UserManagerService.getMaxManagedProfiles() > 2) {
+        int max = mUserManagerService.getMaxUsersOfTypePerParent(USER_TYPE_PROFILE_MANAGED);
+        if (max < 0 || max > 2) {
             UserInfo profileBadgeOne = addProfile(secondaryUser);
             profileBadgeOne.profileBadge = 1;
             // 0 and 2 are free, we should reuse 0 rather than 2.
             assertEquals("Lower index not used", 0,
-                    mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id));
+                    mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id,
+                            USER_TYPE_PROFILE_MANAGED));
         }
     }
 
     @Test
-    public void testNumberOfBadges() {
-        assertTrue("Max profiles greater than number of badges",
-                UserManagerService.MAX_MANAGED_PROFILES
-                <= IconDrawableFactory.CORP_BADGE_COLORS.length);
-        assertEquals("Num colors doesn't match number of badge labels",
-                IconDrawableFactory.CORP_BADGE_COLORS.length,
-                ApplicationPackageManager.CORP_BADGE_LABEL_RES_ID.length);
-    }
-
-    @Test
     public void testCanAddMoreManagedProfiles_removeProfile() {
         // if device is low-ram or doesn't support managed profiles for some other reason, just
         // skip the test
@@ -171,6 +200,10 @@
                 false /* disallow remove */)) {
             return;
         }
+        if (mUserManagerService.getMaxUsersOfTypePerParent(USER_TYPE_PROFILE_MANAGED) < 0) {
+            // Indicates no limit, so we cannot run this test;
+            return;
+        }
 
         // GIVEN we've reached the limit of managed profiles possible on the system user
         while (mUserManagerService.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM,
@@ -192,6 +225,10 @@
                 false /* disallow remove */)) {
             return;
         }
+        if (mUserManagerService.getMaxUsersOfTypePerParent(USER_TYPE_PROFILE_MANAGED) < 0) {
+            // Indicates no limit, so we cannot run this test;
+            return;
+        }
 
         // GIVEN we've reached the limit of managed profiles possible on the system user
         // GIVEN that the profiles are not enabled yet
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 6d5b994..1e760cc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -16,11 +16,29 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.UserInfo.FLAG_DEMO;
+import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
+import static android.content.pm.UserInfo.FLAG_FULL;
+import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.content.pm.UserInfo.FLAG_PROFILE;
+import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static android.content.pm.UserInfo.FLAG_SYSTEM;
+import static android.os.UserManager.USER_TYPE_FULL_DEMO;
+import static android.os.UserManager.USER_TYPE_FULL_GUEST;
+import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED;
+import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
+import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
+import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
+import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.annotation.UserIdInt;
 import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.UserHandle;
@@ -121,8 +139,61 @@
         assertEquals("A Name", mUserManagerService.getUserInfo(TEST_ID).name);
     }
 
+    /** Test UMS.getUserTypeForUser(). */
+    @Test
+    public void testGetUserTypeForUser() throws Exception {
+        final String typeSys = mUserManagerService.getUserTypeForUser(UserHandle.USER_SYSTEM);
+        assertTrue("System user was of invalid type " + typeSys,
+                typeSys.equals(USER_TYPE_SYSTEM_HEADLESS) || typeSys.equals(USER_TYPE_FULL_SYSTEM));
+
+        final int testId = 100;
+        final String typeName = "A type";
+        UserInfo userInfo = createUser(testId, 0, typeName);
+        mUserManagerService.putUserInfo(userInfo);
+        assertEquals(typeName, mUserManagerService.getUserTypeForUser(testId));
+    }
+
+    /** Tests upgradeIfNecessaryLP (but without locking) for upgrading from version 8 to 9+. */
+    @Test
+    public void testUpgradeIfNecessaryLP_9() {
+        final int versionToTest = 9;
+
+        mUserManagerService.putUserInfo(createUser(100, FLAG_MANAGED_PROFILE, null));
+        mUserManagerService.putUserInfo(createUser(101,
+                FLAG_GUEST | FLAG_EPHEMERAL | FLAG_FULL, null));
+        mUserManagerService.putUserInfo(createUser(102, FLAG_RESTRICTED | FLAG_FULL, null));
+        mUserManagerService.putUserInfo(createUser(103, FLAG_FULL, null));
+        mUserManagerService.putUserInfo(createUser(104, FLAG_SYSTEM, null));
+        mUserManagerService.putUserInfo(createUser(105, FLAG_SYSTEM | FLAG_FULL, null));
+        mUserManagerService.putUserInfo(createUser(106, FLAG_DEMO | FLAG_FULL, null));
+
+        mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1);
+
+        assertEquals(USER_TYPE_PROFILE_MANAGED, mUserManagerService.getUserTypeForUser(100));
+        assertTrue((mUserManagerService.getUserInfo(100).flags & FLAG_PROFILE) != 0);
+
+        assertEquals(USER_TYPE_FULL_GUEST, mUserManagerService.getUserTypeForUser(101));
+
+        assertEquals(USER_TYPE_FULL_RESTRICTED, mUserManagerService.getUserTypeForUser(102));
+        assertTrue((mUserManagerService.getUserInfo(102).flags & FLAG_PROFILE) == 0);
+
+        assertEquals(USER_TYPE_FULL_SECONDARY, mUserManagerService.getUserTypeForUser(103));
+        assertTrue((mUserManagerService.getUserInfo(103).flags & FLAG_PROFILE) == 0);
+
+        assertEquals(USER_TYPE_SYSTEM_HEADLESS, mUserManagerService.getUserTypeForUser(104));
+
+        assertEquals(USER_TYPE_FULL_SYSTEM, mUserManagerService.getUserTypeForUser(105));
+
+        assertEquals(USER_TYPE_FULL_DEMO, mUserManagerService.getUserTypeForUser(106));
+    }
+
+    /** Creates a UserInfo with the given flags and userType. */
+    private UserInfo createUser(@UserIdInt int userId, @UserInfoFlag int flags, String userType) {
+        return new UserInfo(userId, "A Name", "A path", flags, userType);
+    }
+
     private UserInfo createUser() {
-        UserInfo user = new UserInfo(/*id*/ 21, "A Name", "A path", /*flags*/ 0x0ff0ff);
+        UserInfo user = new UserInfo(/*id*/ 21, "A Name", "A path", /*flags*/ 0x0ff0ff, "A type");
         user.serialNumber = 5;
         user.creationTime = 4L << 32;
         user.lastLoggedInTime = 5L << 32;
@@ -141,6 +212,7 @@
         assertEquals("Name not preserved", one.name, two.name);
         assertEquals("Icon path not preserved", one.iconPath, two.iconPath);
         assertEquals("Flags not preserved", one.flags, two.flags);
+        assertEquals("UserType not preserved", one.userType, two.userType);
         assertEquals("profile group not preserved", one.profileGroupId,
                 two.profileGroupId);
         assertEquals("restricted profile parent not preseved", one.restrictedProfileParentId,
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
new file mode 100644
index 0000000..7aadd87
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.UserInfo.FLAG_DEMO;
+import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
+import static android.content.pm.UserInfo.FLAG_FULL;
+import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.content.pm.UserInfo.FLAG_PROFILE;
+import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static android.content.pm.UserInfo.FLAG_SYSTEM;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
+
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.os.UserManager;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Tests for {@link UserTypeDetails} and {@link UserTypeFactory}.
+ *
+ * <p>Run with: atest UserManagerServiceUserTypeTest
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class UserManagerServiceUserTypeTest {
+
+    @Test
+    public void testUserTypeBuilder_createUserType() {
+        UserTypeDetails type = new UserTypeDetails.Builder()
+                .setName("a.name")
+                .setEnabled(true)
+                .setMaxAllowed(21)
+                .setBaseType(FLAG_FULL)
+                .setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
+                .setBadgeLabels(23, 24, 25)
+                .setBadgeColors(26, 27)
+                .setIconBadge(28)
+                .setBadgePlain(29)
+                .setBadgeNoBackground(30)
+                .setLabel(31)
+                .setMaxAllowedPerParent(32)
+                .setDefaultRestrictions(new ArrayList<>(Arrays.asList("r1", "r2")))
+                .createUserTypeDetails();
+
+        assertEquals("a.name", type.getName());
+        assertTrue(type.isEnabled());
+        assertEquals(21, type.getMaxAllowed());
+        assertEquals(FLAG_FULL | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
+        assertEquals(28, type.getIconBadge());
+        assertEquals(29, type.getBadgePlain());
+        assertEquals(30, type.getBadgeNoBackground());
+        assertEquals(31, type.getLabel());
+        assertEquals(32, type.getMaxAllowedPerParent());
+        assertEquals(new ArrayList<>(Arrays.asList("r1", "r2")), type.getDefaultRestrictions());
+
+
+        assertEquals(23, type.getBadgeLabel(0));
+        assertEquals(24, type.getBadgeLabel(1));
+        assertEquals(25, type.getBadgeLabel(2));
+        assertEquals(25, type.getBadgeLabel(3));
+        assertEquals(25, type.getBadgeLabel(4));
+        assertEquals(Resources.ID_NULL, type.getBadgeLabel(-1));
+
+        assertEquals(26, type.getBadgeColor(0));
+        assertEquals(27, type.getBadgeColor(1));
+        assertEquals(27, type.getBadgeColor(2));
+        assertEquals(27, type.getBadgeColor(3));
+        assertEquals(Resources.ID_NULL, type.getBadgeColor(-100));
+
+        assertTrue(type.hasBadge());
+    }
+
+    @Test
+    public void testUserTypeBuilder_defaults() {
+        UserTypeDetails type = new UserTypeDetails.Builder()
+                .setName("name") // Required (no default allowed)
+                .setBaseType(FLAG_FULL) // Required (no default allowed)
+                .createUserTypeDetails();
+
+        assertTrue(type.isEnabled());
+        assertEquals(UserTypeDetails.UNLIMITED_NUMBER_OF_USERS, type.getMaxAllowed());
+        assertEquals(UserTypeDetails.UNLIMITED_NUMBER_OF_USERS, type.getMaxAllowedPerParent());
+        assertEquals(FLAG_FULL, type.getDefaultUserInfoFlags());
+        assertEquals(Resources.ID_NULL, type.getIconBadge());
+        assertEquals(Resources.ID_NULL, type.getBadgePlain());
+        assertEquals(Resources.ID_NULL, type.getBadgeNoBackground());
+        assertEquals(Resources.ID_NULL, type.getBadgeLabel(0));
+        assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
+        assertEquals(Resources.ID_NULL, type.getLabel());
+        assertTrue(type.getDefaultRestrictions().isEmpty());
+
+        assertFalse(type.hasBadge());
+    }
+
+    @Test
+    public void testUserTypeBuilder_nameIsRequired() {
+        assertThrows(IllegalArgumentException.class,
+                () -> new UserTypeDetails.Builder()
+                        .setMaxAllowed(21)
+                        .setBaseType(FLAG_FULL)
+                        .createUserTypeDetails());
+    }
+
+    @Test
+    public void testUserTypeBuilder_baseTypeIsRequired() {
+        assertThrows(IllegalArgumentException.class,
+                () -> new UserTypeDetails.Builder()
+                        .setName("name")
+                        .createUserTypeDetails());
+    }
+
+    @Test
+    public void testUserTypeBuilder_colorIsRequiredIfBadged() {
+        assertThrows(IllegalArgumentException.class,
+                () -> getMinimalBuilder()
+                        .setIconBadge(1)
+                        .setBadgeLabels(2)
+                        .createUserTypeDetails());
+    }
+
+    @Test
+    public void testUserTypeBuilder_badgeLabelIsRequiredIfBadged() {
+        assertThrows(IllegalArgumentException.class,
+                () -> getMinimalBuilder()
+                        .setIconBadge(1)
+                        .setBadgeColors(2)
+                        .createUserTypeDetails());
+    }
+
+    @Test
+    public void testCheckUserTypeConsistency() {
+        assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_GUEST));
+        assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_GUEST | FLAG_EPHEMERAL));
+        assertTrue(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE));
+
+        assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_DEMO | FLAG_RESTRICTED));
+        assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE | FLAG_SYSTEM));
+        assertFalse(UserManagerService.checkUserTypeConsistency(FLAG_PROFILE | FLAG_FULL));
+    }
+
+    @Test
+    public void testGetDefaultUserType() {
+        // Simple example.
+        assertEquals(UserManager.USER_TYPE_FULL_RESTRICTED,
+                UserInfo.getDefaultUserType(FLAG_RESTRICTED));
+
+        // Type plus a non-type flag.
+        assertEquals(UserManager.USER_TYPE_FULL_GUEST,
+                UserInfo.getDefaultUserType(FLAG_GUEST | FLAG_EPHEMERAL));
+
+        // Two types, which is illegal.
+        assertThrows(IllegalArgumentException.class,
+                () -> UserInfo.getDefaultUserType(FLAG_MANAGED_PROFILE | FLAG_GUEST));
+
+        // No type, which defaults to {@link UserManager#USER_TYPE_FULL_SECONDARY}.
+        assertEquals(UserManager.USER_TYPE_FULL_SECONDARY,
+                UserInfo.getDefaultUserType(FLAG_EPHEMERAL));
+    }
+
+    /** Returns a minimal {@link UserTypeDetails.Builder} that can legitimately be created. */
+    private UserTypeDetails.Builder getMinimalBuilder() {
+        return new UserTypeDetails.Builder().setName("name").setBaseType(FLAG_FULL);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index e9edba5..d071927 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.pm;
 
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -23,6 +24,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -196,6 +198,62 @@
         }
     }
 
+    /** Tests creating a FULL user via specifying userType. */
+    @MediumTest
+    public void testCreateUserViaTypes() throws Exception {
+        createUserWithTypeAndCheckFlags(UserManager.USER_TYPE_FULL_GUEST,
+                UserInfo.FLAG_GUEST | UserInfo.FLAG_FULL);
+
+        createUserWithTypeAndCheckFlags(UserManager.USER_TYPE_FULL_DEMO,
+                UserInfo.FLAG_DEMO | UserInfo.FLAG_FULL);
+
+        createUserWithTypeAndCheckFlags(UserManager.USER_TYPE_FULL_SECONDARY,
+                UserInfo.FLAG_FULL);
+    }
+
+    /** Tests creating a FULL user via specifying user flags. */
+    @MediumTest
+    public void testCreateUserViaFlags() throws Exception {
+        createUserWithFlagsAndCheckType(UserInfo.FLAG_GUEST, UserManager.USER_TYPE_FULL_GUEST,
+                UserInfo.FLAG_FULL);
+
+        createUserWithFlagsAndCheckType(0, UserManager.USER_TYPE_FULL_SECONDARY,
+                UserInfo.FLAG_FULL);
+
+        createUserWithFlagsAndCheckType(UserInfo.FLAG_FULL, UserManager.USER_TYPE_FULL_SECONDARY,
+                0);
+
+        createUserWithFlagsAndCheckType(UserInfo.FLAG_DEMO, UserManager.USER_TYPE_FULL_DEMO,
+                UserInfo.FLAG_FULL);
+    }
+
+    /** Creates a user of the given user type and checks that the result has the requiredFlags. */
+    private void createUserWithTypeAndCheckFlags(String userType,
+            @UserIdInt int requiredFlags) {
+        final UserInfo userInfo = createUser("Name", userType, 0);
+        assertEquals("Wrong user type", userType, userInfo.userType);
+        assertEquals(
+                "Flags " + userInfo.flags + " did not contain expected " + requiredFlags,
+                requiredFlags, userInfo.flags & requiredFlags);
+        removeUser(userInfo.id);
+    }
+
+    /**
+     * Creates a user of the given flags and checks that the result is of the expectedUserType type
+     * and that it has the expected flags (including both flags and any additionalRequiredFlags).
+     */
+    private void createUserWithFlagsAndCheckType(@UserIdInt int flags, String expectedUserType,
+            @UserIdInt int additionalRequiredFlags) {
+        final UserInfo userInfo = createUser("Name", flags);
+        assertEquals("Wrong user type", expectedUserType, userInfo.userType);
+        additionalRequiredFlags |= flags;
+        assertEquals(
+                "Flags " + userInfo.flags + " did not contain expected " + additionalRequiredFlags,
+                additionalRequiredFlags, userInfo.flags & additionalRequiredFlags);
+        removeUser(userInfo.id);
+    }
+
+
     @MediumTest
     public void testAddGuest() throws Exception {
         UserInfo userInfo1 = createUser("Guest 1", UserInfo.FLAG_GUEST);
@@ -234,7 +292,7 @@
         final int primaryUserId = mUserManager.getPrimaryUser().id;
 
         UserInfo userInfo = createProfileForUser("Profile",
-                UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+                UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
         assertNotNull(userInfo);
         assertNull(mUserManager.getProfileParent(primaryUserId));
         UserInfo parentProfileInfo = mUserManager.getProfileParent(userInfo.id);
@@ -244,17 +302,61 @@
         assertNull(mUserManager.getProfileParent(primaryUserId));
     }
 
+    /** Test that UserManager returns the correct badge information for a managed profile. */
+    @MediumTest
+    public void testProfileTypeInformation() throws Exception {
+        final UserTypeDetails userTypeDetails =
+                UserTypeFactory.getUserTypes().get(UserManager.USER_TYPE_PROFILE_MANAGED);
+        assertNotNull("No " + UserManager.USER_TYPE_PROFILE_MANAGED + " type on device",
+                userTypeDetails);
+        assertEquals(UserManager.USER_TYPE_PROFILE_MANAGED, userTypeDetails.getName());
+
+        final int primaryUserId = mUserManager.getPrimaryUser().id;
+        UserInfo userInfo = createProfileForUser("Managed",
+                UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
+        assertNotNull(userInfo);
+        final int userId = userInfo.id;
+        final UserHandle userHandle = new UserHandle(userId);
+
+        assertEquals(userTypeDetails.hasBadge(),
+                mUserManager.hasBadge(userId));
+        assertEquals(userTypeDetails.getIconBadge(),
+                mUserManager.getUserIconBadgeResId(userId));
+        assertEquals(userTypeDetails.getBadgePlain(),
+                mUserManager.getUserBadgeResId(userId));
+        assertEquals(userTypeDetails.getBadgeNoBackground(),
+                mUserManager.getUserBadgeNoBackgroundResId(userId));
+        assertEquals(userTypeDetails.isProfile(),
+                mUserManager.isProfile(userId));
+        assertEquals(userTypeDetails.getName(),
+                mUserManager.getUserTypeForUser(userHandle));
+
+        final int badgeIndex = userInfo.profileBadge;
+        assertEquals(
+                Resources.getSystem().getColor(userTypeDetails.getBadgeColor(badgeIndex), null),
+                mUserManager.getUserBadgeColor(userId));
+        assertEquals(
+                Resources.getSystem().getString(userTypeDetails.getBadgeLabel(badgeIndex), "Test"),
+                mUserManager.getBadgedLabelForUser("Test", userHandle));
+    }
+
     // Make sure only one managed profile can be created
     @MediumTest
     public void testAddManagedProfile() throws Exception {
         final int primaryUserId = mUserManager.getPrimaryUser().id;
         UserInfo userInfo1 = createProfileForUser("Managed 1",
-                UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+                UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
         UserInfo userInfo2 = createProfileForUser("Managed 2",
-                UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+                UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
 
         assertNotNull(userInfo1);
         assertNull(userInfo2);
+
+        assertEquals(userInfo1.userType, UserManager.USER_TYPE_PROFILE_MANAGED);
+        int requiredFlags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_PROFILE;
+        assertEquals("Wrong flags " + userInfo1.flags, requiredFlags,
+                userInfo1.flags & requiredFlags);
+
         // Verify that current user is not a managed profile
         assertFalse(mUserManager.isManagedProfile());
     }
@@ -264,7 +366,7 @@
     public void testAddManagedProfile_withDisallowedPackages() throws Exception {
         final int primaryUserId = mUserManager.getPrimaryUser().id;
         UserInfo userInfo1 = createProfileForUser("Managed1",
-                UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+                UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
         // Verify that the packagesToVerify are installed by default.
         for (String pkg : PACKAGES) {
             assertTrue("Package should be installed in managed profile: " + pkg,
@@ -273,7 +375,7 @@
         removeUser(userInfo1.id);
 
         UserInfo userInfo2 = createProfileForUser("Managed2",
-                UserInfo.FLAG_MANAGED_PROFILE, primaryUserId, PACKAGES);
+                UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId, PACKAGES);
         // Verify that the packagesToVerify are not installed by default.
         for (String pkg : PACKAGES) {
             assertFalse("Package should not be installed in managed profile when disallowed: "
@@ -287,7 +389,7 @@
     public void testAddManagedProfile_disallowedPackagesInstalledLater() throws Exception {
         final int primaryUserId = mUserManager.getPrimaryUser().id;
         UserInfo userInfo = createProfileForUser("Managed",
-                UserInfo.FLAG_MANAGED_PROFILE, primaryUserId, PACKAGES);
+                UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId, PACKAGES);
         // Verify that the packagesToVerify are not installed by default.
         for (String pkg : PACKAGES) {
             assertFalse("Package should not be installed in managed profile when disallowed: "
@@ -326,7 +428,7 @@
                 primaryUserHandle);
         try {
             UserInfo userInfo = createProfileForUser("Managed",
-                    UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+                    UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
             assertNull(userInfo);
         } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
@@ -343,7 +445,7 @@
                 primaryUserHandle);
         try {
             UserInfo userInfo = createProfileEvenWhenDisallowedForUser("Managed",
-                    UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+                    UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
             assertNotNull(userInfo);
         } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
@@ -359,7 +461,7 @@
         mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, primaryUserHandle);
         try {
             UserInfo userInfo = createProfileForUser("Managed",
-                    UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+                    UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
             assertNotNull(userInfo);
         } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false,
@@ -396,7 +498,7 @@
         final int primaryUserId = mUserManager.getPrimaryUser().id;
         final long startTime = System.currentTimeMillis();
         UserInfo profile = createProfileForUser("Managed 1",
-                UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+                UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
         final long endTime = System.currentTimeMillis();
         assertNotNull(profile);
         if (System.currentTimeMillis() > EPOCH_PLUS_30_YEARS) {
@@ -663,24 +765,32 @@
         return user;
     }
 
-    private UserInfo createProfileForUser(String name, int flags, int userHandle) {
-        return createProfileForUser(name, flags, userHandle, null);
+    private UserInfo createUser(String name, String userType, int flags) {
+        UserInfo user = mUserManager.createUser(name, userType, flags);
+        if (user != null) {
+            usersToRemove.add(user.id);
+        }
+        return user;
     }
 
-    private UserInfo createProfileForUser(String name, int flags, int userHandle,
+    private UserInfo createProfileForUser(String name, String userType, int userHandle) {
+        return createProfileForUser(name, userType, userHandle, null);
+    }
+
+    private UserInfo createProfileForUser(String name, String userType, int userHandle,
             String[] disallowedPackages) {
         UserInfo profile = mUserManager.createProfileForUser(
-                name, flags, userHandle, disallowedPackages);
+                name, userType, 0, userHandle, disallowedPackages);
         if (profile != null) {
             usersToRemove.add(profile.id);
         }
         return profile;
     }
 
-    private UserInfo createProfileEvenWhenDisallowedForUser(String name, int flags,
+    private UserInfo createProfileEvenWhenDisallowedForUser(String name, String userType,
             int userHandle) {
         UserInfo profile = mUserManager.createProfileForUserEvenWhenDisallowed(
-                name, flags, userHandle, null);
+                name, userType, 0, userHandle, null);
         if (profile != null) {
             usersToRemove.add(profile.id);
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index f0b0328..f492932 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -147,7 +147,7 @@
 
         final ArrayMap<String, Integer> expectedOutput = getNewPackageToWhitelistedFlagsMap();
         expectedOutput.put("com.android.package1",
-                UserInfo.PROFILE_FLAGS_MASK | FLAG_SYSTEM | FLAG_GUEST);
+                UserInfo.FLAG_PROFILE | FLAG_SYSTEM | FLAG_GUEST);
         expectedOutput.put("com.android.package2",
                 UserInfo.FLAG_MANAGED_PROFILE);
 
@@ -376,9 +376,9 @@
 
     /** Sets the whitelist mode to the desired value via adb's setprop. */
     private void setUserTypePackageWhitelistMode(int mode) {
-        UiDevice mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         try {
-            String result = mUiDevice.executeShellCommand(String.format("setprop %s %d",
+            String result = uiDevice.executeShellCommand(String.format("setprop %s %d",
                     PACKAGE_WHITELIST_MODE_PROP, mode));
             assertFalse("Failed to set sysprop " + PACKAGE_WHITELIST_MODE_PROP + ": " + result,
                     result != null && result.contains("Failed"));
@@ -390,7 +390,7 @@
     private ArrayMap<String, Integer> getNewPackageToWhitelistedFlagsMap() {
         final ArrayMap<String, Integer> pkgFlagMap = new ArrayMap<>();
         // "android" is always treated as whitelisted, regardless of the xml file.
-        pkgFlagMap.put("android", FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.PROFILE_FLAGS_MASK);
+        pkgFlagMap.put("android", FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.FLAG_PROFILE);
         return pkgFlagMap;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserTests.java b/services/tests/servicestests/src/com/android/server/pm/UserTests.java
new file mode 100644
index 0000000..525382d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/UserTests.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+        UserDataPreparerTest.class,
+        UserLifecycleStressTest.class,
+        UserManagerServiceCreateProfileTest.class,
+        UserManagerServiceIdRecyclingTest.class,
+        UserManagerServiceTest.class,
+        UserManagerServiceUserInfoTest.class,
+        UserManagerServiceUserTypeTest.class,
+        UserManagerTest.class,
+        UserRestrictionsUtilsTest.class,
+        UserSystemPackageInstallerTest.class,
+})
+public class UserTests {
+}
+