Keep track of whether an app is installed for each user.

This add a new per-user state for an app, indicating whether
it is installed for that user.

All system apps are always installed for all users (we still
use disable to "uninstall" them).

Now when you call into the package manager to install an app,
it will only install the app for that user unless you supply
a flag saying to install for all users.  Only being installed
for the user is just the normal install state, but all other
users have marked in their state for that app that it is not
installed.

When you call the package manager APIs for information about
apps, uninstalled apps are treated as really being not visible
(somewhat more-so than disabled apps), unless you use the
GET_UNINSTALLED_PACKAGES flag.

If another user calls to install an app that is already installed,
just not for them, then the normal install process takes place
but in addition that user's installed state is toggled on.

The package manager will not send PACKAGE_ADDED, PACKAGE_REMOVED,
PACKAGE_REPLACED etc broadcasts to users who don't have a package
installed or not being involved in a change in the install state.
There are a few things that are not quite right with this -- for
example if you go through a full install (with a new apk) of an
app for one user who doesn't have it already installed, you will
still get the PACKAGED_REPLACED messages even though this is
technically the first install for your user.  I'm not sure how
much of an issue this is.

When you call the existing API to uninstall an app, this toggles
the installed state of the app for that user to be off.  Only if
that is the last user user that has the app uinstalled will it
actually be removed from the device.  Again there is a new flag
you can pass in to force the app to be uninstalled for all users.

Also fixed issues with cleaning external storage of apps, which
was not dealing with multiple users.  We now keep track of cleaning
each user for each package.

Change-Id: I00e66452b149defc08c5e0183fa673f532465ed5
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index e222936..e6bcaa1 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -210,7 +210,7 @@
 
     ComponentName startServiceLocked(IApplicationThread caller,
             Intent service, String resolvedType,
-            int callingPid, int callingUid) {
+            int callingPid, int callingUid, int userId) {
         if (DEBUG_SERVICE) Slog.v(TAG, "startService: " + service
                 + " type=" + resolvedType + " args=" + service.getExtras());
 
@@ -226,7 +226,7 @@
 
         ServiceLookupResult res =
             retrieveServiceLocked(service, resolvedType,
-                    callingPid, callingUid, UserHandle.getUserId(callingUid), true);
+                    callingPid, callingUid, userId, true);
         if (res == null) {
             return null;
         }
@@ -264,7 +264,7 @@
     }
 
     int stopServiceLocked(IApplicationThread caller, Intent service,
-            String resolvedType) {
+            String resolvedType, int userId) {
         if (DEBUG_SERVICE) Slog.v(TAG, "stopService: " + service
                 + " type=" + resolvedType);
 
@@ -278,9 +278,7 @@
 
         // If this service is active, make sure it is stopped.
         ServiceLookupResult r = retrieveServiceLocked(service, resolvedType,
-                Binder.getCallingPid(), Binder.getCallingUid(),
-                callerApp == null ? UserHandle.getCallingUserId() : callerApp.userId,
-                false);
+                Binder.getCallingPid(), Binder.getCallingUid(), userId, false);
         if (r != null) {
             if (r.record != null) {
                 final long origId = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8fc12d3..2b4f8b1 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -10554,7 +10554,7 @@
     }
     
     public ComponentName startService(IApplicationThread caller, Intent service,
-            String resolvedType) {
+            String resolvedType, int userId) {
         enforceNotIsolatedCaller("startService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
@@ -10566,9 +10566,10 @@
         synchronized(this) {
             final int callingPid = Binder.getCallingPid();
             final int callingUid = Binder.getCallingUid();
+            checkValidCaller(callingUid, userId);
             final long origId = Binder.clearCallingIdentity();
             ComponentName res = mServices.startServiceLocked(caller, service,
-                    resolvedType, callingPid, callingUid);
+                    resolvedType, callingPid, callingUid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
@@ -10581,22 +10582,24 @@
                 Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
             final long origId = Binder.clearCallingIdentity();
             ComponentName res = mServices.startServiceLocked(null, service,
-                    resolvedType, -1, uid);
+                    resolvedType, -1, uid, UserHandle.getUserId(uid));
             Binder.restoreCallingIdentity(origId);
             return res;
         }
     }
 
     public int stopService(IApplicationThread caller, Intent service,
-            String resolvedType) {
+            String resolvedType, int userId) {
         enforceNotIsolatedCaller("stopService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
+        checkValidCaller(Binder.getCallingUid(), userId);
+
         synchronized(this) {
-            return mServices.stopServiceLocked(caller, service, resolvedType);
+            return mServices.stopServiceLocked(caller, service, resolvedType, userId);
         }
     }
 
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 4252b90..b0aca21 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -64,11 +64,12 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
-import android.content.pm.UserInfo;
+import android.content.pm.PackageUserState;
 import android.content.pm.PackageParser.ActivityIntentInfo;
 import android.content.pm.PackageStats;
 import android.content.pm.ParceledListSlice;
@@ -103,7 +104,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings.Secure;
 import android.security.SystemKeyStore;
 import android.util.DisplayMetrics;
@@ -168,6 +168,7 @@
     static final boolean DEBUG_UPGRADE = false;
     private static final boolean DEBUG_INSTALL = false;
     private static final boolean DEBUG_REMOVE = false;
+    private static final boolean DEBUG_BROADCASTS = false;
     private static final boolean DEBUG_SHOW_INFO = false;
     private static final boolean DEBUG_PACKAGE_INFO = false;
     private static final boolean DEBUG_INTENT_MATCHING = false;
@@ -662,15 +663,21 @@
                     break;
                 }
                 case START_CLEANING_PACKAGE: {
-                    String packageName = (String)msg.obj;
                     Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+                    PackageCleanItem item = new PackageCleanItem((String)msg.obj,
+                            msg.arg2 != 0);
                     synchronized (mPackages) {
-                        if (!mSettings.mPackagesToBeCleaned.contains(packageName)) {
-                            mSettings.mPackagesToBeCleaned.add(packageName);
+                        if (msg.arg1 == UserHandle.USER_ALL) {
+                            int[] users = sUserManager.getUserIds();
+                            for (int user : users) {
+                                mSettings.addPackageToCleanLPw(user, item);
+                            }
+                        } else {
+                            mSettings.addPackageToCleanLPw(msg.arg1, item);
                         }
                     }
                     Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                    startCleaningPackages();
+                    startCleaningPackages(-1);
                 } break;
                 case POST_INSTALL: {
                     if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
@@ -692,15 +699,14 @@
                             }
                             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                                     res.pkg.applicationInfo.packageName,
-                                    extras, null, null, UserHandle.USER_ALL);
+                                    extras, null, null, res.users);
                             if (update) {
                                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                                         res.pkg.applicationInfo.packageName,
-                                        extras, null, null, UserHandle.USER_ALL);
+                                        extras, null, null, res.users);
                                 sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                                         null, null,
-                                        res.pkg.applicationInfo.packageName, null,
-                                        UserHandle.USER_ALL);
+                                        res.pkg.applicationInfo.packageName, null, res.users);
                             }
                             if (res.removedInfo.args != null) {
                                 // Remove the replaced package's older resources safely now
@@ -1562,19 +1568,17 @@
     PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
         PackageInfo pi;
-        if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
-            // The package has been uninstalled but has retained data and resources.
-            pi = PackageParser.generatePackageInfo(p, null, flags, 0, 0, null, false, 0, userId);
-        } else {
-            final PackageSetting ps = (PackageSetting) p.mExtras;
-            if (ps == null) {
-                return null;
-            }
-            final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
-            pi = PackageParser.generatePackageInfo(p, gp.gids, flags,
-                    ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
-                    ps.getStopped(userId), ps.getEnabled(userId), userId);
-            pi.applicationInfo.enabledSetting = ps.getEnabled(userId);
+        final PackageSetting ps = (PackageSetting) p.mExtras;
+        if (ps == null) {
+            return null;
+        }
+        final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+        final PackageUserState state = ps.readUserState(userId);
+        pi = PackageParser.generatePackageInfo(p, gp.gids, flags,
+                ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
+                state, userId);
+        if (pi != null) {
+            pi.applicationInfo.enabledSetting = state.enabled;
             pi.applicationInfo.enabled =
                     pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_DEFAULT
                     || pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
@@ -1741,14 +1745,15 @@
         PackageSetting ps = mSettings.mPackages.get(packageName);
         if (ps != null) {
             if (ps.pkg == null) {
-                PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, flags, userId);
+                PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName,
+                        flags, userId);
                 if (pInfo != null) {
                     return pInfo.applicationInfo;
                 }
                 return null;
             }
-            return PackageParser.generateApplicationInfo(ps.pkg, flags, ps.getStopped(userId),
-                    ps.getEnabled(userId), userId);
+            return PackageParser.generateApplicationInfo(ps.pkg, flags,
+                    ps.readUserState(userId), userId);
         }
         return null;
     }
@@ -1758,20 +1763,23 @@
         if (!sUserManager.exists(userId)) return null;
         PackageSetting ps = mSettings.mPackages.get(packageName);
         if (ps != null) {
-            PackageParser.Package pkg = new PackageParser.Package(packageName);
-            if (ps.pkg == null) {
-                ps.pkg = new PackageParser.Package(packageName);
-                ps.pkg.applicationInfo.packageName = packageName;
-                ps.pkg.applicationInfo.flags = ps.pkgFlags;
-                ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
-                ps.pkg.applicationInfo.sourceDir = ps.codePathString;
-                ps.pkg.applicationInfo.dataDir =
+            PackageParser.Package pkg = ps.pkg;
+            if (pkg == null) {
+                if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
+                    return null;
+                }
+                pkg = new PackageParser.Package(packageName);
+                pkg.applicationInfo.packageName = packageName;
+                pkg.applicationInfo.flags = ps.pkgFlags;
+                pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
+                pkg.applicationInfo.sourceDir = ps.codePathString;
+                pkg.applicationInfo.dataDir =
                         getDataPathForPackage(ps.pkg.packageName, 0).getPath();
-                ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
+                pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
             }
-            // ps.pkg.mSetEnabled = ps.getEnabled(userId);
-            // ps.pkg.mSetStopped = ps.getStopped(userId);
-            return generatePackageInfo(ps.pkg, flags, userId);
+            // pkg.mSetEnabled = ps.getEnabled(userId);
+            // pkg.mSetStopped = ps.getStopped(userId);
+            return generatePackageInfo(pkg, flags, userId);
         }
         return null;
     }
@@ -1789,13 +1797,12 @@
                 PackageSetting ps = mSettings.mPackages.get(packageName);
                 if (ps == null) return null;
                 // Note: isEnabledLP() does not apply here - always return info
-                return PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
-                        ps.getEnabled(userId));
+                return PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId));
             }
             if ("android".equals(packageName)||"system".equals(packageName)) {
                 return mAndroidApplication;
             }
-            if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+            if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
                 return generateApplicationInfoFromSettingsLPw(packageName, flags, userId);
             }
         }
@@ -1862,8 +1869,8 @@
             if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
-                        ps.getEnabled(userId), userId);
+                return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
+                        userId);
             }
             if (mResolveComponentName.equals(component)) {
                 return mResolveActivity;
@@ -1882,8 +1889,8 @@
             if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
-                        ps.getEnabled(userId), userId);
+                return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
+                        userId);
             }
         }
         return null;
@@ -1899,8 +1906,8 @@
             if (s != null && mSettings.isEnabledLPr(s.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                return PackageParser.generateServiceInfo(s, flags, ps.getStopped(userId),
-                        ps.getEnabled(userId), userId);
+                return PackageParser.generateServiceInfo(s, flags, ps.readUserState(userId),
+                        userId);
             }
         }
         return null;
@@ -1916,8 +1923,8 @@
             if (p != null && mSettings.isEnabledLPr(p.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                return PackageParser.generateProviderInfo(p, flags, ps.getStopped(userId),
-                        ps.getEnabled(userId), userId);
+                return PackageParser.generateProviderInfo(p, flags, ps.readUserState(userId),
+                        userId);
             }
         }
         return null;
@@ -2870,8 +2877,8 @@
                 } else {
                     final PackageParser.Package p = mPackages.get(packageName);
                     if (p != null && ps != null) {
-                        ai = PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
-                                ps.getEnabled(userId), userId);
+                        ai = PackageParser.generateApplicationInfo(p, flags,
+                                ps.readUserState(userId), userId);
                     }
                 }
 
@@ -2901,10 +2908,13 @@
                         && (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
                         && (!mSafeMode || isSystemApp(p))) {
                     PackageSetting ps = mSettings.mPackages.get(p.packageName);
-                    finalList.add(PackageParser.generateApplicationInfo(p, flags,
-                            ps != null ? ps.getStopped(userId) : false,
-                            ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                            userId));
+                    if (ps != null) {
+                        ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
+                                ps.readUserState(userId), userId);
+                        if (ai != null) {
+                            finalList.add(ai);
+                        }
+                    }
                 }
             }
         }
@@ -2921,14 +2931,12 @@
             PackageSetting ps = provider != null
                     ? mSettings.mPackages.get(provider.owner.packageName)
                     : null;
-            return provider != null
+            return ps != null
                     && mSettings.isEnabledLPr(provider.info, flags, userId)
                     && (!mSafeMode || (provider.info.applicationInfo.flags
                             &ApplicationInfo.FLAG_SYSTEM) != 0)
                     ? PackageParser.generateProviderInfo(provider, flags,
-                            ps != null ? ps.getStopped(userId) : false,
-                            ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                            userId)
+                            ps.readUserState(userId), userId)
                     : null;
         }
     }
@@ -2948,14 +2956,15 @@
                 PackageParser.Provider p = entry.getValue();
                 PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
 
-                if (p.syncable
+                if (ps != null && p.syncable
                         && (!mSafeMode || (p.info.applicationInfo.flags
                                 &ApplicationInfo.FLAG_SYSTEM) != 0)) {
-                    outNames.add(entry.getKey());
-                    outInfo.add(PackageParser.generateProviderInfo(p, 0,
-                            ps != null ? ps.getStopped(userId) : false,
-                            ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                            userId));
+                    ProviderInfo info = PackageParser.generateProviderInfo(p, 0,
+                            ps.readUserState(userId), userId);
+                    if (info != null) {
+                        outNames.add(entry.getKey());
+                        outInfo.add(info);
+                    }
                 }
             }
         }
@@ -2973,7 +2982,7 @@
             while (i.hasNext()) {
                 final PackageParser.Provider p = i.next();
                 PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
-                if (p.info.authority != null
+                if (ps != null && p.info.authority != null
                         && (processName == null
                                 || (p.info.processName.equals(processName)
                                         && UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
@@ -2983,10 +2992,9 @@
                     if (finalList == null) {
                         finalList = new ArrayList<ProviderInfo>(3);
                     }
-                    finalList.add(PackageParser.generateProviderInfo(p, flags,
-                            ps != null ? ps.getStopped(userId) : false,
-                            ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                            userId));
+                    ProviderInfo info = PackageParser.generateProviderInfo(p, flags,
+                            ps.readUserState(userId), userId);
+                    finalList.add(info);
                 }
             }
         }
@@ -3047,7 +3055,7 @@
                 continue;
             }
             PackageParser.Package pkg = scanPackageLI(file,
-                    flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);
+                    flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
             // Don't mess around with apps in system partition.
             if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                     mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
@@ -3115,7 +3123,7 @@
      *  Returns null in case of errors and the error code is stored in mLastScanError
      */
     private PackageParser.Package scanPackageLI(File scanFile,
-            int parseFlags, int scanMode, long currentTime) {
+            int parseFlags, int scanMode, long currentTime, UserHandle user) {
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         String scanPath = scanFile.getPath();
         parseFlags |= mDefParseFlags;
@@ -3215,7 +3223,7 @@
              */
             if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
                     != PackageManager.SIGNATURE_MATCH) {
-                deletePackageLI(pkg.packageName, true, 0, null, false);
+                deletePackageLI(pkg.packageName, null, true, 0, null, false);
                 ps = null;
             } else {
                 /*
@@ -3268,7 +3276,7 @@
         setApplicationInfoPaths(pkg, codePath, resPath);
         // Note that we invoke the following method only if we are about to unpack an application
         PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
-                | SCAN_UPDATE_SIGNATURE, currentTime);
+                | SCAN_UPDATE_SIGNATURE, currentTime, user);
 
         /*
          * If the system app should be overridden by a previously installed
@@ -3472,7 +3480,7 @@
     }
 
     private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
-            int parseFlags, int scanMode, long currentTime) {
+            int parseFlags, int scanMode, long currentTime, UserHandle user) {
         File scanFile = new File(pkg.mScanPath);
         if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
                 pkg.applicationInfo.publicSourceDir == null) {
@@ -3664,7 +3672,7 @@
             // the PkgSetting exists already and doesn't have to be created.
             pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
                     destResourceFile, pkg.applicationInfo.nativeLibraryDir,
-                    pkg.applicationInfo.flags, true, false);
+                    pkg.applicationInfo.flags, user, false);
             if (pkgSetting == null) {
                 Slog.w(TAG, "Creating application package " + pkg.packageName + " failed");
                 mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -4024,7 +4032,9 @@
             // Add the new setting to mPackages
             mPackages.put(pkg.applicationInfo.packageName, pkg);
             // Make sure we don't accidentally delete its data.
-            mSettings.mPackagesToBeCleaned.remove(pkgName);
+            for (int i=0; i<mSettings.mPackagesToBeCleaned.size(); i++) {
+                mSettings.mPackagesToBeCleaned.valueAt(i).remove(pkgName);
+            }
             
             // Take care of first install / last update times.
             if (currentTime != 0) {
@@ -4840,7 +4850,8 @@
                     // System apps are never considered stopped for purposes of
                     // filtering, because there may be no way for the user to
                     // actually re-launch them.
-                    return ps.getStopped(userId) && (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0;
+                    return (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0
+                            && ps.getStopped(userId);
                 }
             }
             return false;
@@ -4863,12 +4874,17 @@
                     &ApplicationInfo.FLAG_SYSTEM) == 0) {
                 return null;
             }
-            final ResolveInfo res = new ResolveInfo();
             PackageSetting ps = (PackageSetting) activity.owner.mExtras;
-            res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags,
-                    ps != null ? ps.getStopped(userId) : false,
-                    ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                    userId);
+            if (ps == null) {
+                return null;
+            }
+            ActivityInfo ai = PackageParser.generateActivityInfo(activity, mFlags,
+                    ps.readUserState(userId), userId);
+            if (ai == null) {
+                return null;
+            }
+            final ResolveInfo res = new ResolveInfo();
+            res.activityInfo = ai;
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = info;
             }
@@ -5031,8 +5047,8 @@
                     // System apps are never considered stopped for purposes of
                     // filtering, because there may be no way for the user to
                     // actually re-launch them.
-                    return ps.getStopped(userId)
-                            && (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0;
+                    return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
+                            && ps.getStopped(userId);
                 }
             }
             return false;
@@ -5056,12 +5072,14 @@
                     &ApplicationInfo.FLAG_SYSTEM) == 0) {
                 return null;
             }
-            final ResolveInfo res = new ResolveInfo();
             PackageSetting ps = (PackageSetting) service.owner.mExtras;
-            res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags,
-                    ps != null ? ps.getStopped(userId) : false,
-                    ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                    userId);
+            if (ps == null) {
+                return null;
+            }
+            ServiceInfo si = PackageParser.generateServiceInfo(service, mFlags,
+                    ps.readUserState(userId), userId);
+            final ResolveInfo res = new ResolveInfo();
+            res.serviceInfo = si;
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = filter;
             }
@@ -5152,13 +5170,14 @@
     };
 
     static final void sendPackageBroadcast(String action, String pkg,
-            Bundle extras, String targetPkg, IIntentReceiver finishedReceiver, int userId) {
+            Bundle extras, String targetPkg, IIntentReceiver finishedReceiver,
+            int[] userIds) {
         IActivityManager am = ActivityManagerNative.getDefault();
         if (am != null) {
             try {
-                int[] userIds = userId == UserHandle.USER_ALL
-                        ? sUserManager.getUserIds() 
-                        : new int[] {userId};
+                if (userIds == null) {
+                    userIds = sUserManager.getUserIds();
+                }
                 for (int id : userIds) {
                     final Intent intent = new Intent(action,
                             pkg != null ? Uri.fromParts("package", pkg, null) : null);
@@ -5170,11 +5189,18 @@
                     }
                     // Modify the UID when posting to other users
                     int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-                    if (uid > 0 && id > 0) {
+                    if (uid > 0 && UserHandle.getUserId(uid) != id) {
                         uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
                         intent.putExtra(Intent.EXTRA_UID, uid);
                     }
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                    if (DEBUG_BROADCASTS) {
+                        RuntimeException here = new RuntimeException("here");
+                        here.fillInStackTrace();
+                        Slog.d(TAG, "Sending to user " + id + ": "
+                                + intent.toShortString(false, true, false, false)
+                                + " " + intent.getExtras(), here);
+                    }
                     am.broadcastIntent(null, intent, null, finishedReceiver,
                             0, null, null, null, finishedReceiver != null, false, id);
                 }
@@ -5192,8 +5218,9 @@
         return mMediaMounted || Environment.isExternalStorageEmulated();
     }
 
-    public String nextPackageToClean(String lastPackage) {
+    public PackageCleanItem nextPackageToClean(PackageCleanItem lastPackage) {
         // writer
+        final int userId = UserHandle.getCallingUserId();
         synchronized (mPackages) {
             if (!isExternalMediaAvailable()) {
                 // If the external storage is no longer mounted at this point,
@@ -5201,34 +5228,65 @@
                 // packages files and can not delete any more.  Bail.
                 return null;
             }
-            if (lastPackage != null) {
-                mSettings.mPackagesToBeCleaned.remove(lastPackage);
+            ArrayList<PackageCleanItem> pkgs = mSettings.mPackagesToBeCleaned.get(userId);
+            if (pkgs != null) {
+                if (lastPackage != null) {
+                    pkgs.remove(lastPackage);
+                }
+                if (pkgs.size() > 0) {
+                    return pkgs.get(0);
+                }
             }
-            return mSettings.mPackagesToBeCleaned.size() > 0
-                    ? mSettings.mPackagesToBeCleaned.get(0) : null;
         }
+        // Move on to the next user to clean.
+        long ident = Binder.clearCallingIdentity();
+        try {
+            startCleaningPackages(userId);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return null;
     }
 
-    void schedulePackageCleaning(String packageName) {
-        mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, packageName));
+    void schedulePackageCleaning(String packageName, int userId, boolean andCode) {
+        if (false) {
+            RuntimeException here = new RuntimeException("here");
+            here.fillInStackTrace();
+            Slog.d(TAG, "Schedule cleaning " + packageName + " user=" + userId
+                    + " andCode=" + andCode, here);
+        }
+        mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE,
+                userId, andCode ? 1 : 0, packageName));
     }
     
-    void startCleaningPackages() {
+    void startCleaningPackages(int lastUser) {
         // reader
+        int nextUser = -1;
         synchronized (mPackages) {
             if (!isExternalMediaAvailable()) {
                 return;
             }
-            if (mSettings.mPackagesToBeCleaned.size() <= 0) {
+            final int N = mSettings.mPackagesToBeCleaned.size();
+            if (N <= 0) {
                 return;
             }
+            for (int i=0; i<N; i++) {
+                int user = mSettings.mPackagesToBeCleaned.keyAt(i);
+                if (user > lastUser) {
+                    nextUser = user;
+                    break;
+                }
+            }
+            if (nextUser < 0) {
+                nextUser = mSettings.mPackagesToBeCleaned.keyAt(0);
+            }
         }
         Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE);
         intent.setComponent(DEFAULT_CONTAINER_COMPONENT);
         IActivityManager am = ActivityManagerNative.getDefault();
         if (am != null) {
             try {
-                am.startService(null, intent, null);
+                am.startService(null, intent, null, nextUser);
             } catch (RemoteException e) {
             }
         }
@@ -5291,7 +5349,7 @@
                                 PackageParser.PARSE_CHATTY |
                                 PackageParser.PARSE_MUST_BE_APK,
                                 SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
-                                System.currentTimeMillis());
+                                System.currentTimeMillis(), null);
                         if (p != null) {
                             /*
                              * TODO this seems dangerous as the package may have
@@ -5320,13 +5378,13 @@
                 extras.putInt(Intent.EXTRA_UID, removedUid);
                 extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
-                        extras, null, null, UserHandle.USER_ALL);
+                        extras, null, null, null);
             }
             if (addedPackage != null) {
                 Bundle extras = new Bundle(1);
                 extras.putInt(Intent.EXTRA_UID, addedUid);
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
-                        extras, null, null, UserHandle.USER_ALL);
+                        extras, null, null, null);
             }
         }
 
@@ -5365,6 +5423,12 @@
                 null);
 
         final int uid = Binder.getCallingUid();
+        UserHandle user;
+        if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) {
+            user = UserHandle.ALL;
+        } else {
+            user = Process.myUserHandle();
+        }
 
         final int filteredFlags;
 
@@ -5379,10 +5443,51 @@
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
         msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
-                verificationParams, encryptionParams);
+                verificationParams, encryptionParams, user);
         mHandler.sendMessage(msg);
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public int installExistingPackage(String packageName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
+                null);
+        PackageSetting pkgSetting;
+        final int uid = Binder.getCallingUid();
+        final int userId = UserHandle.getUserId(uid);
+
+        long callingId = Binder.clearCallingIdentity();
+        try {
+            boolean sendAdded = false;
+            Bundle extras = new Bundle(1);
+
+            // writer
+            synchronized (mPackages) {
+                pkgSetting = mSettings.mPackages.get(packageName);
+                if (pkgSetting == null) {
+                    return PackageManager.INSTALL_FAILED_INVALID_URI;
+                }
+                if (!pkgSetting.getInstalled(userId)) {
+                    pkgSetting.setInstalled(true, userId);
+                    mSettings.writePackageRestrictionsLPr(userId);
+                    extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
+                    sendAdded = true;
+                }
+            }
+
+            if (sendAdded) {
+                sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+                        packageName, extras, null, null, new int[] {userId});
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+
+        return PackageManager.INSTALL_SUCCEEDED;
+    }
+    
     @Override
     public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
         final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
@@ -5840,17 +5945,19 @@
         private int mRet;
         private File mTempPackage;
         final ContainerEncryptionParams encryptionParams;
+        final UserHandle user;
 
         InstallParams(Uri packageURI,
                 IPackageInstallObserver observer, int flags,
                 String installerPackageName, VerificationParams verificationParams,
-                ContainerEncryptionParams encryptionParams) {
+                ContainerEncryptionParams encryptionParams, UserHandle user) {
             this.mPackageURI = packageURI;
             this.flags = flags;
             this.observer = observer;
             this.installerPackageName = installerPackageName;
             this.verificationParams = verificationParams;
             this.encryptionParams = encryptionParams;
+            this.user = user;
         }
 
         public ManifestDigest getManifestDigest() {
@@ -5860,6 +5967,10 @@
             return verificationParams.getManifestDigest();
         }
 
+        public UserHandle getUser() {
+            return user;
+        }
+
         private int installLocationPolicy(PackageInfoLite pkgLite, int flags) {
             String packageName = pkgLite.packageName;
             int installLocation = pkgLite.installLocation;
@@ -5869,6 +5980,16 @@
                 PackageParser.Package pkg = mPackages.get(packageName);
                 if (pkg != null) {
                     if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+                        // Check for downgrading.
+                        if ((flags & PackageManager.INSTALL_ALLOW_DOWNGRADE) == 0) {
+                            if (pkgLite.versionCode < pkg.mVersionCode) {
+                                Slog.w(TAG, "Can't install update of " + packageName
+                                        + " update version " + pkgLite.versionCode
+                                        + " is older than installed version "
+                                        + pkg.mVersionCode);
+                                return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE;
+                            }
+                        }
                         // Check for updated system application.
                         if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                             if (onSd) {
@@ -5995,6 +6116,8 @@
                     ret = PackageManager.INSTALL_FAILED_INVALID_URI;
                 } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
                     ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
+                } else if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
+                    ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
                 } else {
                     // Override with defaults if needed.
                     loc = installLocationPolicy(pkgLite, flags);
@@ -6338,14 +6461,17 @@
         final Uri packageURI;
         final String installerPackageName;
         final ManifestDigest manifestDigest;
+        final UserHandle user;
 
         InstallArgs(Uri packageURI, IPackageInstallObserver observer, int flags,
-                String installerPackageName, ManifestDigest manifestDigest) {
+                String installerPackageName, ManifestDigest manifestDigest,
+                UserHandle user) {
             this.packageURI = packageURI;
             this.flags = flags;
             this.observer = observer;
             this.installerPackageName = installerPackageName;
             this.manifestDigest = manifestDigest;
+            this.user = user;
         }
 
         abstract void createCopyFile();
@@ -6396,11 +6522,12 @@
 
         FileInstallArgs(InstallParams params) {
             super(params.getPackageUri(), params.observer, params.flags,
-                    params.installerPackageName, params.getManifestDigest());
+                    params.installerPackageName, params.getManifestDigest(),
+                    params.getUser());
         }
 
         FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
-            super(null, null, 0, null, null);
+            super(null, null, 0, null, null, null);
             File codeFile = new File(fullCodePath);
             installDir = codeFile.getParentFile();
             codeFileName = fullCodePath;
@@ -6409,7 +6536,7 @@
         }
 
         FileInstallArgs(Uri packageURI, String pkgName, String dataDir) {
-            super(packageURI, null, 0, null, null);
+            super(packageURI, null, 0, null, null, null);
             installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
             String apkName = getNextCodePath(null, pkgName, ".apk");
             codeFileName = new File(installDir, apkName + ".apk").getPath();
@@ -6683,13 +6810,15 @@
 
         AsecInstallArgs(InstallParams params) {
             super(params.getPackageUri(), params.observer, params.flags,
-                    params.installerPackageName, params.getManifestDigest());
+                    params.installerPackageName, params.getManifestDigest(),
+                    params.getUser());
         }
 
         AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
                 boolean isExternal, boolean isForwardLocked) {
             super(null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+                    null, null, null);
             // Extract cid from fullCodePath
             int eidx = fullCodePath.lastIndexOf("/");
             String subStr1 = fullCodePath.substring(0, eidx);
@@ -6700,14 +6829,16 @@
 
         AsecInstallArgs(String cid, boolean isForwardLocked) {
             super(null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+                    null, null, null);
             this.cid = cid;
             setCachePath(PackageHelper.getSdDir(cid));
         }
 
         AsecInstallArgs(Uri packageURI, String cid, boolean isExternal, boolean isForwardLocked) {
             super(packageURI, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+                    null, null, null);
             this.cid = cid;
         }
 
@@ -7031,6 +7162,7 @@
     class PackageInstalledInfo {
         String name;
         int uid;
+        int[] users;
         PackageParser.Package pkg;
         int returnCode;
         PackageRemovedInfo removedInfo;
@@ -7040,14 +7172,12 @@
      * Install a non-existing package.
      */
     private void installNewPackageLI(PackageParser.Package pkg,
-            int parseFlags,
-            int scanMode,
+            int parseFlags, int scanMode, UserHandle user,
             String installerPackageName, PackageInstalledInfo res) {
         // Remember this for later, in case we need to rollback this install
         String pkgName = pkg.packageName;
 
         boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
-        res.name = pkgName;
         synchronized(mPackages) {
             if (mSettings.mRenamedPackages.containsKey(pkgName)) {
                 // A package with the same name is already installed, though
@@ -7070,7 +7200,7 @@
         }
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
-                System.currentTimeMillis());
+                System.currentTimeMillis(), user);
         if (newPackage == null) {
             Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
             if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -7087,17 +7217,15 @@
                 // delete the package data and cache directories that it created in
                 // scanPackageLocked, unless those directories existed before we even tried to
                 // install.
-                deletePackageLI(
-                        pkgName, false,
-                        dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,
+                deletePackageLI(pkgName, UserHandle.ALL, false,
+                        dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
                                 res.removedInfo, true);
             }
         }
     }
 
     private void replacePackageLI(PackageParser.Package pkg,
-            int parseFlags,
-            int scanMode,
+            int parseFlags, int scanMode, UserHandle user,
             String installerPackageName, PackageInstalledInfo res) {
 
         PackageParser.Package oldPackage;
@@ -7114,15 +7242,16 @@
         }
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
-            replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
+            replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
+                    user, installerPackageName, res);
         } else {
-            replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
+            replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
+                    user, installerPackageName, res);
         }
     }
 
     private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
-            PackageParser.Package pkg,
-            int parseFlags, int scanMode,
+            PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
             String installerPackageName, PackageInstalledInfo res) {
         PackageParser.Package newPackage = null;
         String pkgName = deletedPackage.packageName;
@@ -7137,7 +7266,7 @@
         }
 
         // First delete the existing package while retaining the data directory
-        if (!deletePackageLI(pkgName, true, PackageManager.DONT_DELETE_DATA,
+        if (!deletePackageLI(pkgName, null, true, PackageManager.DELETE_KEEP_DATA,
                 res.removedInfo, true)) {
             // If the existing package wasn't successfully deleted
             res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
@@ -7146,7 +7275,7 @@
             // Successfully deleted the old package. Now proceed with re-installation
             mLastScanError = PackageManager.INSTALL_SUCCEEDED;
             newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME,
-                    System.currentTimeMillis());
+                    System.currentTimeMillis(), user);
             if (newPackage == null) {
                 Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
                 if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -7167,8 +7296,8 @@
             // install.
             if(updatedSettings) {
                 deletePackageLI(
-                        pkgName, true,
-                        PackageManager.DONT_DELETE_DATA,
+                        pkgName, null, true,
+                        PackageManager.DELETE_KEEP_DATA,
                                 res.removedInfo, true);
             }
             // Since we failed to install the new package we need to restore the old
@@ -7183,7 +7312,7 @@
                 int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE
                         | SCAN_UPDATE_TIME;
                 if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode,
-                        origUpdateTime) == null) {
+                        origUpdateTime, user) == null) {
                     Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade");
                     return;
                 }
@@ -7201,8 +7330,7 @@
     }
 
     private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
-            PackageParser.Package pkg,
-            int parseFlags, int scanMode,
+            PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
             String installerPackageName, PackageInstalledInfo res) {
         PackageParser.Package newPackage = null;
         boolean updatedSettings = false;
@@ -7251,7 +7379,7 @@
         // Successfully disabled the old package. Now proceed with re-installation
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
-        newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0);
+        newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user);
         if (newPackage == null) {
             Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
             if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -7274,7 +7402,7 @@
                 removePackageLI(newPackage, true);
             }
             // Add back the old system package
-            scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0);
+            scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user);
             // Restore the old system information in Settings
             synchronized(mPackages) {
                 if (updatedSettings) {
@@ -7333,6 +7461,10 @@
                     UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
                             ? UPDATE_PERMISSIONS_ALL : 0));
             res.name = pkgName;
+            PackageSetting ps = mSettings.mPackages.get(pkgName);
+            if (ps != null) {
+                res.users = ps.getInstalledUsers(sUserManager.getUserIds());
+            }
             res.uid = newPackage.applicationInfo.uid;
             res.pkg = newPackage;
             mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
@@ -7448,12 +7580,18 @@
         setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
         pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
         if (replace) {
-            replacePackageLI(pkg, parseFlags, scanMode,
+            replacePackageLI(pkg, parseFlags, scanMode, args.user,
                     installerPackageName, res);
         } else {
-            installNewPackageLI(pkg, parseFlags, scanMode,
+            installNewPackageLI(pkg, parseFlags, scanMode, args.user,
                     installerPackageName,res);
         }
+        synchronized (mPackages) {
+            PackageSetting ps = mSettings.mPackages.get(pkgName);
+            if (ps != null) {
+                res.users = ps.getInstalledUsers(sUserManager.getUserIds());
+            }
+        }
     }
 
     private static boolean isForwardLocked(PackageParser.Package pkg) {
@@ -7544,10 +7682,11 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.DELETE_PACKAGES, null);
         // Queue up an async operation since the package deletion may take a little while.
+        final int uid = Binder.getCallingUid();
         mHandler.post(new Runnable() {
             public void run() {
                 mHandler.removeCallbacks(this);
-                final int returnCode = deletePackageX(packageName, true, true, flags);
+                final int returnCode = deletePackageX(packageName, uid, flags);
                 if (observer != null) {
                     try {
                         observer.packageDeleted(packageName, returnCode);
@@ -7573,8 +7712,7 @@
      *  persisting settings for later use
      *  sending a broadcast if necessary
      */
-    private int deletePackageX(String packageName, boolean sendBroadCast,
-                                   boolean deleteCodeAndResources, int flags) {
+    private int deletePackageX(String packageName, int uid, int flags) {
         final PackageRemovedInfo info = new PackageRemovedInfo();
         final boolean res;
 
@@ -7589,27 +7727,30 @@
         }
         
         synchronized (mInstallLock) {
-            res = deletePackageLI(packageName, deleteCodeAndResources,
-                    flags | REMOVE_CHATTY, info, true);
+            res = deletePackageLI(packageName,
+                    (flags & PackageManager.DELETE_ALL_USERS) != 0
+                            ? UserHandle.ALL : new UserHandle(UserHandle.getUserId(uid)),
+                    true, flags | REMOVE_CHATTY, info, true);
         }
 
-        if (res && sendBroadCast) {
+        if (res) {
             boolean systemUpdate = info.isRemovedPackageSystemUpdate;
-            info.sendBroadcast(deleteCodeAndResources, systemUpdate);
+            info.sendBroadcast(true, systemUpdate);
 
             // If the removed package was a system update, the old system packaged
             // was re-enabled; we need to broadcast this information
             if (systemUpdate) {
                 Bundle extras = new Bundle(1);
-                extras.putInt(Intent.EXTRA_UID, info.removedUid >= 0 ? info.removedUid : info.uid);
+                extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0
+                        ? info.removedAppId : info.uid);
                 extras.putBoolean(Intent.EXTRA_REPLACING, true);
 
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                        extras, null, null, UserHandle.USER_ALL);
+                        extras, null, null, null);
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
-                        extras, null, null, UserHandle.USER_ALL);
+                        extras, null, null, null);
                 sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
-                        null, packageName, null, UserHandle.USER_ALL);
+                        null, packageName, null, null);
             }
         }
         // Force a gc here.
@@ -7618,7 +7759,7 @@
         // other processes clean up before deleting resources.
         if (info.args != null) {
             synchronized (mInstallLock) {
-                info.args.doPostDeleteLI(deleteCodeAndResources);
+                info.args.doPostDeleteLI(true);
             }
         }
 
@@ -7628,29 +7769,30 @@
     static class PackageRemovedInfo {
         String removedPackage;
         int uid = -1;
-        int removedUid = -1;
+        int removedAppId = -1;
+        int[] removedUsers = null;
         boolean isRemovedPackageSystemUpdate = false;
         // Clean up resources deleted packages.
         InstallArgs args = null;
 
         void sendBroadcast(boolean fullRemove, boolean replacing) {
             Bundle extras = new Bundle(1);
-            extras.putInt(Intent.EXTRA_UID, removedUid >= 0 ? removedUid : uid);
+            extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
             extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove);
             if (replacing) {
                 extras.putBoolean(Intent.EXTRA_REPLACING, true);
             }
             if (removedPackage != null) {
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
-                        extras, null, null, UserHandle.USER_ALL);
+                        extras, null, null, removedUsers);
                 if (fullRemove && !replacing) {
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
-                            extras, null, null, UserHandle.USER_ALL);
+                            extras, null, null, removedUsers);
                 }
             }
-            if (removedUid >= 0) {
+            if (removedAppId >= 0) {
                 sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
-                        UserHandle.getUserId(removedUid));
+                        removedUsers);
             }
         }
     }
@@ -7664,17 +7806,19 @@
     private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo,
             int flags, boolean writeSettings) {
         String packageName = p.packageName;
-        if (outInfo != null) {
-            outInfo.removedPackage = packageName;
-        }
         removePackageLI(p, (flags&REMOVE_CHATTY) != 0);
         // Retrieve object to delete permissions for shared user later on
         final PackageSetting deletedPs;
         // reader
         synchronized (mPackages) {
             deletedPs = mSettings.mPackages.get(packageName);
+            if (outInfo != null) {
+                outInfo.removedPackage = packageName;
+                outInfo.removedUsers = deletedPs != null
+                        ? deletedPs.getInstalledUsers(sUserManager.getUserIds()) : null;
+            }
         }
-        if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
+        if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
             int retCode = mInstaller.remove(packageName, 0);
             if (retCode < 0) {
                 Slog.w(TAG, "Couldn't remove app data or cache directory for package: "
@@ -7684,14 +7828,14 @@
                 // TODO: Kill the processes first
                 sUserManager.removePackageForAllUsers(packageName);
             }
-            schedulePackageCleaning(packageName);
+            schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);
         }
         // writer
         synchronized (mPackages) {
             if (deletedPs != null) {
-                if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
+                if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
                     if (outInfo != null) {
-                        outInfo.removedUid = mSettings.removePackageLPw(packageName);
+                        outInfo.removedAppId = mSettings.removePackageLPw(packageName);
                     }
                     if (deletedPs != null) {
                         updatePermissionsLPw(deletedPs.name, null, 0);
@@ -7740,10 +7884,10 @@
         outInfo.isRemovedPackageSystemUpdate = true;
         if (ps.versionCode < p.mVersionCode) {
             // Delete data for downgrades
-            flags &= ~PackageManager.DONT_DELETE_DATA;
+            flags &= ~PackageManager.DELETE_KEEP_DATA;
         } else {
             // Preserve data by setting flag
-            flags |= PackageManager.DONT_DELETE_DATA;
+            flags |= PackageManager.DELETE_KEEP_DATA;
         }
         boolean ret = deleteInstalledPackageLI(p, true, flags, outInfo,
                 writeSettings);
@@ -7760,7 +7904,7 @@
         // Install the system package
         PackageParser.Package newPkg = scanPackageLI(ps.codePath,
                 PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM,
-                SCAN_MONITOR | SCAN_NO_PATHS, 0);
+                SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
 
         if (newPkg == null) {
             Slog.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError);
@@ -7807,7 +7951,7 @@
     /*
      * This method handles package deletion in general
      */
-    private boolean deletePackageLI(String packageName,
+    private boolean deletePackageLI(String packageName, UserHandle user,
             boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo,
             boolean writeSettings) {
         if (packageName == null) {
@@ -7816,6 +7960,8 @@
         }
         PackageParser.Package p;
         boolean dataOnly = false;
+        int removeUser = -1;
+        int appId = -1;
         synchronized (mPackages) {
             p = mPackages.get(packageName);
             if (p == null) {
@@ -7823,15 +7969,53 @@
                 dataOnly = true;
                 PackageSetting ps = mSettings.mPackages.get(packageName);
                 if (ps == null) {
-                    Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
+                    Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
                     return false;
                 }
                 p = ps.pkg;
             }
+            if (p == null) {
+                Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
+                return false;
+            }
+            final PackageSetting ps = (PackageSetting)p.mExtras;
+            if (!isSystemApp(p) && ps != null && user != null
+                    && user.getIdentifier() != UserHandle.USER_ALL) {
+                // The caller is asking that the package only be deleted for a single
+                // user.  To do this, we just mark its uninstalled state and delete
+                // its data.
+                ps.setUserState(user.getIdentifier(),
+                        COMPONENT_ENABLED_STATE_DEFAULT,
+                        false, //installed
+                        true,  //stopped
+                        true,  //notLaunched
+                        null, null);
+                if (ps.isAnyInstalled(sUserManager.getUserIds())) {
+                    // Other user still have this package installed, so all
+                    // we need to do is clear this user's data and save that
+                    // it is uninstalled.
+                    removeUser = user.getIdentifier();
+                    appId = ps.appId;
+                    mSettings.writePackageRestrictionsLPr(removeUser);
+                } else {
+                    // We need to set it back to 'installed' so the uninstall
+                    // broadcasts will be sent correctly.
+                    ps.setInstalled(true, user.getIdentifier());
+                }
+            }
         }
-        if (p == null) {
-            Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
-            return false;
+
+        if (removeUser >= 0) {
+            // From above, we determined that we are deleting this only
+            // for a single user.  Continue the work here.
+            if (outInfo != null) {
+                outInfo.removedPackage = packageName;
+                outInfo.removedAppId = appId;
+                outInfo.removedUsers = new int[] {removeUser};
+            }
+            mInstaller.clearUserData(packageName, removeUser);
+            schedulePackageCleaning(packageName, removeUser, false);
+            return true;
         }
 
         if (dataOnly) {
@@ -7876,7 +8060,7 @@
         }
     }
 
-    private void clearExternalStorageDataSync(String packageName, boolean allData) {
+    private void clearExternalStorageDataSync(String packageName, int userId, boolean allData) {
         final boolean mounted;
         if (Environment.isExternalStorageEmulated()) {
             mounted = true;
@@ -7892,44 +8076,52 @@
         }
 
         final Intent containerIntent = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
-        ClearStorageConnection conn = new ClearStorageConnection();
-        if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE)) {
-            try {
-                long timeout = SystemClock.uptimeMillis() + 5000;
-                synchronized (conn) {
-                    long now = SystemClock.uptimeMillis();
-                    while (conn.mContainerService == null && now < timeout) {
-                        try {
-                            conn.wait(timeout - now);
-                        } catch (InterruptedException e) {
+        int[] users;
+        if (userId == UserHandle.USER_ALL) {
+            users = sUserManager.getUserIds();
+        } else {
+            users = new int[] { userId };
+        }
+        for (int curUser : users) {
+            ClearStorageConnection conn = new ClearStorageConnection();
+            if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE, curUser)) {
+                try {
+                    long timeout = SystemClock.uptimeMillis() + 5000;
+                    synchronized (conn) {
+                        long now = SystemClock.uptimeMillis();
+                        while (conn.mContainerService == null && now < timeout) {
+                            try {
+                                conn.wait(timeout - now);
+                            } catch (InterruptedException e) {
+                            }
                         }
                     }
-                }
-                if (conn.mContainerService == null) {
-                    return;
-                }
-                final File externalCacheDir = Environment
-                        .getExternalStorageAppCacheDirectory(packageName);
-                try {
-                    conn.mContainerService.clearDirectory(externalCacheDir.toString());
-                } catch (RemoteException e) {
-                }
-                if (allData) {
-                    final File externalDataDir = Environment
-                            .getExternalStorageAppDataDirectory(packageName);
+                    if (conn.mContainerService == null) {
+                        return;
+                    }
+                    final File externalCacheDir = Environment
+                            .getExternalStorageAppCacheDirectory(packageName);
                     try {
-                        conn.mContainerService.clearDirectory(externalDataDir.toString());
+                        conn.mContainerService.clearDirectory(externalCacheDir.toString());
                     } catch (RemoteException e) {
                     }
-                    final File externalMediaDir = Environment
-                            .getExternalStorageAppMediaDirectory(packageName);
-                    try {
-                        conn.mContainerService.clearDirectory(externalMediaDir.toString());
-                    } catch (RemoteException e) {
+                    if (allData) {
+                        final File externalDataDir = Environment
+                                .getExternalStorageAppDataDirectory(packageName);
+                        try {
+                            conn.mContainerService.clearDirectory(externalDataDir.toString());
+                        } catch (RemoteException e) {
+                        }
+                        final File externalMediaDir = Environment
+                                .getExternalStorageAppMediaDirectory(packageName);
+                        try {
+                            conn.mContainerService.clearDirectory(externalMediaDir.toString());
+                        } catch (RemoteException e) {
+                        }
                     }
+                } finally {
+                    mContext.unbindService(conn);
                 }
-            } finally {
-                mContext.unbindService(conn);
             }
         }
     }
@@ -7948,7 +8140,7 @@
                 synchronized (mInstallLock) {
                     succeeded = clearApplicationUserDataLI(packageName, userId);
                 }
-                clearExternalStorageDataSync(packageName, true);
+                clearExternalStorageDataSync(packageName, userId, true);
                 if (succeeded) {
                     // invoke DeviceStorageMonitor's update method to clear any notifications
                     DeviceStorageMonitorService dsm = (DeviceStorageMonitorService)
@@ -8022,7 +8214,7 @@
                 synchronized (mInstallLock) {
                     succeded = deleteApplicationCacheFilesLI(packageName, userId);
                 }
-                clearExternalStorageDataSync(packageName, false);
+                clearExternalStorageDataSync(packageName, userId, false);
                 if(observer != null) {
                     try {
                         observer.onRemoveCompleted(packageName, succeded);
@@ -8480,7 +8672,7 @@
         extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
         extras.putInt(Intent.EXTRA_UID, packageUid);
         sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED,  packageName, extras, null, null,
-                UserHandle.getUserId(packageUid));
+                new int[] {UserHandle.getUserId(packageUid)});
     }
 
     public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
@@ -9085,7 +9277,7 @@
             if (DEBUG_SD_INSTALL)
                 Log.i(TAG, "Loading packages");
             loadMediaPackages(processCids, uidArr, removeCids);
-            startCleaningPackages();
+            startCleaningPackages(-1);
         } else {
             if (DEBUG_SD_INSTALL)
                 Log.i(TAG, "Unloading packages");
@@ -9106,7 +9298,7 @@
             }
             String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
                     : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
-            sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserHandle.USER_ALL);
+            sendPackageBroadcast(action, null, extras, null, finishedReceiver, null);
         }
     }
 
@@ -9151,7 +9343,7 @@
                 doGc = true;
                 synchronized (mInstallLock) {
                     final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags,
-                            0, 0);
+                            0, 0, null);
                     // Scan the package
                     if (pkg != null) {
                         /*
@@ -9260,8 +9452,8 @@
             // Delete package internally
             PackageRemovedInfo outInfo = new PackageRemovedInfo();
             synchronized (mInstallLock) {
-                boolean res = deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA,
-                        outInfo, false);
+                boolean res = deletePackageLI(pkgName, null, false,
+                        PackageManager.DELETE_KEEP_DATA, outInfo, false);
                 if (res) {
                     pkgList.add(pkgName);
                 } else {
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index 56f2166..6d31f0e 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -20,11 +20,13 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 
+import android.content.pm.PackageUserState;
+import android.content.pm.UserInfo;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 
 import java.io.File;
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * Settings base class for pending and resolved classes.
@@ -62,19 +64,11 @@
     boolean permissionsFixed;
     boolean haveGids;
 
+    private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();
+
     // Whether this package is currently stopped, thus can not be
     // started until explicitly launched by the user.
-    private SparseArray<Boolean> stopped = new SparseArray<Boolean>();
-
-    // Set to true if we have never launched this app.
-    private SparseArray<Boolean> notLaunched = new SparseArray<Boolean>();
-
-    /* Explicitly disabled components */
-    private SparseArray<HashSet<String>> disabledComponents = new SparseArray<HashSet<String>>();
-    /* Explicitly enabled components */
-    private SparseArray<HashSet<String>> enabledComponents = new SparseArray<HashSet<String>>();
-    /* Enabled state */
-    private SparseIntArray enabled = new SparseIntArray();
+    private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>();
 
     int installStatus = PKG_INSTALL_COMPLETE;
 
@@ -115,12 +109,11 @@
 
         permissionsFixed = base.permissionsFixed;
         haveGids = base.haveGids;
-        notLaunched = base.notLaunched;
-
-        disabledComponents = (SparseArray<HashSet<String>>) base.disabledComponents.clone();
-        enabledComponents = (SparseArray<HashSet<String>>) base.enabledComponents.clone();
-        enabled = (SparseIntArray) base.enabled.clone();
-        stopped = (SparseArray<Boolean>) base.stopped.clone();
+        userState.clear();
+        for (int i=0; i<base.userState.size(); i++) {
+            userState.put(base.userState.keyAt(i),
+                    new PackageUserState(base.userState.valueAt(i)));
+        }
         installStatus = base.installStatus;
 
         origPackage = base.origPackage;
@@ -171,103 +164,174 @@
         signatures = base.signatures;
         permissionsFixed = base.permissionsFixed;
         haveGids = base.haveGids;
-        stopped = base.stopped;
-        notLaunched = base.notLaunched;
-        disabledComponents = base.disabledComponents;
-        enabledComponents = base.enabledComponents;
-        enabled = base.enabled;
+        userState.clear();
+        for (int i=0; i<base.userState.size(); i++) {
+            userState.put(base.userState.keyAt(i), base.userState.valueAt(i));
+        }
         installStatus = base.installStatus;
     }
 
+    private PackageUserState modifyUserState(int userId) {
+        PackageUserState state = userState.get(userId);
+        if (state == null) {
+            state = new PackageUserState();
+            userState.put(userId, state);
+        }
+        return state;
+    }
+
+    public PackageUserState readUserState(int userId) {
+        PackageUserState state = userState.get(userId);
+        return state != null ? state : DEFAULT_USER_STATE;
+    }
+
     void setEnabled(int state, int userId) {
-        enabled.put(userId, state);
+        modifyUserState(userId).enabled = state;
     }
 
     int getEnabled(int userId) {
-        return enabled.get(userId, COMPONENT_ENABLED_STATE_DEFAULT);
+        return readUserState(userId).enabled;
+    }
+
+    void setInstalled(boolean inst, int userId) {
+        modifyUserState(userId).installed = inst;
+    }
+
+    boolean getInstalled(int userId) {
+        return readUserState(userId).installed;
+    }
+
+    boolean isAnyInstalled(int[] users) {
+        for (int user: users) {
+            if (readUserState(user).installed) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    int[] getInstalledUsers(int[] users) {
+        int num = 0;
+        for (int user : users) {
+            if (getInstalled(user)) {
+                num++;
+            }
+        }
+        int[] res = new int[num];
+        num = 0;
+        for (int user : users) {
+            if (getInstalled(user)) {
+                res[num] = user;
+                num++;
+            }
+        }
+        return res;
     }
 
     boolean getStopped(int userId) {
-        return stopped.get(userId, false);
+        return readUserState(userId).stopped;
     }
 
     void setStopped(boolean stop, int userId) {
-        stopped.put(userId, stop);
+        modifyUserState(userId).stopped = stop;
     }
 
     boolean getNotLaunched(int userId) {
-        return notLaunched.get(userId, false);
+        return readUserState(userId).notLaunched;
     }
 
     void setNotLaunched(boolean stop, int userId) {
-        notLaunched.put(userId, stop);
+        modifyUserState(userId).notLaunched = stop;
+    }
+
+    void setUserState(int userId, int enabled, boolean installed, boolean stopped,
+            boolean notLaunched, HashSet<String> enabledComponents,
+            HashSet<String> disabledComponents) {
+        PackageUserState state = modifyUserState(userId);
+        state.enabled = enabled;
+        state.installed = installed;
+        state.stopped = stopped;
+        state.notLaunched = notLaunched;
+        state.enabledComponents = enabledComponents;
+        state.disabledComponents = disabledComponents;
     }
 
     HashSet<String> getEnabledComponents(int userId) {
-        return getComponentHashSet(enabledComponents, userId);
+        return readUserState(userId).enabledComponents;
     }
 
     HashSet<String> getDisabledComponents(int userId) {
-        return getComponentHashSet(disabledComponents, userId);
+        return readUserState(userId).disabledComponents;
     }
 
     void setEnabledComponents(HashSet<String> components, int userId) {
-        enabledComponents.put(userId, components);
+        modifyUserState(userId).enabledComponents = components;
     }
 
     void setDisabledComponents(HashSet<String> components, int userId) {
-        disabledComponents.put(userId, components);
+        modifyUserState(userId).disabledComponents = components;
     }
 
-    private HashSet<String> getComponentHashSet(SparseArray<HashSet<String>> setArray, int userId) {
-        HashSet<String> set = setArray.get(userId);
-        if (set == null) {
-            set = new HashSet<String>(1);
-            setArray.put(userId, set);
+    void setEnabledComponentsCopy(HashSet<String> components, int userId) {
+        modifyUserState(userId).enabledComponents = components != null
+                ? new HashSet<String>(components) : null;
+    }
+
+    void setDisabledComponentsCopy(HashSet<String> components, int userId) {
+        modifyUserState(userId).disabledComponents = components != null
+                ? new HashSet<String>(components) : null;
+    }
+
+    PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) {
+        PackageUserState state = modifyUserState(userId);
+        if (disabled && state.disabledComponents == null) {
+            state.disabledComponents = new HashSet<String>(1);
         }
-        return set;
+        if (enabled && state.enabledComponents == null) {
+            state.enabledComponents = new HashSet<String>(1);
+        }
+        return state;
     }
 
     void addDisabledComponent(String componentClassName, int userId) {
-        HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
-        disabled.add(componentClassName);
+        modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
     }
 
     void addEnabledComponent(String componentClassName, int userId) {
-        HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
-        enabled.add(componentClassName);
+        modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName);
     }
 
     boolean enableComponentLPw(String componentClassName, int userId) {
-        HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
-        HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
-        boolean changed = disabled.remove(componentClassName);
-        changed |= enabled.add(componentClassName);
+        PackageUserState state = modifyUserStateComponents(userId, false, true);
+        boolean changed = state.disabledComponents != null
+                ? state.disabledComponents.remove(componentClassName) : false;
+        changed |= state.enabledComponents.add(componentClassName);
         return changed;
     }
 
     boolean disableComponentLPw(String componentClassName, int userId) {
-        HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
-        HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
-        boolean changed = enabled.remove(componentClassName);
-        changed |= disabled.add(componentClassName);
+        PackageUserState state = modifyUserStateComponents(userId, true, false);
+        boolean changed = state.enabledComponents != null
+                ? state.enabledComponents.remove(componentClassName) : false;
+        changed |= state.disabledComponents.add(componentClassName);
         return changed;
     }
 
     boolean restoreComponentLPw(String componentClassName, int userId) {
-        HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
-        HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
-        boolean changed = enabled.remove(componentClassName);
-        changed |= disabled.remove(componentClassName);
+        PackageUserState state = modifyUserStateComponents(userId, true, true);
+        boolean changed = state.disabledComponents != null
+                ? state.disabledComponents.remove(componentClassName) : false;
+        changed |= state.enabledComponents != null
+                ? state.enabledComponents.remove(componentClassName) : false;
         return changed;
     }
 
     int getCurrentEnabledStateLPr(String componentName, int userId) {
-        HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
-        HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
-        if (enabled.contains(componentName)) {
+        PackageUserState state = readUserState(userId);
+        if (state.enabledComponents != null && state.enabledComponents.contains(componentName)) {
             return COMPONENT_ENABLED_STATE_ENABLED;
-        } else if (disabled.contains(componentName)) {
+        } else if (state.disabledComponents != null
+                && state.disabledComponents.contains(componentName)) {
             return COMPONENT_ENABLED_STATE_DISABLED;
         } else {
             return COMPONENT_ENABLED_STATE_DEFAULT;
@@ -275,11 +339,6 @@
     }
 
     void removeUser(int userId) {
-        enabled.delete(userId);
-        stopped.delete(userId);
-        enabledComponents.delete(userId);
-        disabledComponents.delete(userId);
-        notLaunched.delete(userId);
+        userState.delete(userId);
     }
-
 }
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index cfc0f5c..a341304 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -38,11 +38,13 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ComponentInfo;
+import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PermissionInfo;
 import android.content.pm.Signature;
 import android.content.pm.UserInfo;
+import android.content.pm.PackageUserState;
 import android.content.pm.VerifierDeviceIdentity;
 import android.os.Binder;
 import android.os.Environment;
@@ -92,9 +94,12 @@
     private static final String TAG_PACKAGE = "pkg";
 
     private static final String ATTR_NAME = "name";
+    private static final String ATTR_USER = "user";
+    private static final String ATTR_CODE = "code";
     private static final String ATTR_NOT_LAUNCHED = "nl";
     private static final String ATTR_ENABLED = "enabled";
     private static final String ATTR_STOPPED = "stopped";
+    private static final String ATTR_INSTALLED = "inst";
 
     private final File mSettingsFilename;
     private final File mBackupSettingsFilename;
@@ -156,7 +161,8 @@
 
     // Packages that have been uninstalled and still need their external
     // storage data deleted.
-    final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>();
+    final SparseArray<ArrayList<PackageCleanItem>> mPackagesToBeCleaned
+            = new SparseArray<ArrayList<PackageCleanItem>>();
     
     // Packages that have been renamed since they were first installed.
     // Keys are the new names of the packages, values are the original
@@ -200,10 +206,11 @@
 
     PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
             String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
-            String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) {
+            String nativeLibraryPathString, int pkgFlags, UserHandle user, boolean add) {
         final String name = pkg.packageName;
         PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
-                resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add);
+                resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags,
+                user, add);
         return p;
     }
 
@@ -364,7 +371,8 @@
 
     private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
             String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
-            String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) {
+            String nativeLibraryPathString, int vc, int pkgFlags,
+            UserHandle installUser, boolean add) {
         PackageSetting p = mPackages.get(name);
         if (p != null) {
             if (!p.codePath.equals(codePath)) {
@@ -407,11 +415,6 @@
             }
         }
         if (p == null) {
-            // Create a new PackageSettings entry. this can end up here because
-            // of code path mismatch or user id mismatch of an updated system partition
-            if (!create) {
-                return null;
-            }
             if (origPackage != null) {
                 // We are consuming the data from an existing package.
                 p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
@@ -445,8 +448,20 @@
                     List<UserInfo> users = getAllUsers();
                     if (users != null) {
                         for (UserInfo user : users) {
-                            p.setStopped(true, user.id);
-                            p.setNotLaunched(true, user.id);
+                            // By default we consider this app to be installed
+                            // for the user if no user has been specified (which
+                            // means to leave it at its original value, and the
+                            // original default value is true), or we are being
+                            // asked to install for all users, or this is the
+                            // user we are installing for.
+                            final boolean installed = installUser == null
+                                    || installUser.getIdentifier() == UserHandle.USER_ALL
+                                    || installUser.getIdentifier() == user.id;
+                            p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT,
+                                    installed,
+                                    true, // stopped,
+                                    true, // notLaunched
+                                    null, null);
                             writePackageRestrictionsLPr(user.id);
                         }
                     }
@@ -472,12 +487,10 @@
                         if (users != null) {
                             for (UserInfo user : users) {
                                 int userId = user.id;
-                                p.setDisabledComponents(
-                                        new HashSet<String>(dis.getDisabledComponents(userId)),
-                                        userId);
-                                p.setEnabledComponents(
-                                        new HashSet<String>(dis.getEnabledComponents(userId)),
-                                        userId);
+                                p.setDisabledComponentsCopy(
+                                        dis.getDisabledComponents(userId), userId);
+                                p.setEnabledComponentsCopy(
+                                        dis.getEnabledComponents(userId), userId);
                             }
                         }
                         // Add new setting to list of user ids
@@ -498,6 +511,25 @@
                 // user preferences
                 addPackageSettingLPw(p, name, sharedUser);
             }
+        } else {
+            if (installUser != null) {
+                // The caller has explicitly specified the user they want this
+                // package installed for, and the package already exists.
+                // Make sure it conforms to the new request.
+                List<UserInfo> users = getAllUsers();
+                if (users != null) {
+                    for (UserInfo user : users) {
+                        if (installUser.getIdentifier() == UserHandle.USER_ALL
+                                || installUser.getIdentifier() == user.id) {
+                            boolean installed = p.getInstalled(user.id);
+                            if (!installed) {
+                                p.setInstalled(true, user.id);
+                                writePackageRestrictionsLPr(user.id);
+                            }
+                        }
+                    }
+                }
+            }
         }
         return p;
     }
@@ -778,10 +810,14 @@
                             + "assuming all started");
                     // At first boot, make sure no packages are stopped.
                     // We usually want to have third party apps initialize
-                    // in the stopped state, but not at first boot.
+                    // in the stopped state, but not at first boot.  Also
+                    // consider all applications to be installed.
                     for (PackageSetting pkg : mPackages.values()) {
-                        pkg.setStopped(false, userId);
-                        pkg.setNotLaunched(false, userId);
+                        pkg.setUserState(userId, COMPONENT_ENABLED_STATE_DEFAULT,
+                                true,   // installed
+                                false,  // stopped
+                                false,  // notLaunched
+                                null, null);
                     }
                     return;
                 }
@@ -823,17 +859,21 @@
                         XmlUtils.skipCurrentTag(parser);
                         continue;
                     }
-                    String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
-                    int enabled = enabledStr == null ? COMPONENT_ENABLED_STATE_DEFAULT
-                            : Integer.parseInt(enabledStr);
-                    ps.setEnabled(enabled, userId);
-                    String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
-                    boolean stopped = stoppedStr == null ? false : Boolean.parseBoolean(stoppedStr);
-                    ps.setStopped(stopped, userId);
-                    String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
-                    boolean notLaunched = stoppedStr == null ? false
-                            : Boolean.parseBoolean(notLaunchedStr);
-                    ps.setNotLaunched(notLaunched, userId);
+                    final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
+                    final int enabled = enabledStr == null
+                            ? COMPONENT_ENABLED_STATE_DEFAULT : Integer.parseInt(enabledStr);
+                    final String installedStr = parser.getAttributeValue(null, ATTR_INSTALLED);
+                    final boolean installed = installedStr == null
+                            ? true : Boolean.parseBoolean(installedStr);
+                    final String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
+                    final boolean stopped = stoppedStr == null
+                            ? false : Boolean.parseBoolean(stoppedStr);
+                    final String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
+                    final boolean notLaunched = stoppedStr == null
+                            ? false : Boolean.parseBoolean(notLaunchedStr);
+
+                    HashSet<String> enabledComponents = null;
+                    HashSet<String> disabledComponents = null;
 
                     int packageDepth = parser.getDepth();
                     while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -845,13 +885,14 @@
                         }
                         tagName = parser.getName();
                         if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
-                            HashSet<String> components = readComponentsLPr(parser);
-                            ps.setEnabledComponents(components, userId);
+                            enabledComponents = readComponentsLPr(parser);
                         } else if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
-                            HashSet<String> components = readComponentsLPr(parser);
-                            ps.setDisabledComponents(components, userId);
+                            disabledComponents = readComponentsLPr(parser);
                         }
                     }
+
+                    ps.setUserState(userId, enabled, installed, stopped, notLaunched,
+                            enabledComponents, disabledComponents);
                 } else {
                     Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
                           + parser.getName());
@@ -876,7 +917,7 @@
 
     private HashSet<String> readComponentsLPr(XmlPullParser parser)
             throws IOException, XmlPullParserException {
-        HashSet<String> components = new HashSet<String>();
+        HashSet<String> components = null;
         int type;
         int outerDepth = parser.getDepth();
         String tagName;
@@ -891,6 +932,9 @@
             if (tagName.equals(TAG_ITEM)) {
                 String componentName = parser.getAttributeValue(null, ATTR_NAME);
                 if (componentName != null) {
+                    if (components == null) {
+                        components = new HashSet<String>();
+                    }
                     components.add(componentName);
                 }
             }
@@ -936,41 +980,44 @@
             serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS);
 
             for (final PackageSetting pkg : mPackages.values()) {
-                if (pkg.getStopped(userId)
-                        || pkg.getNotLaunched(userId)
-                        || pkg.getEnabled(userId) != COMPONENT_ENABLED_STATE_DEFAULT
-                        || pkg.getEnabledComponents(userId).size() > 0
-                        || pkg.getDisabledComponents(userId).size() > 0) {
+                PackageUserState ustate = pkg.readUserState(userId);
+                if (ustate.stopped || ustate.notLaunched || !ustate.installed
+                        || ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT
+                        || (ustate.enabledComponents != null
+                                && ustate.enabledComponents.size() > 0)
+                        || (ustate.disabledComponents != null
+                                && ustate.disabledComponents.size() > 0)) {
                     serializer.startTag(null, TAG_PACKAGE);
                     serializer.attribute(null, ATTR_NAME, pkg.name);
-                    boolean stopped = pkg.getStopped(userId);
-                    boolean notLaunched = pkg.getNotLaunched(userId);
-                    int enabled = pkg.getEnabled(userId);
-                    if (DEBUG_MU) Log.i(TAG, "  pkg=" + pkg.name + ", state=" + enabled);
-                    HashSet<String> enabledComponents = pkg.getEnabledComponents(userId);
-                    HashSet<String> disabledComponents = pkg.getDisabledComponents(userId);
+                    if (DEBUG_MU) Log.i(TAG, "  pkg=" + pkg.name + ", state=" + ustate.enabled);
 
-                    if (stopped) {
+                    if (!ustate.installed) {
+                        serializer.attribute(null, ATTR_INSTALLED, "false");
+                    }
+                    if (ustate.stopped) {
                         serializer.attribute(null, ATTR_STOPPED, "true");
                     }
-                    if (notLaunched) {
+                    if (ustate.notLaunched) {
                         serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");
                     }
-                    if (enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
-                        serializer.attribute(null, ATTR_ENABLED, Integer.toString(enabled));
+                    if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
+                        serializer.attribute(null, ATTR_ENABLED,
+                                Integer.toString(ustate.enabled));
                     }
-                    if (enabledComponents.size() > 0) {
+                    if (ustate.enabledComponents != null
+                            && ustate.enabledComponents.size() > 0) {
                         serializer.startTag(null, TAG_ENABLED_COMPONENTS);
-                        for (final String name : enabledComponents) {
+                        for (final String name : ustate.enabledComponents) {
                             serializer.startTag(null, TAG_ITEM);
                             serializer.attribute(null, ATTR_NAME, name);
                             serializer.endTag(null, TAG_ITEM);
                         }
                         serializer.endTag(null, TAG_ENABLED_COMPONENTS);
                     }
-                    if (disabledComponents.size() > 0) {
+                    if (ustate.disabledComponents != null
+                            && ustate.disabledComponents.size() > 0) {
                         serializer.startTag(null, TAG_DISABLED_COMPONENTS);
-                        for (final String name : disabledComponents) {
+                        for (final String name : ustate.disabledComponents) {
                             serializer.startTag(null, TAG_ITEM);
                             serializer.attribute(null, ATTR_NAME, name);
                             serializer.endTag(null, TAG_ITEM);
@@ -1210,9 +1257,17 @@
 
             if (mPackagesToBeCleaned.size() > 0) {
                 for (int i=0; i<mPackagesToBeCleaned.size(); i++) {
-                    serializer.startTag(null, "cleaning-package");
-                    serializer.attribute(null, ATTR_NAME, mPackagesToBeCleaned.get(i));
-                    serializer.endTag(null, "cleaning-package");
+                    final int userId = mPackagesToBeCleaned.keyAt(i);
+                    final String userStr = Integer.toString(userId);
+                    final ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.valueAt(i);
+                    for (int j=0; j<pkgs.size(); j++) {
+                        serializer.startTag(null, "cleaning-package");
+                        PackageCleanItem item = pkgs.get(i);
+                        serializer.attribute(null, ATTR_NAME, item.packageName);
+                        serializer.attribute(null, ATTR_CODE, item.andCode ? "true" : "false");
+                        serializer.attribute(null, ATTR_USER, userStr);
+                        serializer.endTag(null, "cleaning-package");
+                    }
                 }
             }
             
@@ -1468,6 +1523,17 @@
         return ret;
     }
 
+    void addPackageToCleanLPw(int userId, PackageCleanItem pkg) {
+        ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.get(userId);
+        if (pkgs == null) {
+            pkgs = new ArrayList<PackageCleanItem>();
+            mPackagesToBeCleaned.put(userId, pkgs);
+        }
+        if (!pkgs.contains(pkg)) {
+            pkgs.add(pkg);
+        }
+    }
+
     boolean readLPw(List<UserInfo> users) {
         FileInputStream str = null;
         if (mBackupSettingsFilename.exists()) {
@@ -1545,8 +1611,21 @@
                     readDisabledSysPackageLPw(parser);
                 } else if (tagName.equals("cleaning-package")) {
                     String name = parser.getAttributeValue(null, ATTR_NAME);
+                    String userStr = parser.getAttributeValue(null, ATTR_USER);
+                    String codeStr = parser.getAttributeValue(null, ATTR_CODE);
                     if (name != null) {
-                        mPackagesToBeCleaned.add(name);
+                        int user = 0;
+                        boolean andCode = true;
+                        try {
+                            if (userStr != null) {
+                                user = Integer.parseInt(userStr);
+                            }
+                        } catch (NumberFormatException e) {
+                        }
+                        if (codeStr != null) {
+                            andCode = Boolean.parseBoolean(codeStr);
+                        }
+                        addPackageToCleanLPw(user, new PackageCleanItem(name, andCode));
                     }
                 } else if (tagName.equals("renamed-package")) {
                     String nname = parser.getAttributeValue(null, "new");
@@ -1623,7 +1702,8 @@
             if (idObj != null && idObj instanceof SharedUserSetting) {
                 PackageSetting p = getPackageLPw(pp.name, null, pp.realName,
                         (SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
-                        pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true);
+                        pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags,
+                        UserHandle.ALL, true);
                 if (p == null) {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unable to create application package for " + pp.name);
@@ -2292,6 +2372,10 @@
         return ps;
     }
 
+    private String compToString(HashSet<String> cmp) {
+        return cmp != null ? Arrays.toString(cmp.toArray()) : "[]";
+    }
+ 
     boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) {
         if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
             return true;
@@ -2302,24 +2386,26 @@
             Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = "
                     + componentInfo.packageName + " componentName = " + componentInfo.name);
             Log.v(PackageManagerService.TAG, "enabledComponents: "
-                    + Arrays.toString(packageSettings.getEnabledComponents(userId).toArray()));
+                    + compToString(packageSettings.getEnabledComponents(userId)));
             Log.v(PackageManagerService.TAG, "disabledComponents: "
-                    + Arrays.toString(packageSettings.getDisabledComponents(userId).toArray()));
+                    + compToString(packageSettings.getDisabledComponents(userId)));
         }
         if (packageSettings == null) {
             return false;
         }
-        final int enabled = packageSettings.getEnabled(userId);
-        if (enabled == COMPONENT_ENABLED_STATE_DISABLED
-                || enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
+        PackageUserState ustate = packageSettings.readUserState(userId);
+        if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED
+                || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
                 || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
-                    && enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
+                    && ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
             return false;
         }
-        if (packageSettings.getEnabledComponents(userId).contains(componentInfo.name)) {
+        if (ustate.enabledComponents != null
+                && ustate.enabledComponents.contains(componentInfo.name)) {
             return true;
         }
-        if (packageSettings.getDisabledComponents(userId).contains(componentInfo.name)) {
+        if (ustate.disabledComponents != null
+                && ustate.disabledComponents.contains(componentInfo.name)) {
             return false;
         }
         return componentInfo.enabled;
@@ -2378,7 +2464,7 @@
                 if (pkgSetting.installerPackageName != null) {
                     PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
                             pkgSetting.name, null,
-                            pkgSetting.installerPackageName, null, userId);
+                            pkgSetting.installerPackageName, null, new int[] {userId});
                 }
                 pkgSetting.setNotLaunched(false, userId);
             }
@@ -2427,7 +2513,6 @@
         ApplicationInfo.FLAG_RESTORE_ANY_VERSION, "RESTORE_ANY_VERSION",
         ApplicationInfo.FLAG_EXTERNAL_STORAGE, "EXTERNAL_STORAGE",
         ApplicationInfo.FLAG_LARGE_HEAP, "LARGE_HEAP",
-        ApplicationInfo.FLAG_STOPPED, "STOPPED",
         ApplicationInfo.FLAG_FORWARD_LOCK, "FORWARD_LOCK",
         ApplicationInfo.FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
     };
@@ -2537,22 +2622,28 @@
             pw.print("    permissionsFixed="); pw.print(ps.permissionsFixed);
             pw.print(" haveGids="); pw.println(ps.haveGids);
             pw.print("    pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
-            pw.print(" installStatus="); pw.print(ps.installStatus);
+            pw.print(" installStatus="); pw.println(ps.installStatus);
             for (UserInfo user : users) {
-                pw.print(" User "); pw.print(user.id); pw.print(": ");
+                pw.print("    User "); pw.print(user.id); pw.print(": ");
+                pw.print(" installed=");
+                pw.print(ps.getInstalled(user.id));
                 pw.print(" stopped=");
                 pw.print(ps.getStopped(user.id));
+                pw.print(" notLaunched=");
+                pw.print(ps.getNotLaunched(user.id));
                 pw.print(" enabled=");
                 pw.println(ps.getEnabled(user.id));
-                if (ps.getDisabledComponents(user.id).size() > 0) {
-                    pw.println("    disabledComponents:");
-                    for (String s : ps.getDisabledComponents(user.id)) {
+                HashSet<String> cmp = ps.getDisabledComponents(user.id);
+                if (cmp != null && cmp.size() > 0) {
+                    pw.println("      disabledComponents:");
+                    for (String s : cmp) {
                         pw.print("      "); pw.println(s);
                     }
                 }
-                if (ps.getEnabledComponents(user.id).size() > 0) {
-                    pw.println("    enabledComponents:");
-                    for (String s : ps.getEnabledComponents(user.id)) {
+                cmp = ps.getEnabledComponents(user.id);
+                if (cmp != null && cmp.size() > 0) {
+                    pw.println("      enabledComponents:");
+                    for (String s : cmp) {
                         pw.print("      "); pw.println(s);
                     }
                 }
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 6a76c5d..8899ea2 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -262,7 +262,9 @@
      * @return the array of user ids.
      */
     int[] getUserIds() {
-        return mUserIds;
+        synchronized (mUsers) {
+            return mUserIds;
+        }
     }
 
     private void readUserList() {
@@ -611,12 +613,11 @@
      * Caches the list of user ids in an array, adjusting the array size when necessary.
      */
     private void updateUserIdsLocked() {
-        if (mUserIds == null || mUserIds.length != mUsers.size()) {
-            mUserIds = new int[mUsers.size()];
-        }
+        int[] newUsers = new int[mUsers.size()];
         for (int i = 0; i < mUsers.size(); i++) {
-            mUserIds[i] = mUsers.keyAt(i);
+            newUsers[i] = mUsers.keyAt(i);
         }
+        mUserIds = newUsers;
     }
 
     /**