OSDN Git Service

Make it possible to remove current user
authorAmith Yamasani <yamasani@google.com>
Sat, 30 Aug 2014 04:37:27 +0000 (21:37 -0700)
committerAmith Yamasani <yamasani@google.com>
Sat, 30 Aug 2014 05:06:16 +0000 (22:06 -0700)
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
core/java/android/os/UserManager.java
packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/pm/UserManagerService.java

index 713fcd8..3286627 100644 (file)
@@ -52,4 +52,5 @@ interface IUserManager {
     void removeRestrictions();
     void setDefaultGuestRestrictions(in Bundle restrictions);
     Bundle getDefaultGuestRestrictions();
+    boolean markGuestForDeletion(int userHandle);
 }
index e215669..f793667 100644 (file)
@@ -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.
index 7f5ed6a..714b0a8 100644 (file)
@@ -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;
index c2c86ff..2a95e8d 100755 (executable)
@@ -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
index 2929939..4a2cece 100644 (file)
@@ -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