Introduce user types
Introduces the notion of 'user types' to UserManager. Previously, users only had properties, set by UserInfo flags. We now solidify some of these as formal user types. Each user must be of precisely one user type. The configuration of each type is defined in a UserTypeDetails object. Prior 'types' - namely Guest, Restricted, ManagedProfiles, Demo, and 'normal' - that were set via UserInfo flags are now controlled via UserType. Previous UserInfo flag methods for categorizing these are still supported as long as no user of of more than one type. If a user was (e.g.) previously both Guest and Demo, it is now invalid and will be prevented from upgrading. This should not be a problem in AOSP, where these 'types' were assumed mutually exclusive. UserTypeDetails is sufficiently general to support future non-managed profiles. To this end, the logic for specifying badges and their colors/labels is contained by UserTypeDetails, and therefore UserManager is responsible for delivering the corresponding badge information. Bug: 142151520 Test: added new test class UserManagerServiceUserTypeTest Test: added additional tests in UserManagerServiceUserInfoTest Test: added additional tests in UserManagerTest Test: added additional tests in UserManagerServiceCreateProfileTest Test: created UserTests to run the various pm.User*Test's Test: atest \ com.android.server.pm.UserTests \ UserLifecycleTests \ ManagedUserContentResolverTest \ CtsDevicePolicyManagerTestCases \ CtsMultiUserHostTestCases \ com.android.server.devicepolicy.DevicePolicyManagerTest \ com.android.server.am.UserControllerTest \ CreateManagedProfileTaskTest Test: atest com.android.server.pm.UserManagerServiceCreateProfileTest using adb shell getprop persist.sys.max_profiles # for 5 and -1 Change-Id: Id1183a76055e9a7610965cf40b256d1392afe6f8
This commit is contained in:
@@ -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.");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <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 @@ public class UserInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* 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 @@ public class UserInfo implements Parcelable {
|
||||
/**
|
||||
* 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 @@ public class UserInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
|
||||
* <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 @@ 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();
|
||||
|
||||
@@ -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<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();
|
||||
|
||||
@@ -18,6 +18,8 @@ package android.os;
|
||||
|
||||
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 @@ public class UserManager {
|
||||
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
|
||||
@@ -1453,6 +1506,79 @@ public class UserManager {
|
||||
return user != null && user.isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()}
|
||||
@@ -1563,6 +1689,48 @@ public class UserManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -1587,7 +1755,8 @@ public class UserManager {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -1606,23 +1775,6 @@ public class UserManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -2066,6 +2218,26 @@ public class UserManager {
|
||||
return ident >= 0 ? new UserHandle(ident) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -2073,18 +2245,19 @@ public class UserManager {
|
||||
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
|
||||
*
|
||||
* @param name the user's name
|
||||
* @param flags UserInfo flags that identify the type of user and other properties.
|
||||
* @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
|
||||
* @param flags UserInfo flags that specify user properties.
|
||||
* @see UserInfo
|
||||
*
|
||||
* @return the UserInfo object for the created user, or {@code null} if the user could not be
|
||||
* created.
|
||||
* @hide
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
|
||||
public @Nullable UserInfo createUser(@Nullable String name, @NonNull String userType,
|
||||
@UserInfoFlag int flags) {
|
||||
UserInfo user = null;
|
||||
try {
|
||||
user = mService.createUser(name, flags);
|
||||
user = mService.createUser(name, userType, flags);
|
||||
// TODO: Keep this in sync with
|
||||
// UserManagerService.LocalService.createUserEvenWhenDisallowed
|
||||
if (user != null && !user.isAdmin() && !user.isDemo()) {
|
||||
@@ -2098,19 +2271,17 @@ 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.
|
||||
*
|
||||
* <p>This method can be used by OEMs to "warm" up the user creation by pre-creating some users
|
||||
* at the first boot, so they when the "real" user is created (for example,
|
||||
* by {@link #createUser(String, int)} or {@link #createGuest(Context, String)}), it takes
|
||||
* less time.
|
||||
* by {@link #createUser(String, String, int)} or {@link #createGuest(Context, String)}), it
|
||||
* takes less time.
|
||||
*
|
||||
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
|
||||
*
|
||||
* @param flags UserInfo flags that identify the type of user and other properties.
|
||||
* @see UserInfo
|
||||
*
|
||||
* @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
|
||||
* @return the UserInfo object for the created user, or {@code null} if the user could not be
|
||||
* created.
|
||||
*
|
||||
@@ -2119,9 +2290,9 @@ 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<UserInfo> users = getUsers(true);
|
||||
final int totalUserCount = users.size();
|
||||
int aliveUserCount = 0;
|
||||
@@ -2570,6 +2769,22 @@ public class UserManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -2804,8 +3019,112 @@ 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
|
||||
* Returns whether the given user has a badge (generally to put on profiles' icons).
|
||||
*
|
||||
* @param userId userId of the user in question
|
||||
* @return true if the user's icons should display a badge; false otherwise.
|
||||
*
|
||||
* @see #getBadgedIconForUser more information about badging in general
|
||||
* @hide
|
||||
*/
|
||||
public boolean hasBadge(@UserIdInt int userId) {
|
||||
if (!isProfile(userId)) {
|
||||
// Since currently only profiles actually have badges, we can do this optimization.
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return mService.hasBadge(userId);
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the calling app's user has a badge (generally to put on profiles' icons).
|
||||
*
|
||||
* @return true if the user's icons should display a badge; false otherwise.
|
||||
*
|
||||
* @see #getBadgedIconForUser more information about badging in general
|
||||
* @hide
|
||||
*/
|
||||
public boolean hasBadge() {
|
||||
return hasBadge(UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the badge color for the given user (generally to color a profile's icon's badge).
|
||||
*
|
||||
* <p>To check whether a badge color is expected for the user, first call {@link #hasBadge}.
|
||||
*
|
||||
* @return the color (not the resource ID) to be used for the user's badge
|
||||
* @throws Resources.NotFoundException if no valid badge color exists for this user
|
||||
*
|
||||
* @see #getBadgedIconForUser more information about badging in general
|
||||
* @hide
|
||||
*/
|
||||
public @ColorInt int getUserBadgeColor(@UserIdInt int userId) {
|
||||
try {
|
||||
final int resourceId = mService.getUserBadgeColorResId(userId);
|
||||
return Resources.getSystem().getColor(resourceId, null);
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Resource ID of the user's icon badge.
|
||||
*
|
||||
* @return the Resource ID of the user's icon badge if it has one; otherwise
|
||||
* {@link Resources#ID_NULL}.
|
||||
*
|
||||
* @see #getBadgedIconForUser more information about badging in general
|
||||
* @hide
|
||||
*/
|
||||
public @DrawableRes int getUserIconBadgeResId(@UserIdInt int userId) {
|
||||
try {
|
||||
return mService.getUserIconBadgeResId(userId);
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Resource ID of the user's badge.
|
||||
*
|
||||
* @return the Resource ID of the user's badge if it has one; otherwise
|
||||
* {@link Resources#ID_NULL}.
|
||||
*
|
||||
* @see #getBadgedIconForUser more information about badging in general
|
||||
* @hide
|
||||
*/
|
||||
public @DrawableRes int getUserBadgeResId(@UserIdInt int userId) {
|
||||
try {
|
||||
return mService.getUserBadgeResId(userId);
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Resource ID of the user's badge without a background.
|
||||
*
|
||||
* @return the Resource ID of the user's no-background badge if it has one; otherwise
|
||||
* {@link Resources#ID_NULL}.
|
||||
*
|
||||
* @see #getBadgedIconForUser more information about badging in general
|
||||
* @hide
|
||||
*/
|
||||
public @DrawableRes int getUserBadgeNoBackgroundResId(@UserIdInt int userId) {
|
||||
try {
|
||||
return mService.getUserBadgeNoBackgroundResId(userId);
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the target user is a profile of the calling user or the caller
|
||||
* is itself a profile, then this returns a badged copy of the given
|
||||
* icon to be able to distinguish it from the original icon. For badging an
|
||||
* arbitrary drawable use {@link #getBadgedDrawableForUser(
|
||||
* android.graphics.drawable.Drawable, UserHandle, android.graphics.Rect, int)}.
|
||||
@@ -2826,8 +3145,8 @@ 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.
|
||||
*
|
||||
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
|
||||
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
|
||||
* must be in the same profile group of specified user.
|
||||
*
|
||||
* @param label The label to change.
|
||||
* @param user The target user.
|
||||
* @return A label that combines the original label and a badge as
|
||||
@@ -2869,7 +3192,16 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
388
core/java/com/android/server/pm/UserTypeDetails.java
Normal file
388
core/java/com/android/server/pm/UserTypeDetails.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
168
core/java/com/android/server/pm/UserTypeFactory.java
Normal file
168
core/java/com/android/server/pm/UserTypeFactory.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package android.content;
|
||||
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 @@ public class ManagedUserContentResolverTest extends AbstractCrossUserContentReso
|
||||
@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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.graphics.Shader;
|
||||
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 @@ public class UserIconDrawable extends Drawable implements Drawable.Callback {
|
||||
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);
|
||||
|
||||
@@ -143,8 +143,8 @@ public abstract class UserManagerInternal {
|
||||
* <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 @@ 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);
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -404,12 +404,13 @@ class UserSystemPackageInstaller {
|
||||
}
|
||||
// Regardless of the whitelists/blacklists, ensure mandatory packages.
|
||||
result.put("android",
|
||||
UserInfo.FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.PROFILE_FLAGS_MASK);
|
||||
UserInfo.FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.FLAG_PROFILE);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Converts a user types, as used in SystemConfig, to a UserInfo flag. */
|
||||
private static int getFlagsFromUserTypes(Iterable<String> userTypes) {
|
||||
// TODO(b/142482943): Update all this for the new UserTypes.
|
||||
int flags = 0;
|
||||
for (String type : userTypes) {
|
||||
switch (type) {
|
||||
@@ -435,7 +436,7 @@ class UserSystemPackageInstaller {
|
||||
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);
|
||||
|
||||
@@ -9727,13 +9727,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
}
|
||||
}
|
||||
|
||||
int userInfoFlags = 0;
|
||||
if (ephemeral) {
|
||||
userInfoFlags |= UserInfo.FLAG_EPHEMERAL;
|
||||
}
|
||||
if (demo) {
|
||||
userInfoFlags |= UserInfo.FLAG_DEMO;
|
||||
}
|
||||
int userInfoFlags = ephemeral ? UserInfo.FLAG_EPHEMERAL : 0;
|
||||
String userType = demo ? UserManager.USER_TYPE_FULL_DEMO
|
||||
: UserManager.USER_TYPE_FULL_SECONDARY;
|
||||
String[] disallowedPackages = null;
|
||||
if (!leaveAllSystemAppsEnabled) {
|
||||
disallowedPackages = mOverlayPackagesProvider.getNonRequiredApps(admin,
|
||||
@@ -9741,7 +9737,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
new String[0]);
|
||||
}
|
||||
UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name,
|
||||
userInfoFlags, disallowedPackages);
|
||||
userType, userInfoFlags, disallowedPackages);
|
||||
if (userInfo != null) {
|
||||
user = userInfo.getUserHandle();
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public class UserLifecycleStressTest {
|
||||
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(
|
||||
|
||||
@@ -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 @@ public class UserManagerServiceCreateProfileTest {
|
||||
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 @@ public class UserManagerServiceCreateProfileTest {
|
||||
|| 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,29 +176,22 @@ public class UserManagerServiceCreateProfileTest {
|
||||
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
|
||||
@@ -171,6 +200,10 @@ public class UserManagerServiceCreateProfileTest {
|
||||
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 @@ public class UserManagerServiceCreateProfileTest {
|
||||
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
|
||||
|
||||
@@ -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 @@ public class UserManagerServiceUserInfoTest {
|
||||
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 @@ public class UserManagerServiceUserInfoTest {
|
||||
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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ public class UserSystemPackageInstallerTest {
|
||||
|
||||
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 @@ public class UserSystemPackageInstallerTest {
|
||||
|
||||
/** 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 @@ public class UserSystemPackageInstallerTest {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user