diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java index 93afb4376cf29..4bdb92bcafc8c 100644 --- a/core/java/android/os/UserManagerInternal.java +++ b/core/java/android/os/UserManagerInternal.java @@ -127,6 +127,13 @@ public abstract class UserManagerInternal { */ public abstract UserInfo createUserEvenWhenDisallowed(String name, int flags); + /** + * Same as {@link UserManager#removeUser(int userHandle)}, but bypasses the check for + * {@link UserManager#DISALLOW_REMOVE_USER} and does not require the + * {@link android.Manifest.permission#MANAGE_USERS} permission. + */ + public abstract boolean removeUserEvenWhenDisallowed(int userId); + /** * Return whether the given user is running in an * {@code UserState.STATE_RUNNING_UNLOCKING} or diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d0baaf056cb02..a15af3ca9849e 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2490,7 +2490,10 @@ public class UserManagerService extends IUserManager.Stub { Log.w(LOG_TAG, "Cannot remove user. DISALLOW_REMOVE_USER is enabled."); return false; } + return removeUserUnchecked(userHandle); + } + private boolean removeUserUnchecked(int userHandle) { long ident = Binder.clearCallingIdentity(); try { final UserData userData; @@ -3567,6 +3570,11 @@ public class UserManagerService extends IUserManager.Stub { return user; } + @Override + public boolean removeUserEvenWhenDisallowed(int userId) { + return removeUserUnchecked(userId); + } + @Override public boolean isUserRunning(int userId) { synchronized (mUserStates) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index eb85e899dcd82..270488c707b27 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -7340,15 +7340,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean removeUser(ComponentName who, UserHandle userHandle) { Preconditions.checkNotNull(who, "ComponentName is null"); + UserHandle callingUserHandle = mInjector.binderGetCallingUserHandle(); synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); - - long id = mInjector.binderClearCallingIdentity(); - try { - return mUserManager.removeUser(userHandle.getIdentifier()); - } finally { - mInjector.binderRestoreCallingIdentity(id); + } + final long id = mInjector.binderClearCallingIdentity(); + try { + int restrictionSource = mUserManager.getUserRestrictionSource( + UserManager.DISALLOW_REMOVE_USER, callingUserHandle); + if (restrictionSource != UserManager.RESTRICTION_NOT_SET + && restrictionSource != UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) { + Log.w(LOG_TAG, "The device owner cannot remove a user because " + + "DISALLOW_REMOVE_USER is enabled, and was not set by the device " + + "owner"); + return false; } + return mUserManagerInternal.removeUserEvenWhenDisallowed( + userHandle.getIdentifier()); + } finally { + mInjector.binderRestoreCallingIdentity(id); } }