diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 4c689490a138e..137751b175086 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10416,6 +10416,7 @@ package android.os { field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background"; field public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2; // 0x2 field public static final int REMOVE_RESULT_DEFERRED = 1; // 0x1 + field public static final int REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN = -5; // 0xfffffffb field public static final int REMOVE_RESULT_ERROR_SYSTEM_USER = -4; // 0xfffffffc field public static final int REMOVE_RESULT_ERROR_UNKNOWN = -1; // 0xffffffff field public static final int REMOVE_RESULT_ERROR_USER_NOT_FOUND = -3; // 0xfffffffd diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index fc0252487814f..0e4c2b2928d6e 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1826,6 +1826,15 @@ public class UserManager { @SystemApi public static final int REMOVE_RESULT_ERROR_SYSTEM_USER = -4; + /** + * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that + * user being removed is a {@link UserInfo#FLAG_MAIN} user and can't be removed because + * system property {@link com.android.internal.R.bool.isMainUserPermanentAdmin} is true. + * @hide + */ + @SystemApi + public static final int REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN = -5; + /** * Possible response codes from {@link #removeUserWhenPossible(UserHandle, boolean)}. * @@ -1838,6 +1847,7 @@ public class UserManager { REMOVE_RESULT_ERROR_USER_RESTRICTION, REMOVE_RESULT_ERROR_USER_NOT_FOUND, REMOVE_RESULT_ERROR_SYSTEM_USER, + REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN, REMOVE_RESULT_ERROR_UNKNOWN, }) @Retention(RetentionPolicy.SOURCE) @@ -5236,8 +5246,9 @@ public class UserManager { * @return the {@link RemoveResult} code: {@link #REMOVE_RESULT_REMOVED}, * {@link #REMOVE_RESULT_DEFERRED}, {@link #REMOVE_RESULT_ALREADY_BEING_REMOVED}, * {@link #REMOVE_RESULT_ERROR_USER_RESTRICTION}, {@link #REMOVE_RESULT_ERROR_USER_NOT_FOUND}, - * {@link #REMOVE_RESULT_ERROR_SYSTEM_USER}, or {@link #REMOVE_RESULT_ERROR_UNKNOWN}. All error - * codes have negative values. + * {@link #REMOVE_RESULT_ERROR_SYSTEM_USER}, + * {@link #REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN}, or + * {@link #REMOVE_RESULT_ERROR_UNKNOWN}. All error codes have negative values. * * @hide */ diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 6af1d0fc32442..9fc6c6387223c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -3023,6 +3023,10 @@ class PackageManagerShellCommand extends ShellCommand { case UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED: getOutPrintWriter().printf("Success: user %d is already being removed\n", userId); return 0; + case UserManager.REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN: + getOutPrintWriter().printf("Error: user %d is a permanent admin main user\n", + userId); + return 1; default: getErrPrintWriter().printf("Error: couldn't remove or mark ephemeral user id %d\n", userId); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d5095634b4a39..81f83b0591c40 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -5442,6 +5442,12 @@ public class UserManagerService extends IUserManager.Stub { return false; } + if (isNonRemovableMainUser(userData.info)) { + Slog.e(LOG_TAG, "Main user cannot be removed when " + + "it's a permanent admin user."); + return false; + } + if (mRemovingUserIds.get(userId)) { Slog.e(LOG_TAG, TextUtils.formatSimple( "User %d is already scheduled for removal.", userId)); @@ -5546,6 +5552,12 @@ public class UserManagerService extends IUserManager.Stub { return UserManager.REMOVE_RESULT_ERROR_USER_NOT_FOUND; } + if (isNonRemovableMainUser(userData.info)) { + Slog.e(LOG_TAG, "Main user cannot be removed when " + + "it's a permanent admin user."); + return UserManager.REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN; + } + if (mRemovingUserIds.get(userId)) { Slog.e(LOG_TAG, "User " + userId + " is already scheduled for removal."); return UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED; @@ -6764,7 +6776,7 @@ public class UserManagerService extends IUserManager.Stub { public void removeAllUsers() { if (UserHandle.USER_SYSTEM == getCurrentUserId()) { // Remove the non-system users straight away. - removeNonSystemUsers(); + removeAllUsersExceptSystemAndPermanentAdminMain(); } else { // Switch to the system user first and then remove the other users. BroadcastReceiver userSwitchedReceiver = new BroadcastReceiver() { @@ -6776,7 +6788,7 @@ public class UserManagerService extends IUserManager.Stub { return; } mContext.unregisterReceiver(this); - removeNonSystemUsers(); + removeAllUsersExceptSystemAndPermanentAdminMain(); } }; IntentFilter userSwitchedFilter = new IntentFilter(); @@ -7129,14 +7141,14 @@ public class UserManagerService extends IUserManager.Stub { throw new UserManager.CheckedUserOperationException(message, userOperationResult); } - /* Remove all the users except of the system one. */ - private void removeNonSystemUsers() { + /* Remove all the users except the system and permanent admin main.*/ + private void removeAllUsersExceptSystemAndPermanentAdminMain() { ArrayList usersToRemove = new ArrayList<>(); synchronized (mUsersLock) { final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { UserInfo ui = mUsers.valueAt(i).info; - if (ui.id != UserHandle.USER_SYSTEM) { + if (ui.id != UserHandle.USER_SYSTEM && !isNonRemovableMainUser(ui)) { usersToRemove.add(ui); } } @@ -7265,4 +7277,22 @@ public class UserManagerService extends IUserManager.Stub { return mAmInternal; } + /** + * Returns true, when user has {@link UserInfo#FLAG_MAIN} and system property + * {@link com.android.internal.R.bool.isMainUserPermanentAdmin} is true. + */ + private boolean isNonRemovableMainUser(UserInfo userInfo) { + return userInfo.isMain() && isMainUserPermanentAdmin(); + } + + /** + * Returns true, when {@link com.android.internal.R.bool.isMainUserPermanentAdmin} is true. + * If the main user is a permanent admin user it can't be deleted + * or downgraded to non-admin status. + */ + private static boolean isMainUserPermanentAdmin() { + return Resources.getSystem() + .getBoolean(com.android.internal.R.bool.config_isMainUserPermanentAdmin); + } + } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index b697c76566b0d..34dad09224682 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -373,6 +373,29 @@ public final class UserManagerTest { assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue(); } + @MediumTest + @Test + public void testRemoveUserWhenPossible_permanentAdminMainUserReturnsError() throws Exception { + assumeHeadlessModeEnabled(); + assumeTrue("Main user is not permanent admin", isMainUserPermanentAdmin()); + + int currentUser = ActivityManager.getCurrentUser(); + final UserInfo otherUser = createUser("User 1", /* flags= */ UserInfo.FLAG_ADMIN); + UserHandle mainUser = mUserManager.getMainUser(); + + switchUser(otherUser.id, null, true); + + assertThat(mUserManager.removeUserWhenPossible(mainUser, + /* overrideDevicePolicy= */ false)) + .isEqualTo(UserManager.REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN); + + + assertThat(hasUser(mainUser.getIdentifier())).isTrue(); + + // Switch back to the starting user. + switchUser(currentUser, null, true); + } + @MediumTest @Test public void testRemoveUserWhenPossible_invalidUserReturnsError() throws Exception { @@ -1423,4 +1446,10 @@ public final class UserManagerTest { private static UserHandle asHandle(int userId) { return new UserHandle(userId); } + + private boolean isMainUserPermanentAdmin() { + return Resources.getSystem() + .getBoolean(com.android.internal.R.bool.config_isMainUserPermanentAdmin); + } + }