Loading services/core/java/com/android/server/pm/UserManagerInternal.java +8 −0 Original line number Diff line number Diff line Loading @@ -617,6 +617,14 @@ public abstract class UserManagerInternal { public abstract @UserIdInt int getBootUser(boolean waitUntilSet) throws UserManager.CheckedUserOperationException; // NOTE: used only by HsumBootUserInitializer, ideally it should be package-protected, but it's // located in a different package. /** * Same as {@link UserManager#setBootUser(android.os.UserHandle)}, but without checking * permissions. */ public abstract void setBootUserId(@UserIdInt int userId); /** * Returns the user id of the communal profile, or {@link android.os.UserHandle#USER_NULL} * if there is no such user. Loading services/core/java/com/android/server/pm/UserManagerService.java +9 −0 Original line number Diff line number Diff line Loading @@ -1374,6 +1374,10 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setBootUser(@UserIdInt int userId) { checkCreateUsersPermission("Set boot user"); setBootUserIdUnchecked(userId); } private void setBootUserIdUnchecked(@UserIdInt int userId) { synchronized (mUsersLock) { // TODO(b/263381643): Change to EventLog. Slogf.i(LOG_TAG, "setBootUser %d", userId); Loading Loading @@ -8425,6 +8429,11 @@ public class UserManagerService extends IUserManager.Stub { return getBootUserUnchecked(); } @Override public void setBootUserId(@UserIdInt int userId) { setBootUserIdUnchecked(userId); } @Override public @CanBeNULL @UserIdInt int getCommunalProfileId() { return getCommunalProfileIdUnchecked(); Loading services/java/com/android/server/HsumBootUserInitializer.java +97 −15 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ import com.android.server.pm.UserManagerInternal; import com.android.server.utils.Slogf; import com.android.server.utils.TimingsTraceAndSlog; import java.util.Arrays; /** * Class responsible for booting the device in the proper user on headless system user mode. * Loading @@ -42,6 +44,8 @@ final class HsumBootUserInitializer { private static final String TAG = HsumBootUserInitializer.class.getSimpleName(); private static final boolean DEBUG = false; private final UserManagerInternal mUmi; private final ActivityManagerService mAms; private final PackageManagerService mPms; Loading @@ -65,29 +69,76 @@ final class HsumBootUserInitializer { /** Whether this device should always have a non-removable MainUser, including at first boot. */ private final boolean mShouldAlwaysHaveMainUser; /** Whether it should create an initial user, but without setting it as the main user. */ private final boolean mShouldCreateInitialUser; /** Static factory method for creating a {@link HsumBootUserInitializer} instance. */ public static @Nullable HsumBootUserInitializer createInstance(ActivityManagerService am, PackageManagerService pms, ContentResolver contentResolver, boolean shouldAlwaysHaveMainUser) { boolean shouldAlwaysHaveMainUser, boolean shouldCreateInitialUser) { if (!UserManager.isHeadlessSystemUserMode()) { return null; } return new HsumBootUserInitializer( LocalServices.getService(UserManagerInternal.class), am, pms, contentResolver, shouldAlwaysHaveMainUser); LocalServices.getService(UserManagerInternal.class), am, pms, contentResolver, shouldAlwaysHaveMainUser, shouldCreateInitialUser); } @VisibleForTesting HsumBootUserInitializer(UserManagerInternal umi, ActivityManagerService am, PackageManagerService pms, ContentResolver contentResolver, boolean shouldAlwaysHaveMainUser) { boolean shouldAlwaysHaveMainUser, boolean shouldCreateInitialUser) { mUmi = umi; mAms = am; mPms = pms; mContentResolver = contentResolver; mShouldAlwaysHaveMainUser = shouldAlwaysHaveMainUser; mShouldCreateInitialUser = shouldCreateInitialUser; if (DEBUG) { Slogf.d(TAG, "HsumBootUserInitializer(): shouldAlwaysHaveMainUser=%b, " + "shouldCreateInitialUser=%b", shouldAlwaysHaveMainUser, shouldCreateInitialUser); } } // TODO(b/409650316): remove after flag's completely pushed private void preCreateInitialUserFlagInit(TimingsTraceAndSlog t) { Slogf.d(TAG, "preCreateInitialUserFlagInit())"); if (mShouldAlwaysHaveMainUser) { t.traceBegin("createMainUserIfNeeded"); preCreateInitialUserCreateMainUserIfNeeded(); t.traceEnd(); } } // TODO(b/409650316): remove after flag's completely pushed private void preCreateInitialUserCreateMainUserIfNeeded() { final 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.i(TAG, "Successfully created MainUser, userId=%d", newInitialUser.id); } else { // Should never happen in production, but it does on HsumBootUserInitiliazerTest // (we could "fix" it by mocking the call, but it doesn't hurt to check anyways) Slogf.wtf(TAG, "createUserEvenWhenDisallowed() returned null"); } } catch (UserManager.CheckedUserOperationException e) { Slogf.wtf(TAG, "Initial bootable MainUser creation failed", e); } } /** Loading @@ -100,11 +151,24 @@ final class HsumBootUserInitializer { public void init(TimingsTraceAndSlog t) { Slogf.i(TAG, "init())"); if (!android.multiuser.Flags.createInitialUser()) { preCreateInitialUserFlagInit(t); return; } if (mShouldAlwaysHaveMainUser) { t.traceBegin("createMainUserIfNeeded"); createMainUserIfNeeded(); t.traceEnd(); return; } if (mShouldCreateInitialUser) { t.traceBegin("createAdminUserIfNeeded"); createAdminUserIfNeeded(); t.traceEnd(); return; } Slogf.d(TAG, "Not checking if initial user exists (should be handled externally)"); } private void createMainUserIfNeeded() { Loading @@ -113,24 +177,42 @@ final class HsumBootUserInitializer { Slogf.d(TAG, "Found existing MainUser, userId=%d", mainUser); return; } createInitialUser(/* isMainUser= */ true); } Slogf.d(TAG, "Creating a new MainUser"); private void createAdminUserIfNeeded() { int[] userIds = mUmi.getUserIds(); if (userIds != null && userIds.length > 1) { if (DEBUG) { Slogf.d(TAG, "createAdminUserIfNeeded(): already have more than 1 user (%s)", Arrays.toString(userIds)); } return; } createInitialUser(/* isMainUser= */ false); } private void createInitialUser(boolean isMainUser) { String logName; int flags = UserInfo.FLAG_ADMIN; if (isMainUser) { flags |= UserInfo.FLAG_MAIN; logName = "MainUser"; } else { logName = "admin user"; } Slogf.d(TAG, "Creating %s", logName); 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, flags, /* disallowedPackages= */ null, /* token= */ null); if (newInitialUser != null) { Slogf.i(TAG, "Successfully created MainUser, userId=%d", newInitialUser.id); } else { // Should never happen in production, but it does on HsumBootUserInitiliazerTest // (we could "fix" it by mocking the call, but it doesn't hurt to check anyways) Slogf.wtf(TAG, "createUserEvenWhenDisallowed() returned null"); } Slogf.i(TAG, "Successfully created %s, userId=%d", logName, newInitialUser.id); mUmi.setBootUserId(newInitialUser.id); } catch (UserManager.CheckedUserOperationException e) { Slogf.wtf(TAG, "Initial bootable MainUser creation failed", e); Slogf.wtf(TAG, e, "Initial bootable %s creation failed", logName); } } Loading services/java/com/android/server/SystemServer.java +3 −1 Original line number Diff line number Diff line Loading @@ -3020,7 +3020,9 @@ public final class SystemServer implements Dumpable { final HsumBootUserInitializer hsumBootUserInitializer = HsumBootUserInitializer.createInstance( mActivityManagerService, mPackageManagerService, mContentResolver, context.getResources().getBoolean(R.bool.config_isMainUserPermanentAdmin)); context.getResources().getBoolean(R.bool.config_isMainUserPermanentAdmin), context.getResources().getBoolean(R.bool.config_createInitialUser) ); if (hsumBootUserInitializer != null) { t.traceBegin("HsumBootUserInitializer.init"); hsumBootUserInitializer.init(t); Loading services/tests/mockingservicestests/src/com/android/server/HsumBootUserInitializerTest.java +189 −16 Original line number Diff line number Diff line Loading @@ -15,7 +15,9 @@ */ package com.android.server; import static android.multiuser.Flags.FLAG_CREATE_INITIAL_USER; import static android.os.UserHandle.USER_NULL; import static android.os.UserHandle.USER_SYSTEM; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; Loading @@ -28,7 +30,11 @@ import android.annotation.SpecialUsers.CanBeNULL; import android.annotation.UserIdInt; import android.content.ContentResolver; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; import android.os.UserManager; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.util.Log; import com.android.server.am.ActivityManagerService; Loading @@ -39,12 +45,15 @@ import com.android.server.utils.TimingsTraceAndSlog; import com.google.common.truth.Expect; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import java.util.Arrays; public final class HsumBootUserInitializerTest { private static final String TAG = HsumBootUserInitializerTest.class.getSimpleName(); Loading @@ -59,6 +68,10 @@ public final class HsumBootUserInitializerTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @Rule public final SetFlagsRule setFlagsRule = new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); @Mock private UserManagerInternal mMockUmi; @Mock Loading @@ -71,6 +84,13 @@ public final class HsumBootUserInitializerTest { @Nullable // Must be created in the same thread that it's used private TimingsTraceAndSlog mTracer; @Before public void setDefaultExpectations() throws Exception { mockGetMainUserId(USER_NULL); mockGetUserIds(USER_SYSTEM); mockCreateNewUser(NON_SYSTEM_USER_ID); } @After public void expectAllTracingCallsAreFinished() { if (mTracer == null) { Loading @@ -84,9 +104,56 @@ public final class HsumBootUserInitializerTest { } @Test public void testInit_shouldAlwaysHaveMainUserTrue_noMainUser() throws Exception { mockGetMainUserId(USER_NULL); var initializer = createHsumBootUserInitializer(true); @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserFalse_noUsers_dontCreateUser() { var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ false); initializer.init(mTracer); expectNoUserCreated(); } @Test @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserFalse_hasUser_dontCreateUser() { mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ false); initializer.init(mTracer); expectNoUserCreated(); } @Test @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserTrue_noUser_createsUser() { var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectAdminUserCreated(); } @Test @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserTrue_hasUser_dontCreateUser() { mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectNoUserCreated(); } @Test @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserFalse_noMainUser_createsMainUser() { var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ false); initializer.init(mTracer); Loading @@ -94,9 +161,24 @@ public final class HsumBootUserInitializerTest { } @Test public void testInit_shouldAlwaysHaveMainUserTrue_hasMainUser() throws Exception { @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserFalse_hasNonMainUser_createsMainUser() { mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ false); initializer.init(mTracer); expectMainUserCreated(); } @Test @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserFalse_hasMainUser_dontCreateUser() { mockGetMainUserId(NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(true); mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ false); initializer.init(mTracer); Loading @@ -104,19 +186,60 @@ public final class HsumBootUserInitializerTest { } @Test public void testInit_shouldAlwaysHaveMainUserFalse_noMainUser() throws Exception { mockGetMainUserId(USER_NULL); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false); @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserTrue_hasNonMainUser_createsMainUser() { mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectNoUserCreated(); expectMainUserCreated(); } // TODO(b/409650316): remove tests below after flag's completely pushed @Test public void testInit_shouldAlwaysHaveMainUserFalse_hasMainUser() { @DisableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_flagDisabled_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserTrue_noUser_createsMainUser() { var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectMainUserCreated(); } @Test @DisableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_flagDisabled_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserTrue_hasMainUser_dontCreateUser() { mockGetMainUserId(NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectNoUserCreated(); } @Test @DisableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_flagDisabled_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserTrue_noUser_createsAdminUser() throws Exception { var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectNoUserCreated(); } @Test @DisableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_flagDisabled_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserTrue_hasUser_dontCreateUser() { mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); Loading @@ -125,19 +248,31 @@ public final class HsumBootUserInitializerTest { private HsumBootUserInitializer createHsumBootUserInitializer( boolean shouldAlwaysHaveMainUser) { mTracer = new TimingsTraceAndSlog(TAG); return createHsumBootUserInitializer(shouldAlwaysHaveMainUser, /* shouldCreateInitialUser= */ false); } private HsumBootUserInitializer createHsumBootUserInitializer( boolean shouldAlwaysHaveMainUser, boolean shouldCreateInitialUser) { mTracer = new TimingsTraceAndSlog(TAG); return new HsumBootUserInitializer(mMockUmi, mMockAms, mMockPms, mMockContentResolver, shouldAlwaysHaveMainUser); shouldAlwaysHaveMainUser, shouldCreateInitialUser); } private void expectMainUserCreated() { expectUserCreated(UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN); } private void expectAdminUserCreated() { expectUserCreated(UserInfo.FLAG_ADMIN); } private void expectUserCreated(@UserInfoFlag int flags) { try { verify(mMockUmi).createUserEvenWhenDisallowed(null, UserManager.USER_TYPE_FULL_SECONDARY, UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN, null, null); UserManager.USER_TYPE_FULL_SECONDARY, flags, null, null); } catch (Exception e) { String msg = "didn't create main user"; String msg = "didn't create user with flags " + flags; Log.e(TAG, msg, e); expect.withMessage(msg).fail(); } Loading @@ -152,10 +287,48 @@ public final class HsumBootUserInitializerTest { Log.e(TAG, msg, e); expect.withMessage(msg).fail(); } // Since the user was not created, we can automatically infer that the boot user should not // have been set as well expectSetBootUserIdNeverCalled(); } private void expectSetBootUserId(@UserIdInt int userId) { try { verify(mMockUmi).setBootUserId(userId); } catch (Exception e) { String msg = "didn't call setBootUserId(" + userId + ")"; Log.e(TAG, msg, e); expect.withMessage(msg).fail(); } } private void expectSetBootUserIdNeverCalled() { try { verify(mMockUmi, never()).setBootUserId(anyInt()); } catch (Exception e) { String msg = "setBootUserId() should never be called"; Log.e(TAG, msg, e); expect.withMessage(msg).fail(); } } private void mockCreateNewUser(@UserIdInt int userId) throws Exception { @SuppressWarnings("deprecation") UserInfo userInfo = new UserInfo(); userInfo.id = userId; Log.d(TAG, "createUserEvenWhenDisallowed() will return " + userInfo); when(mMockUmi.createUserEvenWhenDisallowed(any(), any(), anyInt(), any(), any())) .thenReturn(userInfo); } private void mockGetMainUserId(@CanBeNULL @UserIdInt int userId) { Log.d(TAG, "mockGetMainUserId(): " + userId); when(mMockUmi.getMainUserId()).thenReturn(userId); } private void mockGetUserIds(@UserIdInt int... userIds) { Log.d(TAG, "mockGetUserIds(): " + Arrays.toString(userIds)); when(mMockUmi.getUserIds()).thenReturn(userIds); } } Loading
services/core/java/com/android/server/pm/UserManagerInternal.java +8 −0 Original line number Diff line number Diff line Loading @@ -617,6 +617,14 @@ public abstract class UserManagerInternal { public abstract @UserIdInt int getBootUser(boolean waitUntilSet) throws UserManager.CheckedUserOperationException; // NOTE: used only by HsumBootUserInitializer, ideally it should be package-protected, but it's // located in a different package. /** * Same as {@link UserManager#setBootUser(android.os.UserHandle)}, but without checking * permissions. */ public abstract void setBootUserId(@UserIdInt int userId); /** * Returns the user id of the communal profile, or {@link android.os.UserHandle#USER_NULL} * if there is no such user. Loading
services/core/java/com/android/server/pm/UserManagerService.java +9 −0 Original line number Diff line number Diff line Loading @@ -1374,6 +1374,10 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setBootUser(@UserIdInt int userId) { checkCreateUsersPermission("Set boot user"); setBootUserIdUnchecked(userId); } private void setBootUserIdUnchecked(@UserIdInt int userId) { synchronized (mUsersLock) { // TODO(b/263381643): Change to EventLog. Slogf.i(LOG_TAG, "setBootUser %d", userId); Loading Loading @@ -8425,6 +8429,11 @@ public class UserManagerService extends IUserManager.Stub { return getBootUserUnchecked(); } @Override public void setBootUserId(@UserIdInt int userId) { setBootUserIdUnchecked(userId); } @Override public @CanBeNULL @UserIdInt int getCommunalProfileId() { return getCommunalProfileIdUnchecked(); Loading
services/java/com/android/server/HsumBootUserInitializer.java +97 −15 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ import com.android.server.pm.UserManagerInternal; import com.android.server.utils.Slogf; import com.android.server.utils.TimingsTraceAndSlog; import java.util.Arrays; /** * Class responsible for booting the device in the proper user on headless system user mode. * Loading @@ -42,6 +44,8 @@ final class HsumBootUserInitializer { private static final String TAG = HsumBootUserInitializer.class.getSimpleName(); private static final boolean DEBUG = false; private final UserManagerInternal mUmi; private final ActivityManagerService mAms; private final PackageManagerService mPms; Loading @@ -65,29 +69,76 @@ final class HsumBootUserInitializer { /** Whether this device should always have a non-removable MainUser, including at first boot. */ private final boolean mShouldAlwaysHaveMainUser; /** Whether it should create an initial user, but without setting it as the main user. */ private final boolean mShouldCreateInitialUser; /** Static factory method for creating a {@link HsumBootUserInitializer} instance. */ public static @Nullable HsumBootUserInitializer createInstance(ActivityManagerService am, PackageManagerService pms, ContentResolver contentResolver, boolean shouldAlwaysHaveMainUser) { boolean shouldAlwaysHaveMainUser, boolean shouldCreateInitialUser) { if (!UserManager.isHeadlessSystemUserMode()) { return null; } return new HsumBootUserInitializer( LocalServices.getService(UserManagerInternal.class), am, pms, contentResolver, shouldAlwaysHaveMainUser); LocalServices.getService(UserManagerInternal.class), am, pms, contentResolver, shouldAlwaysHaveMainUser, shouldCreateInitialUser); } @VisibleForTesting HsumBootUserInitializer(UserManagerInternal umi, ActivityManagerService am, PackageManagerService pms, ContentResolver contentResolver, boolean shouldAlwaysHaveMainUser) { boolean shouldAlwaysHaveMainUser, boolean shouldCreateInitialUser) { mUmi = umi; mAms = am; mPms = pms; mContentResolver = contentResolver; mShouldAlwaysHaveMainUser = shouldAlwaysHaveMainUser; mShouldCreateInitialUser = shouldCreateInitialUser; if (DEBUG) { Slogf.d(TAG, "HsumBootUserInitializer(): shouldAlwaysHaveMainUser=%b, " + "shouldCreateInitialUser=%b", shouldAlwaysHaveMainUser, shouldCreateInitialUser); } } // TODO(b/409650316): remove after flag's completely pushed private void preCreateInitialUserFlagInit(TimingsTraceAndSlog t) { Slogf.d(TAG, "preCreateInitialUserFlagInit())"); if (mShouldAlwaysHaveMainUser) { t.traceBegin("createMainUserIfNeeded"); preCreateInitialUserCreateMainUserIfNeeded(); t.traceEnd(); } } // TODO(b/409650316): remove after flag's completely pushed private void preCreateInitialUserCreateMainUserIfNeeded() { final 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.i(TAG, "Successfully created MainUser, userId=%d", newInitialUser.id); } else { // Should never happen in production, but it does on HsumBootUserInitiliazerTest // (we could "fix" it by mocking the call, but it doesn't hurt to check anyways) Slogf.wtf(TAG, "createUserEvenWhenDisallowed() returned null"); } } catch (UserManager.CheckedUserOperationException e) { Slogf.wtf(TAG, "Initial bootable MainUser creation failed", e); } } /** Loading @@ -100,11 +151,24 @@ final class HsumBootUserInitializer { public void init(TimingsTraceAndSlog t) { Slogf.i(TAG, "init())"); if (!android.multiuser.Flags.createInitialUser()) { preCreateInitialUserFlagInit(t); return; } if (mShouldAlwaysHaveMainUser) { t.traceBegin("createMainUserIfNeeded"); createMainUserIfNeeded(); t.traceEnd(); return; } if (mShouldCreateInitialUser) { t.traceBegin("createAdminUserIfNeeded"); createAdminUserIfNeeded(); t.traceEnd(); return; } Slogf.d(TAG, "Not checking if initial user exists (should be handled externally)"); } private void createMainUserIfNeeded() { Loading @@ -113,24 +177,42 @@ final class HsumBootUserInitializer { Slogf.d(TAG, "Found existing MainUser, userId=%d", mainUser); return; } createInitialUser(/* isMainUser= */ true); } Slogf.d(TAG, "Creating a new MainUser"); private void createAdminUserIfNeeded() { int[] userIds = mUmi.getUserIds(); if (userIds != null && userIds.length > 1) { if (DEBUG) { Slogf.d(TAG, "createAdminUserIfNeeded(): already have more than 1 user (%s)", Arrays.toString(userIds)); } return; } createInitialUser(/* isMainUser= */ false); } private void createInitialUser(boolean isMainUser) { String logName; int flags = UserInfo.FLAG_ADMIN; if (isMainUser) { flags |= UserInfo.FLAG_MAIN; logName = "MainUser"; } else { logName = "admin user"; } Slogf.d(TAG, "Creating %s", logName); 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, flags, /* disallowedPackages= */ null, /* token= */ null); if (newInitialUser != null) { Slogf.i(TAG, "Successfully created MainUser, userId=%d", newInitialUser.id); } else { // Should never happen in production, but it does on HsumBootUserInitiliazerTest // (we could "fix" it by mocking the call, but it doesn't hurt to check anyways) Slogf.wtf(TAG, "createUserEvenWhenDisallowed() returned null"); } Slogf.i(TAG, "Successfully created %s, userId=%d", logName, newInitialUser.id); mUmi.setBootUserId(newInitialUser.id); } catch (UserManager.CheckedUserOperationException e) { Slogf.wtf(TAG, "Initial bootable MainUser creation failed", e); Slogf.wtf(TAG, e, "Initial bootable %s creation failed", logName); } } Loading
services/java/com/android/server/SystemServer.java +3 −1 Original line number Diff line number Diff line Loading @@ -3020,7 +3020,9 @@ public final class SystemServer implements Dumpable { final HsumBootUserInitializer hsumBootUserInitializer = HsumBootUserInitializer.createInstance( mActivityManagerService, mPackageManagerService, mContentResolver, context.getResources().getBoolean(R.bool.config_isMainUserPermanentAdmin)); context.getResources().getBoolean(R.bool.config_isMainUserPermanentAdmin), context.getResources().getBoolean(R.bool.config_createInitialUser) ); if (hsumBootUserInitializer != null) { t.traceBegin("HsumBootUserInitializer.init"); hsumBootUserInitializer.init(t); Loading
services/tests/mockingservicestests/src/com/android/server/HsumBootUserInitializerTest.java +189 −16 Original line number Diff line number Diff line Loading @@ -15,7 +15,9 @@ */ package com.android.server; import static android.multiuser.Flags.FLAG_CREATE_INITIAL_USER; import static android.os.UserHandle.USER_NULL; import static android.os.UserHandle.USER_SYSTEM; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; Loading @@ -28,7 +30,11 @@ import android.annotation.SpecialUsers.CanBeNULL; import android.annotation.UserIdInt; import android.content.ContentResolver; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; import android.os.UserManager; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.util.Log; import com.android.server.am.ActivityManagerService; Loading @@ -39,12 +45,15 @@ import com.android.server.utils.TimingsTraceAndSlog; import com.google.common.truth.Expect; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import java.util.Arrays; public final class HsumBootUserInitializerTest { private static final String TAG = HsumBootUserInitializerTest.class.getSimpleName(); Loading @@ -59,6 +68,10 @@ public final class HsumBootUserInitializerTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @Rule public final SetFlagsRule setFlagsRule = new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); @Mock private UserManagerInternal mMockUmi; @Mock Loading @@ -71,6 +84,13 @@ public final class HsumBootUserInitializerTest { @Nullable // Must be created in the same thread that it's used private TimingsTraceAndSlog mTracer; @Before public void setDefaultExpectations() throws Exception { mockGetMainUserId(USER_NULL); mockGetUserIds(USER_SYSTEM); mockCreateNewUser(NON_SYSTEM_USER_ID); } @After public void expectAllTracingCallsAreFinished() { if (mTracer == null) { Loading @@ -84,9 +104,56 @@ public final class HsumBootUserInitializerTest { } @Test public void testInit_shouldAlwaysHaveMainUserTrue_noMainUser() throws Exception { mockGetMainUserId(USER_NULL); var initializer = createHsumBootUserInitializer(true); @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserFalse_noUsers_dontCreateUser() { var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ false); initializer.init(mTracer); expectNoUserCreated(); } @Test @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserFalse_hasUser_dontCreateUser() { mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ false); initializer.init(mTracer); expectNoUserCreated(); } @Test @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserTrue_noUser_createsUser() { var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectAdminUserCreated(); } @Test @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserTrue_hasUser_dontCreateUser() { mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectNoUserCreated(); } @Test @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserFalse_noMainUser_createsMainUser() { var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ false); initializer.init(mTracer); Loading @@ -94,9 +161,24 @@ public final class HsumBootUserInitializerTest { } @Test public void testInit_shouldAlwaysHaveMainUserTrue_hasMainUser() throws Exception { @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserFalse_hasNonMainUser_createsMainUser() { mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ false); initializer.init(mTracer); expectMainUserCreated(); } @Test @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserFalse_hasMainUser_dontCreateUser() { mockGetMainUserId(NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(true); mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ false); initializer.init(mTracer); Loading @@ -104,19 +186,60 @@ public final class HsumBootUserInitializerTest { } @Test public void testInit_shouldAlwaysHaveMainUserFalse_noMainUser() throws Exception { mockGetMainUserId(USER_NULL); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false); @EnableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserTrue_hasNonMainUser_createsMainUser() { mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectNoUserCreated(); expectMainUserCreated(); } // TODO(b/409650316): remove tests below after flag's completely pushed @Test public void testInit_shouldAlwaysHaveMainUserFalse_hasMainUser() { @DisableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_flagDisabled_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserTrue_noUser_createsMainUser() { var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectMainUserCreated(); } @Test @DisableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_flagDisabled_shouldAlwaysHaveMainUserTrue_shouldCreateInitialUserTrue_hasMainUser_dontCreateUser() { mockGetMainUserId(NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ true, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectNoUserCreated(); } @Test @DisableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_flagDisabled_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserTrue_noUser_createsAdminUser() throws Exception { var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); expectNoUserCreated(); } @Test @DisableFlags(FLAG_CREATE_INITIAL_USER) public void testInit_flagDisabled_shouldAlwaysHaveMainUserFalse_shouldCreateInitialUserTrue_hasUser_dontCreateUser() { mockGetUserIds(USER_SYSTEM, NON_SYSTEM_USER_ID); var initializer = createHsumBootUserInitializer(/* shouldAlwaysHaveMainUser= */ false, /* shouldCreateInitialUser= */ true); initializer.init(mTracer); Loading @@ -125,19 +248,31 @@ public final class HsumBootUserInitializerTest { private HsumBootUserInitializer createHsumBootUserInitializer( boolean shouldAlwaysHaveMainUser) { mTracer = new TimingsTraceAndSlog(TAG); return createHsumBootUserInitializer(shouldAlwaysHaveMainUser, /* shouldCreateInitialUser= */ false); } private HsumBootUserInitializer createHsumBootUserInitializer( boolean shouldAlwaysHaveMainUser, boolean shouldCreateInitialUser) { mTracer = new TimingsTraceAndSlog(TAG); return new HsumBootUserInitializer(mMockUmi, mMockAms, mMockPms, mMockContentResolver, shouldAlwaysHaveMainUser); shouldAlwaysHaveMainUser, shouldCreateInitialUser); } private void expectMainUserCreated() { expectUserCreated(UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN); } private void expectAdminUserCreated() { expectUserCreated(UserInfo.FLAG_ADMIN); } private void expectUserCreated(@UserInfoFlag int flags) { try { verify(mMockUmi).createUserEvenWhenDisallowed(null, UserManager.USER_TYPE_FULL_SECONDARY, UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN, null, null); UserManager.USER_TYPE_FULL_SECONDARY, flags, null, null); } catch (Exception e) { String msg = "didn't create main user"; String msg = "didn't create user with flags " + flags; Log.e(TAG, msg, e); expect.withMessage(msg).fail(); } Loading @@ -152,10 +287,48 @@ public final class HsumBootUserInitializerTest { Log.e(TAG, msg, e); expect.withMessage(msg).fail(); } // Since the user was not created, we can automatically infer that the boot user should not // have been set as well expectSetBootUserIdNeverCalled(); } private void expectSetBootUserId(@UserIdInt int userId) { try { verify(mMockUmi).setBootUserId(userId); } catch (Exception e) { String msg = "didn't call setBootUserId(" + userId + ")"; Log.e(TAG, msg, e); expect.withMessage(msg).fail(); } } private void expectSetBootUserIdNeverCalled() { try { verify(mMockUmi, never()).setBootUserId(anyInt()); } catch (Exception e) { String msg = "setBootUserId() should never be called"; Log.e(TAG, msg, e); expect.withMessage(msg).fail(); } } private void mockCreateNewUser(@UserIdInt int userId) throws Exception { @SuppressWarnings("deprecation") UserInfo userInfo = new UserInfo(); userInfo.id = userId; Log.d(TAG, "createUserEvenWhenDisallowed() will return " + userInfo); when(mMockUmi.createUserEvenWhenDisallowed(any(), any(), anyInt(), any(), any())) .thenReturn(userInfo); } private void mockGetMainUserId(@CanBeNULL @UserIdInt int userId) { Log.d(TAG, "mockGetMainUserId(): " + userId); when(mMockUmi.getMainUserId()).thenReturn(userId); } private void mockGetUserIds(@UserIdInt int... userIds) { Log.d(TAG, "mockGetUserIds(): " + Arrays.toString(userIds)); when(mMockUmi.getUserIds()).thenReturn(userIds); } }