diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 713fcd8889c49..3286627759968 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -52,4 +52,5 @@ interface IUserManager { void removeRestrictions(); void setDefaultGuestRestrictions(in Bundle restrictions); Bundle getDefaultGuestRestrictions(); + boolean markGuestForDeletion(int userHandle); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index e215669c935ad..f7936675b748b 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -675,6 +675,22 @@ public class UserManager { } } + /** + * @hide + * Marks the guest user for deletion to allow a new guest to be created before deleting + * the current user who is a guest. + * @param userHandle + * @return + */ + public boolean markGuestForDeletion(int userHandle) { + try { + return mService.markGuestForDeletion(userHandle); + } catch (RemoteException re) { + Log.w(TAG, "Could not mark guest for deletion", re); + return false; + } + } + /** * Sets the user as enabled, if such an user exists. * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java index 7f5ed6af9e326..714b0a83900e2 100644 --- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java @@ -112,17 +112,23 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { return; } - userManager.removeUser(currentUser.id); + boolean marked = userManager.markGuestForDeletion(currentUser.id); + if (!marked) { + Log.w(TAG, "Couldn't mark the guest for deletion for user " + userId); + return; + } UserInfo newGuest = userManager.createGuest(context, currentUser.name); try { if (newGuest == null) { Log.e(TAG, "Could not create new guest, switching back to owner"); ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER); + userManager.removeUser(currentUser.id); WindowManagerGlobal.getWindowManagerService().lockNow(null /* options */); return; } ActivityManagerNative.getDefault().switchUser(newGuest.id); + userManager.removeUser(currentUser.id); } catch (RemoteException e) { Log.e(TAG, "Couldn't wipe session because ActivityManager or WindowManager is dead"); return; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 51a40d4b9ec80..389369fcece5d 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1107,7 +1107,12 @@ public final class ActivityManagerService extends ActivityManagerNative final ActivityThread mSystemThread; + // Holds the current foreground user's id int mCurrentUserId = 0; + // Holds the target user's id during a user switch + int mTargetUserId = UserHandle.USER_NULL; + // If there are multiple profiles for the current user, their ids are here + // Currently only the primary user can have managed profiles int[] mCurrentProfileIds = new int[] {UserHandle.USER_OWNER}; // Accessed by ActivityStack /** @@ -17935,6 +17940,7 @@ public final class ActivityManagerService extends ActivityManagerNative return false; } userName = userInfo.name; + mTargetUserId = userId; } mHandler.removeMessages(START_USER_SWITCH_MSG); mHandler.sendMessage(mHandler.obtainMessage(START_USER_SWITCH_MSG, userId, 0, userName)); @@ -18002,6 +18008,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (foreground) { mCurrentUserId = userId; + mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up updateCurrentProfileIdsLocked(); mWindowManager.setCurrentUser(userId, mCurrentProfileIds); // Once the internal notion of the active user has switched, we lock the device @@ -18369,7 +18376,7 @@ public final class ActivityManagerService extends ActivityManagerNative private int stopUserLocked(final int userId, final IStopUserCallback callback) { if (DEBUG_MU) Slog.i(TAG_MU, "stopUserLocked userId=" + userId); - if (mCurrentUserId == userId) { + if (mCurrentUserId == userId && mTargetUserId == UserHandle.USER_NULL) { return ActivityManager.USER_OP_IS_CURRENT; } @@ -18509,12 +18516,13 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException(msg); } synchronized (this) { - return getUserManagerLocked().getUserInfo(mCurrentUserId); + int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId; + return getUserManagerLocked().getUserInfo(userId); } } int getCurrentUserIdLocked() { - return mCurrentUserId; + return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId; } @Override diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 29299394c91fa..4a2cecea17f48 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -519,7 +519,7 @@ public class UserManagerService extends IUserManager.Stub { for (int i = 0; i < totalUserCount; i++) { UserInfo user = mUsers.valueAt(i); if (!mRemovingUserIds.get(user.id) - && !user.isGuest()) { + && !user.isGuest() && !user.partial) { aliveUserCount++; } } @@ -1179,6 +1179,47 @@ public class UserManagerService extends IUserManager.Stub { return count; } + /** + * Mark this guest user for deletion to allow us to create another guest + * and switch to that user before actually removing this guest. + * @param userHandle the userid of the current guest + * @return whether the user could be marked for deletion + */ + public boolean markGuestForDeletion(int userHandle) { + checkManageUsersPermission("Only the system can remove users"); + if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean( + UserManager.DISALLOW_REMOVE_USER, false)) { + Log.w(LOG_TAG, "Cannot remove user. DISALLOW_REMOVE_USER is enabled."); + return false; + } + + long ident = Binder.clearCallingIdentity(); + try { + final UserInfo user; + synchronized (mPackagesLock) { + user = mUsers.get(userHandle); + if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) { + return false; + } + if (!user.isGuest()) { + return false; + } + // 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. + user.partial = true; + // Mark it as disabled, so that it isn't returned any more when + // profiles are queried. + user.flags |= UserInfo.FLAG_DISABLED; + user.flags &= ~UserInfo.FLAG_GUEST; + writeUserLocked(user); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + return true; + } + /** * Removes a user and all data directories created for that user. This method should be called * after the user's processes have been terminated.