summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/multiuser.aconfig10
-rw-r--r--core/java/android/os/UserManager.java8
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java73
4 files changed, 119 insertions, 15 deletions
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index fa3bc9e16a6b..35f9cff1aedb 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -533,3 +533,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "ignore_restrictions_when_deleting_private_profile"
+ namespace: "multiuser"
+ description: "Ignore any user restrictions when deleting private profiles."
+ bug: "350953833"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index bd3da0d56cbd..acf4a2f3ec71 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -772,9 +772,10 @@ public class UserManager {
public static final String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
/**
- * When set on the admin user this specifies if the user can remove users.
+ * When set on the admin user this specifies if the user can remove secondary users. Managed
+ * profiles and private profiles can still be removed even if this is set on the admin user.
* When set on a non-admin secondary user, this specifies if the user can remove itself.
- * This restriction has no effect on managed profiles.
+ * This restriction has no effect when set on managed profiles.
* The default value is <code>false</code>.
*
* <p>Holders of the permission
@@ -793,7 +794,8 @@ public class UserManager {
* Specifies if managed profiles of this user can be removed, other than by its profile owner.
* The default value is <code>false</code>.
* <p>
- * This restriction has no effect on managed profiles.
+ * This restriction has no effect on managed profiles, and this restriction does not block the
+ * removal of private profiles of this user.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index daf413bb30b3..6ac8b221b21f 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -195,6 +195,7 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
@@ -6261,14 +6262,16 @@ public class UserManagerService extends IUserManager.Stub {
Slog.i(LOG_TAG, "removeUser u" + userId);
checkCreateUsersPermission("Only the system can remove users");
- final String restriction = getUserRemovalRestriction(userId);
- if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
- Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
+ final Optional<String> restrictionOptional = getUserRemovalRestrictionOptional(userId);
+ if (!restrictionOptional.isEmpty()
+ && getUserRestrictions(UserHandle.getCallingUserId())
+ .getBoolean(restrictionOptional.get(), false)) {
+ Slog.w(LOG_TAG, "Cannot remove user. " + restrictionOptional.get() + " is enabled.");
return false;
}
if (mCurrentBootPhase < SystemService.PHASE_ACTIVITY_MANAGER_READY) {
Slog.w(LOG_TAG, "Cannot remove user, removeUser is called too early during boot. "
- + "ActivityManager is not ready yet.");
+ + "ActivityManager is not ready yet.");
return false;
}
return removeUserWithProfilesUnchecked(userId);
@@ -6335,18 +6338,30 @@ public class UserManagerService extends IUserManager.Stub {
}
/**
- * Returns the string name of the restriction to check for user removal. The restriction name
- * varies depending on whether the user is a managed profile.
+ * Returns an optional string name of the restriction to check for user removal. The restriction
+ * name varies depending on whether the user is a managed profile.
+ *
+ * <p>If the flag android.multiuser.ignore_restrictions_when_deleting_private_profile is enabled
+ * and the user is a private profile (i.e. has no removal restrictions) the method will return
+ * {@code Optional.empty()}.
*/
- private String getUserRemovalRestriction(@UserIdInt int userId) {
+ private Optional<String> getUserRemovalRestrictionOptional(@UserIdInt int userId) {
+ final boolean isPrivateProfile;
final boolean isManagedProfile;
final UserInfo userInfo;
synchronized (mUsersLock) {
userInfo = getUserInfoLU(userId);
}
+ isPrivateProfile = userInfo != null && userInfo.isPrivateProfile();
isManagedProfile = userInfo != null && userInfo.isManagedProfile();
- return isManagedProfile
- ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER;
+ if (android.multiuser.Flags.ignoreRestrictionsWhenDeletingPrivateProfile()
+ && isPrivateProfile) {
+ return Optional.empty();
+ }
+ return Optional.of(
+ isManagedProfile
+ ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE
+ : UserManager.DISALLOW_REMOVE_USER);
}
private boolean removeUserUnchecked(@UserIdInt int userId) {
@@ -6455,9 +6470,13 @@ public class UserManagerService extends IUserManager.Stub {
checkCreateUsersPermission("Only the system can remove users");
if (!overrideDevicePolicy) {
- final String restriction = getUserRemovalRestriction(userId);
- if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
- Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
+ final Optional<String> restrictionOptional = getUserRemovalRestrictionOptional(userId);
+ if (!restrictionOptional.isEmpty()
+ && getUserRestrictions(UserHandle.getCallingUserId())
+ .getBoolean(restrictionOptional.get(), false)) {
+ Slog.w(
+ LOG_TAG,
+ "Cannot remove user. " + restrictionOptional.get() + " is enabled.");
return UserManager.REMOVE_RESULT_ERROR_USER_RESTRICTION;
}
}
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 e652df5cdf1a..f9946604ad5d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -539,6 +539,79 @@ public final class UserManagerTest {
@MediumTest
@Test
+ public void testRemoveUser_shouldRemovePrivateUser() {
+ UserInfo privateProfileUser =
+ createProfileForUser(
+ "Private profile",
+ UserManager.USER_TYPE_PROFILE_PRIVATE,
+ mUserManager.getMainUser().getIdentifier());
+ assertThat(privateProfileUser).isNotNull();
+ assertThat(hasUser(privateProfileUser.id)).isTrue();
+
+ removeUser(privateProfileUser.id);
+
+ assertThat(hasUser(privateProfileUser.id)).isFalse();
+ }
+
+ @MediumTest
+ @Test
+ @RequiresFlagsEnabled(
+ android.multiuser.Flags.FLAG_IGNORE_RESTRICTIONS_WHEN_DELETING_PRIVATE_PROFILE)
+ public void testRemoveUser_shouldRemovePrivateUser_withDisallowRemoveUserRestriction() {
+ UserHandle mainUser = mUserManager.getMainUser();
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_REMOVE_USER, /* value= */ true, mainUser);
+ try {
+ UserInfo privateProfileUser =
+ createProfileForUser(
+ "Private profile",
+ UserManager.USER_TYPE_PROFILE_PRIVATE,
+ mainUser.getIdentifier());
+ assertThat(privateProfileUser).isNotNull();
+ assertThat(hasUser(privateProfileUser.id)).isTrue();
+ removeUser(privateProfileUser.id);
+
+ assertThat(hasUser(privateProfileUser.id)).isFalse();
+ } finally {
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_REMOVE_USER, /* value= */ false, mainUser);
+ }
+ }
+
+ @MediumTest
+ @Test
+ public void testRemoveUser_withDisallowRemoveUserRestrictionAndMultipleUsersPresent() {
+ UserInfo privateProfileUser =
+ createProfileForUser(
+ "Private profile",
+ UserManager.USER_TYPE_PROFILE_PRIVATE,
+ mUserManager.getMainUser().getIdentifier());
+ assertThat(privateProfileUser).isNotNull();
+ assertThat(hasUser(privateProfileUser.id)).isTrue();
+ UserInfo testUser = createUser("TestUser", /* flags= */ 0);
+ assertThat(testUser).isNotNull();
+ assertThat(hasUser(testUser.id)).isTrue();
+ UserHandle mainUser = mUserManager.getMainUser();
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_REMOVE_USER, /* value= */ true, mainUser);
+ try {
+ assertThat(
+ mUserManager.removeUserWhenPossible(
+ testUser.getUserHandle(), /* overrideDevicePolicy= */ false))
+ .isEqualTo(UserManager.REMOVE_RESULT_ERROR_USER_RESTRICTION);
+
+ // Non private profile users should be prevented from being removed.
+ assertThat(mUserManager.removeUser(testUser.id)).isEqualTo(false);
+
+ assertThat(hasUser(testUser.id)).isTrue();
+ } finally {
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_REMOVE_USER, /* value= */ false, mainUser);
+ }
+ }
+
+ @MediumTest
+ @Test
public void testRemoveUserShouldNotRemoveTargetUser_DuringUserSwitch() {
final int startUser = ActivityManager.getCurrentUser();
final UserInfo testUser = createUser("TestUser", /* flags= */ 0);