Integrate new boot user flow with auto

Bug: 261913541
Bug: 261924826
Test: atest UserManagerServiceTest
Change-Id: I4740fe58bef942e534b6eef24bc5a1ddce270d02
This commit is contained in:
Mayank Garg
2023-02-01 13:17:55 -08:00
parent f7f05bae00
commit ad154e15e2
5 changed files with 94 additions and 52 deletions

View File

@@ -536,5 +536,6 @@ public abstract class UserManagerInternal {
* @throws UserManager.CheckedUserOperationException if no switchable user can be found
*/
public abstract @UserIdInt int getBootUser() throws UserManager.CheckedUserOperationException;
public abstract @UserIdInt int getBootUser(boolean waitUntilSet)
throws UserManager.CheckedUserOperationException;
}

View File

@@ -160,7 +160,9 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -277,6 +279,8 @@ public class UserManagerService extends IUserManager.Stub {
static final int WRITE_USER_MSG = 1;
static final int WRITE_USER_DELAY = 2*1000; // 2 seconds
private static final long BOOT_USER_SET_TIMEOUT_MS = 300_000;
// Tron counters
private static final String TRON_GUEST_CREATED = "users_guest_created";
private static final String TRON_USER_CREATED = "users_user_created";
@@ -332,6 +336,8 @@ public class UserManagerService extends IUserManager.Stub {
/** Indicates that this is the 1st boot after the system user mode was changed by emulation. */
private boolean mUpdatingSystemUserMode;
/** Count down latch to wait while boot user is not set.*/
private final CountDownLatch mBootUserLatch = new CountDownLatch(1);
/**
* Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
*/
@@ -969,18 +975,62 @@ public class UserManagerService extends IUserManager.Stub {
Slogf.i(LOG_TAG, "setBootUser %d", userId);
mBootUser = userId;
}
mBootUserLatch.countDown();
}
@Override
public @UserIdInt int getBootUser() {
checkCreateUsersPermission("Get boot user");
try {
return mLocalService.getBootUser();
return getBootUserUnchecked();
} catch (UserManager.CheckedUserOperationException e) {
throw e.toServiceSpecificException();
}
}
private @UserIdInt int getBootUserUnchecked() throws UserManager.CheckedUserOperationException {
synchronized (mUsersLock) {
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 found. Uh oh!
throw new UserManager.CheckedUserOperationException(
"No switchable users found", USER_OPERATION_ERROR_UNKNOWN);
}
// Not HSUM, return system user.
return UserHandle.USER_SYSTEM;
}
@Override
public int getPreviousFullUserToEnterForeground() {
checkQueryOrCreateUsersPermission("get previous user");
@@ -7275,47 +7325,29 @@ public class UserManagerService extends IUserManager.Stub {
}
@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);
public @UserIdInt int getBootUser(boolean waitUntilSet)
throws UserManager.CheckedUserOperationException {
if (waitUntilSet) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("wait-boot-user");
try {
if (mBootUserLatch.getCount() != 0) {
Slogf.d(LOG_TAG,
"Sleeping for boot user to be set. "
+ "Max sleep for Time: %d", BOOT_USER_SET_TIMEOUT_MS);
}
if (!mBootUserLatch.await(BOOT_USER_SET_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
Slogf.w(LOG_TAG, "Boot user not set. Timeout: %d",
BOOT_USER_SET_TIMEOUT_MS);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Slogf.w(LOG_TAG, e, "InterruptedException during wait for boot user.");
}
t.traceEnd();
}
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 found. Uh oh!
throw new UserManager.CheckedUserOperationException(
"No switchable users found", USER_OPERATION_ERROR_UNKNOWN);
}
// Not HSUM, return system user.
return UserHandle.USER_SYSTEM;
return getBootUserUnchecked();
}
} // class LocalService

View File

@@ -18,6 +18,7 @@ package com.android.server;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.os.Handler;
@@ -27,6 +28,7 @@ import android.os.UserManager;
import android.provider.Settings;
import com.android.server.am.ActivityManagerService;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;
@@ -41,6 +43,7 @@ final class HsumBootUserInitializer {
private final UserManagerInternal mUmi;
private final ActivityManagerService mAms;
private final PackageManagerService mPms;
private final ContentResolver mContentResolver;
private final ContentObserver mDeviceProvisionedObserver =
@@ -63,20 +66,23 @@ final class HsumBootUserInitializer {
/** Static factory method for creating a {@link HsumBootUserInitializer} instance. */
public static @Nullable HsumBootUserInitializer createInstance(ActivityManagerService am,
ContentResolver contentResolver, boolean shouldAlwaysHaveMainUser) {
PackageManagerService pms, ContentResolver contentResolver,
boolean shouldAlwaysHaveMainUser) {
if (!UserManager.isHeadlessSystemUserMode()) {
return null;
}
return new HsumBootUserInitializer(
LocalServices.getService(UserManagerInternal.class),
am, contentResolver, shouldAlwaysHaveMainUser);
am, pms, contentResolver, shouldAlwaysHaveMainUser);
}
private HsumBootUserInitializer(UserManagerInternal umi, ActivityManagerService am,
ContentResolver contentResolver, boolean shouldAlwaysHaveMainUser) {
PackageManagerService pms, ContentResolver contentResolver,
boolean shouldAlwaysHaveMainUser) {
mUmi = umi;
mAms = am;
mPms = pms;
mContentResolver = contentResolver;
mShouldAlwaysHaveMainUser = shouldAlwaysHaveMainUser;
}
@@ -131,7 +137,8 @@ final class HsumBootUserInitializer {
try {
t.traceBegin("getBootUser");
final int bootUser = mUmi.getBootUser();
final int bootUser = mUmi.getBootUser(/* waitUntilSet= */ mPms
.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, /* version= */0));
t.traceEnd();
t.traceBegin("switchToBootUser-" + bootUser);
switchToBootUser(bootUser);

View File

@@ -2719,7 +2719,7 @@ public final class SystemServer implements Dumpable {
// on it in their setup, but likely needs to be done after LockSettingsService is ready.
final HsumBootUserInitializer hsumBootUserInitializer =
HsumBootUserInitializer.createInstance(
mActivityManagerService, mContentResolver,
mActivityManagerService, mPackageManagerService, mContentResolver,
context.getResources().getBoolean(R.bool.config_isMainUserPermanentAdmin));
if (hsumBootUserInitializer != null) {
t.traceBegin("HsumBootUserInitializer.init");
@@ -3011,8 +3011,7 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
t.traceEnd();
if (hsumBootUserInitializer != null && !isAutomotive) {
// TODO(b/261924826): remove isAutomotive check once the workflow is finalized
if (hsumBootUserInitializer != null) {
t.traceBegin("HsumBootUserInitializer.systemRunning");
hsumBootUserInitializer.systemRunning(t);
t.traceEnd();

View File

@@ -261,7 +261,7 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
mUms.setBootUser(OTHER_USER_ID);
assertWithMessage("getBootUser")
.that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
.that(mUmi.getBootUser(/* waitUntilSet= */ false)).isEqualTo(OTHER_USER_ID);
}
@Test
@@ -274,7 +274,8 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
mUms.setBootUser(PROFILE_USER_ID);
assertWithMessage("getBootUser")
.that(mUmi.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
.that(mUmi.getBootUser(/* waitUntilSet= */ false))
.isEqualTo(UserHandle.USER_SYSTEM);
}
@Test
@@ -290,7 +291,7 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
// Boot user not switchable so return most recently in foreground.
assertWithMessage("getBootUser")
.that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
.that(mUmi.getBootUser(/* waitUntilSet= */ false)).isEqualTo(OTHER_USER_ID);
}
@Test
@@ -300,7 +301,8 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
addUser(OTHER_USER_ID);
assertWithMessage("getBootUser")
.that(mUmi.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
.that(mUmi.getBootUser(/* waitUntilSet= */ false))
.isEqualTo(UserHandle.USER_SYSTEM);
}
@Test
@@ -313,14 +315,15 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
assertWithMessage("getBootUser")
.that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
.that(mUmi.getBootUser(/* waitUntilSet= */ false)).isEqualTo(OTHER_USER_ID);
}
@Test
public void testGetBootUser_Headless_ThrowsIfOnlySystemUserExists() throws Exception {
setSystemUserHeadless(true);
assertThrows(UserManager.CheckedUserOperationException.class, () -> mUmi.getBootUser());
assertThrows(UserManager.CheckedUserOperationException.class,
() -> mUmi.getBootUser(/* waitUntilSet= */ false));
}
private void mockCurrentUser(@UserIdInt int userId) {