Merge "Introduce user types"
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 5661347..0113f69 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 b096049..ed21b72 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
@@ -1479,6 +1532,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()}
      */
@@ -1589,6 +1715,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.
@@ -1612,7 +1780,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.
@@ -1632,23 +1801,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.
@@ -2120,23 +2272,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()) {
@@ -2150,19 +2323,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.
      *
@@ -2171,9 +2342,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();
         }
@@ -2188,7 +2359,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);
@@ -2202,6 +2373,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.
@@ -2209,20 +2381,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.
      *
@@ -2230,28 +2426,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();
         }
@@ -2595,6 +2792,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;
@@ -2625,6 +2824,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.
@@ -2858,8 +3073,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)}.
@@ -2880,8 +3199,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
@@ -2911,11 +3230,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
@@ -2923,7 +3246,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 4361676..31ceb38 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 525d357..232374c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2453,27 +2453,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) {
@@ -2490,16 +2503,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) {
@@ -3421,9 +3438,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 5c9b9c9..8ddfad9 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;
@@ -1116,6 +1144,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");
@@ -1139,11 +1206,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();
         }
     }
 
@@ -1896,33 +2030,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")
@@ -2207,9 +2401,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);
@@ -2298,6 +2501,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);
@@ -2320,12 +2561,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;
@@ -2410,6 +2650,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));
@@ -2570,6 +2811,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;
@@ -2613,6 +2855,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);
@@ -2678,7 +2922,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;
@@ -2745,108 +2989,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;
             }
         }
 
@@ -2857,10 +3128,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;
@@ -2874,21 +3146,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)) {
@@ -2908,30 +3182,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;
@@ -2940,7 +3207,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);
@@ -2966,7 +3233,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);
@@ -2978,6 +3245,7 @@
             }
             updateUserIds();
             Bundle restrictions = new Bundle();
+            // TODO(b/142482943): Generalize this using UserTypeDetails default restrictions.
             if (isGuest) {
                 synchronized (mGuestRestrictions) {
                     restrictions.putAll(mGuestRestrictions);
@@ -3026,6 +3294,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);
@@ -3045,39 +3329,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;
                 }
@@ -3087,6 +3355,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();
@@ -3111,7 +3391,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;
         }
@@ -3217,6 +3497,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 {
@@ -3264,6 +3550,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
@@ -4035,6 +4322,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: ");
@@ -4119,11 +4407,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);
@@ -4322,10 +4619,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);
@@ -4410,7 +4707,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 "
@@ -4529,36 +4826,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 4654cca..95197b0 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -411,12 +411,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) {
@@ -442,7 +443,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 9dac03f..f1dbd7b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9724,13 +9724,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,
@@ -9738,7 +9734,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 {
+}
+