Merge "HsumBootUserInitializer creates MainUser earlier"

This commit is contained in:
Adam Bookatz
2023-02-03 01:45:52 +00:00
committed by Android (Google) Code Review
5 changed files with 105 additions and 57 deletions

View File

@@ -553,11 +553,12 @@ public abstract class UserManagerInternal {
* 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.
* this will be the user who was last in the foreground on this device.
*
* <p>In non-headless system user mode, the return value will be {@link UserHandle#USER_SYSTEM}.
* <p>In non-headless system user mode, the return value will be
* {@link android.os.UserHandle#USER_SYSTEM}.
* @throws UserManager.CheckedUserOperationException if no switchable user can be found
*/
public abstract @UserIdInt int getBootUser()
throws UserManager.CheckedUserOperationException;
public abstract @UserIdInt int getBootUser() throws UserManager.CheckedUserOperationException;
}

View File

@@ -5049,6 +5049,8 @@ public class UserManagerService extends IUserManager.Stub {
//...then external ones
Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
addedIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
// In HSUM, MainUser might be created before PHASE_ACTIVITY_MANAGER_READY has been sent.
addedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
// Also, add the UserHandle for mainline modules which can't use the @hide
// EXTRA_USER_HANDLE.
@@ -6758,18 +6760,6 @@ 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,
@@ -7249,15 +7239,9 @@ public class UserManagerService extends IUserManager.Stub {
}
}
}
// 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;
// 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;
@@ -7437,14 +7421,14 @@ public class UserManagerService extends IUserManager.Stub {
/**
* Returns true, when user has {@link UserInfo#FLAG_MAIN} and system property
* {@link com.android.internal.R.bool.isMainUserPermanentAdmin} is true.
* {@link com.android.internal.R.bool#config_isMainUserPermanentAdmin} is true.
*/
private boolean isNonRemovableMainUser(UserInfo userInfo) {
return userInfo.isMain() && isMainUserPermanentAdmin();
}
/**
* Returns true, when {@link com.android.internal.R.bool.isMainUserPermanentAdmin} is true.
* Returns true if {@link com.android.internal.R.bool#config_isMainUserPermanentAdmin} is true.
* If the main user is a permanent admin user it can't be deleted
* or downgraded to non-admin status.
*/

View File

@@ -15,8 +15,10 @@
*/
package com.android.server;
import android.annotation.Nullable;
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;
@@ -30,22 +32,43 @@ import com.android.server.utils.TimingsTraceAndSlog;
* Class responsible for booting the device in the proper user on headless system user mode.
*
*/
// TODO(b/204091126): STOPSHIP - provide proper APIs
final class BootUserInitializer {
final class HsumBootUserInitializer {
private static final String TAG = BootUserInitializer.class.getSimpleName();
// TODO(b/204091126): STOPSHIP - set to false or dynamic value
private static final boolean DEBUG = true;
private static final String TAG = HsumBootUserInitializer.class.getSimpleName();
private final UserManagerInternal mUmi;
private final ActivityManagerService mAms;
private final ContentResolver mContentResolver;
BootUserInitializer(ActivityManagerService am, ContentResolver contentResolver) {
mAms = am;
mContentResolver = contentResolver;
/** Whether this device should always have a non-removable MainUser, including at first boot. */
private final boolean mShouldAlwaysHaveMainUser;
/** Static factory method for creating a {@link HsumBootUserInitializer} instance. */
public static @Nullable HsumBootUserInitializer createInstance(ActivityManagerService am,
ContentResolver contentResolver, boolean shouldAlwaysHaveMainUser) {
if (!UserManager.isHeadlessSystemUserMode()) {
return null;
}
return new HsumBootUserInitializer(
LocalServices.getService(UserManagerInternal.class),
am, contentResolver, shouldAlwaysHaveMainUser);
}
private HsumBootUserInitializer(UserManagerInternal umi, ActivityManagerService am,
ContentResolver contentResolver, boolean shouldAlwaysHaveMainUser) {
mUmi = umi;
mAms = am;
mContentResolver = contentResolver;
this.mShouldAlwaysHaveMainUser = shouldAlwaysHaveMainUser;
}
/**
* Initialize this object, and create MainUser if needed.
*
* Should be called before PHASE_SYSTEM_SERVICES_READY as services' setups may require MainUser,
* but probably after PHASE_LOCK_SETTINGS_READY since that may be needed for user creation.
*/
public void init(TimingsTraceAndSlog t) {
Slogf.i(TAG, "init())");
@@ -53,17 +76,56 @@ final class BootUserInitializer {
// this class or the setup wizard app
provisionHeadlessSystemUser();
if (mShouldAlwaysHaveMainUser) {
t.traceBegin("createMainUserIfNeeded");
createMainUserIfNeeded();
t.traceEnd();
}
}
private void createMainUserIfNeeded() {
int mainUser = mUmi.getMainUserId();
if (mainUser != UserHandle.USER_NULL) {
Slogf.d(TAG, "Found existing MainUser, userId=%d", mainUser);
return;
}
Slogf.d(TAG, "Creating a new MainUser");
try {
final UserInfo newInitialUser = mUmi.createUserEvenWhenDisallowed(
/* name= */ null, // null will appear as "Owner" in on-demand localisation
UserManager.USER_TYPE_FULL_SECONDARY,
UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN,
/* disallowedPackages= */ null,
/* token= */ null);
if (newInitialUser == null) {
Slogf.wtf(TAG, "Initial bootable MainUser creation failed: returned null");
} else {
Slogf.i(TAG, "Successfully created MainUser, userId=%d", newInitialUser.id);
}
} catch (UserManager.CheckedUserOperationException e) {
Slogf.wtf(TAG, "Initial bootable MainUser creation failed", e);
}
}
/**
* Put the device into the correct user state: unlock the system and switch to the boot user.
*
* Should only call once PHASE_THIRD_PARTY_APPS_CAN_START is reached to ensure that privileged
* apps have had the chance to set the boot user, if applicable.
*/
public void systemRunning(TimingsTraceAndSlog t) {
unlockSystemUser(t);
try {
t.traceBegin("getBootUser");
int bootUser = LocalServices.getService(UserManagerInternal.class).getBootUser();
final int bootUser = mUmi.getBootUser();
t.traceEnd();
t.traceBegin("switchToBootUser-" + bootUser);
switchToBootUser(bootUser);
t.traceEnd();
} catch (UserManager.CheckedUserOperationException e) {
Slogf.wtf(TAG, "Failed to created boot user", e);
Slogf.wtf(TAG, "Failed to switch to boot user since there isn't one.");
}
}

View File

@@ -75,7 +75,6 @@ import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -2694,6 +2693,18 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_LOCK_SETTINGS_READY);
t.traceEnd();
// Create initial user if needed, which should be done early since some system services rely
// on it in their setup, but likely needs to be done after LockSettingsService is ready.
final HsumBootUserInitializer hsumBootUserInitializer =
HsumBootUserInitializer.createInstance(
mActivityManagerService, mContentResolver,
context.getResources().getBoolean(R.bool.config_isMainUserPermanentAdmin));
if (hsumBootUserInitializer != null) {
t.traceBegin("HsumBootUserInitializer.init");
hsumBootUserInitializer.init(t);
t.traceEnd();
}
t.traceBegin("StartBootPhaseSystemServicesReady");
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_SYSTEM_SERVICES_READY);
t.traceEnd();
@@ -2961,10 +2972,10 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
t.traceEnd();
if (UserManager.isHeadlessSystemUserMode() && !isAutomotive) {
// TODO(b/204091126): remove isAutomotive check once the workflow is finalized
t.traceBegin("BootUserInitializer");
new BootUserInitializer(mActivityManagerService, mContentResolver).init(t);
if (hsumBootUserInitializer != null && !isAutomotive) {
// TODO(b/261924826): remove isAutomotive check once the workflow is finalized
t.traceBegin("HsumBootUserInitializer.systemRunning");
hsumBootUserInitializer.systemRunning(t);
t.traceEnd();
}

View File

@@ -20,6 +20,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -316,21 +317,10 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
}
@Test
public void testGetBootUser_Headless_UserCreatedIfOnlySystemUserExists() throws Exception {
public void testGetBootUser_Headless_ThrowsIfOnlySystemUserExists() 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();
assertThrows(UserManager.CheckedUserOperationException.class, () -> mUmi.getBootUser());
}
private void mockCurrentUser(@UserIdInt int userId) {