Loading services/core/java/com/android/server/am/UserController.java +63 −7 Original line number Diff line number Diff line Loading @@ -103,6 +103,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.LockPatternUtils; Loading Loading @@ -162,6 +163,7 @@ class UserController implements Handler.Callback { static final int USER_UNLOCKED_MSG = 105; static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110; static final int START_USER_SWITCH_FG_MSG = 120; static final int COMPLETE_USER_SWITCH_MSG = 130; // Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if // the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not Loading Loading @@ -385,6 +387,7 @@ class UserController implements Handler.Callback { @VisibleForTesting UserController(Injector injector) { mInjector = injector; // This should be called early to avoid a null mHandler inside the injector mHandler = mInjector.getHandler(this); mUiHandler = mInjector.getUiHandler(this); // User 0 is the first and only user that runs at boot. Loading Loading @@ -1535,8 +1538,11 @@ class UserController implements Handler.Callback { // with the option to show the user switcher on the keyguard. if (userSwitchUiEnabled) { mInjector.getWindowManager().setSwitchingUser(true); // Only lock if the user has a secure keyguard PIN/Pattern/Pwd if (mInjector.getKeyguardManager().isDeviceSecure(userId)) { mInjector.getWindowManager().lockNow(null); } } } else { final Integer currentUserIdInt = mCurrentUserId; updateCurrentProfileIds(); Loading Loading @@ -1967,11 +1973,10 @@ class UserController implements Handler.Callback { EventLog.writeEvent(EventLogTags.UC_CONTINUE_USER_SWITCH, oldUserId, newUserId); if (isUserSwitchUiEnabled()) { t.traceBegin("stopFreezingScreen"); mInjector.getWindowManager().stopFreezingScreen(); t.traceEnd(); } // Do the keyguard dismiss and unfreeze later mHandler.removeMessages(COMPLETE_USER_SWITCH_MSG); mHandler.sendMessage(mHandler.obtainMessage(COMPLETE_USER_SWITCH_MSG, newUserId, 0)); uss.switching = false; mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG); mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0)); Loading @@ -1981,6 +1986,34 @@ class UserController implements Handler.Callback { t.traceEnd(); // end continueUserSwitch } @VisibleForTesting void completeUserSwitch(int newUserId) { if (isUserSwitchUiEnabled()) { // If there is no challenge set, dismiss the keyguard right away if (!mInjector.getKeyguardManager().isDeviceSecure(newUserId)) { // Wait until the keyguard is dismissed to unfreeze mInjector.dismissKeyguard( new Runnable() { public void run() { unfreezeScreen(); } }, "User Switch"); return; } else { unfreezeScreen(); } } } @VisibleForTesting void unfreezeScreen() { TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("stopFreezingScreen"); mInjector.getWindowManager().stopFreezingScreen(); t.traceEnd(); } private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) { boolean homeInFront = mInjector.taskSupervisorSwitchUser(newUserId, uss); if (homeInFront) { Loading Loading @@ -2772,6 +2805,9 @@ class UserController implements Handler.Callback { case CLEAR_USER_JOURNEY_SESSION_MSG: logAndClearSessionId(msg.arg1); break; case COMPLETE_USER_SWITCH_MSG: completeUserSwitch(msg.arg1); break; } return false; } Loading Loading @@ -2961,13 +2997,14 @@ class UserController implements Handler.Callback { private final ActivityManagerService mService; private UserManagerService mUserManager; private UserManagerInternal mUserManagerInternal; private Handler mHandler; Injector(ActivityManagerService service) { mService = service; } protected Handler getHandler(Handler.Callback callback) { return new Handler(mService.mHandlerThread.getLooper(), callback); return mHandler = new Handler(mService.mHandlerThread.getLooper(), callback); } protected Handler getUiHandler(Handler.Callback callback) { Loading Loading @@ -3165,5 +3202,24 @@ class UserController implements Handler.Callback { protected IStorageManager getStorageManager() { return IStorageManager.Stub.asInterface(ServiceManager.getService("mount")); } protected void dismissKeyguard(Runnable runnable, String reason) { getWindowManager().dismissKeyguard(new IKeyguardDismissCallback.Stub() { @Override public void onDismissError() throws RemoteException { mHandler.post(runnable); } @Override public void onDismissSucceeded() throws RemoteException { mHandler.post(runnable); } @Override public void onDismissCancelled() throws RemoteException { mHandler.post(runnable); } }, reason); } } } services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +49 −4 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareC import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.android.server.am.UserController.COMPLETE_USER_SWITCH_MSG; import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG; import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG; import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG; Loading Loading @@ -59,6 +60,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.IUserSwitchObserver; import android.app.KeyguardManager; import android.content.Context; import android.content.IIntentReceiver; import android.content.Intent; Loading Loading @@ -325,8 +327,16 @@ public class UserControllerTest { assertWithMessage("No messages should be sent").that(actualCodes).isEmpty(); } 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); } @Test public void testContinueUserSwitch() throws RemoteException { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); // Start user -- this will update state of mUserController mUserController.startUser(TEST_USER_ID, true); Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG); Loading @@ -336,7 +346,28 @@ public class UserControllerTest { int newUserId = reportMsg.arg2; mInjector.mHandler.clearAllRecordedMessages(); // Verify that continueUserSwitch worked as expected mUserController.continueUserSwitch(userState, oldUserId, newUserId); continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector, times(0)).dismissKeyguard(any(), anyString()); verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen(); continueUserSwitchAssertions(TEST_USER_ID, false); } @Test public void testContinueUserSwitchDismissKeyguard() throws RemoteException { when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(false); mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); // Start user -- this will update state of mUserController mUserController.startUser(TEST_USER_ID, true); Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG); assertNotNull(reportMsg); UserState userState = (UserState) reportMsg.obj; int oldUserId = reportMsg.arg1; int newUserId = reportMsg.arg2; mInjector.mHandler.clearAllRecordedMessages(); // Verify that continueUserSwitch worked as expected continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector, times(1)).dismissKeyguard(any(), anyString()); verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen(); continueUserSwitchAssertions(TEST_USER_ID, false); } Loading @@ -355,7 +386,7 @@ public class UserControllerTest { int newUserId = reportMsg.arg2; mInjector.mHandler.clearAllRecordedMessages(); // Verify that continueUserSwitch worked as expected mUserController.continueUserSwitch(userState, oldUserId, newUserId); continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector.getWindowManager(), never()).stopFreezingScreen(); continueUserSwitchAssertions(TEST_USER_ID, false); } Loading @@ -363,6 +394,7 @@ public class UserControllerTest { private void continueUserSwitchAssertions(int expectedUserId, boolean backgroundUserStopping) throws RemoteException { Set<Integer> expectedCodes = new LinkedHashSet<>(); expectedCodes.add(COMPLETE_USER_SWITCH_MSG); expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG); if (backgroundUserStopping) { expectedCodes.add(0); // this is for directly posting in stopping. Loading Loading @@ -397,7 +429,7 @@ public class UserControllerTest { } @Test public void testExplicitSystenUserStartInBackground() { public void testExplicitSystemUserStartInBackground() { setUpUser(UserHandle.USER_SYSTEM, 0); assertFalse(mUserController.isSystemUserStarted()); assertTrue(mUserController.startUser(UserHandle.USER_SYSTEM, false, null)); Loading Loading @@ -646,7 +678,7 @@ public class UserControllerTest { mUserStates.put(newUserId, userState); mInjector.mHandler.clearAllRecordedMessages(); // Verify that continueUserSwitch worked as expected mUserController.continueUserSwitch(userState, oldUserId, newUserId); continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector.getWindowManager(), times(expectedNumberOfCalls)) .stopFreezingScreen(); continueUserSwitchAssertions(newUserId, expectOldUserStopping); Loading Loading @@ -701,6 +733,7 @@ public class UserControllerTest { private final IStorageManager mStorageManagerMock; private final UserManagerInternal mUserManagerInternalMock; private final WindowManagerService mWindowManagerMock; private final KeyguardManager mKeyguardManagerMock; private final Context mCtx; Loading @@ -715,6 +748,8 @@ public class UserControllerTest { mUserManagerInternalMock = mock(UserManagerInternal.class); mWindowManagerMock = mock(WindowManagerService.class); mStorageManagerMock = mock(IStorageManager.class); mKeyguardManagerMock = mock(KeyguardManager.class); when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true); } @Override Loading Loading @@ -753,6 +788,11 @@ public class UserControllerTest { return mWindowManagerMock; } @Override KeyguardManager getKeyguardManager() { return mKeyguardManagerMock; } @Override void updateUserConfiguration() { Log.i(TAG, "updateUserConfiguration"); Loading Loading @@ -787,6 +827,11 @@ public class UserControllerTest { protected IStorageManager getStorageManager() { return mStorageManagerMock; } @Override protected void dismissKeyguard(Runnable runnable, String reason) { runnable.run(); } } private static class TestHandler extends Handler { Loading Loading
services/core/java/com/android/server/am/UserController.java +63 −7 Original line number Diff line number Diff line Loading @@ -103,6 +103,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.LockPatternUtils; Loading Loading @@ -162,6 +163,7 @@ class UserController implements Handler.Callback { static final int USER_UNLOCKED_MSG = 105; static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110; static final int START_USER_SWITCH_FG_MSG = 120; static final int COMPLETE_USER_SWITCH_MSG = 130; // Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if // the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not Loading Loading @@ -385,6 +387,7 @@ class UserController implements Handler.Callback { @VisibleForTesting UserController(Injector injector) { mInjector = injector; // This should be called early to avoid a null mHandler inside the injector mHandler = mInjector.getHandler(this); mUiHandler = mInjector.getUiHandler(this); // User 0 is the first and only user that runs at boot. Loading Loading @@ -1535,8 +1538,11 @@ class UserController implements Handler.Callback { // with the option to show the user switcher on the keyguard. if (userSwitchUiEnabled) { mInjector.getWindowManager().setSwitchingUser(true); // Only lock if the user has a secure keyguard PIN/Pattern/Pwd if (mInjector.getKeyguardManager().isDeviceSecure(userId)) { mInjector.getWindowManager().lockNow(null); } } } else { final Integer currentUserIdInt = mCurrentUserId; updateCurrentProfileIds(); Loading Loading @@ -1967,11 +1973,10 @@ class UserController implements Handler.Callback { EventLog.writeEvent(EventLogTags.UC_CONTINUE_USER_SWITCH, oldUserId, newUserId); if (isUserSwitchUiEnabled()) { t.traceBegin("stopFreezingScreen"); mInjector.getWindowManager().stopFreezingScreen(); t.traceEnd(); } // Do the keyguard dismiss and unfreeze later mHandler.removeMessages(COMPLETE_USER_SWITCH_MSG); mHandler.sendMessage(mHandler.obtainMessage(COMPLETE_USER_SWITCH_MSG, newUserId, 0)); uss.switching = false; mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG); mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0)); Loading @@ -1981,6 +1986,34 @@ class UserController implements Handler.Callback { t.traceEnd(); // end continueUserSwitch } @VisibleForTesting void completeUserSwitch(int newUserId) { if (isUserSwitchUiEnabled()) { // If there is no challenge set, dismiss the keyguard right away if (!mInjector.getKeyguardManager().isDeviceSecure(newUserId)) { // Wait until the keyguard is dismissed to unfreeze mInjector.dismissKeyguard( new Runnable() { public void run() { unfreezeScreen(); } }, "User Switch"); return; } else { unfreezeScreen(); } } } @VisibleForTesting void unfreezeScreen() { TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("stopFreezingScreen"); mInjector.getWindowManager().stopFreezingScreen(); t.traceEnd(); } private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) { boolean homeInFront = mInjector.taskSupervisorSwitchUser(newUserId, uss); if (homeInFront) { Loading Loading @@ -2772,6 +2805,9 @@ class UserController implements Handler.Callback { case CLEAR_USER_JOURNEY_SESSION_MSG: logAndClearSessionId(msg.arg1); break; case COMPLETE_USER_SWITCH_MSG: completeUserSwitch(msg.arg1); break; } return false; } Loading Loading @@ -2961,13 +2997,14 @@ class UserController implements Handler.Callback { private final ActivityManagerService mService; private UserManagerService mUserManager; private UserManagerInternal mUserManagerInternal; private Handler mHandler; Injector(ActivityManagerService service) { mService = service; } protected Handler getHandler(Handler.Callback callback) { return new Handler(mService.mHandlerThread.getLooper(), callback); return mHandler = new Handler(mService.mHandlerThread.getLooper(), callback); } protected Handler getUiHandler(Handler.Callback callback) { Loading Loading @@ -3165,5 +3202,24 @@ class UserController implements Handler.Callback { protected IStorageManager getStorageManager() { return IStorageManager.Stub.asInterface(ServiceManager.getService("mount")); } protected void dismissKeyguard(Runnable runnable, String reason) { getWindowManager().dismissKeyguard(new IKeyguardDismissCallback.Stub() { @Override public void onDismissError() throws RemoteException { mHandler.post(runnable); } @Override public void onDismissSucceeded() throws RemoteException { mHandler.post(runnable); } @Override public void onDismissCancelled() throws RemoteException { mHandler.post(runnable); } }, reason); } } }
services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +49 −4 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareC import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.android.server.am.UserController.COMPLETE_USER_SWITCH_MSG; import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG; import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG; import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG; Loading Loading @@ -59,6 +60,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.IUserSwitchObserver; import android.app.KeyguardManager; import android.content.Context; import android.content.IIntentReceiver; import android.content.Intent; Loading Loading @@ -325,8 +327,16 @@ public class UserControllerTest { assertWithMessage("No messages should be sent").that(actualCodes).isEmpty(); } 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); } @Test public void testContinueUserSwitch() throws RemoteException { mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); // Start user -- this will update state of mUserController mUserController.startUser(TEST_USER_ID, true); Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG); Loading @@ -336,7 +346,28 @@ public class UserControllerTest { int newUserId = reportMsg.arg2; mInjector.mHandler.clearAllRecordedMessages(); // Verify that continueUserSwitch worked as expected mUserController.continueUserSwitch(userState, oldUserId, newUserId); continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector, times(0)).dismissKeyguard(any(), anyString()); verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen(); continueUserSwitchAssertions(TEST_USER_ID, false); } @Test public void testContinueUserSwitchDismissKeyguard() throws RemoteException { when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(false); mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true, /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); // Start user -- this will update state of mUserController mUserController.startUser(TEST_USER_ID, true); Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG); assertNotNull(reportMsg); UserState userState = (UserState) reportMsg.obj; int oldUserId = reportMsg.arg1; int newUserId = reportMsg.arg2; mInjector.mHandler.clearAllRecordedMessages(); // Verify that continueUserSwitch worked as expected continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector, times(1)).dismissKeyguard(any(), anyString()); verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen(); continueUserSwitchAssertions(TEST_USER_ID, false); } Loading @@ -355,7 +386,7 @@ public class UserControllerTest { int newUserId = reportMsg.arg2; mInjector.mHandler.clearAllRecordedMessages(); // Verify that continueUserSwitch worked as expected mUserController.continueUserSwitch(userState, oldUserId, newUserId); continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector.getWindowManager(), never()).stopFreezingScreen(); continueUserSwitchAssertions(TEST_USER_ID, false); } Loading @@ -363,6 +394,7 @@ public class UserControllerTest { private void continueUserSwitchAssertions(int expectedUserId, boolean backgroundUserStopping) throws RemoteException { Set<Integer> expectedCodes = new LinkedHashSet<>(); expectedCodes.add(COMPLETE_USER_SWITCH_MSG); expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG); if (backgroundUserStopping) { expectedCodes.add(0); // this is for directly posting in stopping. Loading Loading @@ -397,7 +429,7 @@ public class UserControllerTest { } @Test public void testExplicitSystenUserStartInBackground() { public void testExplicitSystemUserStartInBackground() { setUpUser(UserHandle.USER_SYSTEM, 0); assertFalse(mUserController.isSystemUserStarted()); assertTrue(mUserController.startUser(UserHandle.USER_SYSTEM, false, null)); Loading Loading @@ -646,7 +678,7 @@ public class UserControllerTest { mUserStates.put(newUserId, userState); mInjector.mHandler.clearAllRecordedMessages(); // Verify that continueUserSwitch worked as expected mUserController.continueUserSwitch(userState, oldUserId, newUserId); continueAndCompleteUserSwitch(userState, oldUserId, newUserId); verify(mInjector.getWindowManager(), times(expectedNumberOfCalls)) .stopFreezingScreen(); continueUserSwitchAssertions(newUserId, expectOldUserStopping); Loading Loading @@ -701,6 +733,7 @@ public class UserControllerTest { private final IStorageManager mStorageManagerMock; private final UserManagerInternal mUserManagerInternalMock; private final WindowManagerService mWindowManagerMock; private final KeyguardManager mKeyguardManagerMock; private final Context mCtx; Loading @@ -715,6 +748,8 @@ public class UserControllerTest { mUserManagerInternalMock = mock(UserManagerInternal.class); mWindowManagerMock = mock(WindowManagerService.class); mStorageManagerMock = mock(IStorageManager.class); mKeyguardManagerMock = mock(KeyguardManager.class); when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true); } @Override Loading Loading @@ -753,6 +788,11 @@ public class UserControllerTest { return mWindowManagerMock; } @Override KeyguardManager getKeyguardManager() { return mKeyguardManagerMock; } @Override void updateUserConfiguration() { Log.i(TAG, "updateUserConfiguration"); Loading Loading @@ -787,6 +827,11 @@ public class UserControllerTest { protected IStorageManager getStorageManager() { return mStorageManagerMock; } @Override protected void dismissKeyguard(Runnable runnable, String reason) { runnable.run(); } } private static class TestHandler extends Handler { Loading