Replaces DPM.clearLogoutUser() by logoutUser()

Prior to this change, internal components like Settings or
CarUserManager would need to call 3 APIs to logout a managed user:

1. previousUserId = getLogoutUser();
2. switchUser(previousUserId);
3. clearLogoutUser();

This CL encapsulates the whole process into a new DPM.logoutUser(),
but it keeps the getLogoutUser() (as Car need to know which user it
will be switched to in advance, due to the Vehicle HAL integration).

Test: m update-api
Test: atest FrameworksServicesTests:DevicePolicyManagerTest
Test: atest com.android.cts.devicepolicy.DeviceOwnerTest#testCreateAndManageUser_LogoutUser_system

Fixes: 214336184

Change-Id: Ifffc229ecede5d086338e9c7fa08dce0e0de884c
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 280a0db..540967b 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -63,8 +63,8 @@
 
   public class DevicePolicyManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void acknowledgeNewUserDisclaimer();
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearLogoutUser();
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getLogoutUser();
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getLogoutUser();
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int logoutUser();
     field public static final String ACTION_SHOW_NEW_USER_DISCLAIMER = "android.app.action.SHOW_NEW_USER_DISCLAIMER";
   }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 96d037c..d741293 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10018,12 +10018,29 @@
     }
 
     /**
+     * Same as {@link #logoutUser(ComponentName)}, but called by system (like Settings), not admin.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS})
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public @UserOperationResult int logoutUser() {
+        // TODO(b/214336184): add CTS test
+        try {
+            return mService.logoutUserInternal();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+    /**
      * Gets the user a {@link #logoutUser(ComponentName)} call would switch to,
      * or {@code null} if the current user is not in a session.
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS})
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public @Nullable UserHandle getLogoutUser() {
         // TODO(b/214336184): add CTS test
@@ -10036,24 +10053,6 @@
     }
 
     /**
-     * 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)
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public void clearLogoutUser() {
-        // TODO(b/214336184): add CTS test
-        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 f663c17..0fc984b 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -264,8 +264,8 @@
     int startUserInBackground(in ComponentName who, in UserHandle userHandle);
     int stopUser(in ComponentName who, in UserHandle userHandle);
     int logoutUser(in ComponentName who);
+    int logoutUserInternal(); // AIDL doesn't allow overloading name (logoutUser())
     int getLogoutUserId();
-    void clearLogoutUser();
     List<UserHandle> getSecondaryUsers(in ComponentName who);
     void acknowledgeNewUserDisclaimer();
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 40196db..b010693 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10977,7 +10977,8 @@
 
     @Override
     public int getLogoutUserId() {
-        Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+        Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())
+                || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS));
 
         return getLogoutUserIdUnchecked();
     }
@@ -10992,16 +10993,7 @@
         }
     }
 
-    @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() {
+    private void clearLogoutUser() {
         if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore
 
         synchronized (getLockObject()) {
@@ -11102,6 +11094,21 @@
             return stopUserUnchecked(callingUserId);
         }
 
+        return logoutUserUnchecked(/* userIdToStop= */ callingUserId);
+    }
+
+    @Override
+    public int logoutUserInternal() {
+        CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                canManageUsers(caller) || hasCallingOrSelfPermission(permission.CREATE_USERS));
+
+        int result = logoutUserUnchecked(getCurrentForegroundUserId());
+        Slogf.d(LOG_TAG, "logout called by uid %d. Result: %d", caller.getUid(), result);
+        return result;
+    }
+
+    private int logoutUserUnchecked(@UserIdInt int userIdToStop) {
         int logoutUserId = getLogoutUserIdUnchecked();
         if (logoutUserId == UserHandle.USER_NULL) {
             // Could happen on devices using headless system user mode when called before calling
@@ -11117,7 +11124,7 @@
                 // This should never happen as target user is determined by getPreviousUserId()
                 return UserManager.USER_OPERATION_ERROR_UNKNOWN;
             }
-            clearLogoutUserUnchecked();
+            clearLogoutUser();
         } catch (RemoteException e) {
             // Same process, should not happen.
             return UserManager.USER_OPERATION_ERROR_UNKNOWN;
@@ -11125,7 +11132,7 @@
             mInjector.binderRestoreCallingIdentity(id);
         }
 
-        return stopUserUnchecked(callingUserId);
+        return stopUserUnchecked(userIdToStop);
     }
 
     private int stopUserUnchecked(@UserIdInt int userId) {