In Android 5, user switching function is implemented by AMS.
In frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
20367 public boolean switchUser(final int userId) { 20368 // fosmod_multiple_users begin 20369 return switchUser(userId, true); 20370 } 20371 20372 public boolean switchUser(final int userId, final boolean showKeyguard) { 20373 mShowKeyguard = showKeyguard; 20374 // fosmod_multiple_users end 20375 enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId); 20376 String userName; 20383 synchronized (this) { 20384 UserInfo userInfo = getUserManagerLocked().getUserInfo(userId); 20385 if (userInfo == null) { 20386 Slog.w(TAG, "No user info for user #" + userId); 20387 return false; 20388 } 20389 if (userInfo.isManagedProfile()) { 20390 Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user"); 20391 return false; 20392 } 20393 userName = userInfo.name; 20394 mTargetUserId = userId; 20395 } 20396 mHandler.removeMessages(START_USER_SWITCH_MSG); 20397 mHandler.sendMessage(mHandler.obtainMessage(START_USER_SWITCH_MSG, userId, 0, userName)); 20398 return true; 20399 }1825 case START_USER_SWITCH_MSG: { 1826 showUserSwitchDialog(msg.arg1, (String) msg.obj); 1827 break; 1828 }20401 private void showUserSwitchDialog(int userId, String userName) { 20408 Dialog d = new UserSwitchingDialog(this, mContext, userId, userName, 20409 true /* above system */); 20410 d.show(); 20413 } It create an Alert dialog (showUserSwitchDialog) and show it. The dialog is to indicate that system is switching user. In frameworks/base/services/core/java/com/android/server/am/UserSwitchingDialog.java68 @Override 69 public void show() { 70 // Slog.v(TAG, "show called"); 71 super.show(); // show dialog 72 final View decorView = getWindow().getDecorView(); 73 if (decorView != null) { 74 decorView.getViewTreeObserver().addOnWindowShownListener(this); // register listener, so onWindowShown will be called if dialog is shown on the screen. 75 } 76 } 77 78 @Override 79 public void onWindowShown() { 80 // Slog.v(TAG, "onWindowShown called"); 81 mService.startUserInForeground(mUserId, this); // In the callback function, it will call AMS.startUserInForeground 82 final View decorView = getWindow().getDecorView(); 83 if (decorView != null) { 84 decorView.getViewTreeObserver().removeOnWindowShownListener(this); 85 } 86 }
In frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java20324 boolean startUserInForeground(final int userId, Dialog dlg) { 20325 boolean result = startUser(userId, /* foreground */ true); // do user switching operations. 20326 dlg.dismiss(); // dismiss the dialog 20327 return result; 20328 }
But onWindowShown method will no be called if screen is off. So there may be a special case that user click "user switching" button first, and quickly click power key.
In this case UserSwitchingDialog will be shown, but onWindowShown method will not be called. So system will not switch user, and the dialog will not be dismissed.
And user can do nothing on the screen because the dialog is on the top of all window. Its window type is WindowManager.LayoutParams.TYPE_SYSTEM_ERROR.
In Android N, there is a fix for this issue.
commit 5e5cb463b6e2540a7ef6c261ba5fed074be536e1
Author: Amith Yamasani <yamasani@google.com>
Date: Mon Feb 9 15:45:26 2015 -0800
Timeout to ensure user-switching dialog is closed
In the event of user-switch invoked by a remote device admin call,
the window manager does not make window-shown callbacks when the
screen is off. Add a timeout to continue the user switch in case
the callback doesn't come through.
This fixes a stuck "Switching to user.." dialog when a profile
owner requests a user wipe.
Bug: 19275716
Change-Id: I3a51bbaaf3d9ac6c006464360f1146b2499d205b