From 1df1473008c24487701c5bc15f39ed9f9697f421 Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Fri, 29 Aug 2014 21:37:27 -0700 Subject: [PATCH] Make it possible to remove current user Due to the async nature of switching users, it's not possible to switch and remove immediately. So mark the switch target user as soon as the user switch is requested, so that a remove will proceed without failing at stopUserLocked(). Also, fix a similar problem with deleting the current guest and switching to a new guest. It was attempting to remove the current user which will result in a failed stopping of the user. Added a way to mark the current guest for deletion so that a new one can be created, switched to and the old one deleted. If runtime fails, old guest is already marked for deletion and will be cleaned up on restart. Bug: 17321533 Change-Id: I4be0fb956227e0bb95588b5b1f2867fb1e655c0d --- core/java/android/os/IUserManager.aidl | 1 + core/java/android/os/UserManager.java | 16 ++++++++ .../systemui/GuestResumeSessionReceiver.java | 8 +++- .../android/server/am/ActivityManagerService.java | 14 +++++-- .../com/android/server/pm/UserManagerService.java | 43 +++++++++++++++++++++- 5 files changed, 77 insertions(+), 5 deletions(-) diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 713fcd8889c4..328662775996 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 e215669c935a..f7936675b748 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -676,6 +676,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. * Note that the default is true, it's only that managed profiles might not be enabled. diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java index 7f5ed6af9e32..714b0a83900e 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 c2c86fffc011..2a95e8dd7ae0 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 /** @@ -17937,6 +17942,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)); @@ -18004,6 +18010,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 @@ -18371,7 +18378,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; } @@ -18511,12 +18518,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 29299394c91f..4a2cecea17f4 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++; } } @@ -1180,6 +1180,47 @@ public class UserManagerService extends IUserManager.Stub { } /** + * 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. * @param userHandle the user's id -- 2.11.0