diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6aa49d276391d..2045cca8be168 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8613,7 +8613,7 @@ public class ActivityManagerService extends IActivityManager.Stub // On Automotive / Headless System User Mode, at this point the system user has already been // started and unlocked, and some of the tasks we do here have already been done. So skip // those in that case. The duplicate system user start is guarded in SystemServiceManager. - // TODO(b/242195409): this workaround shouldn't be necessary once we move the headless-user + // TODO(b/266158156): this workaround shouldn't be necessary once we move the headless-user // start logic to UserManager-land. mUserController.onSystemUserStarting(); @@ -8646,7 +8646,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Some systems - like automotive - will explicitly unlock system user then switch // to a secondary user. - // TODO(b/242195409): this workaround shouldn't be necessary once we move + // TODO(b/266158156): this workaround shouldn't be necessary once we move // the headless-user start logic to UserManager-land. if (isBootingSystemUser && !UserManager.isHeadlessSystemUserMode()) { t.traceBegin("startHomeOnAllDisplays"); @@ -8669,26 +8669,10 @@ public class ActivityManagerService extends IActivityManager.Stub final int callingPid = Binder.getCallingPid(); final long ident = Binder.clearCallingIdentity(); try { - Intent intent = new Intent(Intent.ACTION_USER_STARTED); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND); - intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId); - broadcastIntentLocked(null, null, null, intent, - null, null, 0, null, null, null, null, null, OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, - currentUserId); - intent = new Intent(Intent.ACTION_USER_STARTING); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId); - broadcastIntentLocked(null, null, null, intent, null, - new IIntentReceiver.Stub() { - @Override - public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered, boolean sticky, - int sendingUser) {} - }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, null, null, - OP_NONE, null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, - UserHandle.USER_ALL); + mUserController.sendUserStartedBroadcast( + currentUserId, callingUid, callingPid); + mUserController.sendUserStartingBroadcast( + currentUserId, callingUid, callingPid); } catch (Throwable e) { Slog.wtf(TAG, "Failed sending first user broadcasts", e); } finally { diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 0e49c66144830..1b378837e558b 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -365,13 +365,13 @@ class UserController implements Handler.Callback { private volatile ArraySet mCurWaitingUserSwitchCallbacks; /** - * Messages for for switching from {@link android.os.UserHandle#SYSTEM}. + * Messages for switching from {@link android.os.UserHandle#SYSTEM}. */ @GuardedBy("mLock") private String mSwitchingFromSystemUserMessage; /** - * Messages for for switching to {@link android.os.UserHandle#SYSTEM}. + * Messages for switching to {@link android.os.UserHandle#SYSTEM}. */ @GuardedBy("mLock") private String mSwitchingToSystemUserMessage; @@ -384,6 +384,16 @@ class UserController implements Handler.Callback { private final LockPatternUtils mLockPatternUtils; + // TODO(b/266158156): remove this once we improve/refactor the way broadcasts are sent for + // the system user in HSUM. + @GuardedBy("mLock") + private boolean mIsBroadcastSentForSystemUserStarted; + + // TODO(b/266158156): remove this once we improve/refactor the way broadcasts are sent for + // the system user in HSUM. + @GuardedBy("mLock") + private boolean mIsBroadcastSentForSystemUserStarting; + volatile boolean mBootCompleted; /** @@ -635,7 +645,7 @@ 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 (!(UserManager.isHeadlessSystemUserMode() && uss.mHandle.isSystem())) { + if (!(mInjector.isHeadlessSystemUserMode() && uss.mHandle.isSystem())) { // ACTION_LOCKED_BOOT_COMPLETED sendLockedBootCompletedBroadcast(resultTo, userId); } @@ -1823,15 +1833,17 @@ class UserController implements Handler.Callback { needStart = false; } - if (needStart) { - // Send USER_STARTED broadcast - Intent intent = new Intent(Intent.ACTION_USER_STARTED); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND); - intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - mInjector.broadcastIntent(intent, - null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, userId); + // In most cases, broadcast for the system user starting/started is sent by + // ActivityManagerService#systemReady(). However on some HSUM devices (e.g. tablets) + // the user switches from the system user to a secondary user while running + // ActivityManagerService#systemReady(), thus broadcast is not sent for the system user. + // Therefore we send the broadcast for the system user here as well in HSUM. + // TODO(b/266158156): Improve/refactor the way broadcasts are sent for the system user + // in HSUM. Ideally it'd be best to have one single place that sends this notification. + final boolean isSystemUserInHeadlessMode = (userId == UserHandle.USER_SYSTEM) + && mInjector.isHeadlessSystemUserMode(); + if (needStart || isSystemUserInHeadlessMode) { + sendUserStartedBroadcast(userId, callingUid, callingPid); } t.traceEnd(); @@ -1845,23 +1857,9 @@ class UserController implements Handler.Callback { t.traceEnd(); } - if (needStart) { + if (needStart || isSystemUserInHeadlessMode) { t.traceBegin("sendRestartBroadcast"); - Intent intent = new Intent(Intent.ACTION_USER_STARTING); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - mInjector.broadcastIntent(intent, - null, new IIntentReceiver.Stub() { - @Override - public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered, - boolean sticky, - int sendingUser) throws RemoteException { - } - }, 0, null, null, - new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, - UserHandle.USER_ALL); + sendUserStartingBroadcast(userId, callingUid, callingPid); t.traceEnd(); } } finally { @@ -2283,6 +2281,62 @@ class UserController implements Handler.Callback { EventLogTags.writeAmSwitchUser(newUserId); } + // The two methods sendUserStartedBroadcast() and sendUserStartingBroadcast() + // could be merged for better reuse. However, the params they are calling broadcastIntent() + // with are different - resultCode receiver, permissions, ordered, and userId, etc. Therefore, + // we decided to keep two separate methods for better code readability/clarity. + // TODO(b/266158156): Improve/refactor the way broadcasts are sent for the system user + // in HSUM. Ideally it'd be best to have one single place that sends this notification. + /** Sends {@code ACTION_USER_STARTED} broadcast. */ + void sendUserStartedBroadcast(@UserIdInt int userId, int callingUid, int callingPid) { + if (userId == UserHandle.USER_SYSTEM) { + synchronized (mLock) { + // Make sure that the broadcast is sent only once for the system user. + if (mIsBroadcastSentForSystemUserStarted) { + return; + } + mIsBroadcastSentForSystemUserStarted = true; + } + } + final Intent intent = new Intent(Intent.ACTION_USER_STARTED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + mInjector.broadcastIntent(intent, /* resolvedType= */ null, /* resultTo= */ null, + /* resultCode= */ 0, /* resultData= */ null, /* resultExtras= */ null, + /* requiredPermissions= */ null, AppOpsManager.OP_NONE, /* bOptions= */ null, + /* ordered= */ false, /* sticky= */ false, MY_PID, SYSTEM_UID, + callingUid, callingPid, userId); + } + + /** Sends {@code ACTION_USER_STARTING} broadcast. */ + void sendUserStartingBroadcast(@UserIdInt int userId, int callingUid, int callingPid) { + if (userId == UserHandle.USER_SYSTEM) { + synchronized (mLock) { + // Make sure that the broadcast is sent only once for the system user. + if (mIsBroadcastSentForSystemUserStarting) { + return; + } + mIsBroadcastSentForSystemUserStarting = true; + } + } + final Intent intent = new Intent(Intent.ACTION_USER_STARTING); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + mInjector.broadcastIntent(intent, /* resolvedType= */ null, + new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean ordered, + boolean sticky, + int sendingUser) throws RemoteException { + } + }, /* resultCode= */ 0, /* resultData= */ null, /* resultExtras= */ null, + new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, /* bOptions= */ null, + /* ordered= */ true, /* sticky= */ false, MY_PID, SYSTEM_UID, + callingUid, callingPid, UserHandle.USER_ALL); + } + void sendUserSwitchBroadcasts(int oldUserId, int newUserId) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); @@ -2568,7 +2622,7 @@ class UserController implements Handler.Callback { for (int i = 0; i < startedUsers.size(); i++) { int userId = startedUsers.keyAt(i); UserState uss = startedUsers.valueAt(i); - if (!UserManager.isHeadlessSystemUserMode()) { + if (!mInjector.isHeadlessSystemUserMode()) { finishUserBoot(uss, resultTo); } else { if (userId == UserHandle.USER_SYSTEM) { @@ -2589,9 +2643,9 @@ class UserController implements Handler.Callback { mInjector.reportCurWakefulnessUsageEvent(); } - // TODO(b/242195409): remove this method if initial system user boot logic is refactored? + // TODO(b/266158156): remove this method if initial system user boot logic is refactored? void onSystemUserStarting() { - if (!UserManager.isHeadlessSystemUserMode()) { + if (!mInjector.isHeadlessSystemUserMode()) { // Don't need to call on HSUM because it will be called when the system user is // restarted on background mInjector.onUserStarting(UserHandle.USER_SYSTEM); @@ -3059,6 +3113,10 @@ class UserController implements Handler.Callback { pw.println(" mMaxRunningUsers:" + mMaxRunningUsers); pw.println(" mUserSwitchUiEnabled:" + mUserSwitchUiEnabled); pw.println(" mInitialized:" + mInitialized); + pw.println(" mIsBroadcastSentForSystemUserStarted:" + + mIsBroadcastSentForSystemUserStarted); + pw.println(" mIsBroadcastSentForSystemUserStarting:" + + mIsBroadcastSentForSystemUserStarting); if (mSwitchingFromSystemUserMessage != null) { pw.println(" mSwitchingFromSystemUserMessage: " + mSwitchingFromSystemUserMessage); } @@ -3749,6 +3807,10 @@ class UserController implements Handler.Callback { }, /* message= */ null); } + boolean isHeadlessSystemUserMode() { + return UserManager.isHeadlessSystemUserMode(); + } + boolean isUsersOnSecondaryDisplaysEnabled() { return UserManager.isVisibleBackgroundUsersEnabled(); } 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 89a5b12c09459..8994a488bd56a 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -133,6 +133,7 @@ public class UserControllerTest { private static final int TEST_USER_ID1 = 101; private static final int TEST_USER_ID2 = 102; private static final int TEST_USER_ID3 = 103; + private static final int SYSTEM_USER_ID = UserHandle.SYSTEM.getIdentifier(); private static final int NONEXIST_USER_ID = 2; private static final int TEST_PRE_CREATED_USER_ID = 103; @@ -230,6 +231,31 @@ public class UserControllerTest { verifyUserAssignedToDisplay(TEST_USER_ID, Display.DEFAULT_DISPLAY); } + @Test + public void testStartUser_sendsNoBroadcastsForSystemUserInNonHeadlessMode() { + setUpUser(SYSTEM_USER_ID, UserInfo.FLAG_SYSTEM, /* preCreated= */ false, + UserManager.USER_TYPE_FULL_SYSTEM); + mockIsHeadlessSystemUserMode(false); + + mUserController.startUser(SYSTEM_USER_ID, USER_START_MODE_FOREGROUND); + + assertWithMessage("Broadcasts for starting the system user in non-headless mode") + .that(mInjector.mSentIntents).isEmpty(); + } + + @Test + public void testStartUser_sendsBroadcastsForSystemUserInHeadlessMode() { + setUpUser(SYSTEM_USER_ID, UserInfo.FLAG_SYSTEM, /* preCreated= */ false, + UserManager.USER_TYPE_SYSTEM_HEADLESS); + mockIsHeadlessSystemUserMode(true); + + mUserController.startUser(SYSTEM_USER_ID, USER_START_MODE_FOREGROUND); + + assertWithMessage("Broadcasts for starting the system user in headless mode") + .that(getActions(mInjector.mSentIntents)).containsExactly( + Intent.ACTION_USER_STARTED, Intent.ACTION_USER_STARTING); + } + @Test public void testStartUser_displayAssignmentFailed() { doReturn(UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE) @@ -999,6 +1025,10 @@ public class UserControllerTest { } } + private void mockIsHeadlessSystemUserMode(boolean value) { + when(mInjector.isHeadlessSystemUserMode()).thenReturn(value); + } + private void mockIsUsersOnSecondaryDisplaysEnabled(boolean value) { when(mInjector.isUsersOnSecondaryDisplaysEnabled()).thenReturn(value); }