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

Commit 157f3aa5 authored by Eric Biggers's avatar Eric Biggers
Browse files

UserController: simplify and fix the delayed user unlocking logic

There's no reason for the "delay user unlocking until boot complete"
logic to be conditional on HSUM (Headless System User Mode), since
non-HSUM doesn't unlock users too early, and if it did then it would
need the same fix anyway.

Therefore, make this logic unconditional.

As part of this, merge onBootCompletedOnHeadlessSystemUserModeDevices()
into sendBootCompleted(), and rename it to onBootComplete().  As a side
effect this fixes a bug where in HSUM, the BOOT_COMPLETED broadcast
could be sent before LOCKED_BOOT_COMPLETED.  That isn't known to have
been causing problems, but it differed from non-HSUM devices.

Finally, update some comments to be more useful.

Test: atest UserControllerTest # with cf_x86_phone
Test: atest UserControllerTest # with cf_x86_64_auto
Bug: 232452368
Bug: 241929666
Change-Id: Icb2060061740ff76e5288eb579c490fb3ab66872
parent e436b298
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -5283,7 +5283,7 @@ public class ActivityManagerService extends IActivityManager.Stub
            // Tell anyone interested that we are done booting!
            SystemProperties.set("sys.boot_completed", "1");
            SystemProperties.set("dev.bootcomplete", "1");
            mUserController.sendBootCompleted(
            mUserController.onBootComplete(
                    new IIntentReceiver.Stub() {
                        @Override
                        public void performReceive(Intent intent, int resultCode,
+32 −46
Original line number Diff line number Diff line
@@ -439,11 +439,6 @@ class UserController implements Handler.Callback {
        mUserLru.add(UserHandle.USER_SYSTEM);
        mLockPatternUtils = mInjector.getLockPatternUtils();
        updateStartedUserArrayLU();

        // TODO(b/232452368): currently mAllowUserUnlocking is only used on devices with HSUM
        // (Headless System User Mode), but on master it will be used by all devices (and hence this
        // initial assignment should be removed).
        mAllowUserUnlocking = !UserManager.isHeadlessSystemUserMode();
    }

    void setInitialConfig(boolean userSwitchUiEnabled, int maxRunningUsers,
@@ -602,8 +597,11 @@ class UserController implements Handler.Callback {
            if (!mInjector.getUserManager().isPreCreated(userId)) {
                mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
                        userId, 0));
                // In case of headless system user mode, do not send boot complete broadcast for
                // system user as it is sent by sendBootCompleted call.
                // The "locked boot complete" broadcast for the system user is supposed be sent when
                // the device has finished booting.  Normally, that is the same time that the system
                // user transitions to RUNNING_LOCKED.  However, in "headless system user mode", the
                // system user is explicitly started before the device has finished booting.  In
                // that case, we need to wait until onBootComplete() to send the broadcast.
                if (!(UserManager.isHeadlessSystemUserMode() && uss.mHandle.isSystem())) {
                    // ACTION_LOCKED_BOOT_COMPLETED
                    sendLockedBootCompletedBroadcast(resultTo, userId);
@@ -1808,15 +1806,13 @@ class UserController implements Handler.Callback {
     */
    private boolean maybeUnlockUser(@UserIdInt int userId, @Nullable IProgressListener listener) {

        // Delay user unlocking for headless system user mode until the system boot
        // completes. When the system boot completes, the {@link #onBootCompleted()}
        // method unlocks all started users for headless system user mode. This is done
        // to prevent unlocking the users too early during the system boot up.
        // Otherwise, emulated volumes are mounted too early during the system
        // boot up. When vold is reset on boot complete, vold kills all apps/services
        // (that use these emulated volumes) before unmounting the volumes(b/241929666).
        // In the past, these killings have caused the system to become too unstable on
        // some occasions.
        // We cannot allow users to be unlocked before PHASE_BOOT_COMPLETED, for two reasons.
        // First, emulated volumes aren't supposed to be used until then; StorageManagerService
        // assumes it can reset everything upon reaching PHASE_BOOT_COMPLETED.  Second, on some
        // devices the Weaver HAL needed to unlock the user's storage isn't available until sometime
        // shortly before PHASE_BOOT_COMPLETED.  The below logic enforces a consistent flow across
        // all devices, regardless of their Weaver implementation.
        //
        // Any unlocks that get delayed by this will be done by onBootComplete() instead.
        if (!mAllowUserUnlocking) {
            Slogf.i(TAG, "Not unlocking user %d yet because boot hasn't completed", userId);
@@ -2424,11 +2420,8 @@ class UserController implements Handler.Callback {
        }
    }

    /**
     * @deprecated TODO(b/232452368): this logic will be merged into sendBootCompleted
     */
    @Deprecated
    private void onBootCompletedOnHeadlessSystemUserModeDevices() {
    void onBootComplete(IIntentReceiver resultTo) {
        // Now that PHASE_BOOT_COMPLETED has been reached, user unlocking is allowed.
        setAllowUserUnlocking(true);

        // Get a copy of mStartedUsers to use outside of lock.
@@ -2436,37 +2429,30 @@ class UserController implements Handler.Callback {
        synchronized (mLock) {
            startedUsers = mStartedUsers.clone();
        }
        // In non-headless system user mode, call finishUserBoot() to transition the system user
        // from the BOOTING state to RUNNING_LOCKED, then to RUNNING_UNLOCKED if possible.
        //
        // In headless system user mode, additional users may have been started, and all users
        // (including the system user) that ever get started are started explicitly.  In this case,
        // we should *not* transition users out of the BOOTING state using finishUserBoot(), as that
        // doesn't handle issuing the needed onUserStarting() call, and it would just race with an
        // explicit start anyway.  We do, however, need to send the "locked boot complete" broadcast
        // for the system user, as that got skipped earlier due to the *device* boot not being
        // complete yet.  We also need to try to unlock all started users, since until now explicit
        // user starts didn't proceed to unlocking, due to it being too early in the device boot.
        //
        // USER_SYSTEM must be processed first.  It will be first in the array, as its ID is lowest.
        Preconditions.checkArgument(startedUsers.keyAt(0) == UserHandle.USER_SYSTEM);
        for (int i = 0; i < startedUsers.size(); i++) {
            UserState uss = startedUsers.valueAt(i);
            int userId = uss.mHandle.getIdentifier();
            Slogf.i(TAG, "Attempting to unlock user %d on boot complete", userId);
            maybeUnlockUser(userId);
        }
    }

    void sendBootCompleted(IIntentReceiver resultTo) {
        if (UserManager.isHeadlessSystemUserMode()) {
            // Unlocking users is delayed until boot complete for headless system user mode.
            onBootCompletedOnHeadlessSystemUserModeDevices();
        }

        // Get a copy of mStartedUsers to use outside of lock
        SparseArray<UserState> startedUsers;
        synchronized (mLock) {
            startedUsers = mStartedUsers.clone();
        }
        for (int i = 0; i < startedUsers.size(); i++) {
            int userId = startedUsers.keyAt(i);
            UserState uss = startedUsers.valueAt(i);
            if (!UserManager.isHeadlessSystemUserMode()) {
                finishUserBoot(uss, resultTo);
            } else if (uss.mHandle.isSystem()) {
                // In case of headless system user mode, send only locked boot complete broadcast
                // for system user since finishUserBoot call will be made using other code path;
                // for non-system user, do nothing since finishUserBoot will be called elsewhere.
                sendLockedBootCompletedBroadcast(resultTo, uss.mHandle.getIdentifier());
                return;
            } else {
                if (userId == UserHandle.USER_SYSTEM) {
                    sendLockedBootCompletedBroadcast(resultTo, userId);
                }
                maybeUnlockUser(userId);
            }
        }
    }
+1 −4
Original line number Diff line number Diff line
@@ -181,11 +181,8 @@ public class UserControllerTest {
            doNothing().when(mInjector).taskSupervisorRemoveUser(anyInt());
            mockIsUsersOnSecondaryDisplaysEnabled(false);
            // All UserController params are set to default.
            mUserController = new UserController(mInjector);

            // TODO(b/232452368): need to explicitly call setAllowUserUnlocking(), otherwise most
            // tests would fail. But we might need to disable it for the onBootComplete() test (i.e,
            // to make sure the users are unlocked at the right time)
            mUserController = new UserController(mInjector);
            mUserController.setAllowUserUnlocking(true);
            setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS);
            setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated= */ true, null);