diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java index e74e4a958eb9f..278a78676a0b1 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 @@ public class UserLifecycleTests { /** 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 03ef286c48c13..9165a9510f84f 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -92,7 +92,6 @@ import android.text.TextUtils; 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 @@ public class ApplicationPackageManager extends PackageManager { @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 @@ public class ApplicationPackageManager extends PackageManager { 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 @@ public class ApplicationPackageManager extends PackageManager { @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 @@ public class ApplicationPackageManager extends PackageManager { 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 @@ public class ApplicationPackageManager extends PackageManager { @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 @@ public class ApplicationPackageManager extends PackageManager { 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 1e88ce7e3f5ca..42d64d81b17e7 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.UserHandle; 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 @@ import java.lang.annotation.RetentionPolicy; * Per-user information. * *
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: *
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 class UserInfo implements Parcelable { 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 @@ public class UserInfo implements Parcelable { FLAG_EPHEMERAL, FLAG_DEMO, FLAG_FULL, - FLAG_SYSTEM + FLAG_SYSTEM, + FLAG_PROFILE }) @Retention(RetentionPolicy.SOURCE) public @interface UserInfoFlag { @@ -170,6 +181,13 @@ public class UserInfo implements Parcelable { @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 @@ public class UserInfo implements Parcelable { @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 class UserInfo implements Parcelable { */ 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. + + *
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 @@ public class UserInfo implements Parcelable {
@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 class UserInfo implements Parcelable {
}
public boolean isDemo() {
- return (flags & FLAG_DEMO) == FLAG_DEMO;
+ return UserManager.isUserTypeDemo(userType);
}
public boolean isFull() {
@@ -304,7 +364,7 @@ public class UserInfo implements Parcelable {
// Don't support switching to an ephemeral user with removal in progress.
return false;
}
- return !isManagedProfile();
+ return !isProfile();
}
/**
@@ -316,9 +376,10 @@ public class UserInfo implements Parcelable {
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 @@ public class UserInfo implements Parcelable {
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 @@ public class UserInfo implements Parcelable {
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 class UserInfo implements Parcelable {
public String toFullString() {
return "UserInfo[id=" + id
+ ", name=" + name
+ + ", type=" + userType
+ ", flags=" + flagsToString(flags)
+ (preCreated ? " (pre-created)" : "")
+ (partial ? " (partial)" : "")
@@ -387,6 +451,7 @@ public class UserInfo implements Parcelable {
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 @@ public class UserInfo implements Parcelable {
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 e8cc73f43a3d4..40048d9154deb 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -35,77 +35,84 @@ interface IUserManager {
/*
* 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 Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
* @param name the user's name
- * @param flags UserInfo flags that identify the type of user and other properties.
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
+ * @param flags UserInfo flags that specify user properties.
* @see UserInfo
*
* @return the UserInfo object for the created user, or {@code null} if the user could not be
* created.
* @hide
*/
- @UnsupportedAppUsage
- public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
+ public @Nullable UserInfo createUser(@Nullable String name, @NonNull String userType,
+ @UserInfoFlag int flags) {
UserInfo user = null;
try {
- user = mService.createUser(name, flags);
+ user = mService.createUser(name, userType, flags);
// TODO: Keep this in sync with
// UserManagerService.LocalService.createUserEvenWhenDisallowed
if (user != null && !user.isAdmin() && !user.isDemo()) {
@@ -2098,19 +2271,17 @@ public class UserManager {
}
/**
- * 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.
*
* 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.
*
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
- * @param flags UserInfo flags that identify the type of user and other properties.
- * @see UserInfo
- *
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
* @return the UserInfo object for the created user, or {@code null} if the user could not be
* created.
*
@@ -2119,9 +2290,9 @@ public class UserManager {
*
* @hide
*/
- public @Nullable UserInfo preCreateUser(@UserInfoFlag int flags) {
+ public @Nullable UserInfo preCreateUser(@NonNull String userType) {
try {
- return mService.preCreateUser(flags);
+ return mService.preCreateUser(userType);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2136,7 +2307,7 @@ public class UserManager {
public UserInfo createGuest(Context context, String name) {
UserInfo guest = null;
try {
- guest = mService.createUser(name, UserInfo.FLAG_GUEST);
+ guest = mService.createUser(name, USER_TYPE_FULL_GUEST, 0);
if (guest != null) {
Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
@@ -2150,6 +2321,7 @@ public class UserManager {
/**
* Creates a user with the specified name and options as a profile of another user.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * The type of profile must be specified using the given flags.
*
* @param name the user's name
* @param flags flags that identify the type of user and other properties.
@@ -2157,20 +2329,44 @@ public class UserManager {
*
* @return the {@link UserInfo} object for the created user, or null if the user
* could not be created.
+ * @throws IllegalArgumentException if flags do not correspond to a valid user type.
+ * @deprecated Use {@link #createProfileForUser(String, String, int, int)} instead.
* @hide
*/
@UnsupportedAppUsage
- public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userId) {
- return createProfileForUser(name, flags, userId, null);
+ @Deprecated
+ public UserInfo createProfileForUser(String name, @UserInfoFlag int flags,
+ @UserIdInt int userId) {
+ return createProfileForUser(name, UserInfo.getDefaultUserType(flags), flags,
+ userId, null);
}
/**
- * Version of {@link #createProfileForUser(String, int, int)} that allows you to specify
+ * Creates a user with the specified name and options as a profile of another user.
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ *
+ * @param name the user's name
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ * @param flags UserInfo flags that specify user properties.
+ * @param userId new user will be a profile of this user.
+ *
+ * @return the {@link UserInfo} object for the created user, or null if the user
+ * could not be created.
+ * @hide
+ */
+ public UserInfo createProfileForUser(String name, @NonNull String userType,
+ @UserInfoFlag int flags, @UserIdInt int userId) {
+ return createProfileForUser(name, userType, flags, userId, null);
+ }
+
+ /**
+ * Version of {@link #createProfileForUser(String, String, int, int)} that allows you to specify
* any packages that should not be installed in the new profile by default, these packages can
* still be installed later by the user if needed.
*
* @param name the user's name
- * @param flags flags that identify the type of user and other properties.
+ * @param userType the type of user, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+ * @param flags UserInfo flags that specify user properties.
* @param userId new user will be a profile of this user.
* @param disallowedPackages packages that will not be installed in the profile being created.
*
@@ -2178,28 +2374,29 @@ public class UserManager {
* could not be created.
* @hide
*/
- public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userId,
- String[] disallowedPackages) {
+ public UserInfo createProfileForUser(String name, @NonNull String userType,
+ @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages) {
try {
- return mService.createProfileForUser(name, flags, userId, disallowedPackages);
+ return mService.createProfileForUser(name, userType, flags, userId, disallowedPackages);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
/**
- * Similar to {@link #createProfileForUser(String, int, int, String[])}
+ * Similar to {@link #createProfileForUser(String, String, int, int, String[])}
* except bypassing the checking of {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
- * @see #createProfileForUser(String, int, int, String[])
+ * @see #createProfileForUser(String, String, int, int, String[])
* @hide
*/
- public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags,
- @UserIdInt int userId, String[] disallowedPackages) {
+ public UserInfo createProfileForUserEvenWhenDisallowed(String name,
+ @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId,
+ String[] disallowedPackages) {
try {
- return mService.createProfileForUserEvenWhenDisallowed(name, flags, userId,
- disallowedPackages);
+ return mService.createProfileForUserEvenWhenDisallowed(name, userType, flags,
+ userId, disallowedPackages);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2541,6 +2738,8 @@ public class UserManager {
* @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 To check whether a badge color is expected for the user, first call {@link #hasBadge}.
+ *
+ * @return the color (not the resource ID) to be used for the user's badge
+ * @throws Resources.NotFoundException if no valid badge color exists for this user
+ *
+ * @see #getBadgedIconForUser more information about badging in general
+ * @hide
+ */
+ public @ColorInt int getUserBadgeColor(@UserIdInt int userId) {
+ try {
+ final int resourceId = mService.getUserBadgeColorResId(userId);
+ return Resources.getSystem().getColor(resourceId, null);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the Resource ID of the user's icon badge.
+ *
+ * @return the Resource ID of the user's icon badge if it has one; otherwise
+ * {@link Resources#ID_NULL}.
+ *
+ * @see #getBadgedIconForUser more information about badging in general
+ * @hide
+ */
+ public @DrawableRes int getUserIconBadgeResId(@UserIdInt int userId) {
+ try {
+ return mService.getUserIconBadgeResId(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the Resource ID of the user's badge.
+ *
+ * @return the Resource ID of the user's badge if it has one; otherwise
+ * {@link Resources#ID_NULL}.
+ *
+ * @see #getBadgedIconForUser more information about badging in general
+ * @hide
+ */
+ public @DrawableRes int getUserBadgeResId(@UserIdInt int userId) {
+ try {
+ return mService.getUserBadgeResId(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the Resource ID of the user's badge without a background.
+ *
+ * @return the Resource ID of the user's no-background badge if it has one; otherwise
+ * {@link Resources#ID_NULL}.
+ *
+ * @see #getBadgedIconForUser more information about badging in general
+ * @hide
+ */
+ public @DrawableRes int getUserBadgeNoBackgroundResId(@UserIdInt int userId) {
+ try {
+ return mService.getUserBadgeNoBackgroundResId(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * If the target user is a profile of the calling user or the caller
+ * is itself a profile, then this returns a badged copy of the given
* icon to be able to distinguish it from the original icon. For badging an
* arbitrary drawable use {@link #getBadgedDrawableForUser(
* android.graphics.drawable.Drawable, UserHandle, android.graphics.Rect, int)}.
@@ -2826,8 +3145,8 @@ public class UserManager {
}
/**
- * If the target user is a managed profile of the calling user or the caller
- * is itself a managed profile, then this returns a badged copy of the given
+ * If the target user is a profile of the calling user or the caller
+ * is itself a profile, then this returns a badged copy of the given
* drawable allowing the user to distinguish it from the original drawable.
* The caller can specify the location in the bounds of the drawable to be
* badged where the badge should be applied as well as the density of the
@@ -2857,11 +3176,15 @@ public class UserManager {
}
/**
- * 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.
*
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
+ * must be in the same profile group of specified user.
+ *
* @param label The label to change.
* @param user The target user.
* @return A label that combines the original label and a badge as
@@ -2869,7 +3192,16 @@ public class UserManager {
* @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 d90b65e221716..d86ebf3a44b34 100644
--- a/core/java/android/util/IconDrawableFactory.java
+++ b/core/java/android/util/IconDrawableFactory.java
@@ -26,8 +26,6 @@ import android.graphics.drawable.Drawable;
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 @@ public class IconDrawableFactory {
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 @@ public class IconDrawableFactory {
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 8501eb5883d5e..e652e17bceb35 100644
--- a/core/java/android/util/LauncherIcons.java
+++ b/core/java/android/util/LauncherIcons.java
@@ -106,9 +106,11 @@ public final class LauncherIcons {
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 0000000000000..5fc3ba1fc5382
--- /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 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}.
+ *
+ * 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).
+ *
+ * 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}.
+ *
+ * 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 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).
+ * 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 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 @@ public abstract class UserManagerInternal {
/**
* 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 @@ public abstract class UserManagerInternal {
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 0ea913f340b00..83e3da597e297 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 @@ class UserController implements Handler.Callback {
// 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 2b6c347fe726b..bd9566728b1be 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 @@ public class LauncherAppsService extends SystemService {
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 f1c84b8701ba6..7e7da4c137325 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2414,27 +2414,40 @@ class PackageManagerShellCommand extends ShellCommand {
String name;
int userId = -1;
int flags = 0;
+ String userType = null;
String opt;
boolean preCreateOnly = false;
while ((opt = getNextOption()) != null) {
+ String newUserType = null;
if ("--profileOf".equals(opt)) {
userId = UserHandle.parseUserArg(getNextArgRequired());
} else if ("--managed".equals(opt)) {
- flags |= UserInfo.FLAG_MANAGED_PROFILE;
+ newUserType = UserManager.USER_TYPE_PROFILE_MANAGED;
} else if ("--restricted".equals(opt)) {
- flags |= UserInfo.FLAG_RESTRICTED;
+ newUserType = UserManager.USER_TYPE_FULL_RESTRICTED;
+ } else if ("--guest".equals(opt)) {
+ newUserType = UserManager.USER_TYPE_FULL_GUEST;
+ } else if ("--demo".equals(opt)) {
+ newUserType = UserManager.USER_TYPE_FULL_DEMO;
} else if ("--ephemeral".equals(opt)) {
flags |= UserInfo.FLAG_EPHEMERAL;
- } else if ("--guest".equals(opt)) {
- flags |= UserInfo.FLAG_GUEST;
- } else if ("--demo".equals(opt)) {
- flags |= UserInfo.FLAG_DEMO;
} else if ("--pre-create-only".equals(opt)) {
preCreateOnly = true;
+ } else if ("--user-type".equals(opt)) {
+ newUserType = getNextArgRequired();
} else {
getErrPrintWriter().println("Error: unknown option " + opt);
return 1;
}
+ // Ensure only one user-type was specified.
+ if (newUserType != null) {
+ if (userType != null && !userType.equals(newUserType)) {
+ getErrPrintWriter().println("Error: more than one user type was specified ("
+ + userType + " and " + newUserType + ")");
+ return 1;
+ }
+ userType = newUserType;
+ }
}
String arg = getNextArg();
if (arg == null && !preCreateOnly) {
@@ -2451,16 +2464,20 @@ class PackageManagerShellCommand extends ShellCommand {
ServiceManager.getService(Context.USER_SERVICE));
IAccountManager accm = IAccountManager.Stub.asInterface(
ServiceManager.getService(Context.ACCOUNT_SERVICE));
- if ((flags & UserInfo.FLAG_RESTRICTED) != 0) {
+ if (userType == null) {
+ userType = UserInfo.getDefaultUserType(flags);
+ }
+ if (UserManager.isUserTypeRestricted(userType)) {
// In non-split user mode, userId can only be SYSTEM
int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
info = um.createRestrictedProfile(name, parentUserId);
accm.addSharedAccountsFromParentUser(parentUserId, userId,
(Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
} else if (userId < 0) {
- info = preCreateOnly ? um.preCreateUser(flags) : um.createUser(name, flags);
+ info = preCreateOnly ?
+ um.preCreateUser(userType) : um.createUser(name, userType, flags);
} else {
- info = um.createProfileForUser(name, flags, userId, null);
+ info = um.createProfileForUser(name, userType, flags, userId, null);
}
if (info != null) {
@@ -3375,9 +3392,15 @@ class PackageManagerShellCommand extends ShellCommand {
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 4d2512c264226..4b047264a11ee 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_EXCLUDE_FROM_RECENTS;
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.os.storage.StorageManager;
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 @@ public class UserManagerService extends IUserManager.Stub {
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 @@ public class UserManagerService extends IUserManager.Stub {
@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
@@ -303,6 +303,12 @@ public class UserManagerService extends IUserManager.Stub {
@GuardedBy("mUsersLock")
private final SparseArray 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 @@ public class UserManagerService extends IUserManager.Stub {
/** Assume permissions already checked and caller's identity cleared */
@GuardedBy("mUsersLock")
- private List 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 @@ public class UserManagerService extends IUserManager.Stub {
if (profile.partial) {
continue;
}
+ if (userType != null && !userType.equals(profile.userType)) {
+ continue;
+ }
result.add(profile.id);
}
return result;
@@ -1110,6 +1138,45 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ /**
+ * 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.
+ * 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.
+ * No permissions checks are made (but userId checks may be made).
+ */
+ private @Nullable UserTypeDetails getUserTypeDetailsNoChecks(@UserIdInt int userId) {
+ final String typeStr = getUserTypeNoChecks(userId);
+ return typeStr != null ? mUserTypes.get(typeStr) : null;
+ }
+
+ /**
+ * Returns the UserTypeDetails of the given userInfo's user type (or null for a null userInfo).
+ */
+ private @Nullable UserTypeDetails getUserTypeDetails(@Nullable UserInfo userInfo) {
+ final String typeStr = userInfo != null ? userInfo.userType : null;
+ return typeStr != null ? mUserTypes.get(typeStr) : null;
+ }
+
@Override
public UserInfo getUserInfo(@UserIdInt int userId) {
checkManageOrCreateUsersPermission("query user");
@@ -1133,11 +1200,78 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
- public int getManagedProfileBadge(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getManagedProfileBadge");
+ public boolean hasBadge(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "hasBadge");
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+ return userTypeDetails != null && userTypeDetails.hasBadge();
+ }
+
+ @Override
+ public @StringRes int getUserBadgeLabelResId(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeLabelResId");
+ final UserInfo userInfo = getUserInfoNoChecks(userId);
+ final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
+ if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
+ Slog.e(LOG_TAG, "Requested badge label for non-badged user " + userId);
+ return Resources.ID_NULL;
+ }
+ final int badgeIndex = userInfo.profileBadge;
+ return userTypeDetails.getBadgeLabel(badgeIndex);
+ }
+
+ @Override
+ public @ColorRes int getUserBadgeColorResId(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeColorResId");
+ final UserInfo userInfo = getUserInfoNoChecks(userId);
+ final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
+ if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
+ Slog.e(LOG_TAG, "Requested badge color for non-badged user " + userId);
+ return Resources.ID_NULL;
+ }
+ final int badgeIndex = userInfo.profileBadge;
+ return userTypeDetails.getBadgeColor(badgeIndex);
+ }
+
+ @Override
+ public @DrawableRes int getUserIconBadgeResId(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserIconBadgeResId");
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+ if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
+ Slog.e(LOG_TAG, "Requested icon badge for non-badged user " + userId);
+ return Resources.ID_NULL;
+ }
+ return userTypeDetails.getIconBadge();
+ }
+
+ @Override
+ public @DrawableRes int getUserBadgeResId(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeResId");
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+ if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
+ Slog.e(LOG_TAG, "Requested badge for non-badged user " + userId);
+ return Resources.ID_NULL;
+ }
+ return userTypeDetails.getBadgePlain();
+ }
+
+ @Override
+ public @DrawableRes int getUserBadgeNoBackgroundResId(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId,
+ "getUserBadgeNoBackgroundResId");
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+ if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
+ Slog.e(LOG_TAG, "Requested badge (no background) for non-badged user " + userId);
+ return Resources.ID_NULL;
+ }
+ return userTypeDetails.getBadgeNoBackground();
+ }
+
+ @Override
+ public boolean isProfile(@UserIdInt int userId) {
+ checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isProfile");
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
- return userInfo != null ? userInfo.profileBadge : 0;
+ return userInfo != null && userInfo.isProfile();
}
}
@@ -1888,33 +2022,93 @@ public class UserManagerService extends IUserManager.Stub {
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).
+ *
+ * For checking whether more profiles can be added to a particular parent use
+ * {@link #canAddMoreProfilesToUser}.
+ */
+ private boolean canAddMoreUsersOfType(UserTypeDetails userTypeDetails) {
+ final int max = userTypeDetails.getMaxAllowed();
+ if (max == UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+ return true; // Indicates that there is no max.
+ }
+ return getNumberOfUsersOfType(userTypeDetails.getName()) < max;
+ }
+
+ /**
+ * Gets the number of users of the given user type.
+ * Does not include users that are about to die.
+ */
+ private int getNumberOfUsersOfType(String userType) {
+ int count = 0;
+ synchronized (mUsersLock) {
+ final int size = mUsers.size();
+ for (int i = 0; i < size; i++) {
+ final UserInfo user = mUsers.valueAt(i).info;
+ if (user.userType.equals(userType)
+ && !user.guestToRemove
+ && !mRemovingUserIds.get(user.id)
+ && !user.preCreated) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
@Override
public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) {
- checkManageUsersPermission("check if more managed profiles can be added.");
- if (ActivityManager.isLowRamDeviceStatic()) {
+ return canAddMoreProfilesToUser(UserManager.USER_TYPE_PROFILE_MANAGED, userId,
+ allowedToRemoveOne);
+ }
+
+ /** Returns whether more profiles of the given type can be added to the given parent userId. */
+ @Override
+ public boolean canAddMoreProfilesToUser(String userType, @UserIdInt int userId,
+ boolean allowedToRemoveOne) {
+ checkManageUsersPermission("check if more profiles can be added.");
+ final UserTypeDetails type = mUserTypes.get(userType);
+ if (type == null) {
return false;
}
- if (!mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_MANAGED_USERS)) {
- return false;
+ // Managed profiles have their own specific rules.
+ final boolean isManagedProfile = type.isManagedProfile();
+ if (isManagedProfile) {
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ return false;
+ }
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_MANAGED_USERS)) {
+ return false;
+ }
}
- // Limit number of managed profiles that can be created
- final int managedProfilesCount = getProfiles(userId, false).size() - 1;
- final int profilesRemovedCount = managedProfilesCount > 0 && allowedToRemoveOne ? 1 : 0;
- if (managedProfilesCount - profilesRemovedCount >= getMaxManagedProfiles()) {
- return false;
- }
- synchronized(mUsersLock) {
+ synchronized (mUsersLock) {
+ // Check if the parent exists and its type is even allowed to have a profile.
UserInfo userInfo = getUserInfoLU(userId);
if (userInfo == null || !userInfo.canHaveProfile()) {
return false;
}
- int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
- - profilesRemovedCount;
- // We allow creating a managed profile in the special case where there is only one user.
- return usersCountAfterRemoving == 1
- || usersCountAfterRemoving < UserManager.getMaxSupportedUsers();
+
+ // Limit the number of profiles that can be created
+ final int maxUsersOfType = getMaxUsersOfTypePerParent(type);
+ if (maxUsersOfType != UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
+ final int userTypeCount = getProfileIds(userId, userType, false).length;
+ final int profilesRemovedCount = userTypeCount > 0 && allowedToRemoveOne ? 1 : 0;
+ if (userTypeCount - profilesRemovedCount >= maxUsersOfType) {
+ return false;
+ }
+ // Allow creating a managed profile in the special case where there is only one user
+ if (isManagedProfile) {
+ int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
+ - profilesRemovedCount;
+ return usersCountAfterRemoving == 1
+ || usersCountAfterRemoving < UserManager.getMaxSupportedUsers();
+ }
+ }
}
+ return true;
}
@GuardedBy("mUsersLock")
@@ -2199,9 +2393,18 @@ public class UserManagerService extends IUserManager.Stub {
*/
@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 Should be used only during user creation, so the pre-created user can be used (instead of
* creating and initializing a new user from scratch).
*/
// TODO(b/143092698): add unit test
@GuardedBy("mUsersLock")
- private @Nullable UserData getPreCreatedUserLU(@UserInfoFlag int flags) {
- if (DBG) {
- Slog.d(LOG_TAG, "getPreCreatedUser(): initialFlags= " + UserInfo.flagsToString(flags));
- }
- flags |= UserInfo.FLAG_FULL;
- if (UserInfo.isGuest(flags) && areGuestUsersEphemeral()) {
- flags |= UserInfo.FLAG_EPHEMERAL;
- }
- if (DBG) {
- Slog.d(LOG_TAG, "getPreCreatedUser(): targetFlags= " + UserInfo.flagsToString(flags));
- }
+ private @Nullable UserData getPreCreatedUserLU(String userType) {
+ if (DBG) Slog.d(LOG_TAG, "getPreCreatedUser(): userType= " + userType);
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
final UserData user = mUsers.valueAt(i);
if (DBG) Slog.d(LOG_TAG, i + ":" + user.info.toFullString());
- if (user.info.preCreated
- && (user.info.flags & ~UserInfo.FLAG_INITIALIZED) == flags) {
+ if (user.info.preCreated && user.info.userType.equals(userType)) {
if (!user.info.isInitialized()) {
- Slog.w(LOG_TAG, "found pre-created user for flags "
- + "" + UserInfo.flagsToString(flags)
+ Slog.w(LOG_TAG, "found pre-created user of type " + userType
+ ", but it's not initialized yet: " + user.info.toFullString());
continue;
}
@@ -3079,6 +3347,18 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
+ /**
+ * Returns whether a user with the given userTypeDetails is eligible to be
+ * {@link UserInfo#preCreated}.
+ */
+ private static boolean isUserTypeEligibleForPreCreation(UserTypeDetails userTypeDetails) {
+ if (userTypeDetails == null) {
+ return false;
+ }
+ return !userTypeDetails.isProfile()
+ && !userTypeDetails.getName().equals(UserManager.USER_TYPE_FULL_RESTRICTED);
+ }
+
@VisibleForTesting
UserData putUserInfo(UserInfo userInfo) {
final UserData userData = new UserData();
@@ -3103,7 +3383,7 @@ public class UserManagerService extends IUserManager.Stub {
public UserInfo createRestrictedProfile(String name, int parentUserId) {
checkManageOrCreateUsersPermission("setupRestrictedProfile");
final UserInfo user = createProfileForUser(
- name, UserInfo.FLAG_RESTRICTED, parentUserId, null);
+ name, UserManager.USER_TYPE_FULL_RESTRICTED, 0, parentUserId, null);
if (user == null) {
return null;
}
@@ -3209,6 +3489,12 @@ public class UserManagerService extends IUserManager.Stub {
return removeUserUnchecked(userId);
}
+ @Override
+ public boolean removeUserEvenWhenDisallowed(@UserIdInt int userId) {
+ checkManageOrCreateUsersPermission("Only the system can remove users");
+ return removeUserUnchecked(userId);
+ }
+
private boolean removeUserUnchecked(@UserIdInt int userId) {
long ident = Binder.clearCallingIdentity();
try {
@@ -3256,6 +3542,7 @@ public class UserManagerService extends IUserManager.Stub {
Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user.", e);
}
+ // TODO(b/142482943): Send some sort of broadcast for profiles even if non-managed?
if (userData.info.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
&& userData.info.isManagedProfile()) {
// Send broadcast to notify system that the user removed was a
@@ -4022,6 +4309,7 @@ public class UserManagerService extends IUserManager.Stub {
pw.print(" 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 e9edba58a3dd7..d071927681820 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.Intent;
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 @@ public class UserManagerTest extends AndroidTestCase {
}
}
+ /** 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 @@ public class UserManagerTest extends AndroidTestCase {
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 @@ public class UserManagerTest extends AndroidTestCase {
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 class UserManagerTest extends AndroidTestCase {
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 @@ public class UserManagerTest extends AndroidTestCase {
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 class UserManagerTest extends AndroidTestCase {
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 @@ public class UserManagerTest extends AndroidTestCase {
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 @@ public class UserManagerTest extends AndroidTestCase {
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 @@ public class UserManagerTest extends AndroidTestCase {
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 @@ public class UserManagerTest extends AndroidTestCase {
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 @@ public class UserManagerTest extends AndroidTestCase {
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 f0b0328ff7d46..f492932a0d774 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 @@ public class UserSystemPackageInstallerTest {
final ArrayMap