Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 15598953 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add ActivityManager.logoutUser() hidden API" into main

parents 6fcab4dc 61920753
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -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.
     *
+2 −0
Original line number Diff line number Diff line
@@ -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);
+11 −0
Original line number Diff line number Diff line
@@ -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);
+76 −0
Original line number Diff line number Diff line
@@ -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() {
+8 −0
Original line number Diff line number Diff line
@@ -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