diff options
| -rw-r--r-- | services/core/java/com/android/server/pm/UserManagerInternal.java | 11 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/UserManagerService.java | 30 | ||||
| -rw-r--r-- | services/java/com/android/server/HsumBootUserInitializer.java (renamed from services/java/com/android/server/BootUserInitializer.java) | 80 | ||||
| -rw-r--r-- | services/java/com/android/server/SystemServer.java | 21 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java | 16 |
5 files changed, 103 insertions, 55 deletions
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java index eb3be544e1f6..896b490164b5 100644 --- a/services/core/java/com/android/server/pm/UserManagerInternal.java +++ b/services/core/java/com/android/server/pm/UserManagerInternal.java @@ -551,11 +551,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; } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 762d1f665653..ce7dc5b953ee 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -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. */ diff --git a/services/java/com/android/server/BootUserInitializer.java b/services/java/com/android/server/HsumBootUserInitializer.java index 3d71739924f7..cc6c36ef1ed5 100644 --- a/services/java/com/android/server/BootUserInitializer.java +++ b/services/java/com/android/server/HsumBootUserInitializer.java @@ -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) { + /** 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."); } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index a15c6d288cfd..d22be9ec01d0 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -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(); } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java index d03d1968190d..1ed2f789d9b2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java @@ -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) { |