User management and switching

Broadcast intents that get sent out when users are added/removed/switched.

More work on generating user-specific information in package manager queries.
APIs to update user name and query a user by id.
Removed Package.mSetStopped and mSetEnabled, since they're not user specific.

User removal:
- Cleanup ActivityManager, PackageManager, WallpaperManager, AppWidgetService
  and AccountManager.
- Shutdown processes belonging to the user.

Don't show vibrate option in long-press power if there's no vibrator.

Lock the screen when switching users, to force unlocking.

Change-Id: Ib23a721cb75285eef5fd6ba8c7272462764038fa
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
index 959e570..4e9e666 100644
--- a/services/java/com/android/server/pm/UserManager.java
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.pm;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 
 import android.content.pm.ApplicationInfo;
@@ -58,7 +59,7 @@
     private static final String USER_INFO_DIR = "system" + File.separator + "users";
     private static final String USER_LIST_FILENAME = "userlist.xml";
 
-    private SparseArray<UserInfo> mUsers;
+    private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
 
     private final File mUsersDir;
     private final File mUserListFile;
@@ -91,11 +92,36 @@
     }
 
     public List<UserInfo> getUsers() {
-        ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
-        for (int i = 0; i < mUsers.size(); i++) {
-            users.add(mUsers.valueAt(i));
+        synchronized (mUsers) {
+            ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+            for (int i = 0; i < mUsers.size(); i++) {
+                users.add(mUsers.valueAt(i));
+            }
+            return users;
         }
-        return users;
+    }
+
+    public UserInfo getUser(int userId) {
+        synchronized (mUsers) {
+            UserInfo info = mUsers.get(userId);
+            return info;
+        }
+    }
+
+    public boolean exists(int userId) {
+        synchronized (mUsers) {
+            return ArrayUtils.contains(mUserIds, userId);
+        }
+    }
+
+    public void updateUserName(int userId, String name) {
+        synchronized (mUsers) {
+            UserInfo info = mUsers.get(userId);
+            if (name != null && !name.equals(info.name)) {
+                info.name = name;
+                writeUserLocked(info);
+            }
+        }
     }
 
     /**
@@ -108,9 +134,14 @@
     }
 
     private void readUserList() {
-        mUsers = new SparseArray<UserInfo>();
+        synchronized (mUsers) {
+            readUserListLocked();
+        }
+    }
+
+    private void readUserListLocked() {
         if (!mUserListFile.exists()) {
-            fallbackToSingleUser();
+            fallbackToSingleUserLocked();
             return;
         }
         FileInputStream fis = null;
@@ -126,7 +157,7 @@
 
             if (type != XmlPullParser.START_TAG) {
                 Slog.e(LOG_TAG, "Unable to read user list");
-                fallbackToSingleUser();
+                fallbackToSingleUserLocked();
                 return;
             }
 
@@ -139,11 +170,11 @@
                     }
                 }
             }
-            updateUserIds();
+            updateUserIdsLocked();
         } catch (IOException ioe) {
-            fallbackToSingleUser();
+            fallbackToSingleUserLocked();
         } catch (XmlPullParserException pe) {
-            fallbackToSingleUser();
+            fallbackToSingleUserLocked();
         } finally {
             if (fis != null) {
                 try {
@@ -154,15 +185,15 @@
         }
     }
 
-    private void fallbackToSingleUser() {
+    private void fallbackToSingleUserLocked() {
         // Create the primary user
         UserInfo primary = new UserInfo(0, "Primary",
                 UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
         mUsers.put(0, primary);
-        updateUserIds();
+        updateUserIdsLocked();
 
-        writeUserList();
-        writeUser(primary);
+        writeUserListLocked();
+        writeUserLocked(primary);
     }
 
     /*
@@ -172,7 +203,7 @@
      *   <name>Primary</name>
      * </user>
      */
-    private void writeUser(UserInfo userInfo) {
+    private void writeUserLocked(UserInfo userInfo) {
         FileOutputStream fos = null;
         try {
             final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
@@ -216,7 +247,7 @@
      *   <user id="2"></user>
      * </users>
      */
-    private void writeUserList() {
+    private void writeUserListLocked() {
         FileOutputStream fos = null;
         try {
             fos = new FileOutputStream(mUserListFile);
@@ -309,17 +340,19 @@
         return null;
     }
 
-    public UserInfo createUser(String name, int flags, List<ApplicationInfo> apps) {
+    public UserInfo createUser(String name, int flags) {
         int userId = getNextAvailableId();
         UserInfo userInfo = new UserInfo(userId, name, flags);
         File userPath = new File(mBaseUserPath, Integer.toString(userId));
-        if (!createPackageFolders(userId, userPath, apps)) {
+        if (!createPackageFolders(userId, userPath)) {
             return null;
         }
-        mUsers.put(userId, userInfo);
-        writeUserList();
-        writeUser(userInfo);
-        updateUserIds();
+        synchronized (mUsers) {
+            mUsers.put(userId, userInfo);
+            writeUserListLocked();
+            writeUserLocked(userInfo);
+            updateUserIdsLocked();
+        }
         return userInfo;
     }
 
@@ -328,7 +361,13 @@
      * after the user's processes have been terminated.
      * @param id the user's id
      */
-    public void removeUser(int id) {
+    public boolean removeUser(int id) {
+        synchronized (mUsers) {
+            return removeUserLocked(id);
+        }
+    }
+
+    private boolean removeUserLocked(int id) {
         // Remove from the list
         UserInfo userInfo = mUsers.get(id);
         if (userInfo != null) {
@@ -338,11 +377,11 @@
             File userFile = new File(mUsersDir, id + ".xml");
             userFile.delete();
             // Update the user list
-            writeUserList();
-            // Remove the data directories for all packages for this user
-            removePackageFolders(id);
-            updateUserIds();
+            writeUserListLocked();
+            updateUserIdsLocked();
+            return true;
         }
+        return false;
     }
 
     public void installPackageForAllUsers(String packageName, int uid) {
@@ -376,7 +415,7 @@
     /**
      * Caches the list of user ids in an array, adjusting the array size when necessary.
      */
-    private void updateUserIds() {
+    private void updateUserIdsLocked() {
         if (mUserIds == null || mUserIds.length != mUsers.size()) {
             mUserIds = new int[mUsers.size()];
         }
@@ -402,11 +441,10 @@
         return i;
     }
 
-    private boolean createPackageFolders(int id, File userPath, final List<ApplicationInfo> apps) {
+    private boolean createPackageFolders(int id, File userPath) {
         // mInstaller may not be available for unit-tests.
-        if (mInstaller == null || apps == null) return true;
+        if (mInstaller == null) return true;
 
-        final long startTime = SystemClock.elapsedRealtime();
         // Create the user path
         userPath.mkdir();
         FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
@@ -414,13 +452,10 @@
 
         mInstaller.cloneUserData(0, id, false);
 
-        final long stopTime = SystemClock.elapsedRealtime();
-        Log.i(LOG_TAG,
-                "Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");
         return true;
     }
 
-    private boolean removePackageFolders(int id) {
+    boolean removePackageFolders(int id) {
         // mInstaller may not be available for unit-tests.
         if (mInstaller == null) return true;