Merge changes from topic "user_templates" into qt-qpr1-dev
* changes: Added new (hidden) UserManager API to get all users, including pre-created ones. Added option to pre-create user templates to optimize first user creation time.
This commit is contained in:
committed by
Android (Google) Code Review
commit
8959c2dd6b
@@ -16,12 +16,17 @@
|
|||||||
|
|
||||||
package android.content.pm;
|
package android.content.pm;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
import android.annotation.UnsupportedAppUsage;
|
import android.annotation.UnsupportedAppUsage;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
|
import android.util.DebugUtils;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per-user information.
|
* Per-user information.
|
||||||
@@ -94,6 +99,25 @@ public class UserInfo implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
public static final int FLAG_DEMO = 0x00000200;
|
public static final int FLAG_DEMO = 0x00000200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@IntDef(flag = true, prefix = "FLAG_", value = {
|
||||||
|
FLAG_PRIMARY,
|
||||||
|
FLAG_ADMIN,
|
||||||
|
FLAG_GUEST,
|
||||||
|
FLAG_RESTRICTED,
|
||||||
|
FLAG_INITIALIZED,
|
||||||
|
FLAG_MANAGED_PROFILE,
|
||||||
|
FLAG_DISABLED,
|
||||||
|
FLAG_QUIET_MODE,
|
||||||
|
FLAG_EPHEMERAL,
|
||||||
|
FLAG_DEMO
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface UserInfoFlag {
|
||||||
|
}
|
||||||
|
|
||||||
public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
|
public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
|
||||||
|
|
||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
@@ -128,6 +152,18 @@ public class UserInfo implements Parcelable {
|
|||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
public boolean guestToRemove;
|
public boolean guestToRemove;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to optimize the creation of an user, i.e. OEMs might choose to pre-create a
|
||||||
|
* number of users at the first boot, so the actual creation later is faster.
|
||||||
|
*
|
||||||
|
* <p>A {@code preCreated} user is not a real user yet, so it should not show up on regular
|
||||||
|
* user operations (other than user creation per se).
|
||||||
|
*
|
||||||
|
* <p>Once the pre-created is used to create a "real" user later on, {@code preCreate} is set to
|
||||||
|
* {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean preCreated;
|
||||||
|
|
||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
public UserInfo(int id, String name, int flags) {
|
public UserInfo(int id, String name, int flags) {
|
||||||
this(id, name, null, flags);
|
this(id, name, null, flags);
|
||||||
@@ -155,6 +191,13 @@ public class UserInfo implements Parcelable {
|
|||||||
|
|
||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
public boolean isGuest() {
|
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 (flags & FLAG_GUEST) == FLAG_GUEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +208,13 @@ public class UserInfo implements Parcelable {
|
|||||||
|
|
||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
public boolean isManagedProfile() {
|
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 (flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,6 +302,7 @@ public class UserInfo implements Parcelable {
|
|||||||
lastLoggedInTime = orig.lastLoggedInTime;
|
lastLoggedInTime = orig.lastLoggedInTime;
|
||||||
lastLoggedInFingerprint = orig.lastLoggedInFingerprint;
|
lastLoggedInFingerprint = orig.lastLoggedInFingerprint;
|
||||||
partial = orig.partial;
|
partial = orig.partial;
|
||||||
|
preCreated = orig.preCreated;
|
||||||
profileGroupId = orig.profileGroupId;
|
profileGroupId = orig.profileGroupId;
|
||||||
restrictedProfileParentId = orig.restrictedProfileParentId;
|
restrictedProfileParentId = orig.restrictedProfileParentId;
|
||||||
guestToRemove = orig.guestToRemove;
|
guestToRemove = orig.guestToRemove;
|
||||||
@@ -268,6 +319,22 @@ public class UserInfo implements Parcelable {
|
|||||||
return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
|
return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public String toFullString() {
|
||||||
|
return "UserInfo[id=" + id
|
||||||
|
+ ", name=" + name
|
||||||
|
+ ", flags=" + flagsToString(flags)
|
||||||
|
+ (preCreated ? " (pre-created)" : "")
|
||||||
|
+ (partial ? " (partial)" : "")
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static String flagsToString(int flags) {
|
||||||
|
return DebugUtils.flagsToString(UserInfo.class, "FLAG_", flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -281,9 +348,10 @@ public class UserInfo implements Parcelable {
|
|||||||
dest.writeLong(creationTime);
|
dest.writeLong(creationTime);
|
||||||
dest.writeLong(lastLoggedInTime);
|
dest.writeLong(lastLoggedInTime);
|
||||||
dest.writeString(lastLoggedInFingerprint);
|
dest.writeString(lastLoggedInFingerprint);
|
||||||
dest.writeInt(partial ? 1 : 0);
|
dest.writeBoolean(partial);
|
||||||
|
dest.writeBoolean(preCreated);
|
||||||
dest.writeInt(profileGroupId);
|
dest.writeInt(profileGroupId);
|
||||||
dest.writeInt(guestToRemove ? 1 : 0);
|
dest.writeBoolean(guestToRemove);
|
||||||
dest.writeInt(restrictedProfileParentId);
|
dest.writeInt(restrictedProfileParentId);
|
||||||
dest.writeInt(profileBadge);
|
dest.writeInt(profileBadge);
|
||||||
}
|
}
|
||||||
@@ -308,10 +376,11 @@ public class UserInfo implements Parcelable {
|
|||||||
creationTime = source.readLong();
|
creationTime = source.readLong();
|
||||||
lastLoggedInTime = source.readLong();
|
lastLoggedInTime = source.readLong();
|
||||||
lastLoggedInFingerprint = source.readString();
|
lastLoggedInFingerprint = source.readString();
|
||||||
partial = source.readInt() != 0;
|
partial = source.readBoolean();
|
||||||
profileGroupId = source.readInt();
|
profileGroupId = source.readInt();
|
||||||
guestToRemove = source.readInt() != 0;
|
guestToRemove = source.readBoolean();
|
||||||
restrictedProfileParentId = source.readInt();
|
restrictedProfileParentId = source.readInt();
|
||||||
profileBadge = source.readInt();
|
profileBadge = source.readInt();
|
||||||
|
preCreated = source.readBoolean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ interface IUserManager {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
UserInfo createUser(in String name, int flags);
|
UserInfo createUser(in String name, int flags);
|
||||||
|
UserInfo preCreateUser(int flags);
|
||||||
UserInfo createProfileForUser(in String name, int flags, int userHandle,
|
UserInfo createProfileForUser(in String name, int flags, int userHandle,
|
||||||
in String[] disallowedPackages);
|
in String[] disallowedPackages);
|
||||||
UserInfo createRestrictedProfile(String name, int parentUserHandle);
|
UserInfo createRestrictedProfile(String name, int parentUserHandle);
|
||||||
@@ -53,7 +54,7 @@ interface IUserManager {
|
|||||||
void setUserIcon(int userHandle, in Bitmap icon);
|
void setUserIcon(int userHandle, in Bitmap icon);
|
||||||
ParcelFileDescriptor getUserIcon(int userHandle);
|
ParcelFileDescriptor getUserIcon(int userHandle);
|
||||||
UserInfo getPrimaryUser();
|
UserInfo getPrimaryUser();
|
||||||
List<UserInfo> getUsers(boolean excludeDying);
|
List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated);
|
||||||
List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
|
List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
|
||||||
int[] getProfileIds(int userId, boolean enabledOnly);
|
int[] getProfileIds(int userId, boolean enabledOnly);
|
||||||
boolean canAddMoreManagedProfiles(int userHandle, boolean allowedToRemoveOne);
|
boolean canAddMoreManagedProfiles(int userHandle, boolean allowedToRemoveOne);
|
||||||
@@ -92,6 +93,7 @@ interface IUserManager {
|
|||||||
boolean someUserHasSeedAccount(in String accountName, in String accountType);
|
boolean someUserHasSeedAccount(in String accountName, in String accountType);
|
||||||
boolean isManagedProfile(int userId);
|
boolean isManagedProfile(int userId);
|
||||||
boolean isDemoUser(int userId);
|
boolean isDemoUser(int userId);
|
||||||
|
boolean isPreCreated(int userId);
|
||||||
UserInfo createProfileForUserEvenWhenDisallowed(in String name, int flags, int userHandle,
|
UserInfo createProfileForUserEvenWhenDisallowed(in String name, int flags, int userHandle,
|
||||||
in String[] disallowedPackages);
|
in String[] disallowedPackages);
|
||||||
boolean isUserUnlockingOrUnlocked(int userId);
|
boolean isUserUnlockingOrUnlocked(int userId);
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import android.content.Intent;
|
|||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.IntentSender;
|
import android.content.IntentSender;
|
||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
|
import android.content.pm.UserInfo.UserInfoFlag;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@@ -2003,18 +2004,20 @@ public class UserManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a user with the specified name and options. For non-admin users, default user
|
* Creates a user with the specified name and options. For non-admin users, default user
|
||||||
* restrictions are going to be applied.
|
* restrictions will be applied.
|
||||||
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
|
*
|
||||||
|
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
|
||||||
*
|
*
|
||||||
* @param name the user's name
|
* @param name the user's name
|
||||||
* @param flags flags that identify the type of user and other properties.
|
* @param flags UserInfo flags that identify the type of user and other properties.
|
||||||
* @see UserInfo
|
* @see UserInfo
|
||||||
*
|
*
|
||||||
* @return the UserInfo object for the created user, or null if the user could not be created.
|
* @return the UserInfo object for the created user, or {@code null} if the user could not be
|
||||||
|
* created.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
public UserInfo createUser(String name, int flags) {
|
public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
|
||||||
UserInfo user = null;
|
UserInfo user = null;
|
||||||
try {
|
try {
|
||||||
user = mService.createUser(name, flags);
|
user = mService.createUser(name, flags);
|
||||||
@@ -2030,6 +2033,36 @@ public class UserManager {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-creates a user with the specified name and options. 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.
|
||||||
|
*
|
||||||
|
* <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
|
||||||
|
*
|
||||||
|
* @param flags UserInfo flags that identify the type of user and other properties.
|
||||||
|
* @see UserInfo
|
||||||
|
*
|
||||||
|
* @return the UserInfo object for the created user, or {@code null} if the user could not be
|
||||||
|
* created.
|
||||||
|
*
|
||||||
|
* @throw {@link IllegalArgumentException} if {@code flags} contains
|
||||||
|
* {@link UserInfo#FLAG_MANAGED_PROFILE}.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public @Nullable UserInfo preCreateUser(@UserInfoFlag int flags) {
|
||||||
|
try {
|
||||||
|
return mService.preCreateUser(flags);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
throw re.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a guest user and configures it.
|
* Creates a guest user and configures it.
|
||||||
* @param context an application context
|
* @param context an application context
|
||||||
@@ -2346,15 +2379,26 @@ public class UserManager {
|
|||||||
/**
|
/**
|
||||||
* Returns information for all users on this device, including ones marked for deletion.
|
* Returns information for all users on this device, including ones marked for deletion.
|
||||||
* To retrieve only users that are alive, use {@link #getUsers(boolean)}.
|
* To retrieve only users that are alive, use {@link #getUsers(boolean)}.
|
||||||
* <p>
|
*
|
||||||
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
|
|
||||||
* @return the list of users that exist on the device.
|
* @return the list of users that exist on the device.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
|
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
|
||||||
public List<UserInfo> getUsers() {
|
public List<UserInfo> getUsers() {
|
||||||
|
return getUsers(/* excludeDying= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns information for all users on this device, based on the filtering parameters.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
|
||||||
|
public List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
|
||||||
|
boolean excludePreCreated) {
|
||||||
try {
|
try {
|
||||||
return mService.getUsers(false);
|
return mService.getUsers(excludePartial, excludeDying, excludePreCreated);
|
||||||
} catch (RemoteException re) {
|
} catch (RemoteException re) {
|
||||||
throw re.rethrowFromSystemServer();
|
throw re.rethrowFromSystemServer();
|
||||||
}
|
}
|
||||||
@@ -2370,16 +2414,12 @@ public class UserManager {
|
|||||||
@SystemApi
|
@SystemApi
|
||||||
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
|
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
|
||||||
public long[] getSerialNumbersOfUsers(boolean excludeDying) {
|
public long[] getSerialNumbersOfUsers(boolean excludeDying) {
|
||||||
try {
|
List<UserInfo> users = getUsers(excludeDying);
|
||||||
List<UserInfo> users = mService.getUsers(excludeDying);
|
|
||||||
long[] result = new long[users.size()];
|
long[] result = new long[users.size()];
|
||||||
for (int i = 0; i < result.length; i++) {
|
for (int i = 0; i < result.length; i++) {
|
||||||
result[i] = users.get(i).serialNumber;
|
result[i] = users.get(i).serialNumber;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} catch (RemoteException re) {
|
|
||||||
throw re.rethrowFromSystemServer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2765,11 +2805,8 @@ public class UserManager {
|
|||||||
*/
|
*/
|
||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
|
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
|
||||||
try {
|
return getUsers(/*excludePartial= */ true, excludeDying,
|
||||||
return mService.getUsers(excludeDying);
|
/* excludePreCreated= */ true);
|
||||||
} catch (RemoteException re) {
|
|
||||||
throw re.rethrowFromSystemServer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -134,12 +134,12 @@ class UserController implements Handler.Callback {
|
|||||||
static final int CONTINUE_USER_SWITCH_MSG = 20;
|
static final int CONTINUE_USER_SWITCH_MSG = 20;
|
||||||
static final int USER_SWITCH_TIMEOUT_MSG = 30;
|
static final int USER_SWITCH_TIMEOUT_MSG = 30;
|
||||||
static final int START_PROFILES_MSG = 40;
|
static final int START_PROFILES_MSG = 40;
|
||||||
static final int SYSTEM_USER_START_MSG = 50;
|
static final int USER_START_MSG = 50;
|
||||||
static final int SYSTEM_USER_CURRENT_MSG = 60;
|
static final int USER_CURRENT_MSG = 60;
|
||||||
static final int FOREGROUND_PROFILE_CHANGED_MSG = 70;
|
static final int FOREGROUND_PROFILE_CHANGED_MSG = 70;
|
||||||
static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80;
|
static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80;
|
||||||
static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90;
|
static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90;
|
||||||
static final int SYSTEM_USER_UNLOCK_MSG = 100;
|
static final int USER_UNLOCK_MSG = 100;
|
||||||
static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
|
static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
|
||||||
static final int START_USER_SWITCH_FG_MSG = 120;
|
static final int START_USER_SWITCH_FG_MSG = 120;
|
||||||
|
|
||||||
@@ -369,6 +369,7 @@ class UserController implements Handler.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mInjector.getUserManager().isPreCreated(userId)) {
|
||||||
mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
|
mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
|
||||||
userId, 0));
|
userId, 0));
|
||||||
Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
|
Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
|
||||||
@@ -380,6 +381,7 @@ class UserController implements Handler.Callback {
|
|||||||
AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
|
AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
|
||||||
Binder.getCallingUid(), Binder.getCallingPid(), userId);
|
Binder.getCallingUid(), Binder.getCallingPid(), userId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We need to delay unlocking managed profiles until the parent user
|
// We need to delay unlocking managed profiles until the parent user
|
||||||
// is also unlocked.
|
// is also unlocked.
|
||||||
@@ -439,8 +441,7 @@ class UserController implements Handler.Callback {
|
|||||||
|
|
||||||
// Dispatch unlocked to system services; when fully dispatched,
|
// Dispatch unlocked to system services; when fully dispatched,
|
||||||
// that calls through to the next "unlocked" phase
|
// that calls through to the next "unlocked" phase
|
||||||
mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
|
mHandler.obtainMessage(USER_UNLOCK_MSG, userId, 0, uss).sendToTarget();
|
||||||
.sendToTarget();
|
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -556,6 +557,17 @@ class UserController implements Handler.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userInfo.preCreated) {
|
||||||
|
Slog.i(TAG, "Stopping pre-created user " + userInfo.toFullString());
|
||||||
|
// Pre-created user was started right after creation so services could properly
|
||||||
|
// intialize it; it should be stopped right away as it's not really a "real" user.
|
||||||
|
// TODO(b/140750212): in the long-term, we should add a onCreateUser() callback
|
||||||
|
// on SystemService instead.
|
||||||
|
stopUser(userInfo.id, /* force= */ true, /* stopUserCallback= */ null,
|
||||||
|
/* keyEvictedCallback= */ null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Spin up app widgets prior to boot-complete, so they can be ready promptly
|
// Spin up app widgets prior to boot-complete, so they can be ready promptly
|
||||||
mInjector.startUserWidgets(userId);
|
mInjector.startUserWidgets(userId);
|
||||||
|
|
||||||
@@ -808,7 +820,8 @@ class UserController implements Handler.Callback {
|
|||||||
mInjector.systemServiceManagerCleanupUser(userId);
|
mInjector.systemServiceManagerCleanupUser(userId);
|
||||||
mInjector.stackSupervisorRemoveUser(userId);
|
mInjector.stackSupervisorRemoveUser(userId);
|
||||||
// Remove the user if it is ephemeral.
|
// Remove the user if it is ephemeral.
|
||||||
if (getUserInfo(userId).isEphemeral()) {
|
UserInfo userInfo = getUserInfo(userId);
|
||||||
|
if (userInfo.isEphemeral() && !userInfo.preCreated) {
|
||||||
mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
|
mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1065,6 +1078,11 @@ class UserController implements Handler.Callback {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (foreground && userInfo.preCreated) {
|
||||||
|
Slog.w(TAG, "Cannot start pre-created user #" + userId + " as foreground");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (foreground && mUserSwitchUiEnabled) {
|
if (foreground && mUserSwitchUiEnabled) {
|
||||||
mInjector.getWindowManager().startFreezingScreen(
|
mInjector.getWindowManager().startFreezingScreen(
|
||||||
R.anim.screen_user_exit, R.anim.screen_user_enter);
|
R.anim.screen_user_exit, R.anim.screen_user_enter);
|
||||||
@@ -1159,13 +1177,11 @@ class UserController implements Handler.Callback {
|
|||||||
// Booting up a new user, need to tell system services about it.
|
// Booting up a new user, need to tell system services about it.
|
||||||
// Note that this is on the same handler as scheduling of broadcasts,
|
// Note that this is on the same handler as scheduling of broadcasts,
|
||||||
// which is important because it needs to go first.
|
// which is important because it needs to go first.
|
||||||
mHandler.sendMessage(
|
mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, 0));
|
||||||
mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foreground) {
|
if (foreground) {
|
||||||
mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
|
mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId));
|
||||||
oldUserId));
|
|
||||||
mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
|
mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
|
||||||
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
|
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
|
||||||
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
|
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
|
||||||
@@ -1174,6 +1190,10 @@ class UserController implements Handler.Callback {
|
|||||||
oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS);
|
oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userInfo.preCreated) {
|
||||||
|
needStart = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (needStart) {
|
if (needStart) {
|
||||||
// Send USER_STARTED broadcast
|
// Send USER_STARTED broadcast
|
||||||
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
|
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
|
||||||
@@ -2131,13 +2151,13 @@ class UserController implements Handler.Callback {
|
|||||||
case START_PROFILES_MSG:
|
case START_PROFILES_MSG:
|
||||||
startProfiles();
|
startProfiles();
|
||||||
break;
|
break;
|
||||||
case SYSTEM_USER_START_MSG:
|
case USER_START_MSG:
|
||||||
mInjector.batteryStatsServiceNoteEvent(
|
mInjector.batteryStatsServiceNoteEvent(
|
||||||
BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
|
BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
|
||||||
Integer.toString(msg.arg1), msg.arg1);
|
Integer.toString(msg.arg1), msg.arg1);
|
||||||
mInjector.getSystemServiceManager().startUser(msg.arg1);
|
mInjector.getSystemServiceManager().startUser(msg.arg1);
|
||||||
break;
|
break;
|
||||||
case SYSTEM_USER_UNLOCK_MSG:
|
case USER_UNLOCK_MSG:
|
||||||
final int userId = msg.arg1;
|
final int userId = msg.arg1;
|
||||||
mInjector.getSystemServiceManager().unlockUser(userId);
|
mInjector.getSystemServiceManager().unlockUser(userId);
|
||||||
// Loads recents on a worker thread that allows disk I/O
|
// Loads recents on a worker thread that allows disk I/O
|
||||||
@@ -2146,7 +2166,7 @@ class UserController implements Handler.Callback {
|
|||||||
});
|
});
|
||||||
finishUserUnlocked((UserState) msg.obj);
|
finishUserUnlocked((UserState) msg.obj);
|
||||||
break;
|
break;
|
||||||
case SYSTEM_USER_CURRENT_MSG:
|
case USER_CURRENT_MSG:
|
||||||
mInjector.batteryStatsServiceNoteEvent(
|
mInjector.batteryStatsServiceNoteEvent(
|
||||||
BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
|
BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
|
||||||
Integer.toString(msg.arg2), msg.arg2);
|
Integer.toString(msg.arg2), msg.arg2);
|
||||||
|
|||||||
@@ -2257,6 +2257,7 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
int userId = -1;
|
int userId = -1;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
String opt;
|
String opt;
|
||||||
|
boolean preCreateOnly = false;
|
||||||
while ((opt = getNextOption()) != null) {
|
while ((opt = getNextOption()) != null) {
|
||||||
if ("--profileOf".equals(opt)) {
|
if ("--profileOf".equals(opt)) {
|
||||||
userId = UserHandle.parseUserArg(getNextArgRequired());
|
userId = UserHandle.parseUserArg(getNextArgRequired());
|
||||||
@@ -2270,6 +2271,8 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
flags |= UserInfo.FLAG_GUEST;
|
flags |= UserInfo.FLAG_GUEST;
|
||||||
} else if ("--demo".equals(opt)) {
|
} else if ("--demo".equals(opt)) {
|
||||||
flags |= UserInfo.FLAG_DEMO;
|
flags |= UserInfo.FLAG_DEMO;
|
||||||
|
} else if ("--pre-create-only".equals(opt)) {
|
||||||
|
preCreateOnly = true;
|
||||||
} else {
|
} else {
|
||||||
getErrPrintWriter().println("Error: unknown option " + opt);
|
getErrPrintWriter().println("Error: unknown option " + opt);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -2293,7 +2296,7 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
accm.addSharedAccountsFromParentUser(parentUserId, userId,
|
accm.addSharedAccountsFromParentUser(parentUserId, userId,
|
||||||
(Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
|
(Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
|
||||||
} else if (userId < 0) {
|
} else if (userId < 0) {
|
||||||
info = um.createUser(name, flags);
|
info = preCreateOnly ? um.preCreateUser(flags) : um.createUser(name, flags);
|
||||||
} else {
|
} else {
|
||||||
info = um.createProfileForUser(name, flags, userId, null);
|
info = um.createProfileForUser(name, flags, userId, null);
|
||||||
}
|
}
|
||||||
@@ -3169,8 +3172,11 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
pw.println(" trim-caches DESIRED_FREE_SPACE [internal|UUID]");
|
pw.println(" trim-caches DESIRED_FREE_SPACE [internal|UUID]");
|
||||||
pw.println(" Trim cache files to reach the given free space.");
|
pw.println(" Trim cache files to reach the given free space.");
|
||||||
pw.println("");
|
pw.println("");
|
||||||
|
pw.println(" list users");
|
||||||
|
pw.println(" Lists the current users.");
|
||||||
|
pw.println("");
|
||||||
pw.println(" create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]");
|
pw.println(" create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]");
|
||||||
pw.println(" [--guest] USER_NAME");
|
pw.println(" [--guest] [--pre-create-only] USER_NAME");
|
||||||
pw.println(" Create a new user with the given USER_NAME, printing the new user identifier");
|
pw.println(" Create a new user with the given USER_NAME, printing the new user identifier");
|
||||||
pw.println(" of the user.");
|
pw.println(" of the user.");
|
||||||
pw.println("");
|
pw.println("");
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import android.content.pm.PackageManager;
|
|||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.pm.ShortcutServiceInternal;
|
import android.content.pm.ShortcutServiceInternal;
|
||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
|
import android.content.pm.UserInfo.UserInfoFlag;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
@@ -66,6 +67,7 @@ import android.os.ShellCallback;
|
|||||||
import android.os.ShellCommand;
|
import android.os.ShellCommand;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
|
import android.os.Trace;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.os.UserManager.EnforcingUser;
|
import android.os.UserManager.EnforcingUser;
|
||||||
@@ -83,6 +85,7 @@ import android.util.SparseArray;
|
|||||||
import android.util.SparseBooleanArray;
|
import android.util.SparseBooleanArray;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
import android.util.TimeUtils;
|
import android.util.TimeUtils;
|
||||||
|
import android.util.TimingsTraceLog;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
@@ -155,6 +158,7 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
private static final String ATTR_SERIAL_NO = "serialNumber";
|
private static final String ATTR_SERIAL_NO = "serialNumber";
|
||||||
private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
|
private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
|
||||||
private static final String ATTR_PARTIAL = "partial";
|
private static final String ATTR_PARTIAL = "partial";
|
||||||
|
private static final String ATTR_PRE_CREATED = "preCreated";
|
||||||
private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove";
|
private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove";
|
||||||
private static final String ATTR_USER_VERSION = "version";
|
private static final String ATTR_USER_VERSION = "version";
|
||||||
private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId";
|
private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId";
|
||||||
@@ -582,7 +586,8 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
final int userSize = mUsers.size();
|
final int userSize = mUsers.size();
|
||||||
for (int i = 0; i < userSize; i++) {
|
for (int i = 0; i < userSize; i++) {
|
||||||
UserInfo ui = mUsers.valueAt(i).info;
|
UserInfo ui = mUsers.valueAt(i).info;
|
||||||
if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
|
if ((ui.partial || ui.guestToRemove || (ui.isEphemeral() && !ui.preCreated))
|
||||||
|
&& i != 0) {
|
||||||
partials.add(ui);
|
partials.add(ui);
|
||||||
addRemovingUserIdLocked(ui.id);
|
addRemovingUserIdLocked(ui.id);
|
||||||
ui.partial = true;
|
ui.partial = true;
|
||||||
@@ -645,21 +650,26 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
|
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
|
||||||
|
return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
|
||||||
|
boolean excludePreCreated) {
|
||||||
checkManageOrCreateUsersPermission("query users");
|
checkManageOrCreateUsersPermission("query users");
|
||||||
synchronized (mUsersLock) {
|
synchronized (mUsersLock) {
|
||||||
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
|
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
|
||||||
final int userSize = mUsers.size();
|
final int userSize = mUsers.size();
|
||||||
for (int i = 0; i < userSize; i++) {
|
for (int i = 0; i < userSize; i++) {
|
||||||
UserInfo ui = mUsers.valueAt(i).info;
|
UserInfo ui = mUsers.valueAt(i).info;
|
||||||
if (ui.partial) {
|
if ((excludePartial && ui.partial)
|
||||||
|
|| (excludeDying && mRemovingUserIds.get(ui.id))
|
||||||
|
|| (excludePreCreated && ui.preCreated)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!excludeDying || !mRemovingUserIds.get(ui.id)) {
|
|
||||||
users.add(userWithName(ui));
|
users.add(userWithName(ui));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1179,8 +1189,9 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkManageOrInteractPermIfCallerInOtherProfileGroup(int userId, String name) {
|
private void checkManageOrInteractPermIfCallerInOtherProfileGroup(@UserIdInt int userId,
|
||||||
int callingUserId = UserHandle.getCallingUserId();
|
String name) {
|
||||||
|
final int callingUserId = UserHandle.getCallingUserId();
|
||||||
if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
|
if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
|
||||||
hasManageUsersPermission()) {
|
hasManageUsersPermission()) {
|
||||||
return;
|
return;
|
||||||
@@ -1193,8 +1204,8 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDemoUser(int userId) {
|
public boolean isDemoUser(@UserIdInt int userId) {
|
||||||
int callingUserId = UserHandle.getCallingUserId();
|
final int callingUserId = UserHandle.getCallingUserId();
|
||||||
if (callingUserId != userId && !hasManageUsersPermission()) {
|
if (callingUserId != userId && !hasManageUsersPermission()) {
|
||||||
throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
|
throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
|
||||||
+ " is a demo user");
|
+ " is a demo user");
|
||||||
@@ -1205,6 +1216,19 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPreCreated(@UserIdInt int userId) {
|
||||||
|
final int callingUserId = UserHandle.getCallingUserId();
|
||||||
|
if (callingUserId != userId && !hasManageUsersPermission()) {
|
||||||
|
throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
|
||||||
|
+ " is pre-created");
|
||||||
|
}
|
||||||
|
synchronized (mUsersLock) {
|
||||||
|
UserInfo userInfo = getUserInfoLU(userId);
|
||||||
|
return userInfo != null && userInfo.preCreated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isRestricted() {
|
public boolean isRestricted() {
|
||||||
synchronized (mUsersLock) {
|
synchronized (mUsersLock) {
|
||||||
@@ -1859,7 +1883,7 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
// Skip over users being removed
|
// Skip over users being removed
|
||||||
for (int i = 0; i < totalUserCount; i++) {
|
for (int i = 0; i < totalUserCount; i++) {
|
||||||
UserInfo user = mUsers.valueAt(i).info;
|
UserInfo user = mUsers.valueAt(i).info;
|
||||||
if (!mRemovingUserIds.get(user.id) && !user.isGuest()) {
|
if (!mRemovingUserIds.get(user.id) && !user.isGuest() && !user.preCreated) {
|
||||||
aliveUserCount++;
|
aliveUserCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2320,6 +2344,9 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
if (userInfo.partial) {
|
if (userInfo.partial) {
|
||||||
serializer.attribute(null, ATTR_PARTIAL, "true");
|
serializer.attribute(null, ATTR_PARTIAL, "true");
|
||||||
}
|
}
|
||||||
|
if (userInfo.preCreated) {
|
||||||
|
serializer.attribute(null, ATTR_PRE_CREATED, "true");
|
||||||
|
}
|
||||||
if (userInfo.guestToRemove) {
|
if (userInfo.guestToRemove) {
|
||||||
serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true");
|
serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true");
|
||||||
}
|
}
|
||||||
@@ -2476,6 +2503,7 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
int profileBadge = 0;
|
int profileBadge = 0;
|
||||||
int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID;
|
int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID;
|
||||||
boolean partial = false;
|
boolean partial = false;
|
||||||
|
boolean preCreated = false;
|
||||||
boolean guestToRemove = false;
|
boolean guestToRemove = false;
|
||||||
boolean persistSeedData = false;
|
boolean persistSeedData = false;
|
||||||
String seedAccountName = null;
|
String seedAccountName = null;
|
||||||
@@ -2520,6 +2548,10 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
if ("true".equals(valueString)) {
|
if ("true".equals(valueString)) {
|
||||||
partial = true;
|
partial = true;
|
||||||
}
|
}
|
||||||
|
valueString = parser.getAttributeValue(null, ATTR_PRE_CREATED);
|
||||||
|
if ("true".equals(valueString)) {
|
||||||
|
preCreated = true;
|
||||||
|
}
|
||||||
valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE);
|
valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE);
|
||||||
if ("true".equals(valueString)) {
|
if ("true".equals(valueString)) {
|
||||||
guestToRemove = true;
|
guestToRemove = true;
|
||||||
@@ -2573,6 +2605,7 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
userInfo.lastLoggedInTime = lastLoggedInTime;
|
userInfo.lastLoggedInTime = lastLoggedInTime;
|
||||||
userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint;
|
userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint;
|
||||||
userInfo.partial = partial;
|
userInfo.partial = partial;
|
||||||
|
userInfo.preCreated = preCreated;
|
||||||
userInfo.guestToRemove = guestToRemove;
|
userInfo.guestToRemove = guestToRemove;
|
||||||
userInfo.profileGroupId = profileGroupId;
|
userInfo.profileGroupId = profileGroupId;
|
||||||
userInfo.profileBadge = profileBadge;
|
userInfo.profileBadge = profileBadge;
|
||||||
@@ -2644,7 +2677,8 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags, int userId,
|
public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags, int userId,
|
||||||
String[] disallowedPackages) {
|
String[] disallowedPackages) {
|
||||||
checkManageOrCreateUsersPermission(flags);
|
checkManageOrCreateUsersPermission(flags);
|
||||||
return createUserInternalUnchecked(name, flags, userId, disallowedPackages);
|
return createUserInternalUnchecked(name, flags, userId, /* preCreate= */ false,
|
||||||
|
disallowedPackages);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2659,12 +2693,27 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
return createUserInternal(name, flags, UserHandle.USER_NULL);
|
return createUserInternal(name, flags, UserHandle.USER_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserInfo createUserInternal(String name, int flags, int parentId) {
|
@Override
|
||||||
|
public UserInfo preCreateUser(int flags) {
|
||||||
|
checkManageOrCreateUsersPermission(flags);
|
||||||
|
|
||||||
|
Preconditions.checkArgument(!UserInfo.isManagedProfile(flags),
|
||||||
|
"cannot pre-create managed profiles");
|
||||||
|
|
||||||
|
Slog.i(LOG_TAG, "Pre-creating user with flags " + UserInfo.flagsToString(flags));
|
||||||
|
|
||||||
|
return createUserInternalUnchecked(/* name= */ null, flags,
|
||||||
|
/* parentId= */ UserHandle.USER_NULL, /* preCreate= */ true,
|
||||||
|
/* disallowedPackages= */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
|
||||||
|
@UserIdInt int parentId) {
|
||||||
return createUserInternal(name, flags, parentId, null);
|
return createUserInternal(name, flags, parentId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserInfo createUserInternal(String name, int flags, int parentId,
|
private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
|
||||||
String[] disallowedPackages) {
|
@UserIdInt int parentId, @Nullable String[] disallowedPackages) {
|
||||||
String restriction = ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0)
|
String restriction = ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0)
|
||||||
? UserManager.DISALLOW_ADD_MANAGED_PROFILE
|
? UserManager.DISALLOW_ADD_MANAGED_PROFILE
|
||||||
: UserManager.DISALLOW_ADD_USER;
|
: UserManager.DISALLOW_ADD_USER;
|
||||||
@@ -2672,19 +2721,64 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
Log.w(LOG_TAG, "Cannot add user. " + restriction + " is enabled.");
|
Log.w(LOG_TAG, "Cannot add user. " + restriction + " is enabled.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return createUserInternalUnchecked(name, flags, parentId, disallowedPackages);
|
return createUserInternalUnchecked(name, flags, parentId, /* preCreate= */ false,
|
||||||
|
disallowedPackages);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserInfo createUserInternalUnchecked(@Nullable String name, @UserInfoFlag int flags,
|
||||||
|
@UserIdInt int parentId, boolean preCreate,
|
||||||
|
@Nullable String[] disallowedPackages) {
|
||||||
|
final TimingsTraceLog t = new TimingsTraceLog(LOG_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
|
||||||
|
t.traceBegin("createUser-" + flags);
|
||||||
|
try {
|
||||||
|
return createUserInternalUncheckedNoTracing(name, flags, parentId, preCreate,
|
||||||
|
disallowedPackages, t);
|
||||||
|
} finally {
|
||||||
|
t.traceEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name,
|
||||||
|
@UserInfoFlag int flags, @UserIdInt int parentId, boolean preCreate,
|
||||||
|
@Nullable String[] disallowedPackages, @NonNull TimingsTraceLog t) {
|
||||||
|
|
||||||
|
// First try to use a pre-created user (if available).
|
||||||
|
// NOTE: currently we don't support pre-created managed profiles
|
||||||
|
if (!preCreate && (parentId < 0 && !UserInfo.isManagedProfile(flags))) {
|
||||||
|
final UserData preCreatedUserData;
|
||||||
|
synchronized (mUsersLock) {
|
||||||
|
preCreatedUserData = getPreCreatedUserLU(flags);
|
||||||
|
}
|
||||||
|
if (preCreatedUserData != null) {
|
||||||
|
final UserInfo preCreatedUser = preCreatedUserData.info;
|
||||||
|
Log.i(LOG_TAG, "Reusing pre-created user " + preCreatedUser.id + " for flags + "
|
||||||
|
+ UserInfo.flagsToString(flags));
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(LOG_TAG, "pre-created user flags: "
|
||||||
|
+ UserInfo.flagsToString(preCreatedUser.flags)
|
||||||
|
+ " new-user flags: " + UserInfo.flagsToString(flags));
|
||||||
|
}
|
||||||
|
preCreatedUser.name = name;
|
||||||
|
preCreatedUser.preCreated = false;
|
||||||
|
preCreatedUser.creationTime = getCreationTime();
|
||||||
|
|
||||||
|
dispatchUserAddedIntent(preCreatedUser);
|
||||||
|
|
||||||
|
writeUserLP(preCreatedUserData);
|
||||||
|
writeUserListLP();
|
||||||
|
|
||||||
|
return preCreatedUser;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserInfo createUserInternalUnchecked(String name, int flags, int parentId,
|
|
||||||
String[] disallowedPackages) {
|
|
||||||
DeviceStorageMonitorInternal dsm = LocalServices
|
DeviceStorageMonitorInternal dsm = LocalServices
|
||||||
.getService(DeviceStorageMonitorInternal.class);
|
.getService(DeviceStorageMonitorInternal.class);
|
||||||
if (dsm.isMemoryLow()) {
|
if (dsm.isMemoryLow()) {
|
||||||
Log.w(LOG_TAG, "Cannot add user. Not enough space on disk.");
|
Log.w(LOG_TAG, "Cannot add user. Not enough space on disk.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
|
final boolean isGuest = UserInfo.isGuest(flags);
|
||||||
final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
|
final boolean isManagedProfile = UserInfo.isManagedProfile(flags);
|
||||||
final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
|
final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
|
||||||
final boolean isDemo = (flags & UserInfo.FLAG_DEMO) != 0;
|
final boolean isDemo = (flags & UserInfo.FLAG_DEMO) != 0;
|
||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
@@ -2705,8 +2799,8 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
|
if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
|
||||||
// If we're not adding a guest/demo user or a managed profile and the limit has
|
// If we're not adding a guest/demo user or a managed profile,
|
||||||
// been reached, cannot add a user.
|
// and the limit has been reached, cannot add a user.
|
||||||
Log.e(LOG_TAG, "Cannot add user. Maximum user limit is reached.");
|
Log.e(LOG_TAG, "Cannot add user. Maximum user limit is reached.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -2747,8 +2841,7 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
|
|
||||||
userId = getNextAvailableId();
|
userId = getNextAvailableId();
|
||||||
Environment.getUserSystemDirectory(userId).mkdirs();
|
Environment.getUserSystemDirectory(userId).mkdirs();
|
||||||
boolean ephemeralGuests = Resources.getSystem()
|
boolean ephemeralGuests = areGuestUsersEphemeral();
|
||||||
.getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
|
|
||||||
|
|
||||||
synchronized (mUsersLock) {
|
synchronized (mUsersLock) {
|
||||||
// Add ephemeral flag to guests/users if required. Also inherit it from parent.
|
// Add ephemeral flag to guests/users if required. Also inherit it from parent.
|
||||||
@@ -2759,9 +2852,9 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
|
|
||||||
userInfo = new UserInfo(userId, name, null, flags);
|
userInfo = new UserInfo(userId, name, null, flags);
|
||||||
userInfo.serialNumber = mNextSerialNumber++;
|
userInfo.serialNumber = mNextSerialNumber++;
|
||||||
long now = System.currentTimeMillis();
|
userInfo.creationTime = getCreationTime();
|
||||||
userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
|
|
||||||
userInfo.partial = true;
|
userInfo.partial = true;
|
||||||
|
userInfo.preCreated = preCreate;
|
||||||
userInfo.lastLoggedInFingerprint = Build.FINGERPRINT;
|
userInfo.lastLoggedInFingerprint = Build.FINGERPRINT;
|
||||||
if (isManagedProfile && parentId != UserHandle.USER_NULL) {
|
if (isManagedProfile && parentId != UserHandle.USER_NULL) {
|
||||||
userInfo.profileBadge = getFreeProfileBadgeLU(parentId);
|
userInfo.profileBadge = getFreeProfileBadgeLU(parentId);
|
||||||
@@ -2808,18 +2901,97 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
mBaseUserRestrictions.append(userId, restrictions);
|
mBaseUserRestrictions.append(userId, restrictions);
|
||||||
}
|
}
|
||||||
mPm.onNewUserCreated(userId);
|
mPm.onNewUserCreated(userId);
|
||||||
Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
|
if (preCreate) {
|
||||||
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
|
// Must start user (which will be stopped right away, through
|
||||||
mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
|
// UserController.finishUserUnlockedCompleted) so services can properly
|
||||||
android.Manifest.permission.MANAGE_USERS);
|
// intialize it.
|
||||||
MetricsLogger.count(mContext, isGuest ? TRON_GUEST_CREATED
|
// TODO(b/140750212): in the long-term, we should add a onCreateUser() callback
|
||||||
: (isDemo ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);
|
// on SystemService instead.
|
||||||
|
Slog.i(LOG_TAG, "starting pre-created user " + userInfo.toFullString());
|
||||||
|
final IActivityManager am = ActivityManager.getService();
|
||||||
|
try {
|
||||||
|
am.startUserInBackground(userId);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Slog.w(LOG_TAG, "could not start pre-created user " + userId, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dispatchUserAddedIntent(userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(ident);
|
Binder.restoreCallingIdentity(ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(b/140750212): it's possible to reach "max users overflow" when the user is created
|
||||||
|
// "from scratch" (i.e., not from a pre-created user) and reaches the maximum number of
|
||||||
|
// users without counting the pre-created one. Then when the pre-created is converted, the
|
||||||
|
// "effective" number of max users is exceeds. Example:
|
||||||
|
// Max: 3 Current: 2 full (u0 and u10) + 1 pre-created (u11)
|
||||||
|
// Step 1: create(/* flags doesn't match u11 */): u12 is created, "effective max" is now 3
|
||||||
|
// (u0, u10, u12) but "real" max is 4 (u0, u10, u11, u12)
|
||||||
|
// Step 2: create(/* flags match u11 */): u11 is converted, now "effective max" is also 4
|
||||||
|
// (u0, u10, u11, u12)
|
||||||
|
// One way to avoid this issue is by removing a pre-created user from the pool when the
|
||||||
|
// "real" max exceeds the max here.
|
||||||
|
|
||||||
return userInfo;
|
return userInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long getCreationTime() {
|
||||||
|
final long now = System.currentTimeMillis();
|
||||||
|
return (now > EPOCH_PLUS_30_YEARS) ? now : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dispatchUserAddedIntent(@NonNull UserInfo userInfo) {
|
||||||
|
Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
|
||||||
|
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
|
||||||
|
mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
|
||||||
|
android.Manifest.permission.MANAGE_USERS);
|
||||||
|
MetricsLogger.count(mContext, userInfo.isGuest() ? TRON_GUEST_CREATED
|
||||||
|
: (userInfo.isDemo() ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean areGuestUsersEphemeral() {
|
||||||
|
return Resources.getSystem()
|
||||||
|
.getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a pre-created user for the given flag.
|
||||||
|
*
|
||||||
|
* <p>Should be used only during user creation, so the pre-created user can be used (instead of
|
||||||
|
* creating and initializing a new user from scratch).
|
||||||
|
*/
|
||||||
|
// TODO(b/140750212): add unit test
|
||||||
|
@GuardedBy("mUsersLock")
|
||||||
|
private @Nullable UserData getPreCreatedUserLU(@UserInfoFlag int flags) {
|
||||||
|
if (DBG) {
|
||||||
|
Slog.d(LOG_TAG, "getPreCreatedUser(): initialFlags= " + UserInfo.flagsToString(flags));
|
||||||
|
}
|
||||||
|
if (UserInfo.isGuest(flags) && areGuestUsersEphemeral()) {
|
||||||
|
flags |= UserInfo.FLAG_EPHEMERAL;
|
||||||
|
}
|
||||||
|
if (DBG) {
|
||||||
|
Slog.d(LOG_TAG, "getPreCreatedUser(): targetFlags= " + UserInfo.flagsToString(flags));
|
||||||
|
}
|
||||||
|
final int userSize = mUsers.size();
|
||||||
|
for (int i = 0; i < userSize; i++) {
|
||||||
|
final UserData user = mUsers.valueAt(i);
|
||||||
|
if (DBG) Slog.d(LOG_TAG, i + ":" + user.info.toFullString());
|
||||||
|
if (user.info.preCreated
|
||||||
|
&& (user.info.flags & ~UserInfo.FLAG_INITIALIZED) == flags) {
|
||||||
|
if (!user.info.isInitialized()) {
|
||||||
|
Slog.w(LOG_TAG, "found pre-created user for flags "
|
||||||
|
+ "" + UserInfo.flagsToString(flags)
|
||||||
|
+ ", but it's not initialized yet: " + user.info.toFullString());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
UserData putUserInfo(UserInfo userInfo) {
|
UserData putUserInfo(UserInfo userInfo) {
|
||||||
final UserData userData = new UserData();
|
final UserData userData = new UserData();
|
||||||
@@ -3651,7 +3823,7 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
try {
|
try {
|
||||||
switch(cmd) {
|
switch(cmd) {
|
||||||
case "list":
|
case "list":
|
||||||
return runList(pw);
|
return runList(pw, shell);
|
||||||
default:
|
default:
|
||||||
return shell.handleDefaultCommands(cmd);
|
return shell.handleDefaultCommands(cmd);
|
||||||
}
|
}
|
||||||
@@ -3661,17 +3833,58 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int runList(PrintWriter pw) throws RemoteException {
|
private int runList(PrintWriter pw, Shell shell) throws RemoteException {
|
||||||
|
boolean all = false;
|
||||||
|
boolean verbose = false;
|
||||||
|
String opt;
|
||||||
|
while ((opt = shell.getNextOption()) != null) {
|
||||||
|
switch (opt) {
|
||||||
|
case "-v":
|
||||||
|
verbose = true;
|
||||||
|
break;
|
||||||
|
case "--all":
|
||||||
|
all = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pw.println("Invalid option: " + opt);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
final IActivityManager am = ActivityManager.getService();
|
final IActivityManager am = ActivityManager.getService();
|
||||||
final List<UserInfo> users = getUsers(false);
|
final List<UserInfo> users = getUsers(/* excludePartial= */ !all,
|
||||||
|
/* excludingDying=*/ false, /* excludePreCreated= */ !all);
|
||||||
if (users == null) {
|
if (users == null) {
|
||||||
pw.println("Error: couldn't get users");
|
pw.println("Error: couldn't get users");
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
|
final int size = users.size();
|
||||||
|
int currentUser = UserHandle.USER_NULL;
|
||||||
|
if (verbose) {
|
||||||
|
pw.printf("%d users:\n\n", size);
|
||||||
|
currentUser = am.getCurrentUser().id;
|
||||||
|
} else {
|
||||||
|
// NOTE: the standard "list users" command is used by integration tests and
|
||||||
|
// hence should not be changed. If you need to add more info, use the
|
||||||
|
// verbose option.
|
||||||
pw.println("Users:");
|
pw.println("Users:");
|
||||||
for (int i = 0; i < users.size(); i++) {
|
}
|
||||||
String running = am.isUserRunning(users.get(i).id, 0) ? " running" : "";
|
for (int i = 0; i < size; i++) {
|
||||||
pw.println("\t" + users.get(i).toString() + running);
|
final UserInfo user = users.get(i);
|
||||||
|
final boolean running = am.isUserRunning(user.id, 0);
|
||||||
|
final boolean current = user.id == currentUser;
|
||||||
|
if (verbose) {
|
||||||
|
pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s\n", i, user.id, user.name,
|
||||||
|
UserInfo.flagsToString(user.flags),
|
||||||
|
running ? " (running)" : "",
|
||||||
|
user.partial ? " (partial)" : "",
|
||||||
|
user.preCreated ? " (pre-created)" : "",
|
||||||
|
current ? " (current)" : "");
|
||||||
|
} else {
|
||||||
|
// NOTE: the standard "list users" command is used by integration tests and
|
||||||
|
// hence should not be changed. If you need to add more info, use the
|
||||||
|
// verbose option.
|
||||||
|
pw.printf("\t%s%s\n", user, running ? " running" : "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -3708,6 +3921,9 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
if (userInfo.partial) {
|
if (userInfo.partial) {
|
||||||
pw.print(" <partial>");
|
pw.print(" <partial>");
|
||||||
}
|
}
|
||||||
|
if (userInfo.preCreated) {
|
||||||
|
pw.print(" <pre-created>");
|
||||||
|
}
|
||||||
pw.println();
|
pw.println();
|
||||||
pw.print(" State: ");
|
pw.print(" State: ");
|
||||||
final int state;
|
final int state;
|
||||||
@@ -3788,11 +4004,12 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
|
|
||||||
// Dump some capabilities
|
// Dump some capabilities
|
||||||
pw.println();
|
pw.println();
|
||||||
pw.println(" Max users: " + UserManager.getMaxSupportedUsers());
|
pw.print(" Max users: " + UserManager.getMaxSupportedUsers());
|
||||||
|
pw.println(" (limit reached: " + isUserLimitReached() + ")");
|
||||||
pw.println(" Supports switchable users: " + UserManager.supportsMultipleUsers());
|
pw.println(" Supports switchable users: " + UserManager.supportsMultipleUsers());
|
||||||
pw.println(" All guests ephemeral: " + Resources.getSystem().getBoolean(
|
pw.println(" All guests ephemeral: " + areGuestUsersEphemeral());
|
||||||
com.android.internal.R.bool.config_guestUserEphemeral));
|
|
||||||
pw.println(" Is split-system user: " + UserManager.isSplitSystemUser());
|
pw.println(" Is split-system user: " + UserManager.isSplitSystemUser());
|
||||||
|
pw.println(" User version: " + mUserVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
|
private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
|
||||||
@@ -3977,7 +4194,7 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
public UserInfo createUserEvenWhenDisallowed(String name, int flags,
|
public UserInfo createUserEvenWhenDisallowed(String name, int flags,
|
||||||
String[] disallowedPackages) {
|
String[] disallowedPackages) {
|
||||||
UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL,
|
UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL,
|
||||||
disallowedPackages);
|
/* preCreated= */ false, disallowedPackages);
|
||||||
// Keep this in sync with UserManager.createUser
|
// Keep this in sync with UserManager.createUser
|
||||||
if (user != null && !user.isAdmin() && !user.isDemo()) {
|
if (user != null && !user.isAdmin() && !user.isDemo()) {
|
||||||
setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
|
setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
|
||||||
@@ -4142,7 +4359,7 @@ public class UserManagerService extends IUserManager.Stub {
|
|||||||
pw.println(" help");
|
pw.println(" help");
|
||||||
pw.println(" Print this help text.");
|
pw.println(" Print this help text.");
|
||||||
pw.println("");
|
pw.println("");
|
||||||
pw.println(" list");
|
pw.println(" list [-v] [-all]");
|
||||||
pw.println(" Prints all users on the system.");
|
pw.println(" Prints all users on the system.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
|
|||||||
import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
|
import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
|
||||||
import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
|
import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
|
||||||
import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
|
import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
|
||||||
import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
|
import static com.android.server.am.UserController.USER_CURRENT_MSG;
|
||||||
import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
|
import static com.android.server.am.UserController.USER_START_MSG;
|
||||||
import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
|
import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
|
||||||
|
|
||||||
import static com.google.android.collect.Lists.newArrayList;
|
import static com.google.android.collect.Lists.newArrayList;
|
||||||
@@ -53,11 +53,13 @@ import static org.mockito.Mockito.validateMockitoUsage;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.annotation.UserIdInt;
|
||||||
import android.app.IUserSwitchObserver;
|
import android.app.IUserSwitchObserver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.IIntentReceiver;
|
import android.content.IIntentReceiver;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
|
import android.content.pm.UserInfo.UserInfoFlag;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -107,6 +109,10 @@ public class UserControllerTest {
|
|||||||
private static final int TEST_USER_ID1 = 101;
|
private static final int TEST_USER_ID1 = 101;
|
||||||
private static final int TEST_USER_ID2 = 102;
|
private static final int TEST_USER_ID2 = 102;
|
||||||
private static final int NONEXIST_USER_ID = 2;
|
private static final int NONEXIST_USER_ID = 2;
|
||||||
|
private static final int TEST_PRE_CREATED_USER_ID = 103;
|
||||||
|
|
||||||
|
private static final int NO_USERINFO_FLAGS = 0;
|
||||||
|
|
||||||
private static final String TAG = UserControllerTest.class.getSimpleName();
|
private static final String TAG = UserControllerTest.class.getSimpleName();
|
||||||
|
|
||||||
private static final long HANDLER_WAIT_TIME_MS = 100;
|
private static final long HANDLER_WAIT_TIME_MS = 100;
|
||||||
@@ -128,11 +134,11 @@ public class UserControllerTest {
|
|||||||
private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
|
private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
|
||||||
REPORT_USER_SWITCH_MSG,
|
REPORT_USER_SWITCH_MSG,
|
||||||
USER_SWITCH_TIMEOUT_MSG,
|
USER_SWITCH_TIMEOUT_MSG,
|
||||||
SYSTEM_USER_START_MSG,
|
USER_START_MSG,
|
||||||
SYSTEM_USER_CURRENT_MSG);
|
USER_CURRENT_MSG);
|
||||||
|
|
||||||
private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
|
private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
|
||||||
SYSTEM_USER_START_MSG,
|
USER_START_MSG,
|
||||||
REPORT_LOCKED_BOOT_COMPLETE_MSG);
|
REPORT_LOCKED_BOOT_COMPLETE_MSG);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -149,7 +155,8 @@ public class UserControllerTest {
|
|||||||
doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
|
doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
|
||||||
doNothing().when(mInjector).stackSupervisorRemoveUser(anyInt());
|
doNothing().when(mInjector).stackSupervisorRemoveUser(anyInt());
|
||||||
mUserController = new UserController(mInjector);
|
mUserController = new UserController(mInjector);
|
||||||
setUpUser(TEST_USER_ID, 0);
|
setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS);
|
||||||
|
setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +197,31 @@ public class UserControllerTest {
|
|||||||
startForegroundUserAssertions();
|
startForegroundUserAssertions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartPreCreatedUser_foreground() {
|
||||||
|
assertFalse(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartPreCreatedUser_background() throws Exception {
|
||||||
|
assertTrue(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ false));
|
||||||
|
|
||||||
|
verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
|
||||||
|
verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
|
||||||
|
verify(mInjector, never()).clearAllLockedTasks(anyString());
|
||||||
|
|
||||||
|
assertWithMessage("should not have received intents")
|
||||||
|
.that(getActions(mInjector.mSentIntents)).isEmpty();
|
||||||
|
// TODO(b/140868593): should have received a USER_UNLOCK_MSG message as well, but it doesn't
|
||||||
|
// because StorageManager.isUserKeyUnlocked(TEST_PRE_CREATED_USER_ID) returns false - to
|
||||||
|
// properly fix it, we'd need to move this class to FrameworksMockingServicesTests so we can
|
||||||
|
// mock static methods (but moving this class would involve changing the presubmit tests,
|
||||||
|
// and the cascade effect goes on...). In fact, a better approach would to not assert the
|
||||||
|
// binder calls, but their side effects (in this case, that the user is stopped right away)
|
||||||
|
assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes())
|
||||||
|
.containsExactly(USER_START_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
private void startUserAssertions(
|
private void startUserAssertions(
|
||||||
List<String> expectedActions, Set<Integer> expectedMessageCodes) {
|
List<String> expectedActions, Set<Integer> expectedMessageCodes) {
|
||||||
assertEquals(expectedActions, getActions(mInjector.mSentIntents));
|
assertEquals(expectedActions, getActions(mInjector.mSentIntents));
|
||||||
@@ -469,9 +501,15 @@ public class UserControllerTest {
|
|||||||
continueUserSwitchAssertions(newUserId, expectOldUserStopping);
|
continueUserSwitchAssertions(newUserId, expectOldUserStopping);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpUser(int userId, int flags) {
|
private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) {
|
||||||
|
setUpUser(userId, flags, /* preCreated= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags, boolean preCreated) {
|
||||||
UserInfo userInfo = new UserInfo(userId, "User" + userId, flags);
|
UserInfo userInfo = new UserInfo(userId, "User" + userId, flags);
|
||||||
|
userInfo.preCreated = preCreated;
|
||||||
when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo);
|
when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo);
|
||||||
|
when(mInjector.mUserManagerMock.isPreCreated(userId)).thenReturn(preCreated);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<String> getActions(List<Intent> intents) {
|
private static List<String> getActions(List<Intent> intents) {
|
||||||
|
|||||||
Reference in New Issue
Block a user