UserController refactoring

Addressed comments from the previous cl. Added getters for UserController
fields, direct access is no longer allowed. Moved the following methods:
 - getUserManagerLocked. Became getUserManager() - because locking is not
   necessary, double checked locking will suffice.
 - startUserInForeground /showUserSwitchDialog/sendUserSwitchBroadcastsLocked
 - handleIncomingUser/unsafeConvertIncomingUserLocked/isUserRunningLocked/
   getUsers/getProfileIds

Bug: 24745840
Change-Id: Id5a5cfb9604e08add29bd9a03c8fe5200bc51fef
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index ff74d83..4085489 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -18,11 +18,17 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+
 import static android.app.ActivityManager.USER_OP_IS_CURRENT;
 import static android.app.ActivityManager.USER_OP_SUCCESS;
+import static android.os.Process.SYSTEM_UID;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
+import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL;
+import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE;
+import static com.android.server.am.ActivityManagerService.MY_PID;
 import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG;
 import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG;
 import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG;
@@ -31,8 +37,10 @@
 
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
+import android.app.Dialog;
 import android.app.IStopUserCallback;
 import android.app.IUserSwitchObserver;
+import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -40,11 +48,15 @@
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.IRemoteCallback;
+import android.os.IUserManager;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
@@ -58,7 +70,9 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
@@ -76,9 +90,9 @@
     private final Handler mHandler;
 
     // Holds the current foreground user's id
-    int mCurrentUserId = 0;
+    private int mCurrentUserId = UserHandle.USER_SYSTEM;
     // Holds the target user's id during a user switch
-    int mTargetUserId = UserHandle.USER_NULL;
+    private int mTargetUserId = UserHandle.USER_NULL;
 
     /**
      * Which users have been started, so are allowed to run code.
@@ -96,7 +110,7 @@
 
     // 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[] {}; // Accessed by ActivityStack
+    private int[] mCurrentProfileIds = new int[] {};
 
     /**
      * Mapping from each known user ID to the profile group ID it is associated with.
@@ -106,7 +120,7 @@
     /**
      * Registered observers of the user switching mechanics.
      */
-    final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
+    private final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
             = new RemoteCallbackList<>();
 
     /**
@@ -114,6 +128,8 @@
      */
     Object mCurUserSwitchCallback;
 
+    private volatile UserManagerService mUserManager;
+
     UserController(ActivityManagerService service) {
         mService = service;
         mHandler = mService.mHandler;
@@ -176,8 +192,7 @@
                 mService.broadcastIntentLocked(null, null, intent,
                         null, null, 0, null, null,
                         new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
-                        AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
-                        Process.SYSTEM_UID, userId);
+                        AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
             }
         }
     }
@@ -272,16 +287,14 @@
                         mService.mSystemServiceManager.stopUser(userId);
                         mService.broadcastIntentLocked(null, null, shutdownIntent,
                                 null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE,
-                                null, true, false, ActivityManagerService.MY_PID,
-                                android.os.Process.SYSTEM_UID, userId);
+                                null, true, false, MY_PID, SYSTEM_UID, userId);
                     }
                 };
                 // Kick things off.
                 mService.broadcastIntentLocked(null, null, stoppingIntent,
                         null, stoppingReceiver, 0, null, null,
                         new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
-                        null, true, false, ActivityManagerService.MY_PID, Process.SYSTEM_UID,
-                        UserHandle.USER_ALL);
+                        null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -338,8 +351,7 @@
         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         mService.broadcastIntentLocked(null, null, intent,
                 null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                null, false, false, ActivityManagerService.MY_PID, Process.SYSTEM_UID,
-                UserHandle.USER_ALL);
+                null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
     }
 
 
@@ -357,7 +369,7 @@
                         || oldUss.mState == UserState.STATE_SHUTDOWN) {
                     continue;
                 }
-                UserInfo userInfo = getUserManagerLocked().getUserInfo(oldUserId);
+                UserInfo userInfo = getUserInfo(oldUserId);
                 if (userInfo.isGuest()) {
                     // This is a user to be stopped.
                     stopUserLocked(oldUserId, null);
@@ -369,7 +381,7 @@
 
     void startProfilesLocked() {
         if (DEBUG_MU) Slog.i(TAG, "startProfilesLocked");
-        List<UserInfo> profiles = getUserManagerLocked().getProfiles(
+        List<UserInfo> profiles = getUserManager().getProfiles(
                 mCurrentUserId, false /* enabledOnly */);
         List<UserInfo> profilesToStart = new ArrayList<>(profiles.size());
         for (UserInfo user : profiles) {
@@ -388,8 +400,13 @@
         }
     }
 
-    private UserManagerService getUserManagerLocked() {
-        return mService.getUserManagerLocked();
+    private UserManagerService getUserManager() {
+        UserManagerService userManager = mUserManager;
+        if (userManager == null) {
+            IBinder b = ServiceManager.getService(Context.USER_SERVICE);
+            userManager = mUserManager = (UserManagerService) IUserManager.Stub.asInterface(b);
+        }
+        return userManager;
     }
 
     boolean startUser(final int userId, final boolean foreground) {
@@ -416,7 +433,7 @@
                 mService.mStackSupervisor.setLockTaskModeLocked(null,
                         ActivityManager.LOCK_TASK_MODE_NONE, "startUser", false);
 
-                final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
+                final UserInfo userInfo = getUserInfo(userId);
                 if (userInfo == null) {
                     Slog.w(TAG, "No user info for user #" + userId);
                     return false;
@@ -507,8 +524,7 @@
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                     mService.broadcastIntentLocked(null, null, intent,
                             null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                            null, false, false, ActivityManagerService.MY_PID, Process.SYSTEM_UID,
-                            userId);
+                            null, false, false, MY_PID, SYSTEM_UID, userId);
                 }
 
                 if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
@@ -523,11 +539,10 @@
                                         onUserInitialized(uss, foreground, oldUserId, userId);
                                     }
                                 }, 0, null, null, null, AppOpsManager.OP_NONE,
-                                null, true, false, ActivityManagerService.MY_PID,
-                                Process.SYSTEM_UID, userId);
+                                null, true, false, MY_PID, SYSTEM_UID, userId);
                         uss.initializing = true;
                     } else {
-                        getUserManagerLocked().makeInitialized(userInfo.id);
+                        getUserManager().makeInitialized(userInfo.id);
                     }
                 }
 
@@ -551,9 +566,8 @@
                                         int sendingUser) throws RemoteException {
                                 }
                             }, 0, null, null,
-                            new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
-                            null, true, false, ActivityManagerService.MY_PID, Process.SYSTEM_UID,
-                            UserHandle.USER_ALL);
+                            new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
+                            null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
                 }
             }
         } finally {
@@ -563,6 +577,22 @@
         return true;
     }
 
+    /**
+     * Start user, if its not already running, and bring it to foreground.
+     */
+    boolean startUserInForeground(final int userId, Dialog dlg) {
+        boolean result = startUser(userId, /* foreground */ true);
+        dlg.dismiss();
+        return result;
+    }
+
+    void showUserSwitchDialog(int userId, String userName) {
+        // The dialog will show and then initiate the user switch by calling startUserInForeground
+        Dialog d = new UserSwitchingDialog(mService, mService.mContext, userId, userName,
+                true /* above system */);
+        d.show();
+    }
+
     void dispatchForegroundProfileChanged(int userId) {
         final int observerCount = mUserSwitchObservers.beginBroadcast();
         for (int i = 0; i < observerCount; i++) {
@@ -657,7 +687,7 @@
         synchronized (mService) {
             if (clearInitializing) {
                 uss.initializing = false;
-                getUserManagerLocked().makeInitialized(uss.mHandle.getIdentifier());
+                getUserManager().makeInitialized(uss.mHandle.getIdentifier());
             }
             if (clearSwitching) {
                 uss.switching = false;
@@ -683,8 +713,143 @@
             mService.mStackSupervisor.resumeTopActivitiesLocked();
         }
         EventLogTags.writeAmSwitchUser(newUserId);
-        getUserManagerLocked().onUserForeground(newUserId);
-        mService.sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
+        getUserManager().onUserForeground(newUserId);
+        sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
+    }
+
+    void sendUserSwitchBroadcastsLocked(int oldUserId, int newUserId) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            Intent intent;
+            if (oldUserId >= 0) {
+                // Send USER_BACKGROUND broadcast to all profiles of the outgoing user
+                List<UserInfo> profiles = getUserManager().getProfiles(oldUserId, false);
+                int count = profiles.size();
+                for (int i = 0; i < count; i++) {
+                    int profileUserId = profiles.get(i).id;
+                    intent = new Intent(Intent.ACTION_USER_BACKGROUND);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                            | Intent.FLAG_RECEIVER_FOREGROUND);
+                    intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
+                    mService.broadcastIntentLocked(null, null, intent,
+                            null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+                            null, false, false, MY_PID, SYSTEM_UID, profileUserId);
+                }
+            }
+            if (newUserId >= 0) {
+                // Send USER_FOREGROUND broadcast to all profiles of the incoming user
+                List<UserInfo> profiles = getUserManager().getProfiles(newUserId, false);
+                int count = profiles.size();
+                for (int i = 0; i < count; i++) {
+                    int profileUserId = profiles.get(i).id;
+                    intent = new Intent(Intent.ACTION_USER_FOREGROUND);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                            | Intent.FLAG_RECEIVER_FOREGROUND);
+                    intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
+                    mService.broadcastIntentLocked(null, null, intent,
+                            null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+                            null, false, false, MY_PID, SYSTEM_UID, profileUserId);
+                }
+                intent = new Intent(Intent.ACTION_USER_SWITCHED);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                        | Intent.FLAG_RECEIVER_FOREGROUND);
+                intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
+                mService.broadcastIntentLocked(null, null, intent,
+                        null, null, 0, null, null,
+                        new String[] {android.Manifest.permission.MANAGE_USERS},
+                        AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+                        UserHandle.USER_ALL);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+
+    int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
+            int allowMode, String name, String callerPackage) {
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        if (callingUserId == userId) {
+            return userId;
+        }
+
+        // Note that we may be accessing mCurrentUserId outside of a lock...
+        // shouldn't be a big deal, if this is being called outside
+        // of a locked context there is intrinsically a race with
+        // the value the caller will receive and someone else changing it.
+        // We assume that USER_CURRENT_OR_SELF will use the current user; later
+        // we will switch to the calling user if access to the current user fails.
+        int targetUserId = unsafeConvertIncomingUserLocked(userId);
+
+        if (callingUid != 0 && callingUid != SYSTEM_UID) {
+            final boolean allow;
+            if (mService.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
+                    callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
+                // If the caller has this permission, they always pass go.  And collect $200.
+                allow = true;
+            } else if (allowMode == ALLOW_FULL_ONLY) {
+                // We require full access, sucks to be you.
+                allow = false;
+            } else if (mService.checkComponentPermission(INTERACT_ACROSS_USERS, callingPid,
+                    callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+                // If the caller does not have either permission, they are always doomed.
+                allow = false;
+            } else if (allowMode == ALLOW_NON_FULL) {
+                // We are blanket allowing non-full access, you lucky caller!
+                allow = true;
+            } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) {
+                // We may or may not allow this depending on whether the two users are
+                // in the same profile.
+                allow = isSameProfileGroup(callingUserId, targetUserId);
+            } else {
+                throw new IllegalArgumentException("Unknown mode: " + allowMode);
+            }
+            if (!allow) {
+                if (userId == UserHandle.USER_CURRENT_OR_SELF) {
+                    // In this case, they would like to just execute as their
+                    // owner user instead of failing.
+                    targetUserId = callingUserId;
+                } else {
+                    StringBuilder builder = new StringBuilder(128);
+                    builder.append("Permission Denial: ");
+                    builder.append(name);
+                    if (callerPackage != null) {
+                        builder.append(" from ");
+                        builder.append(callerPackage);
+                    }
+                    builder.append(" asks to run as user ");
+                    builder.append(userId);
+                    builder.append(" but is calling from user ");
+                    builder.append(UserHandle.getUserId(callingUid));
+                    builder.append("; this requires ");
+                    builder.append(INTERACT_ACROSS_USERS_FULL);
+                    if (allowMode != ALLOW_FULL_ONLY) {
+                        builder.append(" or ");
+                        builder.append(INTERACT_ACROSS_USERS);
+                    }
+                    String msg = builder.toString();
+                    Slog.w(TAG, msg);
+                    throw new SecurityException(msg);
+                }
+            }
+        }
+        if (!allowAll && targetUserId < 0) {
+            throw new IllegalArgumentException(
+                    "Call does not support special user #" + targetUserId);
+        }
+        // Check shell permission
+        if (callingUid == Process.SHELL_UID && targetUserId >= UserHandle.USER_SYSTEM) {
+            if (hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId)) {
+                throw new SecurityException("Shell does not have permission to access user "
+                        + targetUserId + "\n " + Debug.getCallers(3));
+            }
+        }
+        return targetUserId;
+    }
+
+    int unsafeConvertIncomingUserLocked(int userId) {
+        return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF)
+                ? getCurrentUserIdLocked(): userId;
     }
 
     void registerUserSwitchObserver(IUserSwitchObserver observer) {
@@ -705,7 +870,7 @@
         mUserSwitchObservers.unregister(observer);
     }
 
-    UserState getStartedUserState(int userId) {
+    UserState getStartedUserStateLocked(int userId) {
         return mStartedUsers.get(userId);
     }
 
@@ -746,9 +911,8 @@
                 intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
                 mService.broadcastIntentLocked(null, null, intent, null,
                         resultTo, 0, null, null,
-                        new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
-                        AppOpsManager.OP_NONE, null, true, false,
-                        ActivityManagerService.MY_PID, Process.SYSTEM_UID, userId);
+                        new String[] {android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+                        AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
             }
         }
     }
@@ -759,7 +923,7 @@
      * background.
      */
     void updateCurrentProfileIdsLocked() {
-        final List<UserInfo> profiles = getUserManagerLocked().getProfiles(mCurrentUserId,
+        final List<UserInfo> profiles = getUserManager().getProfiles(mCurrentUserId,
                 false /* enabledOnly */);
         int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
         for (int i = 0; i < currentProfileIds.length; i++) {
@@ -769,7 +933,7 @@
 
         synchronized (mUserProfileGroupIdsSelfLocked) {
             mUserProfileGroupIdsSelfLocked.clear();
-            final List<UserInfo> users = getUserManagerLocked().getUsers(false);
+            final List<UserInfo> users = getUserManager().getUsers(false);
             for (int i = 0; i < users.size(); i++) {
                 UserInfo user = users.get(i);
                 if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
@@ -783,6 +947,18 @@
         return mStartedUserArray;
     }
 
+    boolean isUserRunningLocked(int userId, boolean orStopped) {
+        UserState state = getStartedUserStateLocked(userId);
+        if (state == null) {
+            return false;
+        }
+        if (orStopped) {
+            return true;
+        }
+        return state.mState != UserState.STATE_STOPPING
+                && state.mState != UserState.STATE_SHUTDOWN;
+    }
+
     UserInfo getCurrentUser() {
         if ((mService.checkCallingPermission(INTERACT_ACROSS_USERS)
                 != PackageManager.PERMISSION_GRANTED) && (
@@ -802,11 +978,50 @@
 
     UserInfo getCurrentUserLocked() {
         int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
-        return getUserManagerLocked().getUserInfo(userId);
+        return getUserInfo(userId);
+    }
+
+    int getCurrentOrTargetUserIdLocked() {
+        return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
     }
 
     int getCurrentUserIdLocked() {
-        return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
+        return mCurrentUserId;
+    }
+
+    int setTargetUserIdLocked(int targetUserId) {
+        return mTargetUserId = targetUserId;
+    }
+
+    int[] getUsers() {
+        UserManagerService ums = getUserManager();
+        return ums != null ? ums.getUserIds() : new int[] { 0 };
+    }
+
+    UserInfo getUserInfo(int userId) {
+        return getUserManager().getUserInfo(userId);
+    }
+
+    int[] getUserIds() {
+        return getUserManager().getUserIds();
+    }
+
+    boolean exists(int userId) {
+        return getUserManager().exists(userId);
+    }
+
+    boolean hasUserRestriction(String restriction, int userId) {
+        return getUserManager().hasUserRestriction(restriction, userId);
+    }
+
+    Set<Integer> getProfileIds(int userId) {
+        Set<Integer> userIds = new HashSet<>();
+        final List<UserInfo> profiles = getUserManager().getProfiles(userId,
+                false /* enabledOnly */);
+        for (UserInfo user : profiles) {
+            userIds.add(user.id);
+        }
+        return userIds;
     }
 
     boolean isSameProfileGroup(int callingUserId, int targetUserId) {
@@ -824,6 +1039,10 @@
         return ArrayUtils.contains(mCurrentProfileIds, userId);
     }
 
+    int[] getCurrentProfileIdsLocked() {
+        return mCurrentProfileIds;
+    }
+
     void dump(PrintWriter pw, boolean dumpAll) {
         pw.println("  mStartedUsers:");
         for (int i = 0; i < mStartedUsers.size(); i++) {
@@ -858,5 +1077,4 @@
             }
         }
     }
-
 }