Merge "HsumBootUserInitializer creates MainUser earlier"
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user