diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5c0fe4e8bcc24..84f5530f31bc0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9154,20 +9154,25 @@ public class ActivityManagerService extends IActivityManager.Stub } t.traceEnd(); } - t.traceBegin("startHomeOnAllDisplays"); - mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady"); - t.traceEnd(); + // Automotive will re-start system user as background (so its unlocked), then start a + // full user as foreground. Hence, we need to skip some steps that would otherwise be + // done twice. + // TODO(b/138956267): this workdound shouldn't be necessary once we move the + // headless-user start logic to UserManager-land + final boolean bootingSystemUser = currentUserId == UserHandle.USER_SYSTEM; + + if (bootingSystemUser) { + t.traceBegin("startHomeOnAllDisplays"); + mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady"); + t.traceEnd(); + } t.traceBegin("showSystemReadyErrorDialogs"); mAtmInternal.showSystemReadyErrorDialogsIfNeeded(); t.traceEnd(); - // Some systems - like automotive - will explicitly unlock system user then switch - // to a secondary user. Hence, we don't want to send duplicate broadcasts for the - // system user here. - boolean sendSystemUserBroadcasts = currentUserId == UserHandle.USER_SYSTEM; - if (sendSystemUserBroadcasts) { + if (bootingSystemUser) { t.traceBegin("sendUserStartBroadcast"); final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); @@ -9208,7 +9213,7 @@ public class ActivityManagerService extends IActivityManager.Stub mAtmInternal.resumeTopActivities(false /* scheduleIdle */); t.traceEnd(); - if (sendSystemUserBroadcasts) { + if (bootingSystemUser) { t.traceBegin("sendUserSwitchBroadcasts"); mUserController.sendUserSwitchBroadcasts(-1, currentUserId); t.traceEnd(); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index b2c40ef897aa6..c2e3fcb377792 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -1745,13 +1745,24 @@ class UserController implements Handler.Callback { } void sendBootCompleted(IIntentReceiver resultTo) { + final boolean systemUserFinishedBooting; + // Get a copy of mStartedUsers to use outside of lock SparseArray startedUsers; synchronized (mLock) { + systemUserFinishedBooting = mCurrentUserId != UserHandle.USER_SYSTEM; startedUsers = mStartedUsers.clone(); } for (int i = 0; i < startedUsers.size(); i++) { UserState uss = startedUsers.valueAt(i); + if (systemUserFinishedBooting && uss.mHandle.isSystem()) { + // Automotive will re-start system user as background, which in turn will call + // finishUserboot(). Hence, we need to check it here to avoid calling it twice. + // TODO(b/138956267): this workdound shouldn't be necessary once we move the + // headless-user start logic to UserManager-land + Slog.d(TAG, "sendBootCompleted(): skipping on non-current system user"); + continue; + } finishUserBoot(uss, resultTo); } }