Merge "Headless: Switch to supplied user or previous user after boot."
This commit is contained in:
@@ -10403,6 +10403,7 @@ package android.os {
|
||||
method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.MANAGE_USERS"}) public boolean isUserVisible();
|
||||
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle);
|
||||
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int removeUserWhenPossible(@NonNull android.os.UserHandle, boolean);
|
||||
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public void setBootUser(@NonNull android.os.UserHandle);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
|
||||
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount(@NonNull String, @NonNull String);
|
||||
|
||||
@@ -2025,6 +2025,7 @@ package android.os {
|
||||
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createProfileForUser(@Nullable String, @NonNull String, int, int, @Nullable String[]);
|
||||
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String);
|
||||
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int);
|
||||
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getBootUser();
|
||||
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.Set<java.lang.String> getPreInstallableSystemPackages(@NonNull String);
|
||||
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType();
|
||||
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
|
||||
|
||||
@@ -142,4 +142,8 @@ interface IUserManager {
|
||||
long getUserStartRealtime();
|
||||
long getUserUnlockRealtime();
|
||||
boolean setUserEphemeral(int userId, boolean enableEphemeral);
|
||||
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS})")
|
||||
void setBootUser(int userId);
|
||||
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS})")
|
||||
int getBootUser();
|
||||
}
|
||||
|
||||
@@ -5671,6 +5671,40 @@ public class UserManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user who should be in the foreground when boot completes. This should be called
|
||||
* during boot, and the provided user must be a full user (i.e. not a profile).
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
|
||||
Manifest.permission.CREATE_USERS})
|
||||
public void setBootUser(@NonNull UserHandle bootUser) {
|
||||
try {
|
||||
mService.setBootUser(bootUser.getIdentifier());
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user who should be in the foreground when boot completes.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
|
||||
Manifest.permission.CREATE_USERS})
|
||||
@SuppressWarnings("[AndroidFrameworkContextUserId]")
|
||||
public @NonNull UserHandle getBootUser() {
|
||||
try {
|
||||
return UserHandle.of(mService.getBootUser());
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/* Cache key for anything that assumes that userIds cannot be re-used without rebooting. */
|
||||
private static final String CACHE_KEY_STATIC_USER_PROPERTIES = "cache_key.static_user_props";
|
||||
|
||||
|
||||
@@ -513,4 +513,20 @@ public abstract class UserManagerInternal {
|
||||
* @see UserManager#isMainUser()
|
||||
*/
|
||||
public abstract @UserIdInt int getMainUserId();
|
||||
|
||||
/**
|
||||
* Returns the id of the user which should be in the foreground after boot completes.
|
||||
*
|
||||
* <p>If a boot user has been provided by calling {@link UserManager#setBootUser}, the
|
||||
* returned value will be whatever was specified, as long as that user exists and can be
|
||||
* switched to.
|
||||
*
|
||||
* <p>Otherwise, in {@link UserManager#isHeadlessSystemUserMode() headless system user mode},
|
||||
* this will be the user who was last in the foreground on this device. If there is no
|
||||
* switchable user on the device, a new user will be created and its id will be returned.
|
||||
*
|
||||
* <p>In non-headless system user mode, the return value will be {@link UserHandle#USER_SYSTEM}.
|
||||
*/
|
||||
public abstract @UserIdInt int getBootUser()
|
||||
throws UserManager.CheckedUserOperationException;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.os.UserManager.DEV_CREATE_OVERRIDE_PROPERTY;
|
||||
import static android.os.UserManager.DISALLOW_USER_SWITCH;
|
||||
import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY;
|
||||
import static android.os.UserManager.USER_OPERATION_ERROR_UNKNOWN;
|
||||
|
||||
import android.Manifest;
|
||||
import android.accounts.Account;
|
||||
@@ -637,6 +638,9 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
|
||||
private final UserVisibilityMediator mUserVisibilityMediator;
|
||||
|
||||
@GuardedBy("mUsersLock")
|
||||
private @UserIdInt int mBootUser = UserHandle.USER_NULL;
|
||||
|
||||
private static UserManagerService sInstance;
|
||||
|
||||
public static UserManagerService getInstance() {
|
||||
@@ -935,6 +939,26 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
return UserHandle.USER_NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBootUser(@UserIdInt int userId) {
|
||||
checkCreateUsersPermission("Set boot user");
|
||||
synchronized (mUsersLock) {
|
||||
// TODO(b/263381643): Change to EventLog.
|
||||
Slogf.i(LOG_TAG, "setBootUser %d", userId);
|
||||
mBootUser = userId;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @UserIdInt int getBootUser() {
|
||||
checkCreateUsersPermission("Get boot user");
|
||||
try {
|
||||
return mLocalService.getBootUser();
|
||||
} catch (UserManager.CheckedUserOperationException e) {
|
||||
throw e.toServiceSpecificException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreviousFullUserToEnterForeground() {
|
||||
checkQueryOrCreateUsersPermission("get previous user");
|
||||
@@ -1569,6 +1593,8 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
Slog.w(LOG_TAG, "System user instantiated at least " + number + " times");
|
||||
}
|
||||
name = getOwnerName();
|
||||
} else if (orig.isMain()) {
|
||||
name = getOwnerName();
|
||||
} else if (orig.isGuest()) {
|
||||
name = getGuestName();
|
||||
}
|
||||
@@ -4535,7 +4561,7 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
UserHandle.USER_NULL, null);
|
||||
|
||||
if (userInfo == null) {
|
||||
throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN);
|
||||
throw new ServiceSpecificException(USER_OPERATION_ERROR_UNKNOWN);
|
||||
}
|
||||
} catch (UserManager.CheckedUserOperationException e) {
|
||||
throw e.toServiceSpecificException();
|
||||
@@ -4664,7 +4690,7 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
if (parent == null) {
|
||||
throwCheckedUserOperationException(
|
||||
"Cannot find user data for parent user " + parentId,
|
||||
UserManager.USER_OPERATION_ERROR_UNKNOWN);
|
||||
USER_OPERATION_ERROR_UNKNOWN);
|
||||
}
|
||||
}
|
||||
if (!preCreate && !canAddMoreUsersOfType(userTypeDetails)) {
|
||||
@@ -4692,7 +4718,7 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
&& !isCreationOverrideEnabled()) {
|
||||
throwCheckedUserOperationException(
|
||||
"Cannot add restricted profile - parent user must be system",
|
||||
UserManager.USER_OPERATION_ERROR_UNKNOWN);
|
||||
USER_OPERATION_ERROR_UNKNOWN);
|
||||
}
|
||||
|
||||
userId = getNextAvailableId();
|
||||
@@ -6459,6 +6485,9 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
if (DBG_ALLOCATION) {
|
||||
pw.println(" System user allocations: " + mUser0Allocations.get());
|
||||
}
|
||||
synchronized (mUsersLock) {
|
||||
pw.println(" Boot user: " + mBootUser);
|
||||
}
|
||||
|
||||
pw.println();
|
||||
pw.println("Number of listeners for");
|
||||
@@ -6651,6 +6680,18 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
return mLocalService.isUserInitialized(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user, intended to be the initial user on a device in headless system user mode.
|
||||
*/
|
||||
private UserInfo createInitialUserForHsum() throws UserManager.CheckedUserOperationException {
|
||||
final int flags = UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN;
|
||||
|
||||
// Null name will be replaced with "Owner" on-demand to allow for localisation.
|
||||
return createUserInternalUnchecked(/* name= */ null, UserManager.USER_TYPE_FULL_SECONDARY,
|
||||
flags, UserHandle.USER_NULL, /* preCreate= */ false,
|
||||
/* disallowedPackages= */ null, /* token= */ null);
|
||||
}
|
||||
|
||||
private class LocalService extends UserManagerInternal {
|
||||
@Override
|
||||
public void setDevicePolicyUserRestrictions(@UserIdInt int originatingUserId,
|
||||
@@ -7094,6 +7135,56 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
return getMainUserIdUnchecked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @UserIdInt int getBootUser() throws UserManager.CheckedUserOperationException {
|
||||
synchronized (mUsersLock) {
|
||||
// TODO(b/242195409): On Automotive, block if boot user not provided.
|
||||
if (mBootUser != UserHandle.USER_NULL) {
|
||||
final UserData userData = mUsers.get(mBootUser);
|
||||
if (userData != null && userData.info.supportsSwitchToByUser()) {
|
||||
Slogf.i(LOG_TAG, "Using provided boot user: %d", mBootUser);
|
||||
return mBootUser;
|
||||
} else {
|
||||
Slogf.w(LOG_TAG,
|
||||
"Provided boot user cannot be switched to: %d", mBootUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isHeadlessSystemUserMode()) {
|
||||
// Return the previous foreground user, if there is one.
|
||||
final int previousUser = getPreviousFullUserToEnterForeground();
|
||||
if (previousUser != UserHandle.USER_NULL) {
|
||||
Slogf.i(LOG_TAG, "Boot user is previous user %d", previousUser);
|
||||
return previousUser;
|
||||
}
|
||||
// No previous user. Return the first switchable user if there is one.
|
||||
synchronized (mUsersLock) {
|
||||
final int userSize = mUsers.size();
|
||||
for (int i = 0; i < userSize; i++) {
|
||||
final UserData userData = mUsers.valueAt(i);
|
||||
if (userData.info.supportsSwitchToByUser()) {
|
||||
int firstSwitchable = userData.info.id;
|
||||
Slogf.i(LOG_TAG,
|
||||
"Boot user is first switchable user %d", firstSwitchable);
|
||||
return firstSwitchable;
|
||||
}
|
||||
}
|
||||
}
|
||||
// No switchable users. Create the initial user.
|
||||
final UserInfo newInitialUser = createInitialUserForHsum();
|
||||
if (newInitialUser == null) {
|
||||
throw new UserManager.CheckedUserOperationException(
|
||||
"Initial user creation failed", USER_OPERATION_ERROR_UNKNOWN);
|
||||
}
|
||||
Slogf.i(LOG_TAG,
|
||||
"No switchable users. Boot user is new user %d", newInitialUser.id);
|
||||
return newInitialUser.id;
|
||||
}
|
||||
// Not HSUM, return system user.
|
||||
return UserHandle.USER_SYSTEM;
|
||||
}
|
||||
|
||||
} // class LocalService
|
||||
|
||||
|
||||
@@ -7113,7 +7204,7 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
+ restriction + " is enabled.";
|
||||
Slog.w(LOG_TAG, errorMessage);
|
||||
throw new UserManager.CheckedUserOperationException(errorMessage,
|
||||
UserManager.USER_OPERATION_ERROR_UNKNOWN);
|
||||
USER_OPERATION_ERROR_UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ package com.android.server;
|
||||
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
@@ -27,8 +26,6 @@ import com.android.server.pm.UserManagerInternal;
|
||||
import com.android.server.utils.Slogf;
|
||||
import com.android.server.utils.TimingsTraceAndSlog;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class responsible for booting the device in the proper user on headless system user mode.
|
||||
*
|
||||
@@ -56,50 +53,18 @@ final class BootUserInitializer {
|
||||
// this class or the setup wizard app
|
||||
provisionHeadlessSystemUser();
|
||||
|
||||
UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
|
||||
t.traceBegin("get-existing-users");
|
||||
List<UserInfo> existingUsers = um.getUsers(/* excludeDying= */ true);
|
||||
t.traceEnd();
|
||||
|
||||
Slogf.d(TAG, "%d existing users", existingUsers.size());
|
||||
|
||||
int initialUserId = UserHandle.USER_NULL;
|
||||
|
||||
for (int i = 0; i < existingUsers.size(); i++) {
|
||||
UserInfo user = existingUsers.get(i);
|
||||
if (DEBUG) {
|
||||
Slogf.d(TAG, "User at position %d: %s", i, user.toFullString());
|
||||
}
|
||||
if (user.id != UserHandle.USER_SYSTEM && user.isFull()) {
|
||||
if (DEBUG) {
|
||||
Slogf.d(TAG, "Found initial user: %d", user.id);
|
||||
}
|
||||
initialUserId = user.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (initialUserId == UserHandle.USER_NULL) {
|
||||
Slogf.d(TAG, "Creating initial user");
|
||||
t.traceBegin("create-initial-user");
|
||||
try {
|
||||
int flags = UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN;
|
||||
// TODO(b/204091126): proper name for user
|
||||
UserInfo newUser = um.createUserEvenWhenDisallowed("Real User",
|
||||
UserManager.USER_TYPE_FULL_SECONDARY, flags,
|
||||
/* disallowedPackages= */ null, /* token= */ null);
|
||||
Slogf.i(TAG, "Created initial user: %s", newUser.toFullString());
|
||||
initialUserId = newUser.id;
|
||||
} catch (Exception e) {
|
||||
Slogf.wtf(TAG, "failed to created initial user", e);
|
||||
return;
|
||||
} finally {
|
||||
t.traceEnd(); // create-initial-user
|
||||
}
|
||||
}
|
||||
|
||||
unlockSystemUser(t);
|
||||
switchToInitialUser(initialUserId);
|
||||
|
||||
try {
|
||||
t.traceBegin("getBootUser");
|
||||
int bootUser = LocalServices.getService(UserManagerInternal.class).getBootUser();
|
||||
t.traceEnd();
|
||||
t.traceBegin("switchToBootUser-" + bootUser);
|
||||
switchToBootUser(bootUser);
|
||||
t.traceEnd();
|
||||
} catch (UserManager.CheckedUserOperationException e) {
|
||||
Slogf.wtf(TAG, "Failed to created boot user", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO(b/261791491): STOPSHIP - SUW should be responsible for this. */
|
||||
@@ -152,12 +117,12 @@ final class BootUserInitializer {
|
||||
}
|
||||
}
|
||||
|
||||
private void switchToInitialUser(@UserIdInt int initialUserId) {
|
||||
Slogf.i(TAG, "Switching to initial user %d", initialUserId);
|
||||
boolean started = mAms.startUserInForegroundWithListener(initialUserId,
|
||||
private void switchToBootUser(@UserIdInt int bootUserId) {
|
||||
Slogf.i(TAG, "Switching to boot user %d", bootUserId);
|
||||
boolean started = mAms.startUserInForegroundWithListener(bootUserId,
|
||||
/* unlockListener= */ null);
|
||||
if (!started) {
|
||||
Slogf.wtf(TAG, "Failed to start user %d in foreground", initialUserId);
|
||||
Slogf.wtf(TAG, "Failed to start user %d in foreground", bootUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,25 +20,31 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
|
||||
import com.android.internal.widget.LockSettingsInternal;
|
||||
import com.android.server.ExtendedMockitoTestCase;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.am.UserState;
|
||||
import com.android.server.pm.UserManagerService.UserData;
|
||||
import com.android.server.storage.DeviceStorageMonitorInternal;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -86,6 +92,10 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
|
||||
private @Mock PackageManagerService mMockPms;
|
||||
private @Mock UserDataPreparer mMockUserDataPreparer;
|
||||
private @Mock ActivityManagerInternal mActivityManagerInternal;
|
||||
private @Mock DeviceStorageMonitorInternal mDeviceStorageMonitorInternal;
|
||||
private @Mock StorageManager mStorageManager;
|
||||
private @Mock LockSettingsInternal mLockSettingsInternal;
|
||||
private @Mock PackageManagerInternal mPackageManagerInternal;
|
||||
|
||||
/**
|
||||
* Reference to the {@link UserManagerService} being tested.
|
||||
@@ -101,7 +111,8 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
|
||||
protected void initializeSession(StaticMockitoSessionBuilder builder) {
|
||||
builder
|
||||
.spyStatic(UserManager.class)
|
||||
.spyStatic(LocalServices.class);
|
||||
.spyStatic(LocalServices.class)
|
||||
.mockStatic(Settings.Global.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -112,6 +123,14 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
|
||||
// Called when WatchedUserStates is constructed
|
||||
doNothing().when(() -> UserManager.invalidateIsUserUnlockedCache());
|
||||
|
||||
// Called when creating new users
|
||||
when(mDeviceStorageMonitorInternal.isMemoryLow()).thenReturn(false);
|
||||
mockGetLocalService(DeviceStorageMonitorInternal.class, mDeviceStorageMonitorInternal);
|
||||
when(mSpiedContext.getSystemService(StorageManager.class)).thenReturn(mStorageManager);
|
||||
mockGetLocalService(LockSettingsInternal.class, mLockSettingsInternal);
|
||||
mockGetLocalService(PackageManagerInternal.class, mPackageManagerInternal);
|
||||
doNothing().when(mSpiedContext).sendBroadcastAsUser(any(), any(), any());
|
||||
|
||||
// Must construct UserManagerService in the UiThread
|
||||
mUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer,
|
||||
mPackagesLock, mRealContext.getDataDir(), mUsers);
|
||||
@@ -223,6 +242,87 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
|
||||
.that(mUms.isUserRunning(PROFILE_USER_ID)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetBootUser_SuppliedUserIsSwitchable() throws Exception {
|
||||
addUser(USER_ID);
|
||||
addUser(OTHER_USER_ID);
|
||||
|
||||
mUms.setBootUser(OTHER_USER_ID);
|
||||
|
||||
assertWithMessage("getBootUser")
|
||||
.that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetBootUser_NotHeadless_SuppliedUserIsNotSwitchable() throws Exception {
|
||||
setSystemUserHeadless(false);
|
||||
addUser(USER_ID);
|
||||
addUser(OTHER_USER_ID);
|
||||
addDefaultProfileAndParent();
|
||||
|
||||
mUms.setBootUser(PROFILE_USER_ID);
|
||||
|
||||
assertWithMessage("getBootUser")
|
||||
.that(mUmi.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetBootUser_Headless_SuppliedUserIsNotSwitchable() throws Exception {
|
||||
setSystemUserHeadless(true);
|
||||
addUser(USER_ID);
|
||||
setLastForegroundTime(USER_ID, 1_000_000L);
|
||||
addUser(OTHER_USER_ID);
|
||||
setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
|
||||
addDefaultProfileAndParent();
|
||||
|
||||
mUms.setBootUser(PROFILE_USER_ID);
|
||||
|
||||
// Boot user not switchable so return most recently in foreground.
|
||||
assertWithMessage("getBootUser")
|
||||
.that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBootUser_NotHeadless_ReturnsSystemUser() throws Exception {
|
||||
setSystemUserHeadless(false);
|
||||
addUser(USER_ID);
|
||||
addUser(OTHER_USER_ID);
|
||||
|
||||
assertWithMessage("getBootUser")
|
||||
.that(mUmi.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBootUser_Headless_ReturnsMostRecentlyInForeground() throws Exception {
|
||||
setSystemUserHeadless(true);
|
||||
addUser(USER_ID);
|
||||
setLastForegroundTime(USER_ID, 1_000_000L);
|
||||
|
||||
addUser(OTHER_USER_ID);
|
||||
setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
|
||||
|
||||
assertWithMessage("getBootUser")
|
||||
.that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBootUser_Headless_UserCreatedIfOnlySystemUserExists() throws Exception {
|
||||
setSystemUserHeadless(true);
|
||||
|
||||
int bootUser = mUmi.getBootUser();
|
||||
|
||||
assertWithMessage("getStartingUser")
|
||||
.that(bootUser).isNotEqualTo(UserHandle.USER_SYSTEM);
|
||||
|
||||
UserData newUser = mUsers.get(bootUser);
|
||||
assertWithMessage("New boot user is a full user")
|
||||
.that(newUser.info.isFull()).isTrue();
|
||||
assertWithMessage("New boot user is an admin user")
|
||||
.that(newUser.info.isAdmin()).isTrue();
|
||||
assertWithMessage("New boot user is the main user")
|
||||
.that(newUser.info.isMain()).isTrue();
|
||||
}
|
||||
|
||||
private void mockCurrentUser(@UserIdInt int userId) {
|
||||
mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);
|
||||
|
||||
@@ -248,7 +348,7 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
|
||||
|
||||
private void addUser(@UserIdInt int userId) {
|
||||
TestUserData userData = new TestUserData(userId);
|
||||
|
||||
userData.info.flags = UserInfo.FLAG_FULL;
|
||||
addUserData(userData);
|
||||
}
|
||||
|
||||
@@ -277,6 +377,23 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
|
||||
mUsers.put(userData.info.id, userData);
|
||||
}
|
||||
|
||||
private void setSystemUserHeadless(boolean headless) {
|
||||
UserData systemUser = mUsers.get(UserHandle.USER_SYSTEM);
|
||||
if (headless) {
|
||||
systemUser.info.flags &= ~UserInfo.FLAG_FULL;
|
||||
systemUser.info.userType = UserManager.USER_TYPE_SYSTEM_HEADLESS;
|
||||
} else {
|
||||
systemUser.info.flags |= UserInfo.FLAG_FULL;
|
||||
systemUser.info.userType = UserManager.USER_TYPE_FULL_SYSTEM;
|
||||
}
|
||||
doReturn(headless).when(() -> UserManager.isHeadlessSystemUserMode());
|
||||
}
|
||||
|
||||
private void setLastForegroundTime(@UserIdInt int userId, long timeMillis) {
|
||||
UserData userData = mUsers.get(userId);
|
||||
userData.mLastEnteredForegroundTimeMillis = timeMillis;
|
||||
}
|
||||
|
||||
private static final class TestUserData extends UserData {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
||||
Reference in New Issue
Block a user