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) {