Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7f4ce2e authored by Brahim Chikhaoui's avatar Brahim Chikhaoui Committed by Android (Google) Code Review
Browse files

Merge "Add a new boot strategy for headless system user mode" into main

parents c79c65e0 b6665b8b
Loading
Loading
Loading
Loading
+38 −7
Original line number Diff line number Diff line
@@ -341,6 +341,10 @@ public class UserManagerService extends IUserManager.Stub {
    private static final String TRON_USER_CREATED = "users_user_created";
    private static final String TRON_DEMO_CREATED = "users_demo_created";

    // The boot user strategy for HSUM.
    private static final int BOOT_TO_PREVIOUS_OR_FIRST_SWITCHABLE_USER = 0;
    private static final int BOOT_TO_HSU_FOR_PROVISIONED_DEVICE = 1;

    private final Context mContext;
    private final PackageManagerService mPm;

@@ -1391,16 +1395,42 @@ public class UserManagerService extends IUserManager.Stub {
        }

        if (isHeadlessSystemUserMode()) {
            if (mContext.getResources()
                    .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser)) {
                return UserHandle.USER_SYSTEM;
            }
            final int bootStrategy = mContext.getResources()
                    .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
            switch (bootStrategy) {
                case BOOT_TO_PREVIOUS_OR_FIRST_SWITCHABLE_USER:
                    return getPreviousOrFirstSwitchableUser();
                case BOOT_TO_HSU_FOR_PROVISIONED_DEVICE:
                    return getBootUserBasedOnProvisioning();
                default:
                    Slogf.w(LOG_TAG, "Unknown HSUM boot strategy: %d", bootStrategy);
                    return getPreviousOrFirstSwitchableUser();
            }
        }
        // Not HSUM, return system user.
        return UserHandle.USER_SYSTEM;
    }

    private @UserIdInt int getBootUserBasedOnProvisioning()
            throws UserManager.CheckedUserOperationException {
        final boolean provisioned = Settings.Global.getInt(mContext.getContentResolver(),
                                            Settings.Global.DEVICE_PROVISIONED, 0) != 0;
        if (provisioned) {
            return UserHandle.USER_SYSTEM;
        } else {
            final int firstSwitchableFullUser = getFirstSwitchableUser(true);
            if (firstSwitchableFullUser != UserHandle.USER_NULL) {
                Slogf.i(LOG_TAG,
                        "Boot user is first switchable full user %d",
                                firstSwitchableFullUser);
                return firstSwitchableFullUser;
            }
            // No switchable full user found. Uh oh!
            throw new UserManager.CheckedUserOperationException(
                "No switchable full user found", USER_OPERATION_ERROR_UNKNOWN);
        }
    }

    private @UserIdInt int getPreviousOrFirstSwitchableUser()
            throws UserManager.CheckedUserOperationException {
        // Return the previous foreground user, if there is one.
@@ -1410,7 +1440,7 @@ public class UserManagerService extends IUserManager.Stub {
            return previousUser;
        }
        // No previous user. Return the first switchable user if there is one.
        final int firstSwitchableUser = getFirstSwitchableUser();
        final int firstSwitchableUser = getFirstSwitchableUser(false);
        if (firstSwitchableUser != UserHandle.USER_NULL) {
            Slogf.i(LOG_TAG,
                    "Boot user is first switchable user %d", firstSwitchableUser);
@@ -1421,12 +1451,13 @@ public class UserManagerService extends IUserManager.Stub {
            "No switchable users found", USER_OPERATION_ERROR_UNKNOWN);
    }

    private @UserIdInt int getFirstSwitchableUser() {
    private @UserIdInt int getFirstSwitchableUser(boolean fullUserOnly) {
        synchronized (mUsersLock) {
            final int userSize = mUsers.size();
            for (int i = 0; i < userSize; i++) {
                final UserData userData = mUsers.valueAt(i);
                if (userData.info.supportsSwitchToByUser()) {
                if (userData.info.supportsSwitchToByUser() &&
                        (!fullUserOnly || userData.info.isFull())) {
                    int firstSwitchable = userData.info.id;
                    return firstSwitchable;
                }
+46 −6
Original line number Diff line number Diff line
@@ -195,12 +195,12 @@ public final class UserManagerServiceTest {
        doNothing().when(mSpiedContext).sendBroadcastAsUser(any(), any(), any());
        mockIsLowRamDevice(false);

        // Called when getting boot user. config_bootToHeadlessSystemUser is false by default.
        // Called when getting boot user. config_bootToHeadlessSystemUser is 0 by default.
        mSpyResources = spy(mSpiedContext.getResources());
        when(mSpiedContext.getResources()).thenReturn(mSpyResources);
        doReturn(false)
        doReturn(0)
                .when(mSpyResources)
                .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser);
                .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);

        // Must construct UserManagerService in the UiThread
        mTestDir = new File(mRealContext.getDataDir(), "umstest");
@@ -859,15 +859,50 @@ public final class UserManagerServiceTest {
    }

    @Test
    public void testGetBootUser_enableBootToHeadlessSystemUser() {
    public void testGetBootUser_Headless_BootToSystemUserWhenDeviceIsProvisioned() {
        setSystemUserHeadless(true);
        doReturn(true)
        addUser(USER_ID);
        addUser(OTHER_USER_ID);
        mockProvisionedDevice(true);
        doReturn(1)
                .when(mSpyResources)
                .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser);
                .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);

        assertThat(mUms.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
    }

    @Test
    public void testGetBootUser_Headless_BootToFirstSwitchableFullUserWhenDeviceNotProvisioned() {
        setSystemUserHeadless(true);
        addUser(USER_ID);
        addUser(OTHER_USER_ID);
        mockProvisionedDevice(false);
        doReturn(1)
                .when(mSpyResources)
                .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);
        // Even if the headless system user switchable flag is true, the boot user should be the
        // first switchable full user.
        doReturn(true)
                .when(mSpyResources)
                .getBoolean(com.android.internal.R.bool.config_canSwitchToHeadlessSystemUser);

        assertThat(mUms.getBootUser()).isEqualTo(USER_ID);
    }

    @Test
    public void testGetBootUser_Headless_ThrowsIfBootFailsNoFullUserWhenDeviceNotProvisioned()
                throws Exception {
        setSystemUserHeadless(true);
        removeNonSystemUsers();
        mockProvisionedDevice(false);
        doReturn(1)
                .when(mSpyResources)
                .getInteger(com.android.internal.R.integer.config_hsumBootStrategy);

        assertThrows(ServiceSpecificException.class,
                () -> mUms.getBootUser());
    }

    /**
     * Returns true if the user's XML file has Default restrictions
     * @param userId Id of the user.
@@ -935,6 +970,11 @@ public final class UserManagerServiceTest {
                any(), eq(android.provider.Settings.Global.USER_SWITCHER_ENABLED), anyInt()));
    }

    private void mockProvisionedDevice(boolean isProvisionedDevice) {
        doReturn(isProvisionedDevice ? 1 : 0).when(() -> Settings.Global.getInt(
                any(), eq(android.provider.Settings.Global.DEVICE_PROVISIONED), anyInt()));
    }

    private void mockIsLowRamDevice(boolean isLowRamDevice) {
        doReturn(isLowRamDevice).when(ActivityManager::isLowRamDeviceStatic);
    }