diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 9d82274d3f995..65b3ca42fc66e 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3889,7 +3889,7 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.USER_INITIALIZE"; /** - * Sent when a user switch is happening, causing the process's user to be + * Sent after a user switch is complete, if the switch caused the process's user to be * brought to the foreground. This is only sent to receivers registered * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) * Context.registerReceiver}. It is sent to the user that is going to the @@ -3901,7 +3901,7 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.USER_FOREGROUND"; /** - * Sent when a user switch is happening, causing the process's user to be + * Sent after a user switch is complete, if the switch caused the process's user to be * sent to the background. This is only sent to receivers registered * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) * Context.registerReceiver}. It is sent to the user that is going to the @@ -4042,6 +4042,11 @@ public class Intent implements Parcelable, Cloneable { * the current state of the user. * @hide */ + /* + * This broadcast is sent after the user switch is complete. In case a task needs to be done + * while the switch is happening (i.e. while the screen is frozen to hide UI jank), please use + * ActivityManagerService.registerUserSwitchObserver method. + */ @SystemApi public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED"; diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 2ea49b338b389..af0bd633c4710 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -1769,7 +1769,7 @@ class UserController implements Handler.Callback { if (foreground) { t.traceBegin("moveUserToForeground"); - moveUserToForeground(uss, oldUserId, userId); + moveUserToForeground(uss, userId); t.traceEnd(); } else { t.traceBegin("finishUserBoot"); @@ -1983,21 +1983,25 @@ class UserController implements Handler.Callback { /** Called on handler thread */ @VisibleForTesting - void dispatchUserSwitchComplete(@UserIdInt int userId) { + void dispatchUserSwitchComplete(@UserIdInt int oldUserId, @UserIdInt int newUserId) { final TimingsTraceAndSlog t = new TimingsTraceAndSlog(); - t.traceBegin("dispatchUserSwitchComplete-" + userId); + t.traceBegin("dispatchUserSwitchComplete-" + newUserId); mInjector.getWindowManager().setSwitchingUser(false); final int observerCount = mUserSwitchObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { try { - t.traceBegin("onUserSwitchComplete-" + userId + " #" + i + " " + t.traceBegin("onUserSwitchComplete-" + newUserId + " #" + i + " " + mUserSwitchObservers.getBroadcastCookie(i)); - mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId); + mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(newUserId); t.traceEnd(); } catch (RemoteException e) { + // Ignore } } mUserSwitchObservers.finishBroadcast(); + t.traceBegin("sendUserSwitchBroadcasts-" + oldUserId + "-" + newUserId); + sendUserSwitchBroadcasts(oldUserId, newUserId); + t.traceEnd(); t.traceEnd(); } @@ -2159,7 +2163,8 @@ class UserController implements Handler.Callback { // Do the keyguard dismiss and unfreeze later mHandler.removeMessages(COMPLETE_USER_SWITCH_MSG); - mHandler.sendMessage(mHandler.obtainMessage(COMPLETE_USER_SWITCH_MSG, newUserId, 0)); + mHandler.sendMessage(mHandler.obtainMessage( + COMPLETE_USER_SWITCH_MSG, oldUserId, newUserId)); uss.switching = false; stopGuestOrEphemeralUserIfBackground(oldUserId); @@ -2169,7 +2174,7 @@ class UserController implements Handler.Callback { } @VisibleForTesting - void completeUserSwitch(int newUserId) { + void completeUserSwitch(int oldUserId, int newUserId) { final boolean isUserSwitchUiEnabled = isUserSwitchUiEnabled(); final Runnable runnable = () -> { if (isUserSwitchUiEnabled) { @@ -2177,7 +2182,7 @@ class UserController implements Handler.Callback { } mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG); mHandler.sendMessage(mHandler.obtainMessage( - REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0)); + REPORT_USER_SWITCH_COMPLETE_MSG, oldUserId, newUserId)); }; // If there is no challenge set, dismiss the keyguard right away @@ -2202,7 +2207,7 @@ class UserController implements Handler.Callback { t.traceEnd(); } - private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) { + private void moveUserToForeground(UserState uss, int newUserId) { boolean homeInFront = mInjector.taskSupervisorSwitchUser(newUserId, uss); if (homeInFront) { mInjector.startHomeActivity(newUserId, "moveUserToForeground"); @@ -2210,7 +2215,6 @@ class UserController implements Handler.Callback { mInjector.taskSupervisorResumeFocusedStackTopActivity(); } EventLogTags.writeAmSwitchUser(newUserId); - sendUserSwitchBroadcasts(oldUserId, newUserId); } void sendUserSwitchBroadcasts(int oldUserId, int newUserId) { @@ -3080,9 +3084,8 @@ class UserController implements Handler.Callback { dispatchForegroundProfileChanged(msg.arg1); break; case REPORT_USER_SWITCH_COMPLETE_MSG: - dispatchUserSwitchComplete(msg.arg1); - - logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_SWITCH_USER, + dispatchUserSwitchComplete(msg.arg1, msg.arg2); + logUserLifecycleEvent(msg.arg2, USER_LIFECYCLE_EVENT_SWITCH_USER, USER_LIFECYCLE_EVENT_STATE_FINISH); break; case REPORT_LOCKED_BOOT_COMPLETE_MSG: @@ -3100,7 +3103,7 @@ class UserController implements Handler.Callback { logAndClearSessionId(msg.arg1); break; case COMPLETE_USER_SWITCH_MSG: - completeUserSwitch(msg.arg1); + completeUserSwitch(msg.arg1, msg.arg2); break; } return false; 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 e8b8253f06118..537d04fa35e9a 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -113,6 +113,8 @@ import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Tests for {@link UserController}. @@ -146,9 +148,11 @@ public class UserControllerTest { private static final List START_FOREGROUND_USER_ACTIONS = newArrayList( Intent.ACTION_USER_STARTED, - Intent.ACTION_USER_SWITCHED, Intent.ACTION_USER_STARTING); + private static final List START_FOREGROUND_USER_DEFERRED_ACTIONS = newArrayList( + Intent.ACTION_USER_SWITCHED); + private static final List START_BACKGROUND_USER_ACTIONS = newArrayList( Intent.ACTION_USER_STARTED, Intent.ACTION_LOCKED_BOOT_COMPLETED, @@ -397,11 +401,11 @@ public class UserControllerTest { private void continueAndCompleteUserSwitch(UserState userState, int oldUserId, int newUserId) { mUserController.continueUserSwitch(userState, oldUserId, newUserId); mInjector.mHandler.removeMessages(UserController.COMPLETE_USER_SWITCH_MSG); - mUserController.completeUserSwitch(newUserId); + mUserController.completeUserSwitch(oldUserId, newUserId); } @Test - public void testContinueUserSwitch() throws RemoteException { + public void testContinueUserSwitch() { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); // Start user -- this will update state of mUserController @@ -416,12 +420,12 @@ public class UserControllerTest { continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector, times(0)).dismissKeyguard(any(), anyString()); verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen(); - continueUserSwitchAssertions(TEST_USER_ID, false); + continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false); verifySystemUserVisibilityChangesNeverNotified(); } @Test - public void testContinueUserSwitchDismissKeyguard() throws RemoteException { + public void testContinueUserSwitchDismissKeyguard() { when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(false); mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); @@ -437,12 +441,12 @@ public class UserControllerTest { continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector, times(1)).dismissKeyguard(any(), anyString()); verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen(); - continueUserSwitchAssertions(TEST_USER_ID, false); + continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false); verifySystemUserVisibilityChangesNeverNotified(); } @Test - public void testContinueUserSwitchUIDisabled() throws RemoteException { + public void testContinueUserSwitchUIDisabled() { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false, /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); @@ -457,11 +461,11 @@ public class UserControllerTest { // Verify that continueUserSwitch worked as expected continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector.getWindowManager(), never()).stopFreezingScreen(); - continueUserSwitchAssertions(TEST_USER_ID, false); + continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false); } - private void continueUserSwitchAssertions(int expectedUserId, boolean backgroundUserStopping) - throws RemoteException { + private void continueUserSwitchAssertions(int expectedOldUserId, int expectedNewUserId, + boolean backgroundUserStopping) { Set expectedCodes = new LinkedHashSet<>(); expectedCodes.add(COMPLETE_USER_SWITCH_MSG); expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG); @@ -473,7 +477,8 @@ public class UserControllerTest { assertEquals("Unexpected message sent", expectedCodes, actualCodes); Message msg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_COMPLETE_MSG); assertNotNull(msg); - assertEquals("Unexpected userId", expectedUserId, msg.arg1); + assertEquals("Unexpected oldUserId", expectedOldUserId, msg.arg1); + assertEquals("Unexpected newUserId", expectedNewUserId, msg.arg2); } @Test @@ -486,16 +491,21 @@ public class UserControllerTest { mUserController.startUser(TEST_USER_ID, true); Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG); assertNotNull(reportMsg); + int oldUserId = reportMsg.arg1; int newUserId = reportMsg.arg2; mInjector.mHandler.clearAllRecordedMessages(); // Mockito can't reset only interactions, so just verify that this hasn't been // called with 'false' until after dispatchUserSwitchComplete. verify(mInjector.getWindowManager(), never()).setSwitchingUser(false); // Call dispatchUserSwitchComplete - mUserController.dispatchUserSwitchComplete(newUserId); + mUserController.dispatchUserSwitchComplete(oldUserId, newUserId); verify(observer, times(1)).onUserSwitchComplete(anyInt()); verify(observer).onUserSwitchComplete(TEST_USER_ID); verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false); + startUserAssertions(Stream.concat( + START_FOREGROUND_USER_ACTIONS.stream(), + START_FOREGROUND_USER_DEFERRED_ACTIONS.stream() + ).collect(Collectors.toList()), Collections.emptySet()); } @Test @@ -902,8 +912,7 @@ public class UserControllerTest { } private void addForegroundUserAndContinueUserSwitch(int newUserId, int expectedOldUserId, - int expectedNumberOfCalls, boolean expectOldUserStopping) - throws RemoteException { + int expectedNumberOfCalls, boolean expectOldUserStopping) { // Start user -- this will update state of mUserController mUserController.startUser(newUserId, true); Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG); @@ -918,7 +927,7 @@ public class UserControllerTest { continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector.getWindowManager(), times(expectedNumberOfCalls)) .stopFreezingScreen(); - continueUserSwitchAssertions(newUserId, expectOldUserStopping); + continueUserSwitchAssertions(oldUserId, newUserId, expectOldUserStopping); } private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) {