Loading core/java/android/os/UserManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -1816,6 +1816,13 @@ public class UserManager { @SystemApi public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2; /** * A response code indicating that the specified user is removable. * * @hide */ public static final int REMOVE_RESULT_USER_IS_REMOVABLE = 3; /** * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that * an unknown error occurred that prevented the user from being removed or set as ephemeral. Loading Loading @@ -1871,6 +1878,7 @@ public class UserManager { REMOVE_RESULT_REMOVED, REMOVE_RESULT_DEFERRED, REMOVE_RESULT_ALREADY_BEING_REMOVED, REMOVE_RESULT_USER_IS_REMOVABLE, REMOVE_RESULT_ERROR_USER_RESTRICTION, REMOVE_RESULT_ERROR_USER_NOT_FOUND, REMOVE_RESULT_ERROR_SYSTEM_USER, Loading services/core/java/com/android/server/pm/UserManagerService.java +104 −119 Original line number Diff line number Diff line Loading @@ -2391,39 +2391,56 @@ public class UserManagerService extends IUserManager.Stub { @Override public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) { checkCreateUsersPermission("update ephemeral user flag"); UserData userToUpdate = null; return enableEphemeral ? UserManager.isRemoveResultSuccessful(setUserEphemeralUnchecked(userId)) : setUserNonEphemeralUnchecked(userId); } private boolean setUserNonEphemeralUnchecked(@UserIdInt int userId) { synchronized (mPackagesLock) { final UserData userData; synchronized (mUsersLock) { final UserData userData = mUsers.get(userId); userData = mUsers.get(userId); if (userData == null) { Slog.e(LOG_TAG, "User not found for setting ephemeral mode: u" + userId); Slog.e(LOG_TAG, TextUtils.formatSimple( "Cannot set user %d non-ephemeral, invalid user id provided.", userId)); return false; } boolean isEphemeralUser = (userData.info.flags & UserInfo.FLAG_EPHEMERAL) != 0; boolean isEphemeralOnCreateUser = (userData.info.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE) != 0; if (!userData.info.isEphemeral()) { return true; } if ((userData.info.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE) != 0) { // when user is created in ephemeral mode via FLAG_EPHEMERAL // its state cannot be changed to non ephemeral. // its state cannot be changed to non-ephemeral. // FLAG_EPHEMERAL_ON_CREATE is used to keep track of this state if (isEphemeralOnCreateUser && !enableEphemeral) { Slog.e(LOG_TAG, "Failed to change user state to non-ephemeral for user " + userId); Slog.e(LOG_TAG, TextUtils.formatSimple("User %d can not be changed to " + "non-ephemeral because it was set ephemeral on create.", userId)); return false; } if (isEphemeralUser != enableEphemeral) { if (enableEphemeral) { userData.info.flags |= UserInfo.FLAG_EPHEMERAL; } else { } userData.info.flags &= ~UserInfo.FLAG_EPHEMERAL; writeUserLP(userData); } userToUpdate = userData; return true; } private @UserManager.RemoveResult int setUserEphemeralUnchecked(@UserIdInt int userId) { synchronized (mPackagesLock) { final UserData userData; synchronized (mUsersLock) { final int userRemovability = getUserRemovabilityLocked(userId, "set as ephemeral"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return userRemovability; } if (userToUpdate != null) { writeUserLP(userToUpdate); userData = mUsers.get(userId); } userData.info.flags |= UserInfo.FLAG_EPHEMERAL; writeUserLP(userData); } return true; Slog.i(LOG_TAG, TextUtils.formatSimple( "User %d is set ephemeral and will be removed on user switch or reboot.", userId)); return UserManager.REMOVE_RESULT_DEFERRED; } @Override Loading Loading @@ -5366,17 +5383,31 @@ public class UserManagerService extends IUserManager.Stub { } private boolean removeUserWithProfilesUnchecked(@UserIdInt int userId) { UserInfo userInfo = getUserInfoNoChecks(userId); final UserData userData; final boolean isProfile; final IntArray profileIds; synchronized (mUsersLock) { final int userRemovability = getUserRemovabilityLocked(userId, "removed"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return UserManager.isRemoveResultSuccessful(userRemovability); } userData = mUsers.get(userId); isProfile = userData.info.isProfile(); profileIds = isProfile ? null : getProfileIdsLU(userId, null, false); } if (userInfo == null) { Slog.e(LOG_TAG, TextUtils.formatSimple( "Cannot remove user %d, invalid user id provided.", userId)); if (!isProfile) { Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds(); if (userId == currentAndTargetUserIds.first) { Slog.w(LOG_TAG, "Current user cannot be removed."); return false; } if (!userInfo.isProfile()) { int[] profileIds = getProfileIds(userId, false); for (int profileId : profileIds) { if (userId == currentAndTargetUserIds.second) { Slog.w(LOG_TAG, "Target user of an ongoing user switch cannot be removed."); return false; } for (int i = profileIds.size() - 1; i >= 0; i--) { int profileId = profileIds.get(i); if (profileId == userId) { //Remove the associated profiles first and then remove the user continue; Loading Loading @@ -5429,45 +5460,16 @@ public class UserManagerService extends IUserManager.Stub { final long ident = Binder.clearCallingIdentity(); try { final UserData userData; Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds(); if (userId == currentAndTargetUserIds.first) { Slog.w(LOG_TAG, "Current user cannot be removed."); return false; } if (userId == currentAndTargetUserIds.second) { Slog.w(LOG_TAG, "Target user of an ongoing user switch cannot be removed."); return false; } synchronized (mPackagesLock) { synchronized (mUsersLock) { userData = mUsers.get(userId); if (userId == UserHandle.USER_SYSTEM) { Slog.e(LOG_TAG, "System user cannot be removed."); return false; } if (userData == null) { Slog.e(LOG_TAG, TextUtils.formatSimple( "Cannot remove user %d, invalid user id provided.", userId)); return false; final int userRemovability = getUserRemovabilityLocked(userId, "removed"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return UserManager.isRemoveResultSuccessful(userRemovability); } 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)); return false; } userData = mUsers.get(userId); Slog.i(LOG_TAG, "Removing user " + userId); addRemovingUserIdLocked(userId); } // Set this to a partially created user, so that the user will be purged // on next startup, in case the runtime stops now before stopping and // removing the user completely. Loading Loading @@ -5536,6 +5538,7 @@ public class UserManagerService extends IUserManager.Stub { @Override public @UserManager.RemoveResult int removeUserWhenPossible(@UserIdInt int userId, boolean overrideDevicePolicy) { Slog.i(LOG_TAG, "removeUserWhenPossible u" + userId); checkCreateUsersPermission("Only the system can remove users"); if (!overrideDevicePolicy) { Loading @@ -5545,64 +5548,46 @@ public class UserManagerService extends IUserManager.Stub { return UserManager.REMOVE_RESULT_ERROR_USER_RESTRICTION; } } Slog.i(LOG_TAG, "Attempting to immediately remove user " + userId); if (removeUserWithProfilesUnchecked(userId)) { return UserManager.REMOVE_RESULT_REMOVED; } Slog.i(LOG_TAG, TextUtils.formatSimple( "Unable to immediately remove user %d. Now trying to set it ephemeral.", userId)); return setUserEphemeralUnchecked(userId); } /** * Returns the user's removability status. * User is removable if the return value is {@link UserManager#REMOVE_RESULT_USER_IS_REMOVABLE}. * If the user is not removable this method also prints the reason. * See also {@link UserManager#isRemoveResultSuccessful}. */ @GuardedBy("mUsersLock") private @UserManager.RemoveResult int getUserRemovabilityLocked(@UserIdInt int userId, String msg) { String prefix = TextUtils.formatSimple("User %d can not be %s, ", userId, msg); if (userId == UserHandle.USER_SYSTEM) { Slog.e(LOG_TAG, "System user cannot be removed."); Slog.e(LOG_TAG, prefix + "system user cannot be removed."); return UserManager.REMOVE_RESULT_ERROR_SYSTEM_USER; } final long ident = Binder.clearCallingIdentity(); try { final UserData userData; synchronized (mPackagesLock) { synchronized (mUsersLock) { userData = mUsers.get(userId); final UserData userData = mUsers.get(userId); if (userData == null) { Slog.e(LOG_TAG, "Cannot remove user " + userId + ", invalid user id provided."); Slog.e(LOG_TAG, prefix + "invalid user id provided."); 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."); Slog.e(LOG_TAG, prefix + "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."); Slog.w(LOG_TAG, prefix + "it is already scheduled for removal."); return UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED; } return UserManager.REMOVE_RESULT_USER_IS_REMOVABLE; } // Attempt to immediately remove a non-current and non-target user Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds(); if (userId != currentAndTargetUserIds.first && userId != currentAndTargetUserIds.second) { // Attempt to remove the user. This will fail if the user is the current user if (removeUserWithProfilesUnchecked(userId)) { return UserManager.REMOVE_RESULT_REMOVED; } } // If the user was not immediately removed, make sure it is marked as ephemeral. // Don't mark as disabled since, per UserInfo.FLAG_DISABLED documentation, an // ephemeral user should only be marked as disabled when its removal is in progress. Slog.i(LOG_TAG, TextUtils.formatSimple("Unable to immediately remove user %d " + "(%s is %d). User is set as ephemeral and will be removed on " + "user switch or reboot.", userId, userId == currentAndTargetUserIds.first ? "current user" : "target user of an ongoing user switch", userId)); userData.info.flags |= UserInfo.FLAG_EPHEMERAL; writeUserLP(userData); return UserManager.REMOVE_RESULT_DEFERRED; } } finally { Binder.restoreCallingIdentity(ident); } } private void finishRemoveUser(final @UserIdInt int userId) { Slog.i(LOG_TAG, "finishRemoveUser " + userId); Loading Loading
core/java/android/os/UserManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -1816,6 +1816,13 @@ public class UserManager { @SystemApi public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2; /** * A response code indicating that the specified user is removable. * * @hide */ public static final int REMOVE_RESULT_USER_IS_REMOVABLE = 3; /** * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that * an unknown error occurred that prevented the user from being removed or set as ephemeral. Loading Loading @@ -1871,6 +1878,7 @@ public class UserManager { REMOVE_RESULT_REMOVED, REMOVE_RESULT_DEFERRED, REMOVE_RESULT_ALREADY_BEING_REMOVED, REMOVE_RESULT_USER_IS_REMOVABLE, REMOVE_RESULT_ERROR_USER_RESTRICTION, REMOVE_RESULT_ERROR_USER_NOT_FOUND, REMOVE_RESULT_ERROR_SYSTEM_USER, Loading
services/core/java/com/android/server/pm/UserManagerService.java +104 −119 Original line number Diff line number Diff line Loading @@ -2391,39 +2391,56 @@ public class UserManagerService extends IUserManager.Stub { @Override public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) { checkCreateUsersPermission("update ephemeral user flag"); UserData userToUpdate = null; return enableEphemeral ? UserManager.isRemoveResultSuccessful(setUserEphemeralUnchecked(userId)) : setUserNonEphemeralUnchecked(userId); } private boolean setUserNonEphemeralUnchecked(@UserIdInt int userId) { synchronized (mPackagesLock) { final UserData userData; synchronized (mUsersLock) { final UserData userData = mUsers.get(userId); userData = mUsers.get(userId); if (userData == null) { Slog.e(LOG_TAG, "User not found for setting ephemeral mode: u" + userId); Slog.e(LOG_TAG, TextUtils.formatSimple( "Cannot set user %d non-ephemeral, invalid user id provided.", userId)); return false; } boolean isEphemeralUser = (userData.info.flags & UserInfo.FLAG_EPHEMERAL) != 0; boolean isEphemeralOnCreateUser = (userData.info.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE) != 0; if (!userData.info.isEphemeral()) { return true; } if ((userData.info.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE) != 0) { // when user is created in ephemeral mode via FLAG_EPHEMERAL // its state cannot be changed to non ephemeral. // its state cannot be changed to non-ephemeral. // FLAG_EPHEMERAL_ON_CREATE is used to keep track of this state if (isEphemeralOnCreateUser && !enableEphemeral) { Slog.e(LOG_TAG, "Failed to change user state to non-ephemeral for user " + userId); Slog.e(LOG_TAG, TextUtils.formatSimple("User %d can not be changed to " + "non-ephemeral because it was set ephemeral on create.", userId)); return false; } if (isEphemeralUser != enableEphemeral) { if (enableEphemeral) { userData.info.flags |= UserInfo.FLAG_EPHEMERAL; } else { } userData.info.flags &= ~UserInfo.FLAG_EPHEMERAL; writeUserLP(userData); } userToUpdate = userData; return true; } private @UserManager.RemoveResult int setUserEphemeralUnchecked(@UserIdInt int userId) { synchronized (mPackagesLock) { final UserData userData; synchronized (mUsersLock) { final int userRemovability = getUserRemovabilityLocked(userId, "set as ephemeral"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return userRemovability; } if (userToUpdate != null) { writeUserLP(userToUpdate); userData = mUsers.get(userId); } userData.info.flags |= UserInfo.FLAG_EPHEMERAL; writeUserLP(userData); } return true; Slog.i(LOG_TAG, TextUtils.formatSimple( "User %d is set ephemeral and will be removed on user switch or reboot.", userId)); return UserManager.REMOVE_RESULT_DEFERRED; } @Override Loading Loading @@ -5366,17 +5383,31 @@ public class UserManagerService extends IUserManager.Stub { } private boolean removeUserWithProfilesUnchecked(@UserIdInt int userId) { UserInfo userInfo = getUserInfoNoChecks(userId); final UserData userData; final boolean isProfile; final IntArray profileIds; synchronized (mUsersLock) { final int userRemovability = getUserRemovabilityLocked(userId, "removed"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return UserManager.isRemoveResultSuccessful(userRemovability); } userData = mUsers.get(userId); isProfile = userData.info.isProfile(); profileIds = isProfile ? null : getProfileIdsLU(userId, null, false); } if (userInfo == null) { Slog.e(LOG_TAG, TextUtils.formatSimple( "Cannot remove user %d, invalid user id provided.", userId)); if (!isProfile) { Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds(); if (userId == currentAndTargetUserIds.first) { Slog.w(LOG_TAG, "Current user cannot be removed."); return false; } if (!userInfo.isProfile()) { int[] profileIds = getProfileIds(userId, false); for (int profileId : profileIds) { if (userId == currentAndTargetUserIds.second) { Slog.w(LOG_TAG, "Target user of an ongoing user switch cannot be removed."); return false; } for (int i = profileIds.size() - 1; i >= 0; i--) { int profileId = profileIds.get(i); if (profileId == userId) { //Remove the associated profiles first and then remove the user continue; Loading Loading @@ -5429,45 +5460,16 @@ public class UserManagerService extends IUserManager.Stub { final long ident = Binder.clearCallingIdentity(); try { final UserData userData; Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds(); if (userId == currentAndTargetUserIds.first) { Slog.w(LOG_TAG, "Current user cannot be removed."); return false; } if (userId == currentAndTargetUserIds.second) { Slog.w(LOG_TAG, "Target user of an ongoing user switch cannot be removed."); return false; } synchronized (mPackagesLock) { synchronized (mUsersLock) { userData = mUsers.get(userId); if (userId == UserHandle.USER_SYSTEM) { Slog.e(LOG_TAG, "System user cannot be removed."); return false; } if (userData == null) { Slog.e(LOG_TAG, TextUtils.formatSimple( "Cannot remove user %d, invalid user id provided.", userId)); return false; final int userRemovability = getUserRemovabilityLocked(userId, "removed"); if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) { return UserManager.isRemoveResultSuccessful(userRemovability); } 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)); return false; } userData = mUsers.get(userId); Slog.i(LOG_TAG, "Removing user " + userId); addRemovingUserIdLocked(userId); } // Set this to a partially created user, so that the user will be purged // on next startup, in case the runtime stops now before stopping and // removing the user completely. Loading Loading @@ -5536,6 +5538,7 @@ public class UserManagerService extends IUserManager.Stub { @Override public @UserManager.RemoveResult int removeUserWhenPossible(@UserIdInt int userId, boolean overrideDevicePolicy) { Slog.i(LOG_TAG, "removeUserWhenPossible u" + userId); checkCreateUsersPermission("Only the system can remove users"); if (!overrideDevicePolicy) { Loading @@ -5545,64 +5548,46 @@ public class UserManagerService extends IUserManager.Stub { return UserManager.REMOVE_RESULT_ERROR_USER_RESTRICTION; } } Slog.i(LOG_TAG, "Attempting to immediately remove user " + userId); if (removeUserWithProfilesUnchecked(userId)) { return UserManager.REMOVE_RESULT_REMOVED; } Slog.i(LOG_TAG, TextUtils.formatSimple( "Unable to immediately remove user %d. Now trying to set it ephemeral.", userId)); return setUserEphemeralUnchecked(userId); } /** * Returns the user's removability status. * User is removable if the return value is {@link UserManager#REMOVE_RESULT_USER_IS_REMOVABLE}. * If the user is not removable this method also prints the reason. * See also {@link UserManager#isRemoveResultSuccessful}. */ @GuardedBy("mUsersLock") private @UserManager.RemoveResult int getUserRemovabilityLocked(@UserIdInt int userId, String msg) { String prefix = TextUtils.formatSimple("User %d can not be %s, ", userId, msg); if (userId == UserHandle.USER_SYSTEM) { Slog.e(LOG_TAG, "System user cannot be removed."); Slog.e(LOG_TAG, prefix + "system user cannot be removed."); return UserManager.REMOVE_RESULT_ERROR_SYSTEM_USER; } final long ident = Binder.clearCallingIdentity(); try { final UserData userData; synchronized (mPackagesLock) { synchronized (mUsersLock) { userData = mUsers.get(userId); final UserData userData = mUsers.get(userId); if (userData == null) { Slog.e(LOG_TAG, "Cannot remove user " + userId + ", invalid user id provided."); Slog.e(LOG_TAG, prefix + "invalid user id provided."); 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."); Slog.e(LOG_TAG, prefix + "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."); Slog.w(LOG_TAG, prefix + "it is already scheduled for removal."); return UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED; } return UserManager.REMOVE_RESULT_USER_IS_REMOVABLE; } // Attempt to immediately remove a non-current and non-target user Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds(); if (userId != currentAndTargetUserIds.first && userId != currentAndTargetUserIds.second) { // Attempt to remove the user. This will fail if the user is the current user if (removeUserWithProfilesUnchecked(userId)) { return UserManager.REMOVE_RESULT_REMOVED; } } // If the user was not immediately removed, make sure it is marked as ephemeral. // Don't mark as disabled since, per UserInfo.FLAG_DISABLED documentation, an // ephemeral user should only be marked as disabled when its removal is in progress. Slog.i(LOG_TAG, TextUtils.formatSimple("Unable to immediately remove user %d " + "(%s is %d). User is set as ephemeral and will be removed on " + "user switch or reboot.", userId, userId == currentAndTargetUserIds.first ? "current user" : "target user of an ongoing user switch", userId)); userData.info.flags |= UserInfo.FLAG_EPHEMERAL; writeUserLP(userData); return UserManager.REMOVE_RESULT_DEFERRED; } } finally { Binder.restoreCallingIdentity(ident); } } private void finishRemoveUser(final @UserIdInt int userId) { Slog.i(LOG_TAG, "finishRemoveUser " + userId); Loading