Add restriction to remove main user
Add restriction to remove main user when system property
MAIN_USER_PERMANENT_ADMIN is set to true.
Bug: 260200162
Test: atest UserManagerTest -c
Change-Id: Ibce0d8275eed131fad794503924d52ad31b540c2
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 712ac94..3bb90f7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10380,6 +10380,7 @@
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 9915234..73e52a7 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1824,6 +1824,15 @@
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)}.
*
* @hide
@@ -1835,6 +1844,7 @@
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)
@@ -5224,8 +5234,9 @@
* @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 2138c20..55bc0f4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3012,6 +3012,10 @@
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 6bac905..b4d35a1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -5426,6 +5426,12 @@
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));
@@ -5530,6 +5536,12 @@
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;
@@ -6748,7 +6760,7 @@
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() {
@@ -6760,7 +6772,7 @@
return;
}
mContext.unregisterReceiver(this);
- removeNonSystemUsers();
+ removeAllUsersExceptSystemAndPermanentAdminMain();
}
};
IntentFilter userSwitchedFilter = new IntentFilter();
@@ -7113,14 +7125,14 @@
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<UserInfo> 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);
}
}
@@ -7249,4 +7261,22 @@
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 5059ef3..4f49806 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 @@
@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 {
assertThat(hasUser(Integer.MAX_VALUE)).isFalse();
assertThat(mUserManager.removeUserWhenPossible(UserHandle.of(Integer.MAX_VALUE),
@@ -1420,4 +1443,10 @@
private static UserHandle asHandle(int userId) {
return new UserHandle(userId);
}
+
+ private boolean isMainUserPermanentAdmin() {
+ return Resources.getSystem()
+ .getBoolean(com.android.internal.R.bool.config_isMainUserPermanentAdmin);
+ }
+
}