From 77660a446bf86ed6c587920cd586b14b2c75afc3 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Fri, 28 Apr 2023 18:19:05 +0100 Subject: [PATCH] Delay boot complete broadcast for main user on HSUM. This fixes an issue where on a HSUM device, the LOCKED_BOOT_COMPLETE broadcast occurs before boot actually completes. We already delay the sending of this broadcast for user 0, but we need to do the same for the main user because otherwise the broadcast is sent at the end of user switch, and the switch to the main user occurs before boot complete. Bug: 279983151 Test: atest UserControllerTest Test: build & flash; observe logs during boot Change-Id: I885954ae57b3c7bc27b2f03b2d2bfa8583c79293 --- .../com/android/server/am/UserController.java | 15 ++++++----- .../android/server/am/UserControllerTest.java | 26 ++++++++++++++++++- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index b2fdee7a6f89d..419962aa8c38c 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -646,7 +646,10 @@ class UserController implements Handler.Callback { // 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 (!(mInjector.isHeadlessSystemUserMode() && uss.mHandle.isSystem())) { + // Similarly, this occurs after a user switch, but in HSUM we switch to the main + // user before boot is complete, so again this should be delayed until + // onBootComplete if boot has not yet completed. + if (mAllowUserUnlocking) { // ACTION_LOCKED_BOOT_COMPLETED sendLockedBootCompletedBroadcast(resultTo, userId); } @@ -2617,9 +2620,9 @@ class UserController implements Handler.Callback { // 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. + // 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); @@ -2629,9 +2632,7 @@ class UserController implements Handler.Callback { if (!mInjector.isHeadlessSystemUserMode()) { finishUserBoot(uss, resultTo); } else { - if (userId == UserHandle.USER_SYSTEM) { - sendLockedBootCompletedBroadcast(resultTo, userId); - } + sendLockedBootCompletedBroadcast(resultTo, userId); maybeUnlockUser(userId); } } diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index d12741ac8bd67..fe838e4ebd746 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -199,6 +199,7 @@ public class UserControllerTest { mUserController.setAllowUserUnlocking(true); setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS); setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated= */ true, null); + mInjector.mRelevantUser = null; }); } @@ -231,6 +232,25 @@ public class UserControllerTest { verifyUserAssignedToDisplay(TEST_USER_ID, Display.DEFAULT_DISPLAY); } + @Test + public void testStartUser_background_duringBootHsum() { + mockIsHeadlessSystemUserMode(true); + mUserController.setAllowUserUnlocking(false); + mInjector.mRelevantUser = TEST_USER_ID; + boolean started = mUserController.startUser(TEST_USER_ID, USER_START_MODE_BACKGROUND); + assertWithMessage("startUser(%s, foreground=false)", TEST_USER_ID).that(started).isTrue(); + + // ACTION_LOCKED_BOOT_COMPLETED not sent yet + startUserAssertions(newArrayList(Intent.ACTION_USER_STARTED, Intent.ACTION_USER_STARTING), + START_BACKGROUND_USER_MESSAGE_CODES); + + mUserController.onBootComplete(null); + + startUserAssertions(newArrayList(Intent.ACTION_USER_STARTED, Intent.ACTION_USER_STARTING, + Intent.ACTION_LOCKED_BOOT_COMPLETED), + START_BACKGROUND_USER_MESSAGE_CODES); + } + @Test public void testStartUser_sendsNoBroadcastsForSystemUserInNonHeadlessMode() { setUpUser(SYSTEM_USER_ID, UserInfo.FLAG_SYSTEM, /* preCreated= */ false, @@ -1076,6 +1096,8 @@ public class UserControllerTest { private final Context mCtx; + private Integer mRelevantUser; + TestInjector(Context ctx) { super(null); mCtx = ctx; @@ -1162,7 +1184,9 @@ public class UserControllerTest { boolean sticky, int callingPid, int callingUid, int realCallingUid, int realCallingPid, int userId) { Log.i(TAG, "broadcastIntentLocked " + intent); - mSentIntents.add(intent); + if (mRelevantUser == null || mRelevantUser == userId || userId == UserHandle.USER_ALL) { + mSentIntents.add(intent); + } return 0; }