Loading core/java/android/app/ActivityManager.java +29 −0 Original line number Diff line number Diff line Loading @@ -5353,6 +5353,35 @@ public class ActivityManager { return switchUser(user.getIdentifier()); } /** * Logs out the specified user by stopping it. If that user is the foreground user, we first * switch to another appropriate user. The system will determine the next user to switch to * based on the device configuration: * * <ul> * <li>On a device in headless system user mode (HSUM), if {@code * config_canSwitchToHeadlessSystemUser} is {@code true}, the system will switch to the * headless system user. * <li>On other devices, the system will switch to an appropriate user, which is typically the * previously active foreground user. * </ul> * * @param userId the user to logout. * @return true if logout is successfully initiated. Reason for failure could be that no * suitable user can be found to switch to, or any underlying operations fails. * @hide */ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL) @SuppressWarnings("AndroidFrameworkContextUserId") // TODO(b/397755402): It shouldn't need to suppress warning, since this API is hidden. public boolean logoutUser(@UserIdInt int userId) { try { return getService().logoutUser(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Starts the given user in background and assign the user to the given display. * Loading core/java/android/app/IActivityManager.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -405,6 +405,8 @@ interface IActivityManager { boolean switchUser(int userid); String getSwitchingFromUserMessage(int userId); String getSwitchingToUserMessage(int userId); @EnforcePermission("INTERACT_ACROSS_USERS_FULL") boolean logoutUser(int userId); @UnsupportedAppUsage void setStopUserOnSwitch(int value); boolean removeTask(int taskId); Loading services/core/java/com/android/server/am/ActivityManagerService.java +11 −0 Original line number Diff line number Diff line Loading @@ -16201,6 +16201,17 @@ public class ActivityManagerService extends IActivityManager.Stub return mUserController.getSwitchingToUserMessage(userId); } @Override @EnforcePermission(INTERACT_ACROSS_USERS_FULL) public boolean logoutUser(@UserIdInt int userId) { logoutUser_enforcePermission(); if (!android.multiuser.Flags.logoutUserApi()) { throw new UnsupportedOperationException( "aconfig flag android.multiuser.logout_user_api not enabled"); } return mUserController.logoutUser(userId); } @Override public void setStopUserOnSwitch(@StopUserOnSwitch int value) { mUserController.setStopUserOnSwitch(value); services/core/java/com/android/server/am/UserController.java +76 −0 Original line number Diff line number Diff line Loading @@ -967,6 +967,82 @@ class UserController implements Handler.Callback { }); } /** * Initiates the logout process for the specified user. * * <p>The logout process involves two main steps: * * <ol> * <li>If {@code userId} is the foreground user, first switching to an appropriate, active * user. * <li>Stopping the specified {@code userId}. * </ol> * * <p>The logout operation will fail under the following conditions: * * <ul> * <li>No suitable user can be found to switch to, when user switch is needed. * <li>The user switch operation fails for any reason. * <li>The stop user operation fails for any reason. * </ul> * * @see ActivityManager#logoutUser(int) * @param userId The ID of the user to log out. * @return true if logout is successfully initiated. */ boolean logoutUser(@UserIdInt int userId) { boolean shouldSwitchUser = false; synchronized (mLock) { if (userId == UserHandle.USER_SYSTEM) { Slogf.e(TAG, "Cannot logout system user %d", userId); return false; } if (userId == mTargetUserId) { // TODO(b/380125011): Properly handle this case, rather than returning failure. Slogf.e(TAG, "Cannot logout user %d as we're in the process of switching to it, and" + " therefore logout has failed.", userId); return false; } // Since we are logging out of userId, let's not switch to userId (after possible // ongoing user switch). mPendingTargetUserIds.removeIf(id -> id == userId); if (userId == getCurrentUserId() && mTargetUserId == UserHandle.USER_NULL) { shouldSwitchUser = true; } } if (shouldSwitchUser) { final int switchToUserId = mInjector.getUserManagerInternal().getUserToLogoutCurrentUserTo(); if (switchToUserId == UserHandle.USER_NULL) { Slogf.w(TAG, "Logout has no suitable user to switch to, to logout user %d.", userId); return false; } // Due to possible race condition, switchToUserId could be equal to userId. When this is // true: // 1. if they are the current user (due to race condition), then we cannot logout // userId, as we can't switch to another user. // 2. if they are not the current user, we simply need to stop this userId (without // switch user). if (switchToUserId != userId) { if (!switchUser(switchToUserId)) { Slogf.e(TAG, "Cannot logout user %d; switch to user %d failed", userId, switchToUserId); return false; } } } // Now, userId is not current, so we can simply stop it. Attempting to stop system user // will fail. final int result = stopUser(userId, false, null, null); if (result != USER_OP_SUCCESS) { Slogf.e(TAG, "Cannot logout user %d; stop user failed with result %d", userId, result); return false; } return true; } int restartUser(final int userId, @UserStartMode int userStartMode) { return stopUser(userId, /* allowDelayedLocking= */ false, /* stopUserCallback= */ null, new KeyEvictedCallback() { Loading services/core/java/com/android/server/pm/UserManagerInternal.java +8 −0 Original line number Diff line number Diff line Loading @@ -569,6 +569,14 @@ public abstract class UserManagerInternal { */ public abstract @UserIdInt int getUserAssignedToDisplay(int displayId); /** * Returns the user to switch to, when logging out current user. If in HSUM and has interactive * system user, then logout would switch to the system user. Otherwise, logout would switch to * the previous foreground user. Will return USER_NULL if the current user is the SYSTEM or if * no suitable user can be found. */ public abstract @UserIdInt int getUserToLogoutCurrentUserTo(); /** * Gets the user-friendly representation of the {@code result} of a * {@link #assignUserToDisplayOnStart(int, int, boolean, int)} call. Loading Loading
core/java/android/app/ActivityManager.java +29 −0 Original line number Diff line number Diff line Loading @@ -5353,6 +5353,35 @@ public class ActivityManager { return switchUser(user.getIdentifier()); } /** * Logs out the specified user by stopping it. If that user is the foreground user, we first * switch to another appropriate user. The system will determine the next user to switch to * based on the device configuration: * * <ul> * <li>On a device in headless system user mode (HSUM), if {@code * config_canSwitchToHeadlessSystemUser} is {@code true}, the system will switch to the * headless system user. * <li>On other devices, the system will switch to an appropriate user, which is typically the * previously active foreground user. * </ul> * * @param userId the user to logout. * @return true if logout is successfully initiated. Reason for failure could be that no * suitable user can be found to switch to, or any underlying operations fails. * @hide */ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL) @SuppressWarnings("AndroidFrameworkContextUserId") // TODO(b/397755402): It shouldn't need to suppress warning, since this API is hidden. public boolean logoutUser(@UserIdInt int userId) { try { return getService().logoutUser(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Starts the given user in background and assign the user to the given display. * Loading
core/java/android/app/IActivityManager.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -405,6 +405,8 @@ interface IActivityManager { boolean switchUser(int userid); String getSwitchingFromUserMessage(int userId); String getSwitchingToUserMessage(int userId); @EnforcePermission("INTERACT_ACROSS_USERS_FULL") boolean logoutUser(int userId); @UnsupportedAppUsage void setStopUserOnSwitch(int value); boolean removeTask(int taskId); Loading
services/core/java/com/android/server/am/ActivityManagerService.java +11 −0 Original line number Diff line number Diff line Loading @@ -16201,6 +16201,17 @@ public class ActivityManagerService extends IActivityManager.Stub return mUserController.getSwitchingToUserMessage(userId); } @Override @EnforcePermission(INTERACT_ACROSS_USERS_FULL) public boolean logoutUser(@UserIdInt int userId) { logoutUser_enforcePermission(); if (!android.multiuser.Flags.logoutUserApi()) { throw new UnsupportedOperationException( "aconfig flag android.multiuser.logout_user_api not enabled"); } return mUserController.logoutUser(userId); } @Override public void setStopUserOnSwitch(@StopUserOnSwitch int value) { mUserController.setStopUserOnSwitch(value);
services/core/java/com/android/server/am/UserController.java +76 −0 Original line number Diff line number Diff line Loading @@ -967,6 +967,82 @@ class UserController implements Handler.Callback { }); } /** * Initiates the logout process for the specified user. * * <p>The logout process involves two main steps: * * <ol> * <li>If {@code userId} is the foreground user, first switching to an appropriate, active * user. * <li>Stopping the specified {@code userId}. * </ol> * * <p>The logout operation will fail under the following conditions: * * <ul> * <li>No suitable user can be found to switch to, when user switch is needed. * <li>The user switch operation fails for any reason. * <li>The stop user operation fails for any reason. * </ul> * * @see ActivityManager#logoutUser(int) * @param userId The ID of the user to log out. * @return true if logout is successfully initiated. */ boolean logoutUser(@UserIdInt int userId) { boolean shouldSwitchUser = false; synchronized (mLock) { if (userId == UserHandle.USER_SYSTEM) { Slogf.e(TAG, "Cannot logout system user %d", userId); return false; } if (userId == mTargetUserId) { // TODO(b/380125011): Properly handle this case, rather than returning failure. Slogf.e(TAG, "Cannot logout user %d as we're in the process of switching to it, and" + " therefore logout has failed.", userId); return false; } // Since we are logging out of userId, let's not switch to userId (after possible // ongoing user switch). mPendingTargetUserIds.removeIf(id -> id == userId); if (userId == getCurrentUserId() && mTargetUserId == UserHandle.USER_NULL) { shouldSwitchUser = true; } } if (shouldSwitchUser) { final int switchToUserId = mInjector.getUserManagerInternal().getUserToLogoutCurrentUserTo(); if (switchToUserId == UserHandle.USER_NULL) { Slogf.w(TAG, "Logout has no suitable user to switch to, to logout user %d.", userId); return false; } // Due to possible race condition, switchToUserId could be equal to userId. When this is // true: // 1. if they are the current user (due to race condition), then we cannot logout // userId, as we can't switch to another user. // 2. if they are not the current user, we simply need to stop this userId (without // switch user). if (switchToUserId != userId) { if (!switchUser(switchToUserId)) { Slogf.e(TAG, "Cannot logout user %d; switch to user %d failed", userId, switchToUserId); return false; } } } // Now, userId is not current, so we can simply stop it. Attempting to stop system user // will fail. final int result = stopUser(userId, false, null, null); if (result != USER_OP_SUCCESS) { Slogf.e(TAG, "Cannot logout user %d; stop user failed with result %d", userId, result); return false; } return true; } int restartUser(final int userId, @UserStartMode int userStartMode) { return stopUser(userId, /* allowDelayedLocking= */ false, /* stopUserCallback= */ null, new KeyEvictedCallback() { Loading
services/core/java/com/android/server/pm/UserManagerInternal.java +8 −0 Original line number Diff line number Diff line Loading @@ -569,6 +569,14 @@ public abstract class UserManagerInternal { */ public abstract @UserIdInt int getUserAssignedToDisplay(int displayId); /** * Returns the user to switch to, when logging out current user. If in HSUM and has interactive * system user, then logout would switch to the system user. Otherwise, logout would switch to * the previous foreground user. Will return USER_NULL if the current user is the SYSTEM or if * no suitable user can be found. */ public abstract @UserIdInt int getUserToLogoutCurrentUserTo(); /** * Gets the user-friendly representation of the {@code result} of a * {@link #assignUserToDisplayOnStart(int, int, boolean, int)} call. Loading