Merge "Add restriction to remove main user"
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4c68949..137751b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10416,6 +10416,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 fc02524..0e4c2b2 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1827,6 +1827,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
@@ -1838,6 +1847,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)
@@ -5236,8 +5246,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 6af1d0f..9fc6c63 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 @@
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 d509563..81f83b0 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 @@
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 @@
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 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 @@
return;
}
mContext.unregisterReceiver(this);
- removeNonSystemUsers();
+ removeAllUsersExceptSystemAndPermanentAdminMain();
}
};
IntentFilter userSwitchedFilter = new IntentFilter();
@@ -7129,14 +7141,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);
}
}
@@ -7265,4 +7277,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 b697c76..34dad09 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -375,6 +375,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),
@@ -1423,4 +1446,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);
+ }
+
}