summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/pm/UserManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java30
-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.java21
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java16
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) {