New hidden DPM methods: getLogoutUserId() and clearLogoutUser().

Also fixed how the logout user was cleared after a successful switch.

They will be used by CarSystemUi to end a session.

Test: Manual verification with CtsVerifier and TestDpc on automotive

Bug: 205185521
Bug: 204483021

Change-Id: Ieda0a22625ef705fcd705aedfc2dbf2cf570bf81
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9c352df..556dfc7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9610,6 +9610,37 @@
     }
 
     /**
+     * Gets the user a {@link #logoutUser(ComponentName)} call would switch to,
+     * or {@link UserHandle#USER_NULL} if the current user is not in a session.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public @UserIdInt int getLogoutUserId() {
+        try {
+            return mService.getLogoutUserId();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Clears the user that {@link #logoutUser(ComponentName)} would switch to.
+     *
+     * <p>Typically used by system UI after it logout a session.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public void clearLogoutUser() {
+        try {
+            mService.clearLogoutUser();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Called by a device owner to list all secondary users on the device. Managed profiles are not
      * considered as secondary users.
      * <p> Used for various user management APIs, including {@link #switchUser}, {@link #removeUser}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 1c9187d..7c7478bd 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -261,6 +261,8 @@
     int startUserInBackground(in ComponentName who, in UserHandle userHandle);
     int stopUser(in ComponentName who, in UserHandle userHandle);
     int logoutUser(in ComponentName who);
+    int getLogoutUserId();
+    void clearLogoutUser();
     List<UserHandle> getSecondaryUsers(in ComponentName who);
     void resetNewUserDisclaimer();
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index b06b024..ad47232 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -607,6 +607,9 @@
             } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
                 addIfShouldShowAction(tempActions, new ScreenshotAction());
             } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
+                // TODO(b/206032495): should call mDevicePolicyManager.getLogoutUserId() instead of
+                // hardcode it to USER_SYSTEM so it properly supports headless system user mode
+                // (and then call mDevicePolicyManager.clearLogoutUser() after switched)
                 if (mDevicePolicyManager.isLogoutEnabled()
                         && currentUser.get() != null
                         && currentUser.get().id != UserHandle.USER_SYSTEM) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index db13ae2..55c434a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9692,7 +9692,7 @@
                 mStatLogger.dump(pw);
                 pw.println();
                 pw.println("Encryption Status: " + getEncryptionStatusName(getEncryptionStatus()));
-                pw.println("Logout user: " + getLogoutUserId());
+                pw.println("Logout user: " + getLogoutUserIdUnchecked());
                 pw.println();
 
                 if (mPendingUserCreatedCallbackTokens.isEmpty()) {
@@ -10804,7 +10804,7 @@
 
         boolean switched = false;
         // Save previous logout user id in case of failure
-        int logoutUserId = getLogoutUserId();
+        int logoutUserId = getLogoutUserIdUnchecked();
         synchronized (getLockObject()) {
             long id = mInjector.binderClearCallingIdentity();
             try {
@@ -10831,7 +10831,14 @@
         }
     }
 
-    private @UserIdInt int getLogoutUserId() {
+    @Override
+    public int getLogoutUserId() {
+        Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+
+        return getLogoutUserIdUnchecked();
+    }
+
+    private @UserIdInt int getLogoutUserIdUnchecked() {
         if (!mInjector.userManagerIsHeadlessSystemUserMode()) {
             // mLogoutUserId is USER_SYSTEM as well, but there's no need to acquire the lock
             return UserHandle.USER_SYSTEM;
@@ -10841,11 +10848,20 @@
         }
     }
 
-    private void setLogoutUserId(@UserIdInt int userId) {
+    @Override
+    public void clearLogoutUser() {
+        CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(canManageUsers(caller));
+
+        Slogf.i(LOG_TAG, "Clearing logout user as requested by %s", caller);
+        clearLogoutUserUnchecked();
+    }
+
+    private void clearLogoutUserUnchecked() {
         if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore
 
         synchronized (getLockObject()) {
-            setLogoutUserIdLocked(userId);
+            setLogoutUserIdLocked(UserHandle.USER_NULL);
         }
     }
 
@@ -10942,7 +10958,7 @@
             return stopUserUnchecked(callingUserId);
         }
 
-        int logoutUserId = getLogoutUserId();
+        int logoutUserId = getLogoutUserIdUnchecked();
         if (logoutUserId == UserHandle.USER_NULL) {
             // Could happen on devices using headless system user mode when called before calling
             // switchUser() or startUserInBackground() first
@@ -10957,7 +10973,7 @@
                 // This should never happen as target user is determined by getPreviousUserId()
                 return UserManager.USER_OPERATION_ERROR_UNKNOWN;
             }
-            setLogoutUserId(UserHandle.USER_CURRENT);
+            clearLogoutUserUnchecked();
         } catch (RemoteException e) {
             // Same process, should not happen.
             return UserManager.USER_OPERATION_ERROR_UNKNOWN;