Merge "We should not throw for non-fatal errors."
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index fddb429..3d36ebf 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -31,6 +31,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -135,6 +136,8 @@
             runToUri(false);
         } else if (op.equals("to-intent-uri")) {
             runToUri(true);
+        } else if (op.equals("switch-profile")) {
+            runSwitchUser();
         } else {
             throw new IllegalArgumentException("Unknown command: " + op);
         }
@@ -531,7 +534,8 @@
         Intent intent = makeIntent();
         IntentReceiver receiver = new IntentReceiver();
         System.out.println("Broadcasting: " + intent);
-        mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false);
+        mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false,
+                Binder.getOrigCallingUser());
         receiver.waitForFinish();
     }
 
@@ -722,6 +726,14 @@
         mAm.setDebugApp(null, false, true);
     }
 
+    private void runSwitchUser() throws Exception {
+        if (android.os.Process.myUid() != 0) {
+            throw new RuntimeException("switchuser can only be run as root");
+        }
+        String user = nextArgRequired();
+        mAm.switchUser(Integer.parseInt(user));
+    }
+
     class MyActivityController extends IActivityController.Stub {
         final String mGdbPort;
 
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index dd92bbe..203d180 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -148,6 +148,48 @@
     return delete_dir_contents(pkgdir, 1, NULL);
 }
 
+int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy)
+{
+    char src_data_dir[PKG_PATH_MAX];
+    char pkg_path[PKG_PATH_MAX];
+    DIR *d;
+    struct dirent *de;
+    struct stat s;
+    uid_t uid;
+
+    if (create_persona_path(src_data_dir, src_persona)) {
+        return -1;
+    }
+
+    d = opendir(src_data_dir);
+    if (d != NULL) {
+        while ((de = readdir(d))) {
+            const char *name = de->d_name;
+
+            if (de->d_type == DT_DIR) {
+                int subfd;
+                    /* always skip "." and ".." */
+                if (name[0] == '.') {
+                    if (name[1] == 0) continue;
+                    if ((name[1] == '.') && (name[2] == 0)) continue;
+                }
+                /* Create the full path to the package's data dir */
+                create_pkg_path(pkg_path, name, PKG_DIR_POSTFIX, src_persona);
+                /* Get the file stat */
+                if (stat(pkg_path, &s) < 0) continue;
+                /* Get the uid of the package */
+                ALOGI("Adding datadir for uid = %d\n", s.st_uid);
+                uid = (uid_t) s.st_uid % PER_USER_RANGE;
+                /* Create the directory for the target */
+                make_user_data(name, uid + target_persona * PER_USER_RANGE,
+                               target_persona);
+            }
+        }
+        closedir(d);
+    }
+    return 0;
+}
+
 int delete_cache(const char *pkgname)
 {
     char cachedir[PKG_PATH_MAX];
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 569b491..7f94a96 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -107,6 +107,11 @@
     return delete_persona(atoi(arg[0])); /* userid */
 }
 
+static int do_clone_user_data(char **arg, char reply[REPLY_MAX])
+{
+    return clone_persona_data(atoi(arg[0]), atoi(arg[1]), atoi(arg[2]));
+}
+
 static int do_movefiles(char **arg, char reply[REPLY_MAX])
 {
     return movefiles();
@@ -146,6 +151,7 @@
     { "unlinklib",            1, do_unlinklib },
     { "mkuserdata",           3, do_mk_user_data },
     { "rmuser",               1, do_rm_user },
+    { "cloneuserdata",        3, do_clone_user_data },
 };
 
 static int readx(int s, void *_buf, int count)
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 173cabf..78342bb 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -72,6 +72,9 @@
 #define PKG_NAME_MAX  128   /* largest allowed package name */
 #define PKG_PATH_MAX  256   /* max size of any path we use */
 
+#define PER_USER_RANGE ((uid_t)100000)   /* range of uids per user
+                                            uid = persona * PER_USER_RANGE + appid */
+
 /* data structures */
 
 typedef struct {
@@ -143,6 +146,7 @@
 int delete_user_data(const char *pkgname, uid_t persona);
 int make_user_data(const char *pkgname, uid_t uid, uid_t persona);
 int delete_persona(uid_t persona);
+int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy);
 int delete_cache(const char *pkgname);
 int move_dex(const char *src, const char *dst);
 int rm_dex(const char *path);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index c0ba543..f457842 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -16,8 +16,6 @@
 
 package com.android.commands.pm;
 
-import com.android.internal.content.PackageHelper;
-
 import android.app.ActivityManagerNative;
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
@@ -33,14 +31,17 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
+import android.content.pm.UserInfo;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.net.Uri;
-import android.os.Parcel;
+import android.os.Binder;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
+import com.android.internal.content.PackageHelper;
+
 import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -135,13 +136,18 @@
             return;
         }
 
-        if ("createUser".equals(op)) {
-            runCreateUser();
+        if ("create-profile".equals(op)) {
+            runCreateProfile();
             return;
         }
 
-        if ("removeUser".equals(op)) {
-            runRemoveUser();
+        if ("remove-profile".equals(op)) {
+            runRemoveProfile();
+            return;
+        }
+
+        if ("list-profiles".equals(op)) {
+            runListProfiles();
             return;
         }
 
@@ -829,10 +835,10 @@
         }
     }
 
-    public void runCreateUser() {
+    public void runCreateProfile() {
         // Need to be run as root
         if (Process.myUid() != ROOT_UID) {
-            System.err.println("Error: createUser must be run as root");
+            System.err.println("Error: create-profile must be run as root");
             return;
         }
         String name;
@@ -845,7 +851,7 @@
         name = arg;
         try {
             if (mPm.createUser(name, 0) == null) {
-                System.err.println("Error: couldn't create user.");
+                System.err.println("Error: couldn't create profile.");
                 showUsage();
             }
         } catch (RemoteException e) {
@@ -855,10 +861,10 @@
 
     }
 
-    public void runRemoveUser() {
+    public void runRemoveProfile() {
         // Need to be run as root
         if (Process.myUid() != ROOT_UID) {
-            System.err.println("Error: removeUser must be run as root");
+            System.err.println("Error: remove-profile must be run as root");
             return;
         }
         int userId;
@@ -877,7 +883,7 @@
         }
         try {
             if (!mPm.removeUser(userId)) {
-                System.err.println("Error: couldn't remove user.");
+                System.err.println("Error: couldn't remove profile.");
                 showUsage();
             }
         } catch (RemoteException e) {
@@ -886,6 +892,27 @@
         }
     }
 
+    public void runListProfiles() {
+        // Need to be run as root
+        if (Process.myUid() != ROOT_UID) {
+            System.err.println("Error: list-profiles must be run as root");
+            return;
+        }
+        try {
+            List<UserInfo> users = mPm.getUsers();
+            if (users == null) {
+                System.err.println("Error: couldn't get users");
+            } else {
+                System.out.println("Users:");
+                for (int i = 0; i < users.size(); i++) {
+                    System.out.println("\t" + users.get(i).toString());
+                }
+            }
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(PM_NOT_RUNNING_ERR);
+        }
+    }
     class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
         boolean finished;
         boolean result;
@@ -966,7 +993,8 @@
 
         ClearDataObserver obs = new ClearDataObserver();
         try {
-            if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs)) {
+            if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs,
+                    Binder.getOrigCallingUser())) {
                 System.err.println("Failed");
             }
 
@@ -1132,8 +1160,8 @@
         System.err.println("       pm disable-user PACKAGE_OR_COMPONENT");
         System.err.println("       pm set-install-location [0/auto] [1/internal] [2/external]");
         System.err.println("       pm get-install-location");
-        System.err.println("       pm createUser USER_NAME");
-        System.err.println("       pm removeUser USER_ID");
+        System.err.println("       pm create-profile USER_NAME");
+        System.err.println("       pm remove-profile USER_ID");
         System.err.println("");
         System.err.println("pm list packages: prints all packages, optionally only");
         System.err.println("  those whose package name contains the text in FILTER.  Options:");
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 9661b9e..d98d87b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -30,6 +30,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Point;
+import android.os.Binder;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.Parcel;
@@ -975,7 +976,7 @@
     public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
         try {
             return ActivityManagerNative.getDefault().clearApplicationUserData(packageName, 
-                    observer);
+                    observer, Binder.getOrigCallingUser());
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7994d7c..d80902d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -91,7 +91,7 @@
         try {
             getDefault().broadcastIntent(
                 null, intent, null, null, Activity.RESULT_OK, null, null,
-                null /*permission*/, false, true);
+                null /*permission*/, false, true, Binder.getOrigCallingUser());
         } catch (RemoteException ex) {
         }
     }
@@ -306,9 +306,10 @@
             String perm = data.readString();
             boolean serialized = data.readInt() != 0;
             boolean sticky = data.readInt() != 0;
+            int userId = data.readInt();
             int res = broadcastIntent(app, intent, resolvedType, resultTo,
                     resultCode, resultData, resultExtras, perm,
-                    serialized, sticky);
+                    serialized, sticky, userId);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -320,7 +321,8 @@
             IBinder b = data.readStrongBinder();
             IApplicationThread app = b != null ? ApplicationThreadNative.asInterface(b) : null;
             Intent intent = Intent.CREATOR.createFromParcel(data);
-            unbroadcastIntent(app, intent);
+            int userId = data.readInt();
+            unbroadcastIntent(app, intent, userId);
             reply.writeNoException();
             return true;
         }
@@ -900,7 +902,8 @@
             String packageName = data.readString();
             IPackageDataObserver observer = IPackageDataObserver.Stub.asInterface(
                     data.readStrongBinder());
-            boolean res = clearApplicationUserData(packageName, observer);
+            int userId = data.readInt();
+            boolean res = clearApplicationUserData(packageName, observer, userId);
             reply.writeNoException();
             reply.writeInt(res ? 1 : 0);
             return true;
@@ -1819,7 +1822,7 @@
             Intent intent, String resolvedType,  IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle map,
             String requiredPermission, boolean serialized,
-            boolean sticky) throws RemoteException
+            boolean sticky, int userId) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -1834,6 +1837,7 @@
         data.writeString(requiredPermission);
         data.writeInt(serialized ? 1 : 0);
         data.writeInt(sticky ? 1 : 0);
+        data.writeInt(userId);
         mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);
         reply.readException();
         int res = reply.readInt();
@@ -1841,13 +1845,15 @@
         data.recycle();
         return res;
     }
-    public void unbroadcastIntent(IApplicationThread caller, Intent intent) throws RemoteException
+    public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId)
+            throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(caller != null ? caller.asBinder() : null);
         intent.writeToParcel(data, 0);
+        data.writeInt(userId);
         mRemote.transact(UNBROADCAST_INTENT_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -2651,12 +2657,13 @@
         return res;
     }
     public boolean clearApplicationUserData(final String packageName,
-            final IPackageDataObserver observer) throws RemoteException {        
+            final IPackageDataObserver observer, final int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeString(packageName);
         data.writeStrongBinder(observer.asBinder());
+        data.writeInt(userId);
         mRemote.transact(CLEAR_APP_DATA_TRANSACTION, data, reply, 0);
         reply.readException();
         boolean res = reply.readInt() != 0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3c5f53a..e4cfc99 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -47,6 +47,7 @@
 import android.net.ProxyProperties;
 import android.opengl.GLUtils;
 import android.os.AsyncTask;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -60,6 +61,7 @@
 import android.os.ServiceManager;
 import android.os.StrictMode;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -132,6 +134,7 @@
     private static final boolean DEBUG_RESULTS = false;
     private static final boolean DEBUG_BACKUP = true;
     private static final boolean DEBUG_CONFIGURATION = false;
+    private static final boolean DEBUG_SERVICE = true;
     private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
     private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
     private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
@@ -635,6 +638,9 @@
             s.intent = intent;
             s.rebind = rebind;
 
+            if (DEBUG_SERVICE)
+                Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
+                        + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
             queueOrSendMessage(H.BIND_SERVICE, s);
         }
 
@@ -1592,7 +1598,8 @@
         boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
         boolean securityViolation = includeCode && ai.uid != 0
                 && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
-                        ? ai.uid != mBoundApplication.appInfo.uid : true);
+                        ? !UserId.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
+                        : true);
         if ((flags&(Context.CONTEXT_INCLUDE_CODE
                 |Context.CONTEXT_IGNORE_SECURITY))
                 == Context.CONTEXT_INCLUDE_CODE) {
@@ -2294,6 +2301,8 @@
 
     private void handleBindService(BindServiceData data) {
         Service s = mServices.get(data.token);
+        if (DEBUG_SERVICE)
+            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
         if (s != null) {
             try {
                 data.intent.setExtrasClassLoader(s.getClassLoader());
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 180a442..fee2beb 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1177,13 +1177,14 @@
      */
     @Override
     public List<UserInfo> getUsers() {
-        // TODO:
-        // Dummy code, always returns just the primary user
-        ArrayList<UserInfo> users = new ArrayList<UserInfo>();
-        UserInfo primary = new UserInfo(0, "Root!",
-                UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
-        users.add(primary);
-        return users;
+        try {
+            return mPM.getUsers();
+        } catch (RemoteException re) {
+            ArrayList<UserInfo> users = new ArrayList<UserInfo>();
+            UserInfo primary = new UserInfo(0, "Root!", UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+            users.add(primary);
+            return users;
+        }
     }
 
     /**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 2bf1fb7..db5113e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -75,6 +75,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserId;
 import android.os.Vibrator;
 import android.os.storage.StorageManager;
 import android.telephony.TelephonyManager;
@@ -896,7 +897,8 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, false, false);
+                Activity.RESULT_OK, null, null, null, false, false,
+                Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -908,7 +910,8 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, receiverPermission, false, false);
+                Activity.RESULT_OK, null, null, receiverPermission, false, false,
+                Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -921,7 +924,8 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, receiverPermission, true, false);
+                Activity.RESULT_OK, null, null, receiverPermission, true, false,
+                Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -954,7 +958,7 @@
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermission,
-                true, false);
+                true, false, Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -966,7 +970,8 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, false, true);
+                Activity.RESULT_OK, null, null, null, false, true,
+                Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -999,7 +1004,7 @@
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
-                true, true);
+                true, true, Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -1014,7 +1019,7 @@
         try {
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().unbroadcastIntent(
-                mMainThread.getApplicationThread(), intent);
+                    mMainThread.getApplicationThread(), intent, Binder.getOrigCallingUser());
         } catch (RemoteException e) {
         }
     }
@@ -1215,8 +1220,7 @@
 
         int pid = Binder.getCallingPid();
         if (pid != Process.myPid()) {
-            return checkPermission(permission, pid,
-                    Binder.getCallingUid());
+            return checkPermission(permission, pid, Binder.getCallingUid());
         }
         return PackageManager.PERMISSION_DENIED;
     }
@@ -1384,7 +1388,8 @@
             Uri uri, int modeFlags, String message) {
         enforceForUri(
                 modeFlags, checkCallingUriPermission(uri, modeFlags),
-                false, Binder.getCallingUid(), uri, message);
+                false,
+                Binder.getCallingUid(), uri, message);
     }
 
     public void enforceCallingOrSelfUriPermission(
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 5222d37..39817ac 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -114,8 +114,8 @@
     public int broadcastIntent(IApplicationThread caller, Intent intent,
             String resolvedType, IIntentReceiver resultTo, int resultCode,
             String resultData, Bundle map, String requiredPermission,
-            boolean serialized, boolean sticky) throws RemoteException;
-    public void unbroadcastIntent(IApplicationThread caller, Intent intent) throws RemoteException;
+            boolean serialized, boolean sticky, int userId) throws RemoteException;
+    public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) throws RemoteException;
     /* oneway */
     public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast) throws RemoteException;
     public void attachApplication(IApplicationThread app) throws RemoteException;
@@ -209,7 +209,7 @@
             int flags) throws RemoteException;
     public void cancelIntentSender(IIntentSender sender) throws RemoteException;
     public boolean clearApplicationUserData(final String packageName,
-            final IPackageDataObserver observer) throws RemoteException;
+            final IPackageDataObserver observer, int userId) throws RemoteException;
     public String getPackageForIntentSender(IIntentSender sender) throws RemoteException;
     
     public void setProcessLimit(int max) throws RemoteException;
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0c6baeb..fcbcd81 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -36,6 +36,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.StrictMode;
+import android.os.UserId;
 import android.util.AndroidRuntimeException;
 import android.util.Slog;
 import android.view.CompatibilityInfoHolder;
@@ -67,6 +68,8 @@
  */
 public final class LoadedApk {
 
+    private static final String TAG = "LoadedApk";
+
     private final ActivityThread mActivityThread;
     private final ApplicationInfo mApplicationInfo;
     final String mPackageName;
@@ -113,8 +116,13 @@
         mApplicationInfo = aInfo;
         mPackageName = aInfo.packageName;
         mAppDir = aInfo.sourceDir;
-        mResDir = aInfo.uid == Process.myUid() ? aInfo.sourceDir
+        final int myUid = Process.myUid();
+        mResDir = aInfo.uid == myUid ? aInfo.sourceDir
                 : aInfo.publicSourceDir;
+        if (!UserId.isSameUser(aInfo.uid, myUid)) {
+            aInfo.dataDir = PackageManager.getDataDirForUser(UserId.getUserId(myUid),
+                    mPackageName);
+        }
         mSharedLibraries = aInfo.sharedLibraryFiles;
         mDataDir = aInfo.dataDir;
         mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index decb974..bb35c29 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -364,4 +364,6 @@
     VerifierDeviceIdentity getVerifierDeviceIdentity();
 
     boolean isFirstBoot();
+
+    List<UserInfo> getUsers();
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8541748d..26a9181 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -28,6 +28,7 @@
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Environment;
 import android.util.AndroidException;
 import android.util.DisplayMetrics;
 
@@ -753,13 +754,6 @@
     public static final int VERIFICATION_REJECT = -1;
 
     /**
-     * Range of IDs allocated for a user.
-     *
-     * @hide
-     */
-    public static final int PER_USER_RANGE = 100000;
-
-    /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device's
      * audio pipeline is low-latency, more suitable for audio applications sensitive to delays or
      * lag in sound input or output.
@@ -2615,39 +2609,6 @@
     public abstract void updateUserFlags(int id, int flags);
 
     /**
-     * Checks to see if the user id is the same for the two uids, i.e., they belong to the same
-     * user.
-     * @hide
-     */
-    public static boolean isSameUser(int uid1, int uid2) {
-        return getUserId(uid1) == getUserId(uid2);
-    }
-
-    /**
-     * Returns the user id for a given uid.
-     * @hide
-     */
-    public static int getUserId(int uid) {
-        return uid / PER_USER_RANGE;
-    }
-
-    /**
-     * Returns the uid that is composed from the userId and the appId.
-     * @hide
-     */
-    public static int getUid(int userId, int appId) {
-        return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
-    }
-
-    /**
-     * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
-     * @hide
-     */
-    public static int getAppId(int uid) {
-        return uid % PER_USER_RANGE;
-    }
-
-    /**
      * Returns the device identity that verifiers can use to associate their
      * scheme to a particular device. This should not be used by anything other
      * than a package verifier.
@@ -2656,4 +2617,17 @@
      * @hide
      */
     public abstract VerifierDeviceIdentity getVerifierDeviceIdentity();
+
+    /**
+     * Returns the data directory for a particular user and package, given the uid of the package.
+     * @param uid uid of the package, including the userId and appId
+     * @param packageName name of the package
+     * @return the user-specific data directory for the package
+     * @hide
+     */
+    public static String getDataDirForUser(int userId, String packageName) {
+        // TODO: This should be shared with Installer's knowledge of user directory
+        return Environment.getDataDirectory().toString() + "/user/" + userId
+                + "/" + packageName;
+    }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e593d5b..faee873 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -24,18 +24,17 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.PatternMatcher;
+import android.os.UserId;
 import android.util.AttributeSet;
 import android.util.Base64;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
 import android.util.TypedValue;
-import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.BufferedInputStream;
 import java.io.File;
@@ -59,6 +58,11 @@
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
 
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 /**
  * Package archive parsing
  *
@@ -209,6 +213,8 @@
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
             int gids[], int flags, long firstInstallTime, long lastUpdateTime) {
 
+        final int userId = Binder.getOrigCallingUser();
+
         PackageInfo pi = new PackageInfo();
         pi.packageName = p.packageName;
         pi.versionCode = p.mVersionCode;
@@ -250,7 +256,8 @@
                     final Activity activity = p.activities.get(i);
                     if (activity.info.enabled
                         || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags);
+                        pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
+                                userId);
                     }
                 }
             }
@@ -271,7 +278,7 @@
                     final Activity activity = p.receivers.get(i);
                     if (activity.info.enabled
                         || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags);
+                        pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags, userId);
                     }
                 }
             }
@@ -292,7 +299,7 @@
                     final Service service = p.services.get(i);
                     if (service.info.enabled
                         || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.services[j++] = generateServiceInfo(p.services.get(i), flags);
+                        pi.services[j++] = generateServiceInfo(p.services.get(i), flags, userId);
                     }
                 }
             }
@@ -313,7 +320,7 @@
                     final Provider provider = p.providers.get(i);
                     if (provider.info.enabled
                         || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags);
+                        pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, userId);
                     }
                 }
             }
@@ -3241,8 +3248,12 @@
     }
 
     public static ApplicationInfo generateApplicationInfo(Package p, int flags) {
+        return generateApplicationInfo(p, flags, UserId.getUserId(Binder.getCallingUid()));
+    }
+
+    public static ApplicationInfo generateApplicationInfo(Package p, int flags, int userId) {
         if (p == null) return null;
-        if (!copyNeeded(flags, p, null)) {
+        if (!copyNeeded(flags, p, null) && userId == 0) {
             // CompatibilityMode is global state. It's safe to modify the instance
             // of the package.
             if (!sCompatibilityModeEnabled) {
@@ -3258,6 +3269,10 @@
 
         // Make shallow copy so we can store the metadata/libraries safely
         ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
+        if (userId != 0) {
+            ai.uid = UserId.getUid(userId, ai.uid);
+            ai.dataDir = PackageManager.getDataDirForUser(userId, ai.packageName);
+        }
         if ((flags & PackageManager.GET_META_DATA) != 0) {
             ai.metaData = p.mAppMetaData;
         }
@@ -3325,16 +3340,15 @@
         }
     }
 
-    public static final ActivityInfo generateActivityInfo(Activity a,
-            int flags) {
+    public static final ActivityInfo generateActivityInfo(Activity a, int flags, int userId) {
         if (a == null) return null;
-        if (!copyNeeded(flags, a.owner, a.metaData)) {
+        if (!copyNeeded(flags, a.owner, a.metaData) && userId == 0) {
             return a.info;
         }
         // Make shallow copies so we can store the metadata safely
         ActivityInfo ai = new ActivityInfo(a.info);
         ai.metaData = a.metaData;
-        ai.applicationInfo = generateApplicationInfo(a.owner, flags);
+        ai.applicationInfo = generateApplicationInfo(a.owner, flags, userId);
         return ai;
     }
 
@@ -3359,15 +3373,15 @@
         }
     }
 
-    public static final ServiceInfo generateServiceInfo(Service s, int flags) {
+    public static final ServiceInfo generateServiceInfo(Service s, int flags, int userId) {
         if (s == null) return null;
-        if (!copyNeeded(flags, s.owner, s.metaData)) {
+        if (!copyNeeded(flags, s.owner, s.metaData) && userId == 0) {
             return s.info;
         }
         // Make shallow copies so we can store the metadata safely
         ServiceInfo si = new ServiceInfo(s.info);
         si.metaData = s.metaData;
-        si.applicationInfo = generateApplicationInfo(s.owner, flags);
+        si.applicationInfo = generateApplicationInfo(s.owner, flags, userId);
         return si;
     }
 
@@ -3400,12 +3414,12 @@
         }
     }
 
-    public static final ProviderInfo generateProviderInfo(Provider p,
-            int flags) {
+    public static final ProviderInfo generateProviderInfo(Provider p, int flags, int userId) {
         if (p == null) return null;
         if (!copyNeeded(flags, p.owner, p.metaData)
                 && ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
-                        || p.info.uriPermissionPatterns == null)) {
+                        || p.info.uriPermissionPatterns == null)
+                && userId == 0) {
             return p.info;
         }
         // Make shallow copies so we can store the metadata safely
@@ -3414,7 +3428,7 @@
         if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
             pi.uriPermissionPatterns = null;
         }
-        pi.applicationInfo = generateApplicationInfo(p.owner, flags);
+        pi.applicationInfo = generateApplicationInfo(p.owner, flags, userId);
         return pi;
     }
 
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index dfdea38..973fac1 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -44,6 +44,13 @@
      */
     public final static int UNSUPPORTED = -1;
 
+    /** @hide */
+    public static final long KB_IN_BYTES = 1024;
+    /** @hide */
+    public static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
+    /** @hide */
+    public static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+
     /**
      * Special UID value used when collecting {@link NetworkStatsHistory} for
      * removed applications.
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 24569fa..577fc43 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -49,6 +49,7 @@
     private static final boolean FIND_POTENTIAL_LEAKS = false;
     private static final String TAG = "Binder";
 
+    /* mObject is used by native code, do not remove or rename */
     private int mObject;
     private IInterface mOwner;
     private String mDescriptor;
@@ -70,7 +71,35 @@
      * incoming transaction, then its own uid is returned.
      */
     public static final native int getCallingUid();
-    
+
+    /**
+     * Return the original ID of the user assigned to the process that sent you the current
+     * transaction that is being processed. This uid can be used with higher-level system services
+     * to determine its identity and check permissions. If the current thread is not currently
+     * executing an incoming transaction, then its own uid is returned.
+     * <p/>
+     * This value cannot be reset by calls to {@link #clearCallingIdentity()}.
+     * @hide
+     */
+    public static final int getOrigCallingUid() {
+        if (UserId.MU_ENABLED) {
+            return getOrigCallingUidNative();
+        } else {
+            return getCallingUid();
+        }
+    }
+
+    private static final native int getOrigCallingUidNative();
+
+    /**
+     * Utility function to return the user id of the calling process.
+     * @return userId of the calling process, extracted from the callingUid
+     * @hide
+     */
+    public static final int getOrigCallingUser() {
+        return UserId.getUserId(getOrigCallingUid());
+    }
+
     /**
      * Reset the identity of the incoming IPC on the current thread.  This can
      * be useful if, while handling an incoming call, you will be calling
diff --git a/core/java/android/os/UserId.java b/core/java/android/os/UserId.java
new file mode 100644
index 0000000..4124d51
--- /dev/null
+++ b/core/java/android/os/UserId.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * @hide
+ */
+public final class UserId {
+    /**
+     * Range of IDs allocated for a user.
+     *
+     * @hide
+     */
+    public static final int PER_USER_RANGE = 100000;
+
+    public static final int USER_ALL = -1;
+
+    /**
+     * Enable multi-user related side effects. Set this to false if there are problems with single
+     * user usecases.
+     * */
+    public static final boolean MU_ENABLED = true;
+
+    /**
+     * Checks to see if the user id is the same for the two uids, i.e., they belong to the same
+     * user.
+     * @hide
+     */
+    public static final boolean isSameUser(int uid1, int uid2) {
+        return getUserId(uid1) == getUserId(uid2);
+    }
+
+    /**
+     * Checks to see if both uids are referring to the same app id, ignoring the user id part of the
+     * uids.
+     * @param uid1 uid to compare
+     * @param uid2 other uid to compare
+     * @return whether the appId is the same for both uids
+     * @hide
+     */
+    public static final boolean isSameApp(int uid1, int uid2) {
+        return getAppId(uid1) == getAppId(uid2);
+    }
+
+    /**
+     * Returns the user id for a given uid.
+     * @hide
+     */
+    public static final int getUserId(int uid) {
+        if (MU_ENABLED) {
+            return uid / PER_USER_RANGE;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Returns the uid that is composed from the userId and the appId.
+     * @hide
+     */
+    public static final int getUid(int userId, int appId) {
+        if (MU_ENABLED) {
+            return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
+        } else {
+            return appId;
+        }
+    }
+
+    /**
+     * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
+     * @hide
+     */
+    public static final int getAppId(int uid) {
+        return uid % PER_USER_RANGE;
+    }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4a42e92..dcddd47 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -367,12 +367,52 @@
     private class WebViewInputConnection extends BaseInputConnection {
         // Used for mapping characters to keys typed.
         private KeyCharacterMap mKeyCharacterMap;
+        private boolean mIsKeySentByMe;
 
         public WebViewInputConnection() {
             super(WebView.this, true);
         }
 
         @Override
+        public boolean sendKeyEvent(KeyEvent event) {
+            // Latin IME occasionally sends delete codes directly using
+            // sendKeyEvents. WebViewInputConnection should treat this
+            // as a deleteSurroundingText.
+            if (!mIsKeySentByMe
+                    && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
+                Editable editable = getEditable();
+                int selectionStart = Selection.getSelectionStart(editable);
+                int selectionEnd = Selection.getSelectionEnd(editable);
+                if (selectionEnd > 0 && (selectionStart == selectionEnd)) {
+                    int action = event.getAction();
+                    if (action == KeyEvent.ACTION_UP) {
+                        return deleteSurroundingText(1, 0);
+                    } else if (action == KeyEvent.ACTION_DOWN) {
+                        return true; // the delete will happen in ACTION_UP
+                    }
+                }
+            }
+            return super.sendKeyEvent(event);
+        }
+
+        public void setTextAndKeepSelection(CharSequence text) {
+            Editable editable = getEditable();
+            int selectionStart = Selection.getSelectionStart(editable);
+            int selectionEnd = Selection.getSelectionEnd(editable);
+            editable.replace(0, editable.length(), text);
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) {
+                // Since the text has changed, do not allow the IME to replace the
+                // existing text as though it were a completion.
+                imm.restartInput(WebView.this);
+            }
+            // Keep the previous selection.
+            selectionStart = Math.min(selectionStart, editable.length());
+            selectionEnd = Math.min(selectionEnd, editable.length());
+            setSelection(selectionStart, selectionEnd);
+        }
+
+        @Override
         public boolean setComposingText(CharSequence text, int newCursorPosition) {
             Editable editable = getEditable();
             int start = getComposingSpanStart(editable);
@@ -393,7 +433,8 @@
         @Override
         public boolean commitText(CharSequence text, int newCursorPosition) {
             setComposingText(text, newCursorPosition);
-            finishComposingText();
+            int cursorPosition = Selection.getSelectionEnd(getEditable());
+            setComposingRegion(cursorPosition, cursorPosition);
             return true;
         }
 
@@ -417,6 +458,7 @@
          * @param text The new text to replace the changed text.
          */
         private void setNewText(int start, int end, CharSequence text) {
+            mIsKeySentByMe = true;
             Editable editable = getEditable();
             CharSequence original = editable.subSequence(start, end);
             boolean isCharacterAdd = false;
@@ -434,10 +476,8 @@
             }
             if (isCharacterAdd) {
                 sendCharacter(text.charAt(textLength - 1));
-                mTextGeneration++;
             } else if (isCharacterDelete) {
                 sendDeleteKey();
-                mTextGeneration++;
             } else if (textLength != originalLength ||
                     !TextUtils.regionMatches(text, 0, original, 0,
                             textLength)) {
@@ -447,6 +487,7 @@
                         REPLACE_TEXT, start,  end, text.toString());
                 mPrivateHandler.sendMessage(replaceMessage);
             }
+            mIsKeySentByMe = false;
         }
 
         /**
@@ -509,7 +550,7 @@
     private final RectF mVisibleContentRect = new RectF();
     private boolean mGLViewportEmpty = false;
     WebViewInputConnection mInputConnection = null;
-
+    private int mFieldPointer;
 
     /**
      *  Transportation object for returning WebView across thread boundaries.
@@ -803,6 +844,7 @@
     static final int HANDLE_ID_EXTENT = 3;
 
     static boolean sDisableNavcache = false;
+    static boolean sEnableWebTextView = false;
     // the color used to highlight the touch rectangles
     static final int HIGHLIGHT_COLOR = 0x6633b5e5;
     // the region indicating where the user touched on the screen
@@ -1424,7 +1466,6 @@
     private void init() {
         OnTrimMemoryListener.init(getContext());
         sDisableNavcache = nativeDisableNavcache();
-
         setWillNotDraw(false);
         setFocusable(true);
         setFocusableInTouchMode(true);
@@ -5091,6 +5132,9 @@
      * multiline, and what text it contains.  It also removes it if necessary.
      */
     /* package */ void rebuildWebTextView() {
+        if (!sEnableWebTextView) {
+            return; // always use WebKit's text entry
+        }
         // If the WebView does not have focus, do nothing until it gains focus.
         if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
             return;
@@ -8653,14 +8697,17 @@
                 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
                     // Make sure that the textfield is currently focused
                     // and representing the same node as the pointer.
-                    if (inEditingMode() &&
-                            mWebTextView.isSameTextField(msg.arg1)) {
-                        if (msg.arg2 == mTextGeneration) {
-                            String text = (String) msg.obj;
-                            if (null == text) {
-                                text = "";
-                            }
+                    if (msg.arg2 == mTextGeneration) {
+                        String text = (String) msg.obj;
+                        if (null == text) {
+                            text = "";
+                        }
+                        if (inEditingMode() &&
+                                mWebTextView.isSameTextField(msg.arg1)) {
                             mWebTextView.setTextAndKeepSelection(text);
+                        } else if (mInputConnection != null &&
+                                mFieldPointer == msg.arg1) {
+                            mInputConnection.setTextAndKeepSelection(text);
                         }
                     }
                     break;
@@ -8951,15 +8998,8 @@
                 case INIT_EDIT_FIELD:
                     if (mInputConnection != null) {
                         mTextGeneration = 0;
-                        String text = (String)msg.obj;
-                        mInputConnection.beginBatchEdit();
-                        Editable editable = mInputConnection.getEditable();
-                        editable.replace(0, editable.length(), text);
-                        int start = msg.arg1;
-                        int end = msg.arg2;
-                        mInputConnection.setComposingRegion(end, end);
-                        mInputConnection.setSelection(start, end);
-                        mInputConnection.endBatchEdit();
+                        mFieldPointer = msg.arg1;
+                        mInputConnection.setTextAndKeepSelection((String) msg.obj);
                     }
                     break;
 
@@ -9102,7 +9142,7 @@
             if (inEditingMode()
                     && mWebTextView.isSameTextField(nodePointer)) {
                 mWebTextView.setSelectionFromWebKit(data.mStart, data.mEnd);
-            } else if (mInputConnection != null){
+            } else if (mInputConnection != null && mFieldPointer == nodePointer) {
                 mInputConnection.setSelection(data.mStart, data.mEnd);
             }
         }
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 395a638..8582dbc 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2746,12 +2746,16 @@
     }
 
     // called by JNI
-    private void initEditField(String text, int start, int end) {
+    private void initEditField(int pointer, String text, int start, int end) {
         if (mWebView == null) {
             return;
         }
         Message.obtain(mWebView.mPrivateHandler,
-                WebView.INIT_EDIT_FIELD, start, end, text).sendToTarget();
+                WebView.INIT_EDIT_FIELD, pointer, 0, text).sendToTarget();
+        Message.obtain(mWebView.mPrivateHandler,
+                WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
+                0, new TextSelectionData(start, end, 0))
+                .sendToTarget();
     }
 
     private native void nativeUpdateFrameCacheIfLoading(int nativeClass);
@@ -2789,17 +2793,6 @@
     }
 
     // called by JNI
-    private void requestKeyboardWithSelection(int pointer, int selStart,
-            int selEnd, int textGeneration) {
-        if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler,
-                    WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
-                    textGeneration, new TextSelectionData(selStart, selEnd, 0))
-                    .sendToTarget();
-        }
-    }
-
-    // called by JNI
     private void requestKeyboard(boolean showKeyboard) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 1592061..62afd61 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -427,13 +427,22 @@
 
         public SetOnClickPendingIntent(Parcel parcel) {
             viewId = parcel.readInt();
-            pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+
+            // We check a flag to determine if the parcel contains a PendingIntent.
+            if (parcel.readInt() != 0) {
+                pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+            }
         }
 
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(TAG);
             dest.writeInt(viewId);
-            pendingIntent.writeToParcel(dest, 0 /* no flags */);
+
+            // We use a flag to indicate whether the parcel contains a valid object.
+            dest.writeInt(pendingIntent != null ? 1 : 0);
+            if (pendingIntent != null) {
+                pendingIntent.writeToParcel(dest, 0 /* no flags */);
+            }
         }
 
         @Override
@@ -445,35 +454,39 @@
             // sense, do they mean to set a PendingIntent template for the AdapterView's children?
             if (mIsWidgetCollectionChild) {
                 Log.e("RemoteViews", "Cannot setOnClickPendingIntent for collection item " +
-				"(id: " + viewId + ")");
+                        "(id: " + viewId + ")");
                 // TODO: return; We'll let this slide until apps are up to date.
             }
 
-            if (target != null && pendingIntent != null) {
-                OnClickListener listener = new OnClickListener() {
-                    public void onClick(View v) {
-                        // Find target view location in screen coordinates and
-                        // fill into PendingIntent before sending.
-                        final float appScale = v.getContext().getResources()
-                                .getCompatibilityInfo().applicationScale;
-                        final int[] pos = new int[2];
-                        v.getLocationOnScreen(pos);
-
-                        final Rect rect = new Rect();
-                        rect.left = (int) (pos[0] * appScale + 0.5f);
-                        rect.top = (int) (pos[1] * appScale + 0.5f);
-                        rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
-                        rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
-
-                        final Intent intent = new Intent();
-                        intent.setSourceBounds(rect);
-                        startIntentSafely(v.getContext(), pendingIntent, intent);
-                    }
-                };
+            if (target != null) {
+                // If the pendingIntent is null, we clear the onClickListener
+                OnClickListener listener = null;
+                if (pendingIntent != null) {
+                    listener = new OnClickListener() {
+                        public void onClick(View v) {
+                            // Find target view location in screen coordinates and
+                            // fill into PendingIntent before sending.
+                            final float appScale = v.getContext().getResources()
+                                    .getCompatibilityInfo().applicationScale;
+                            final int[] pos = new int[2];
+                            v.getLocationOnScreen(pos);
+    
+                            final Rect rect = new Rect();
+                            rect.left = (int) (pos[0] * appScale + 0.5f);
+                            rect.top = (int) (pos[1] * appScale + 0.5f);
+                            rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
+                            rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
+    
+                            final Intent intent = new Intent();
+                            intent.setSourceBounds(rect);
+                            startIntentSafely(v.getContext(), pendingIntent, intent);
+                        }
+                    };
+                }
                 target.setOnClickListener(listener);
             }
         }
-        
+
         int viewId;
         PendingIntent pendingIntent;
 
@@ -667,6 +680,9 @@
                 Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId)
                         + " methodName=" + this.methodName + " type=" + this.type);
             }
+
+            // For some values that may have been null, we first check a flag to see if they were
+            // written to the parcel.
             switch (this.type) {
                 case BOOLEAN:
                     this.value = in.readInt() != 0;
@@ -699,16 +715,22 @@
                     this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
                     break;
                 case URI:
-                    this.value = Uri.CREATOR.createFromParcel(in);
+                    if (in.readInt() != 0) {
+                        this.value = Uri.CREATOR.createFromParcel(in);
+                    }
                     break;
                 case BITMAP:
-                    this.value = Bitmap.CREATOR.createFromParcel(in);
+                    if (in.readInt() != 0) {
+                        this.value = Bitmap.CREATOR.createFromParcel(in);
+                    }
                     break;
                 case BUNDLE:
                     this.value = in.readBundle();
                     break;
                 case INTENT:
-                    this.value = Intent.CREATOR.createFromParcel(in);
+                    if (in.readInt() != 0) {
+                        this.value = Intent.CREATOR.createFromParcel(in);
+                    }
                     break;
                 default:
                     break;
@@ -725,6 +747,9 @@
                 Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId)
                         + " methodName=" + this.methodName + " type=" + this.type);
             }
+
+            // For some values which are null, we record an integer flag to indicate whether
+            // we have written a valid value to the parcel.
             switch (this.type) {
                 case BOOLEAN:
                     out.writeInt((Boolean) this.value ? 1 : 0);
@@ -757,16 +782,25 @@
                     TextUtils.writeToParcel((CharSequence)this.value, out, flags);   
                     break;
                 case URI:
-                    ((Uri)this.value).writeToParcel(out, flags);
+                    out.writeInt(this.value != null ? 1 : 0);
+                    if (this.value != null) {
+                        ((Uri)this.value).writeToParcel(out, flags);
+                    }
                     break;
                 case BITMAP:
-                    ((Bitmap)this.value).writeToParcel(out, flags);
+                    out.writeInt(this.value != null ? 1 : 0);
+                    if (this.value != null) {
+                        ((Bitmap)this.value).writeToParcel(out, flags);
+                    }
                     break;
                 case BUNDLE:
                     out.writeBundle((Bundle) this.value);
                     break;
                 case INTENT:
-                    ((Intent)this.value).writeToParcel(out, flags);
+                    out.writeInt(this.value != null ? 1 : 0);
+                    if (this.value != null) {
+                        ((Intent)this.value).writeToParcel(out, flags);
+                    }
                     break;
                 default:
                     break;
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index c797a73..a1997a2 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -37,6 +37,8 @@
 
 ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
 
+static KeyedVector<UChar, UChar> gBidiMirrored;
+
 //--------------------------------------------------------------------------------------------------
 
 TextLayoutCache::TextLayoutCache(TextLayoutShaper* shaper) :
@@ -350,6 +352,23 @@
 
     mShaperItem.font = &mFontRec;
     mShaperItem.font->userData = &mShapingPaint;
+
+    // Fill the BiDi mirrored chars map
+    // See: http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
+    gBidiMirrored.add('(', ')');
+    gBidiMirrored.add(')', '(');
+    gBidiMirrored.add('[', ']');
+    gBidiMirrored.add(']', '[');
+    gBidiMirrored.add('{', '}');
+    gBidiMirrored.add('}', '{');
+    gBidiMirrored.add('<', '>');
+    gBidiMirrored.add('>', '<');
+    gBidiMirrored.add(0x00ab, 0x00bb); // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+    gBidiMirrored.add(0x00bb, 0x00ab); // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+    gBidiMirrored.add(0x2039, 0x203a); // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+    gBidiMirrored.add(0x203a, 0x2039); // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+    gBidiMirrored.add(0x2264, 0x2265); // LESS-THAN OR EQUAL TO
+    gBidiMirrored.add(0x2265, 0x2264); // GREATER-THAN OR EQUAL TO
 }
 
 TextLayoutShaper::~TextLayoutShaper() {
@@ -577,6 +596,31 @@
         }
     }
 
+    // Reverse "BiDi mirrored chars" in RTL mode only
+    // See: http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
+    // This is a workaround because Harfbuzz is not able to do mirroring in all cases and
+    // script-run splitting with Harfbuzz is splitting on parenthesis
+    if (isRTL) {
+        for (ssize_t i = 0; i < ssize_t(count); i++) {
+            UChar ch = chars[i];
+            ssize_t index = gBidiMirrored.indexOfKey(ch);
+            // Skip non "BiDi mirrored" chars
+            if (index < 0) {
+                continue;
+            }
+            if (!useNormalizedString) {
+                useNormalizedString = true;
+                mNormalizedString.setTo(false /* not terminated*/, chars, count);
+            }
+            UChar result = gBidiMirrored.valueAt(index);
+            mNormalizedString.setCharAt(i, result);
+#if DEBUG_GLYPHS
+            ALOGD("Rewriting codepoint '%d' to '%d' at position %d",
+                    ch, mNormalizedString[i], int(i));
+#endif
+        }
+    }
+
 #if DEBUG_GLYPHS
     if (useNormalizedString) {
         ALOGD("Will use normalized string '%s', length = %d",
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index 53a0501..26e82aa 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -48,7 +48,7 @@
         return false;
     }
 
-    return lpToneGen->startTone(toneType, durationMs);
+    return lpToneGen->startTone((ToneGenerator::tone_type) toneType, durationMs);
 }
 
 static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 990a617..e00970a 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -739,6 +739,11 @@
     return IPCThreadState::self()->getCallingUid();
 }
 
+static jint android_os_Binder_getOrigCallingUid(JNIEnv* env, jobject clazz)
+{
+    return IPCThreadState::self()->getOrigCallingUid();
+}
+
 static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
 {
     return IPCThreadState::self()->clearCallingIdentity();
@@ -810,6 +815,7 @@
      /* name, signature, funcPtr */
     { "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },
     { "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
+    { "getOrigCallingUidNative", "()I", (void*)android_os_Binder_getOrigCallingUid },
     { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
     { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
     { "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 1068b66..56af9aa 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -785,7 +785,7 @@
   </plurals>
   <plurals name="num_minutes_ago">
     <item quantity="one" msgid="3306787433088810191">"iminithi elingu-1 edlule"</item>
-    <item quantity="other" msgid="2176942008915455116">"amaminithi angu-<xliff:g id="COUNT">%d</xliff:g> adlule.."</item>
+    <item quantity="other" msgid="2176942008915455116">"<xliff:g id="COUNT">%d</xliff:g> amaminithi adlule.."</item>
   </plurals>
   <plurals name="num_hours_ago">
     <item quantity="one" msgid="9150797944610821849">"ihora elingu-1 elidlule"</item>
@@ -822,7 +822,7 @@
   </plurals>
   <plurals name="abbrev_num_minutes_ago">
     <item quantity="one" msgid="6361490147113871545">"iminithi elingu-1 edlule"</item>
-    <item quantity="other" msgid="851164968597150710">"amaminithi angu-<xliff:g id="COUNT">%d</xliff:g> adlule"</item>
+    <item quantity="other" msgid="851164968597150710">"<xliff:g id="COUNT">%d</xliff:g> amaminithi adlule"</item>
   </plurals>
   <plurals name="abbrev_num_hours_ago">
     <item quantity="one" msgid="4796212039724722116">"ihora elingu-1 elidlule"</item>
@@ -1111,7 +1111,7 @@
     <string name="no_matches" msgid="8129421908915840737">"Akukho okufanayo"</string>
     <string name="find_on_page" msgid="1946799233822820384">"Thola ekhasini"</string>
   <plurals name="matches_found">
-    <item quantity="one" msgid="8167147081136579439">"okufanayo okungu-1"</item>
+    <item quantity="one" msgid="8167147081136579439">"1 okufanayo"</item>
     <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> ku-<xliff:g id="TOTAL">%d</xliff:g>"</item>
   </plurals>
     <string name="action_mode_done" msgid="7217581640461922289">"Kwenziwe"</string>
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index 4b1f9fd..d527c0d 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -303,7 +303,8 @@
     public void testSetSticky() throws Exception {
         Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
         intent.putExtra("test", LaunchpadActivity.DATA_1);
-        ActivityManagerNative.getDefault().unbroadcastIntent(null, intent);
+        ActivityManagerNative.getDefault().unbroadcastIntent(null, intent,
+                Binder.getOrigCallingUser());
 
         ActivityManagerNative.broadcastStickyIntent(intent, null);
         addIntermediate("finished-broadcast");
@@ -320,7 +321,8 @@
         ActivityManagerNative.broadcastStickyIntent(intent, null);
 
         ActivityManagerNative.getDefault().unbroadcastIntent(
-                null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null));
+                null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null),
+                Binder.getOrigCallingUser());
         addIntermediate("finished-unbroadcast");
 
         IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index 1df763a..b181122 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -24,6 +24,8 @@
 import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLong;
 import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLong;
 import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
+import static android.net.TrafficStats.GB_IN_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -50,10 +52,6 @@
 
     private static final long TEST_START = 1194220800000L;
 
-    private static final long KB_IN_BYTES = 1024;
-    private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
-    private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
-
     private NetworkStatsHistory stats;
 
     @Override
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
index 3378d97..691ba2f 100644
--- a/include/binder/IPCThreadState.h
+++ b/include/binder/IPCThreadState.h
@@ -41,6 +41,7 @@
 
             int                 getCallingPid();
             int                 getCallingUid();
+            int                 getOrigCallingUid();
 
             void                setStrictModePolicy(int32_t policy);
             int32_t             getStrictModePolicy() const;
@@ -116,6 +117,7 @@
             status_t            mLastError;
             pid_t               mCallingPid;
             uid_t               mCallingUid;
+            uid_t               mOrigCallingUid;
             int32_t             mStrictModePolicy;
             int32_t             mLastTransactionBinderFlags;
 };
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index 1417416..7b0b443 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -226,8 +226,8 @@
     AudioEffect(const effect_uuid_t *type,
                 const effect_uuid_t *uuid = NULL,
                   int32_t priority = 0,
-                  effect_callback_t cbf = 0,
-                  void* user = 0,
+                  effect_callback_t cbf = NULL,
+                  void* user = NULL,
                   int sessionId = 0,
                   audio_io_handle_t io = 0
                   );
@@ -238,8 +238,8 @@
     AudioEffect(const char *typeStr,
                     const char *uuidStr = NULL,
                     int32_t priority = 0,
-                    effect_callback_t cbf = 0,
-                    void* user = 0,
+                    effect_callback_t cbf = NULL,
+                    void* user = NULL,
                     int sessionId = 0,
                     audio_io_handle_t io = 0
                     );
@@ -260,8 +260,8 @@
             status_t    set(const effect_uuid_t *type,
                             const effect_uuid_t *uuid = NULL,
                             int32_t priority = 0,
-                            effect_callback_t cbf = 0,
-                            void* user = 0,
+                            effect_callback_t cbf = NULL,
+                            void* user = NULL,
                             int sessionId = 0,
                             audio_io_handle_t io = 0
                             );
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 76ec3b1..c8c5dba 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -155,8 +155,8 @@
                                     uint32_t channelMask = AUDIO_CHANNEL_IN_MONO,
                                     int frameCount      = 0,
                                     uint32_t flags      = 0,
-                                    callback_t cbf = 0,
-                                    void* user = 0,
+                                    callback_t cbf = NULL,
+                                    void* user = NULL,
                                     int notificationFrames = 0,
                                     int sessionId = 0);
 
@@ -181,8 +181,8 @@
                             uint32_t channelMask = AUDIO_CHANNEL_IN_MONO,
                             int frameCount      = 0,
                             uint32_t flags      = 0,
-                            callback_t cbf = 0,
-                            void* user = 0,
+                            callback_t cbf = NULL,
+                            void* user = NULL,
                             int notificationFrames = 0,
                             bool threadCanCallJava = false,
                             int sessionId = 0);
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 98abfbd..02c85cd 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -148,8 +148,8 @@
                                     int channelMask      = 0,
                                     int frameCount       = 0,
                                     uint32_t flags       = 0,
-                                    callback_t cbf       = 0,
-                                    void* user           = 0,
+                                    callback_t cbf       = NULL,
+                                    void* user           = NULL,
                                     int notificationFrames = 0,
                                     int sessionId = 0);
 
@@ -180,8 +180,8 @@
                                     int channelMask     = 0,
                                     const sp<IMemory>& sharedBuffer = 0,
                                     uint32_t flags      = 0,
-                                    callback_t cbf      = 0,
-                                    void* user          = 0,
+                                    callback_t cbf      = NULL,
+                                    void* user          = NULL,
                                     int notificationFrames = 0,
                                     int sessionId = 0);
 
@@ -204,8 +204,8 @@
                             int channelMask     = 0,
                             int frameCount      = 0,
                             uint32_t flags      = 0,
-                            callback_t cbf      = 0,
-                            void* user          = 0,
+                            callback_t cbf      = NULL,
+                            void* user          = NULL,
                             int notificationFrames = 0,
                             const sp<IMemory>& sharedBuffer = 0,
                             bool threadCanCallJava = false,
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 7c0d886..760595c 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -126,7 +126,7 @@
                                     uint32_t *pSamplingRate,
                                     audio_format_t *pFormat,
                                     uint32_t *pChannels,
-                                    uint32_t acoustics) = 0;
+                                    audio_in_acoustics_t acoustics) = 0;
     virtual status_t closeInput(int input) = 0;
 
     virtual status_t setStreamOutput(audio_stream_type_t stream, int output) = 0;
diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h
index 7d890bd..df0c97e 100644
--- a/include/media/ToneGenerator.h
+++ b/include/media/ToneGenerator.h
@@ -154,7 +154,7 @@
     ToneGenerator(audio_stream_type_t streamType, float volume, bool threadCanCallJava = false);
     ~ToneGenerator();
 
-    bool startTone(int toneType, int durationMs = -1);
+    bool startTone(tone_type toneType, int durationMs = -1);
     void stopTone();
 
     bool isInited() { return (mState == TONE_IDLE)?false:true;}
@@ -274,7 +274,7 @@
     bool prepareWave();
     unsigned int numWaves(unsigned int segmentIdx);
     void clearWaveGens();
-    int getToneForRegion(int toneType);
+    tone_type getToneForRegion(tone_type toneType);
 
     // WaveGenerator generates a single sine wave
     class WaveGenerator {
diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h
index 1a4cbca..60fa15b 100644
--- a/include/media/Visualizer.h
+++ b/include/media/Visualizer.h
@@ -66,8 +66,8 @@
      * See AudioEffect constructor for details on parameters.
      */
                         Visualizer(int32_t priority = 0,
-                                   effect_callback_t cbf = 0,
-                                   void* user = 0,
+                                   effect_callback_t cbf = NULL,
+                                   void* user = NULL,
                                    int sessionId = 0);
 
                         ~Visualizer();
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index ffc546e..dd97ce4 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -76,7 +76,9 @@
                 // Left channel is in [0:15], right channel is in [16:31].
                 // Always read and write the combined pair atomically.
                 // For AudioTrack only, not used by AudioRecord.
-                uint32_t    volumeLR;
+private:
+                uint32_t    mVolumeLR;
+public:
 
                 uint32_t    sampleRate;
                 // NOTE: audio_track_cblk_t::frameSize is not equal to AudioTrack::frameSize() for
@@ -116,6 +118,17 @@
                 uint16_t    getSendLevel_U4_12() const {
                     return mSendLevel;
                 }
+
+                // for AudioTrack client only, caller must limit to 0 <= volumeLR <= 0x10001000
+                void        setVolumeLR(uint32_t volumeLR) {
+                    mVolumeLR = volumeLR;
+                }
+
+                // for AudioFlinger only; the return value must be validated by the caller
+                uint32_t    getVolumeLR() const {
+                    return mVolumeLR;
+                }
+
 };
 
 
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 629b899..b578a6c 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -371,6 +371,11 @@
     return mCallingUid;
 }
 
+int IPCThreadState::getOrigCallingUid()
+{
+    return mOrigCallingUid;
+}
+
 int64_t IPCThreadState::clearCallingIdentity()
 {
     int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
@@ -641,6 +646,7 @@
 {
     pthread_setspecific(gTLS, this);
     clearCaller();
+    mOrigCallingUid = mCallingUid;
     mIn.setDataCapacity(256);
     mOut.setDataCapacity(256);
 }
@@ -987,6 +993,7 @@
             
             mCallingPid = tr.sender_pid;
             mCallingUid = tr.sender_euid;
+            mOrigCallingUid = tr.sender_euid;
             
             int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
             if (gDisableBackgroundScheduling) {
@@ -1045,6 +1052,7 @@
             
             mCallingPid = origPid;
             mCallingUid = origUid;
+            mOrigCallingUid = origUid;
 
             IF_LOG_TRANSACTIONS() {
                 TextOutput::Bundle _b(alog);
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 6639d06..a242846 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -342,7 +342,7 @@
 {
     ALOGW("IEffect died");
     mStatus = NO_INIT;
-    if (mCbf) {
+    if (mCbf != NULL) {
         status_t status = DEAD_OBJECT;
         mCbf(EVENT_ERROR, mUserData, &status);
     }
@@ -363,7 +363,7 @@
             mStatus = ALREADY_EXISTS;
         }
     }
-    if (mCbf) {
+    if (mCbf != NULL) {
         mCbf(EVENT_CONTROL_STATUS_CHANGED, mUserData, &controlGranted);
     }
 }
@@ -373,7 +373,7 @@
     ALOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf);
     if (mStatus == ALREADY_EXISTS) {
         mEnabled = enabled;
-        if (mCbf) {
+        if (mCbf != NULL) {
             mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled);
         }
     }
@@ -389,7 +389,7 @@
         return;
     }
 
-    if (mCbf && cmdCode == EFFECT_CMD_SET_PARAM) {
+    if (mCbf != NULL && cmdCode == EFFECT_CMD_SET_PARAM) {
         effect_param_t *cmd = (effect_param_t *)cmdData;
         cmd->status = *(int32_t *)replyData;
         mCbf(EVENT_PARAMETER_CHANGED, mUserData, cmd);
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 2b3ea38..c96bc76 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -206,7 +206,7 @@
         return status;
     }
 
-    if (cbf != 0) {
+    if (cbf != NULL) {
         mClientRecordThread = new ClientRecordThread(*this, threadCanCallJava);
     }
 
@@ -387,7 +387,7 @@
 
 status_t AudioRecord::setMarkerPosition(uint32_t marker)
 {
-    if (mCbf == 0) return INVALID_OPERATION;
+    if (mCbf == NULL) return INVALID_OPERATION;
 
     mMarkerPosition = marker;
     mMarkerReached = false;
@@ -397,7 +397,7 @@
 
 status_t AudioRecord::getMarkerPosition(uint32_t *marker)
 {
-    if (marker == 0) return BAD_VALUE;
+    if (marker == NULL) return BAD_VALUE;
 
     *marker = mMarkerPosition;
 
@@ -406,7 +406,7 @@
 
 status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
 {
-    if (mCbf == 0) return INVALID_OPERATION;
+    if (mCbf == NULL) return INVALID_OPERATION;
 
     uint32_t curPosition;
     getPosition(&curPosition);
@@ -418,7 +418,7 @@
 
 status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod)
 {
-    if (updatePeriod == 0) return BAD_VALUE;
+    if (updatePeriod == NULL) return BAD_VALUE;
 
     *updatePeriod = mUpdatePeriod;
 
@@ -427,7 +427,7 @@
 
 status_t AudioRecord::getPosition(uint32_t *position)
 {
-    if (position == 0) return BAD_VALUE;
+    if (position == NULL) return BAD_VALUE;
 
     AutoMutex lock(mLock);
     *position = mCblk->user;
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index df5017b..110a294 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -225,7 +225,7 @@
 
     gLock.lock();
     outputDesc = AudioSystem::gOutputs.valueFor(output);
-    if (outputDesc == 0) {
+    if (outputDesc == NULL) {
         ALOGV("getOutputSamplingRate() no output descriptor for output %d in gOutputs", output);
         gLock.unlock();
         const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
@@ -263,7 +263,7 @@
 
     gLock.lock();
     outputDesc = AudioSystem::gOutputs.valueFor(output);
-    if (outputDesc == 0) {
+    if (outputDesc == NULL) {
         gLock.unlock();
         const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
         if (af == 0) return PERMISSION_DENIED;
@@ -294,7 +294,7 @@
 
     gLock.lock();
     outputDesc = AudioSystem::gOutputs.valueFor(output);
-    if (outputDesc == 0) {
+    if (outputDesc == NULL) {
         gLock.unlock();
         const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
         if (af == 0) return PERMISSION_DENIED;
@@ -413,7 +413,7 @@
 
     switch (event) {
     case STREAM_CONFIG_CHANGED:
-        if (param2 == 0) break;
+        if (param2 == NULL) break;
         stream = *(audio_stream_type_t *)param2;
         ALOGV("ioConfigChanged() STREAM_CONFIG_CHANGED stream %d, output %d", stream, ioHandle);
         if (gStreamOutputMap.indexOfKey(stream) >= 0) {
@@ -425,7 +425,7 @@
             ALOGV("ioConfigChanged() opening already existing output! %d", ioHandle);
             break;
         }
-        if (param2 == 0) break;
+        if (param2 == NULL) break;
         desc = (OutputDescriptor *)param2;
 
         OutputDescriptor *outputDesc =  new OutputDescriptor(*desc);
@@ -454,7 +454,7 @@
             ALOGW("ioConfigChanged() modifying unknow output! %d", ioHandle);
             break;
         }
-        if (param2 == 0) break;
+        if (param2 == NULL) break;
         desc = (OutputDescriptor *)param2;
 
         ALOGV("ioConfigChanged() new config for output %d samplingRate %d, format %d channels %d frameCount %d latency %d",
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 17e3d4b..8c33f41 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -257,7 +257,7 @@
         return status;
     }
 
-    if (cbf != 0) {
+    if (cbf != NULL) {
         mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
     }
 
@@ -501,7 +501,7 @@
     mVolume[LEFT] = left;
     mVolume[RIGHT] = right;
 
-    mCblk->volumeLR = (uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000);
+    mCblk->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));
 
     return NO_ERROR;
 }
@@ -604,13 +604,13 @@
 status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount)
 {
     AutoMutex lock(mLock);
-    if (loopStart != 0) {
+    if (loopStart != NULL) {
         *loopStart = mCblk->loopStart;
     }
-    if (loopEnd != 0) {
+    if (loopEnd != NULL) {
         *loopEnd = mCblk->loopEnd;
     }
-    if (loopCount != 0) {
+    if (loopCount != NULL) {
         if (mCblk->loopCount < 0) {
             *loopCount = -1;
         } else {
@@ -623,7 +623,7 @@
 
 status_t AudioTrack::setMarkerPosition(uint32_t marker)
 {
-    if (mCbf == 0) return INVALID_OPERATION;
+    if (mCbf == NULL) return INVALID_OPERATION;
 
     mMarkerPosition = marker;
     mMarkerReached = false;
@@ -633,7 +633,7 @@
 
 status_t AudioTrack::getMarkerPosition(uint32_t *marker)
 {
-    if (marker == 0) return BAD_VALUE;
+    if (marker == NULL) return BAD_VALUE;
 
     *marker = mMarkerPosition;
 
@@ -642,7 +642,7 @@
 
 status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod)
 {
-    if (mCbf == 0) return INVALID_OPERATION;
+    if (mCbf == NULL) return INVALID_OPERATION;
 
     uint32_t curPosition;
     getPosition(&curPosition);
@@ -654,7 +654,7 @@
 
 status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod)
 {
-    if (updatePeriod == 0) return BAD_VALUE;
+    if (updatePeriod == NULL) return BAD_VALUE;
 
     *updatePeriod = mUpdatePeriod;
 
@@ -679,7 +679,7 @@
 
 status_t AudioTrack::getPosition(uint32_t *position)
 {
-    if (position == 0) return BAD_VALUE;
+    if (position == NULL) return BAD_VALUE;
     AutoMutex lock(mLock);
     *position = mFlushed ? 0 : mCblk->server;
 
@@ -837,7 +837,7 @@
         mCblk->stepUser(mCblk->frameCount);
     }
 
-    mCblk->volumeLR = (uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000);
+    mCblk->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000));
     mCblk->setSendLevel(mSendLevel);
     mAudioTrack->attachAuxEffect(mAuxEffectId);
     mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
@@ -1319,8 +1319,8 @@
 
 audio_track_cblk_t::audio_track_cblk_t()
     : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
-    userBase(0), serverBase(0), buffers(0), frameCount(0),
-    loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
+    userBase(0), serverBase(0), buffers(NULL), frameCount(0),
+    loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000),
     mSendLevel(0), flags(0)
 {
 }
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 0d442ef..fc5520f 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -432,7 +432,7 @@
                             uint32_t *pSamplingRate,
                             audio_format_t *pFormat,
                             uint32_t *pChannels,
-                            uint32_t acoustics)
+                            audio_in_acoustics_t acoustics)
     {
         Parcel data, reply;
         uint32_t devices = pDevices ? *pDevices : 0;
@@ -445,7 +445,7 @@
         data.writeInt32(samplingRate);
         data.writeInt32(format);
         data.writeInt32(channels);
-        data.writeInt32(acoustics);
+        data.writeInt32((int32_t) acoustics);
         remote()->transact(OPEN_INPUT, data, &reply);
         int input = reply.readInt32();
         devices = reply.readInt32();
@@ -640,7 +640,7 @@
                 *id = tmp;
             }
             tmp = reply.readInt32();
-            if (enabled) {
+            if (enabled != NULL) {
                 *enabled = tmp;
             }
             effect = interface_cast<IEffect>(reply.readStrongBinder());
@@ -881,13 +881,13 @@
             uint32_t samplingRate = data.readInt32();
             audio_format_t format = (audio_format_t) data.readInt32();
             uint32_t channels = data.readInt32();
-            uint32_t acoutics = data.readInt32();
+            audio_in_acoustics_t acoustics = (audio_in_acoustics_t) data.readInt32();
 
             int input = openInput(&devices,
                                      &samplingRate,
                                      &format,
                                      &channels,
-                                     acoutics);
+                                     acoustics);
             reply->writeInt32(input);
             reply->writeInt32(devices);
             reply->writeInt32(samplingRate);
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
index 5a3f250..9458bc0 100644
--- a/media/libmedia/IAudioFlingerClient.cpp
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -73,7 +73,7 @@
             CHECK_INTERFACE(IAudioFlingerClient, data, reply);
             int event = data.readInt32();
             int ioHandle = data.readInt32();
-            void *param2 = 0;
+            void *param2 = NULL;
             AudioSystem::OutputDescriptor desc;
             uint32_t stream;
             if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 5ceb912..e6e989d 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -751,7 +751,7 @@
 
 // Used by ToneGenerator::getToneForRegion() to convert user specified supervisory tone type
 // to actual tone for current region.
-const unsigned char ToneGenerator::sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONES] = {
+const unsigned char /*tone_type*/ ToneGenerator::sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONES] = {
         {   // ANSI
             TONE_ANSI_DIAL,             // TONE_SUP_DIAL
             TONE_ANSI_BUSY,             // TONE_SUP_BUSY
@@ -811,9 +811,9 @@
     mThreadCanCallJava = threadCanCallJava;
     mStreamType = streamType;
     mVolume = volume;
-    mpAudioTrack = 0;
-    mpToneDesc = 0;
-    mpNewToneDesc = 0;
+    mpAudioTrack = NULL;
+    mpToneDesc = NULL;
+    mpNewToneDesc = NULL;
     // Generate tone by chunks of 20 ms to keep cadencing precision
     mProcessSize = (mSamplingRate * 20) / 1000;
 
@@ -855,7 +855,7 @@
 ToneGenerator::~ToneGenerator() {
     ALOGV("ToneGenerator destructor\n");
 
-    if (mpAudioTrack) {
+    if (mpAudioTrack != NULL) {
         stopTone();
         ALOGV("Delete Track: %p\n", mpAudioTrack);
         delete mpAudioTrack;
@@ -878,7 +878,7 @@
 //        none
 //
 ////////////////////////////////////////////////////////////////////////////////
-bool ToneGenerator::startTone(int toneType, int durationMs) {
+bool ToneGenerator::startTone(tone_type toneType, int durationMs) {
     bool lResult = false;
     status_t lStatus;
 
@@ -1012,7 +1012,7 @@
 
     if (mpAudioTrack) {
         delete mpAudioTrack;
-        mpAudioTrack = 0;
+        mpAudioTrack = NULL;
     }
 
    // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size
@@ -1048,7 +1048,7 @@
     if (mpAudioTrack) {
         ALOGV("Delete Track I: %p\n", mpAudioTrack);
         delete mpAudioTrack;
-        mpAudioTrack = 0;
+        mpAudioTrack = NULL;
     }
 
     return false;
@@ -1317,7 +1317,7 @@
 bool ToneGenerator::prepareWave() {
     unsigned int segmentIdx = 0;
 
-    if (!mpNewToneDesc) {
+    if (mpNewToneDesc == NULL) {
         return false;
     }
 
@@ -1434,13 +1434,13 @@
 //        none
 //
 ////////////////////////////////////////////////////////////////////////////////
-int ToneGenerator::getToneForRegion(int toneType) {
-    int regionTone;
+ToneGenerator::tone_type ToneGenerator::getToneForRegion(tone_type toneType) {
+    tone_type regionTone;
 
     if (mRegion == CEPT || toneType < FIRST_SUP_TONE || toneType > LAST_SUP_TONE) {
         regionTone = toneType;
     } else {
-        regionTone = sToneMappingTable[mRegion][toneType - FIRST_SUP_TONE];
+        regionTone = (tone_type) sToneMappingTable[mRegion][toneType - FIRST_SUP_TONE];
     }
 
     ALOGV("getToneForRegion, tone %d, region %d, regionTone %d", toneType, mRegion, regionTone);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 03e8a06..483e5ab 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -78,6 +78,7 @@
 
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_color_conversion \
+        libstagefright_aacenc \
         libstagefright_avcenc \
         libstagefright_m4vh263enc \
         libstagefright_matroska \
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index af4aa79..381320b 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "OMXCodec"
 #include <utils/Log.h>
 
+#include "include/AACEncoder.h"
 #include "include/AVCEncoder.h"
 #include "include/M4vH263Encoder.h"
 
@@ -68,6 +69,7 @@
 
 #define FACTORY_REF(name) { #name, Make##name },
 
+FACTORY_CREATE_ENCODER(AACEncoder)
 FACTORY_CREATE_ENCODER(AVCEncoder)
 FACTORY_CREATE_ENCODER(M4vH263Encoder)
 
@@ -80,6 +82,7 @@
     };
 
     static const FactoryInfo kFactoryInfo[] = {
+        FACTORY_REF(AACEncoder)
         FACTORY_REF(AVCEncoder)
         FACTORY_REF(M4vH263Encoder)
     };
@@ -145,6 +148,7 @@
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.encoder" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.encoder" },
+    { MEDIA_MIMETYPE_AUDIO_AAC, "AACEncoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.MPEG4E" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" },
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 38c85bb..bcba3c2 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -16,16 +16,23 @@
 
 package com.android.internal.policy.impl;
 
-import android.app.Activity;
+import com.android.internal.app.ShutdownThread;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.R;
+
+import android.app.ActivityManagerNative;
 import android.app.AlertDialog;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.UserInfo;
 import android.media.AudioManager;
 import android.os.Handler;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.telephony.PhoneStateListener;
@@ -39,13 +46,9 @@
 import android.widget.BaseAdapter;
 import android.widget.ImageView;
 import android.widget.TextView;
-import com.android.internal.R;
-import com.android.internal.app.ShutdownThread;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-import com.google.android.collect.Lists;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Helper to show the global actions dialog.  Each item is an {@link Action} that
@@ -101,9 +104,10 @@
     public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
         mKeyguardShowing = keyguardShowing;
         mDeviceProvisioned = isDeviceProvisioned;
-        if (mDialog == null) {
-            mDialog = createDialog();
+        if (mDialog != null) {
+            mDialog.dismiss();
         }
+        mDialog = createDialog();
         prepareDialog();
 
         mDialog.show();
@@ -187,6 +191,31 @@
             mItems.add(mSilentModeAction);
         }
 
+        List<UserInfo> users = mContext.getPackageManager().getUsers();
+        if (users.size() > 1) {
+            for (final UserInfo user : users) {
+                SinglePressAction switchToUser = new SinglePressAction(
+                        com.android.internal.R.drawable.ic_menu_cc,
+                        user.name != null ? user.name : "Primary") {
+                    public void onPress() {
+                        try {
+                            ActivityManagerNative.getDefault().switchUser(user.id);
+                        } catch (RemoteException re) {
+                            Log.e(TAG, "Couldn't switch user " + re);
+                        }
+                    }
+
+                    public boolean showDuringKeyguard() {
+                        return true;
+                    }
+
+                    public boolean showBeforeProvisioning() {
+                        return false;
+                    }
+                };
+                mItems.add(switchToUser);
+            }
+        }
         mAdapter = new MyAdapter();
 
         final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
@@ -341,12 +370,19 @@
     private static abstract class SinglePressAction implements Action {
         private final int mIconResId;
         private final int mMessageResId;
+        private final CharSequence mMessage;
 
         protected SinglePressAction(int iconResId, int messageResId) {
             mIconResId = iconResId;
             mMessageResId = messageResId;
+            mMessage = null;
         }
 
+        protected SinglePressAction(int iconResId, CharSequence message) {
+            mIconResId = iconResId;
+            mMessageResId = 0;
+            mMessage = message;
+        }
         public boolean isEnabled() {
             return true;
         }
@@ -363,7 +399,11 @@
             v.findViewById(R.id.status).setVisibility(View.GONE);
 
             icon.setImageDrawable(context.getResources().getDrawable(mIconResId));
-            messageView.setText(mMessageResId);
+            if (mMessage != null) {
+                messageView.setText(mMessage);
+            } else {
+                messageView.setText(mMessageResId);
+            }
 
             return v;
         }
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 0c44f3f..f71ba0a 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -160,7 +160,10 @@
 
 AudioFlinger::AudioFlinger()
     : BnAudioFlinger(),
-        mPrimaryHardwareDev(NULL), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
+        mPrimaryHardwareDev(NULL),
+        mHardwareStatus(AUDIO_HW_IDLE), // see also onFirstRef()
+        mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
+        mMode(AUDIO_MODE_INVALID),
         mBtNrecIsOff(false)
 {
 }
@@ -172,7 +175,6 @@
     Mutex::Autolock _l(mLock);
 
     /* TODO: move all this work into an Init() function */
-    mHardwareStatus = AUDIO_HW_IDLE;
 
     for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
         const hw_module_t *mod;
@@ -265,13 +267,10 @@
 
     result.append("Clients:\n");
     for (size_t i = 0; i < mClients.size(); ++i) {
-        wp<Client> wClient = mClients.valueAt(i);
-        if (wClient != 0) {
-            sp<Client> client = wClient.promote();
-            if (client != 0) {
-                snprintf(buffer, SIZE, "  pid: %d\n", client->pid());
-                result.append(buffer);
-            }
+        sp<Client> client = mClients.valueAt(i).promote();
+        if (client != 0) {
+            snprintf(buffer, SIZE, "  pid: %d\n", client->pid());
+            result.append(buffer);
         }
     }
 
@@ -971,7 +970,8 @@
 {
     size_t size = mNotificationClients.size();
     for (size_t i = 0; i < size; i++) {
-        mNotificationClients.valueAt(i)->client()->ioConfigChanged(event, ioHandle, param2);
+        mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioHandle,
+                                                                               param2);
     }
 }
 
@@ -985,13 +985,19 @@
 
 // ----------------------------------------------------------------------------
 
-AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id, uint32_t device)
+AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id, uint32_t device,
+        type_t type)
     :   Thread(false),
-        mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0),
-        mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID), mStandby(false), mId(id), mExiting(false),
-        mDevice(device)
+        mType(type),
+        mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0),
+        // mChannelMask
+        mChannelCount(0),
+        mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID),
+        mParamStatus(NO_ERROR),
+        mStandby(false), mId(id), mExiting(false),
+        mDevice(device),
+        mDeathRecipient(new PMDeathRecipient(this))
 {
-    mDeathRecipient = new PMDeathRecipient(this);
 }
 
 AudioFlinger::ThreadBase::~ThreadBase()
@@ -1372,20 +1378,24 @@
 AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger,
                                              AudioStreamOut* output,
                                              int id,
-                                             uint32_t device)
-    :   ThreadBase(audioFlinger, id, device),
-        mMixBuffer(NULL), mSuspended(0), mBytesWritten(0), mOutput(output),
+                                             uint32_t device,
+                                             type_t type)
+    :   ThreadBase(audioFlinger, id, device, type),
+        mMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
+        // Assumes constructor is called by AudioFlinger with it's mLock held,
+        // but it would be safer to explicitly pass initial masterMute as parameter
+        mMasterMute(audioFlinger->masterMute_l()),
+        // mStreamTypes[] initialized in constructor body
+        mOutput(output),
+        // Assumes constructor is called by AudioFlinger with it's mLock held,
+        // but it would be safer to explicitly pass initial masterVolume as parameter
+        mMasterVolume(audioFlinger->masterVolume_l()),
         mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
 {
     snprintf(mName, kNameLength, "AudioOut_%d", id);
 
     readOutputParameters();
 
-    // Assumes constructor is called by AudioFlinger with it's mLock held,
-    // but it would be safer to explicitly pass these as parameters
-    mMasterVolume = mAudioFlinger->masterVolume_l();
-    mMasterMute = mAudioFlinger->masterMute_l();
-
     // mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor
     // There is no AUDIO_STREAM_MIN, and ++ operator does not compile
     for (audio_stream_type_t stream = (audio_stream_type_t) 0; stream < AUDIO_STREAM_CNT;
@@ -1431,13 +1441,10 @@
     result.append(buffer);
     result.append("   Name  Clien Typ Fmt Chn mask   Session Buf  S M F SRate LeftV RighV  Serv       User       Main buf   Aux Buf\n");
     for (size_t i = 0; i < mActiveTracks.size(); ++i) {
-        wp<Track> wTrack = mActiveTracks[i];
-        if (wTrack != 0) {
-            sp<Track> track = wTrack.promote();
-            if (track != 0) {
-                track->dump(buffer, SIZE);
-                result.append(buffer);
-            }
+        sp<Track> track = mActiveTracks[i].promote();
+        if (track != 0) {
+            track->dump(buffer, SIZE);
+            result.append(buffer);
         }
     }
     write(fd, result.string(), result.size());
@@ -1705,7 +1712,7 @@
 // audioConfigChanged_l() must be called with AudioFlinger::mLock held
 void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
     AudioSystem::OutputDescriptor desc;
-    void *param2 = 0;
+    void *param2 = NULL;
 
     ALOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event, param);
 
@@ -1758,7 +1765,7 @@
 
 status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
 {
-    if (halFrames == 0 || dspFrames == 0) {
+    if (halFrames == NULL || dspFrames == NULL) {
         return BAD_VALUE;
     }
     Mutex::Autolock _l(mLock);
@@ -1845,13 +1852,12 @@
 
 // ----------------------------------------------------------------------------
 
-AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
-    :   PlaybackThread(audioFlinger, output, id, device),
-        mAudioMixer(NULL), mPrevMixerStatus(MIXER_IDLE)
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+        int id, uint32_t device, type_t type)
+    :   PlaybackThread(audioFlinger, output, id, device, type),
+        mAudioMixer(new AudioMixer(mFrameCount, mSampleRate)),
+        mPrevMixerStatus(MIXER_IDLE)
 {
-    mType = ThreadBase::MIXER;
-    mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
-
     // FIXME - Current mixer implementation only supports stereo output
     if (mChannelCount == 1) {
         ALOGE("Invalid audio hardware channel count");
@@ -2193,7 +2199,7 @@
                 // read original volumes with volume control
                 float typeVolume = mStreamTypes[track->type()].volume;
                 float v = masterVolume * typeVolume;
-                uint32_t vlr = cblk->volumeLR;
+                uint32_t vlr = cblk->getVolumeLR();
                 vl = vlr & 0xFFFF;
                 vr = vlr >> 16;
                 // track volumes come from shared memory, so can't be trusted and must be clamped
@@ -2515,9 +2521,10 @@
 
 // ----------------------------------------------------------------------------
 AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
-    :   PlaybackThread(audioFlinger, output, id, device)
+    :   PlaybackThread(audioFlinger, output, id, device, DIRECT)
+        // mLeftVolFloat, mRightVolFloat
+        // mLeftVolShort, mRightVolShort
 {
-    mType = ThreadBase::DIRECT;
 }
 
 AudioFlinger::DirectOutputThread::~DirectOutputThread()
@@ -2731,7 +2738,7 @@
                     } else {
                         float typeVolume = mStreamTypes[track->type()].volume;
                         float v = mMasterVolume * typeVolume;
-                        uint32_t vlr = cblk->volumeLR;
+                        uint32_t vlr = cblk->getVolumeLR();
                         float v_clamped = v * (vlr & 0xFFFF);
                         if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
                         left = v_clamped/MAX_GAIN;
@@ -2994,10 +3001,11 @@
 
 // ----------------------------------------------------------------------------
 
-AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
-    :   MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device()), mWaitTimeMs(UINT_MAX)
+AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
+        AudioFlinger::MixerThread* mainThread, int id)
+    :   MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device(), DUPLICATING),
+        mWaitTimeMs(UINT_MAX)
 {
-    mType = ThreadBase::DUPLICATING;
     addOutputTrack(mainThread);
 }
 
@@ -3246,13 +3254,17 @@
     :   RefBase(),
         mThread(thread),
         mClient(client),
-        mCblk(0),
+        mCblk(NULL),
+        // mBuffer
+        // mBufferEnd
         mFrameCount(0),
         mState(IDLE),
         mClientTid(-1),
         mFormat(format),
         mFlags(flags & ~SYSTEM_FLAGS_MASK),
         mSessionId(sessionId)
+        // mChannelCount
+        // mChannelMask
 {
     ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
 
@@ -3268,7 +3280,7 @@
         mCblkMemory = client->heap()->allocate(size);
         if (mCblkMemory != 0) {
             mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
-            if (mCblk) { // construct the shared structure in-place.
+            if (mCblk != NULL) { // construct the shared structure in-place.
                 new(mCblk) audio_track_cblk_t();
                 // clear all buffers
                 mCblk->frameCount = frameCount;
@@ -3311,7 +3323,7 @@
 
 AudioFlinger::ThreadBase::TrackBase::~TrackBase()
 {
-    if (mCblk) {
+    if (mCblk != NULL) {
         mCblk->~audio_track_cblk_t();   // destroy our shared-structure.
         if (mClient == NULL) {
             delete mCblk;
@@ -3319,6 +3331,7 @@
     }
     mCblkMemory.clear();            // and free the shared memory
     if (mClient != NULL) {
+        // Client destructor must run with AudioFlinger mutex locked
         Mutex::Autolock _l(mClient->audioFlinger()->mLock);
         mClient.clear();
     }
@@ -3385,7 +3398,7 @@
                 server %d, serverBase %d, user %d, userBase %d",
                 bufferStart, bufferEnd, mBuffer, mBufferEnd,
                 cblk->server, cblk->serverBase, cblk->user, cblk->userBase);
-        return 0;
+        return NULL;
     }
 
     return bufferStart;
@@ -3470,7 +3483,7 @@
 
 void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
 {
-    uint32_t vlr = mCblk->volumeLR;
+    uint32_t vlr = mCblk->getVolumeLR();
     snprintf(buffer, size, "   %05d %05d %03u %03u 0x%08x %05u   %04u %1d %1d %1d %05u %05u %05u  0x%08x 0x%08x 0x%08x 0x%08x\n",
             mName - AudioMixer::TRACK0,
             (mClient == NULL) ? getpid() : mClient->pid(),
@@ -3829,7 +3842,6 @@
     if (mCblk != NULL) {
         mCblk->flags |= CBLK_DIRECTION_OUT;
         mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
-        mCblk->volumeLR = (MAX_GAIN_INT << 16) | MAX_GAIN_INT;
         mOutBuffer.frameCount = 0;
         playbackThread->mTracks.add(this);
         ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, " \
@@ -4077,13 +4089,12 @@
 AudioFlinger::NotificationClient::NotificationClient(const sp<AudioFlinger>& audioFlinger,
                                                      const sp<IAudioFlingerClient>& client,
                                                      pid_t pid)
-    : mAudioFlinger(audioFlinger), mPid(pid), mClient(client)
+    : mAudioFlinger(audioFlinger), mPid(pid), mAudioFlingerClient(client)
 {
 }
 
 AudioFlinger::NotificationClient::~NotificationClient()
 {
-    mClient.clear();
 }
 
 void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)
@@ -4268,15 +4279,16 @@
                                          uint32_t channels,
                                          int id,
                                          uint32_t device) :
-    ThreadBase(audioFlinger, id, device),
-    mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL)
+    ThreadBase(audioFlinger, id, device, RECORD),
+    mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
+    // mRsmpInIndex and mInputBytes set by readInputParameters()
+    mReqChannelCount(popcount(channels)),
+    mReqSampleRate(sampleRate)
+    // mBytesRead is only meaningful while active, and so is cleared in start()
+    // (but might be better to also clear here for dump?)
 {
-    mType = ThreadBase::RECORD;
-
     snprintf(mName, kNameLength, "AudioIn_%d", id);
 
-    mReqChannelCount = popcount(channels);
-    mReqSampleRate = sampleRate;
     readInputParameters();
 }
 
@@ -4807,7 +4819,7 @@
 
 void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
     AudioSystem::OutputDescriptor desc;
-    void *param2 = 0;
+    void *param2 = NULL;
 
     switch (event) {
     case AudioSystem::INPUT_OPENED:
@@ -4985,10 +4997,10 @@
         }
         mPlaybackThreads.add(id, thread);
 
-        if (pSamplingRate) *pSamplingRate = samplingRate;
-        if (pFormat) *pFormat = format;
-        if (pChannels) *pChannels = channels;
-        if (pLatencyMs) *pLatencyMs = thread->latency();
+        if (pSamplingRate != NULL) *pSamplingRate = samplingRate;
+        if (pFormat != NULL) *pFormat = format;
+        if (pChannels != NULL) *pChannels = channels;
+        if (pLatencyMs != NULL) *pLatencyMs = thread->latency();
 
         // notify client processes of the new output creation
         thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
@@ -5040,7 +5052,7 @@
                 }
             }
         }
-        void *param2 = 0;
+        void *param2 = NULL;
         audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2);
         mPlaybackThreads.removeItem(output);
     }
@@ -5091,7 +5103,7 @@
                                 uint32_t *pSamplingRate,
                                 audio_format_t *pFormat,
                                 uint32_t *pChannels,
-                                uint32_t acoustics)
+                                audio_in_acoustics_t acoustics)
 {
     status_t status;
     RecordThread *thread = NULL;
@@ -5116,7 +5128,7 @@
 
     status = inHwDev->open_input_stream(inHwDev, *pDevices, &format,
                                         &channels, &samplingRate,
-                                        (audio_in_acoustics_t)acoustics,
+                                        acoustics,
                                         &inStream);
     ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, acoustics %x, status %d",
             inStream,
@@ -5136,7 +5148,7 @@
         ALOGV("openInput() reopening with proposed sampling rate and channels");
         status = inHwDev->open_input_stream(inHwDev, *pDevices, &format,
                                             &channels, &samplingRate,
-                                            (audio_in_acoustics_t)acoustics,
+                                            acoustics,
                                             &inStream);
     }
 
@@ -5156,9 +5168,9 @@
                                   device);
         mRecordThreads.add(id, thread);
         ALOGV("openInput() created record thread: ID %d thread %p", id, thread);
-        if (pSamplingRate) *pSamplingRate = reqSamplingRate;
-        if (pFormat) *pFormat = format;
-        if (pChannels) *pChannels = reqChannels;
+        if (pSamplingRate != NULL) *pSamplingRate = reqSamplingRate;
+        if (pFormat != NULL) *pFormat = format;
+        if (pChannels != NULL) *pChannels = reqChannels;
 
         input->stream->common.standby(&input->stream->common);
 
@@ -5183,7 +5195,7 @@
         }
 
         ALOGV("closeInput() %d", input);
-        void *param2 = 0;
+        void *param2 = NULL;
         audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, param2);
         mRecordThreads.removeItem(input);
     }
@@ -5245,12 +5257,8 @@
             return;
         }
     }
-    AudioSessionRef *ref = new AudioSessionRef();
-    ref->sessionid = audioSession;
-    ref->pid = caller;
-    ref->cnt = 1;
-    mAudioSessionRefs.push(ref);
-    ALOGV(" added new entry for %d", ref->sessionid);
+    mAudioSessionRefs.push(new AudioSessionRef(audioSession, caller));
+    ALOGV(" added new entry for %d", audioSession);
 }
 
 void AudioFlinger::releaseAudioSessionId(int audioSession)
@@ -5794,7 +5802,7 @@
         // create effect handle and connect it to effect module
         handle = new EffectHandle(effect, client, effectClient, priority);
         lStatus = effect->addHandle(handle);
-        if (enabled) {
+        if (enabled != NULL) {
             *enabled = (int)effect->isEnabled();
         }
     }
@@ -6228,7 +6236,7 @@
 
     bool enabled = false;
     EffectHandle *hdl = handle.unsafe_get();
-    if (hdl) {
+    if (hdl != NULL) {
         ALOGV("removeHandle() unsafe_get OK");
         enabled = hdl->enabled();
     }
@@ -6864,7 +6872,7 @@
     if (mCblkMemory != 0) {
         mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer());
 
-        if (mCblk) {
+        if (mCblk != NULL) {
             new(mCblk) effect_param_cblk_t();
             mBuffer = (uint8_t *)mCblk + bufOffset;
          }
@@ -6961,7 +6969,7 @@
     // release sp on module => module destructor can be called now
     mEffect.clear();
     if (mClient != 0) {
-        if (mCblk) {
+        if (mCblk != NULL) {
             mCblk->~effect_param_cblk_t();   // destroy our shared-structure.
         }
         mCblkMemory.clear();            // and free the shared memory
@@ -7091,7 +7099,7 @@
 
 void AudioFlinger::EffectHandle::dump(char* buffer, size_t size)
 {
-    bool locked = mCblk ? tryLock(mCblk->lock) : false;
+    bool locked = mCblk != NULL && tryLock(mCblk->lock);
 
     snprintf(buffer, size, "\t\t\t%05d %05d    %01u    %01u      %05u  %05u\n",
             (mClient == NULL) ? getpid() : mClient->pid(),
@@ -7553,7 +7561,8 @@
             ALOGV("setEffectSuspendedAll_l() add entry for 0");
         }
         if (desc->mRefCount++ == 0) {
-            Vector< sp<EffectModule> > effects = getSuspendEligibleEffects();
+            Vector< sp<EffectModule> > effects;
+            getSuspendEligibleEffects(effects);
             for (size_t i = 0; i < effects.size(); i++) {
                 setEffectSuspended_l(&effects[i]->desc().type, true);
             }
@@ -7604,16 +7613,14 @@
     return true;
 }
 
-Vector< sp<AudioFlinger::EffectModule> > AudioFlinger::EffectChain::getSuspendEligibleEffects()
+void AudioFlinger::EffectChain::getSuspendEligibleEffects(Vector< sp<AudioFlinger::EffectModule> > &effects)
 {
-    Vector< sp<EffectModule> > effects;
+    effects.clear();
     for (size_t i = 0; i < mEffects.size(); i++) {
-        if (!isEffectEligibleForSuspend(mEffects[i]->desc())) {
-            continue;
+        if (isEffectEligibleForSuspend(mEffects[i]->desc())) {
+            effects.add(mEffects[i]);
         }
-        effects.add(mEffects[i]);
     }
-    return effects;
 }
 
 sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectIfEnabled(
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index ece4ba2..3f3188c 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -131,7 +131,7 @@
                             uint32_t *pSamplingRate,
                             audio_format_t *pFormat,
                             uint32_t *pChannels,
-                            uint32_t acoustics);
+                            audio_in_acoustics_t acoustics);
 
     virtual status_t closeInput(int input);
 
@@ -233,9 +233,9 @@
     private:
                             Client(const Client&);
                             Client& operator = (const Client&);
-        sp<AudioFlinger>    mAudioFlinger;
-        sp<MemoryDealer>    mMemoryDealer;
-        pid_t               mPid;
+        const sp<AudioFlinger> mAudioFlinger;
+        const sp<MemoryDealer> mMemoryDealer;
+        const pid_t         mPid;
     };
 
     // --- Notification Client ---
@@ -246,7 +246,7 @@
                                                 pid_t pid);
         virtual             ~NotificationClient();
 
-                sp<IAudioFlingerClient>    client() { return mClient; }
+                sp<IAudioFlingerClient> audioFlingerClient() const { return mAudioFlingerClient; }
 
                 // IBinder::DeathRecipient
                 virtual     void        binderDied(const wp<IBinder>& who);
@@ -255,9 +255,9 @@
                             NotificationClient(const NotificationClient&);
                             NotificationClient& operator = (const NotificationClient&);
 
-        sp<AudioFlinger>        mAudioFlinger;
-        pid_t                   mPid;
-        sp<IAudioFlingerClient> mClient;
+        const sp<AudioFlinger>  mAudioFlinger;
+        const pid_t             mPid;
+        const sp<IAudioFlingerClient> mAudioFlingerClient;
     };
 
     class TrackHandle;
@@ -277,17 +277,17 @@
 
     class ThreadBase : public Thread {
     public:
-        ThreadBase (const sp<AudioFlinger>& audioFlinger, int id, uint32_t device);
-        virtual             ~ThreadBase();
 
-
-        enum type {
+        enum type_t {
             MIXER,              // Thread class is MixerThread
             DIRECT,             // Thread class is DirectOutputThread
             DUPLICATING,        // Thread class is DuplicatingThread
             RECORD              // Thread class is RecordThread
         };
 
+        ThreadBase (const sp<AudioFlinger>& audioFlinger, int id, uint32_t device, type_t type);
+        virtual             ~ThreadBase();
+
         status_t dumpBase(int fd, const Vector<String16>& args);
         status_t dumpEffectChains(int fd, const Vector<String16>& args);
 
@@ -367,8 +367,8 @@
             bool step();
             void reset();
 
-            wp<ThreadBase>      mThread;
-            sp<Client>          mClient;
+            const wp<ThreadBase> mThread;
+            /*const*/ sp<Client> mClient;   // see explanation at ~TrackBase() why not const
             sp<IMemory>         mCblkMemory;
             audio_track_cblk_t* mCblk;
             void*               mBuffer;
@@ -377,9 +377,9 @@
             // we don't really need a lock for these
             track_state         mState;
             int                 mClientTid;
-            audio_format_t      mFormat;
+            const audio_format_t mFormat;
             uint32_t            mFlags;
-            int                 mSessionId;
+            const int           mSessionId;
             uint8_t             mChannelCount;
             uint32_t            mChannelMask;
         };
@@ -408,7 +408,7 @@
         };
 
         virtual     status_t    initCheck() const = 0;
-                    int         type() const { return mType; }
+                    type_t      type() const { return mType; }
                     uint32_t    sampleRate() const;
                     int         channelCount() const;
                     audio_format_t format() const;
@@ -530,9 +530,9 @@
         friend class RecordThread;
         friend class RecordTrack;
 
-                    int                     mType;
+                    const type_t            mType;
                     Condition               mWaitWorkCV;
-                    sp<AudioFlinger>        mAudioFlinger;
+                    const sp<AudioFlinger>  mAudioFlinger;
                     uint32_t                mSampleRate;
                     size_t                  mFrameCount;
                     uint32_t                mChannelMask;
@@ -553,7 +553,7 @@
                     char                    mName[kNameLength];
                     sp<IPowerManager>       mPowerManager;
                     sp<IBinder>             mWakeLockToken;
-                    sp<PMDeathRecipient>    mDeathRecipient;
+                    const sp<PMDeathRecipient> mDeathRecipient;
                     // list of suspended effects per session and per type. The first vector is
                     // keyed by session ID, the second by type UUID timeLow field
                     KeyedVector< int, KeyedVector< int, sp<SuspendedSessionDesc> > >  mSuspendedSessions;
@@ -671,7 +671,7 @@
                     bool        write(int16_t* data, uint32_t frames);
                     bool        bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; }
                     bool        isActive() { return mActive; }
-            wp<ThreadBase>&     thread()  { return mThread; }
+            const wp<ThreadBase>& thread() { return mThread; }
 
         private:
 
@@ -688,10 +688,11 @@
             Vector < Buffer* >          mBufferQueue;
             AudioBufferProvider::Buffer mOutBuffer;
             bool                        mActive;
-            DuplicatingThread*          mSourceThread;
+            DuplicatingThread* const mSourceThread; // for waitTimeMs() in write()
         };  // end of OutputTrack
 
-        PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
+        PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id,
+                        uint32_t device, type_t type);
         virtual             ~PlaybackThread();
 
         virtual     status_t    dump(int fd, const Vector<String16>& args);
@@ -817,7 +818,8 @@
         MixerThread (const sp<AudioFlinger>& audioFlinger,
                      AudioStreamOut* output,
                      int id,
-                     uint32_t device);
+                     uint32_t device,
+                     type_t type = MIXER);
         virtual             ~MixerThread();
 
         // Thread virtuals
@@ -917,7 +919,7 @@
         virtual status_t onTransact(
             uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
     private:
-        sp<PlaybackThread::Track> mTrack;
+        const sp<PlaybackThread::Track> mTrack;
     };
 
     friend class Client;
@@ -1021,8 +1023,8 @@
                 int16_t                             *mRsmpInBuffer;
                 size_t                              mRsmpInIndex;
                 size_t                              mInputBytes;
-                int                                 mReqChannelCount;
-                uint32_t                            mReqSampleRate;
+                const int                           mReqChannelCount;
+                const uint32_t                      mReqSampleRate;
                 ssize_t                             mBytesRead;
     };
 
@@ -1036,7 +1038,7 @@
         virtual status_t onTransact(
             uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
     private:
-        sp<RecordThread::RecordTrack> mRecordTrack;
+        const sp<RecordThread::RecordTrack> mRecordTrack;
     };
 
     //--- Audio Effect Management
@@ -1105,7 +1107,7 @@
         int16_t     *outBuffer() { return mConfig.outputCfg.buffer.s16; }
         void        setChain(const wp<EffectChain>& chain) { mChain = chain; }
         void        setThread(const wp<ThreadBase>& thread) { mThread = thread; }
-        wp<ThreadBase>& thread() { return mThread; }
+        const wp<ThreadBase>& thread() { return mThread; }
 
         status_t addHandle(const sp<EffectHandle>& handle);
         void disconnect(const wp<EffectHandle>& handle, bool unpiniflast);
@@ -1325,7 +1327,8 @@
 
         // get a list of effect modules to suspend when an effect of the type
         // passed is enabled.
-        Vector< sp<EffectModule> > getSuspendEligibleEffects();
+        void                       getSuspendEligibleEffects(Vector< sp<EffectModule> > &effects);
+
         // get an effect module if it is currently enable
         sp<EffectModule> getEffectIfEnabled(const effect_uuid_t *type);
         // true if the effect whose descriptor is passed can be suspended
@@ -1377,8 +1380,11 @@
     };
 
     struct AudioSessionRef {
-        int sessionid;
-        pid_t pid;
+        // FIXME rename parameter names when fields get "m" prefix
+        AudioSessionRef(int sessionid_, pid_t pid_) :
+            sessionid(sessionid_), pid(pid_), cnt(1) {}
+        const int sessionid;
+        const pid_t pid;
         int cnt;
     };
 
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index a01c6a8..0b9f8ba 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -48,9 +48,10 @@
     mState.enabledTracks= 0;
     mState.needsChanged = 0;
     mState.frameCount   = frameCount;
+    mState.hook         = process__nop;
     mState.outputTemp   = NULL;
     mState.resampleTemp = NULL;
-    mState.hook         = process__nop;
+    // mState.reserved
     track_t* t = mState.tracks;
     for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
         t->needs = 0;
@@ -70,12 +71,13 @@
         t->enabled = 0;
         t->format = 16;
         t->channelMask = AUDIO_CHANNEL_OUT_STEREO;
-        t->buffer.raw = 0;
         t->bufferProvider = NULL;
+        t->buffer.raw = NULL;
+        // t->buffer.frameCount
         t->hook = NULL;
+        t->in = NULL;
         t->resampler = NULL;
         t->sampleRate = mSampleRate;
-        t->in = NULL;
         t->mainBuffer = NULL;
         t->auxBuffer = NULL;
         t++;
@@ -123,7 +125,7 @@
         track.enabled = 0;
         invalidateState(1<<name);
     }
-    if (track.resampler) {
+    if (track.resampler != NULL) {
         // delete  the resampler
         delete track.resampler;
         track.resampler = NULL;
@@ -807,7 +809,7 @@
             while (outFrames) {
                 t1.buffer.frameCount = outFrames;
                 t1.bufferProvider->getNextBuffer(&t1.buffer);
-                if (!t1.buffer.raw) break;
+                if (t1.buffer.raw == NULL) break;
                 outFrames -= t1.buffer.frameCount;
                 t1.bufferProvider->releaseBuffer(&t1.buffer);
             }
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 7695d2b..1dddbb3 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -144,9 +144,9 @@
     }
     mInputs.clear();
 
-    if (mpAudioPolicy && mpAudioPolicyDev)
+    if (mpAudioPolicy != NULL && mpAudioPolicyDev != NULL)
         mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy);
-    if (mpAudioPolicyDev)
+    if (mpAudioPolicyDev != NULL)
         audio_policy_dev_close(mpAudioPolicyDev);
 }
 
@@ -789,7 +789,8 @@
     return NO_ERROR;
 }
 
-void AudioPolicyService::AudioCommandThread::startToneCommand(int type, audio_stream_type_t stream)
+void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::tone_type type,
+        audio_stream_type_t stream)
 {
     AudioCommand *command = new AudioCommand();
     command->mCommand = START_TONE;
@@ -1159,7 +1160,7 @@
     if (param == NULL && value == NULL) {
         // try to parse simple parameter form {int int}
         param = root->first_child;
-        if (param) {
+        if (param != NULL) {
             // Note: that a pair of random strings is read as 0 0
             int *ptr = (int *)fx_param->data;
             int *ptr2 = (int *)((char *)param + sizeof(effect_param_t));
@@ -1418,7 +1419,7 @@
                                             uint32_t *pSamplingRate,
                                             audio_format_t *pFormat,
                                             uint32_t *pChannels,
-                                            uint32_t acoustics)
+                                            audio_in_acoustics_t acoustics)
 {
     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
     if (af == NULL) {
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 3c0f5ed..62219e5 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -79,7 +79,7 @@
                                     audio_format_t format = AUDIO_FORMAT_DEFAULT,
                                     uint32_t channels = 0,
                                     audio_in_acoustics_t acoustics =
-                                            (audio_in_acoustics_t)0,
+                                            (audio_in_acoustics_t)0 /*AUDIO_IN_ACOUSTICS_NONE*/,
                                     int audioSession = 0);
     virtual status_t startInput(audio_io_handle_t input);
     virtual status_t stopInput(audio_io_handle_t input);
@@ -171,7 +171,8 @@
         virtual     bool        threadLoop();
 
                     void        exit();
-                    void        startToneCommand(int type = 0, audio_stream_type_t stream = AUDIO_STREAM_VOICE_CALL);
+                    void        startToneCommand(ToneGenerator::tone_type type,
+                                                 audio_stream_type_t stream);
                     void        stopToneCommand();
                     status_t    volumeCommand(audio_stream_type_t stream, float volume, int output, int delayMs = 0);
                     status_t    parametersCommand(int ioHandle, const char *keyValuePairs, int delayMs = 0);
@@ -198,7 +199,7 @@
 
         class ToneData {
         public:
-            int mType;      // tone type (START_TONE only)
+            ToneGenerator::tone_type mType; // tone type (START_TONE only)
             audio_stream_type_t mStream;    // stream type (START_TONE only)
         };
 
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 4f81178..081f1f4 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -24,67 +24,35 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.Intent.FilterComparison;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.net.Uri;
 import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
-import android.util.TypedValue;
-import android.util.Xml;
+import android.util.SparseArray;
 import android.widget.RemoteViews;
 
 import com.android.internal.appwidget.IAppWidgetHost;
 import com.android.internal.appwidget.IAppWidgetService;
-import com.android.internal.os.AtomicFile;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.widget.IRemoteViewsAdapterConnection;
-import com.android.internal.widget.IRemoteViewsFactory;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
-import java.util.Set;
 
+
+/**
+ * Redirects calls to this service to the instance of the service for the appropriate user.
+ */
 class AppWidgetService extends IAppWidgetService.Stub
 {
     private static final String TAG = "AppWidgetService";
 
-    private static final String SETTINGS_FILENAME = "appwidgets.xml";
-    private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
-
     /*
      * When identifying a Host or Provider based on the calling process, use the uid field.
      * When identifying a Host or Provider based on a package manager broadcast, use the
@@ -125,11 +93,9 @@
      * globally and may lead us to leak AppWidgetService instances (if there were more than one).
      */
     static class ServiceConnectionProxy implements ServiceConnection {
-        private final Pair<Integer, Intent.FilterComparison> mKey;
         private final IBinder mConnectionCb;
 
         ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
-            mKey = key;
             mConnectionCb = connectionCb;
         }
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -155,13 +121,6 @@
         }
     }
 
-    // Manages active connections to RemoteViewsServices
-    private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
-        mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
-    // Manages persistent references to RemoteViewsServices from different App Widgets
-    private final HashMap<FilterComparison, HashSet<Integer>>
-        mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
-
     Context mContext;
     Locale mLocale;
     PackageManager mPackageManager;
@@ -171,35 +130,32 @@
     final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
     ArrayList<Host> mHosts = new ArrayList<Host>();
     boolean mSafeMode;
-    boolean mStateLoaded;
 
-    // These are for debugging only -- widgets are going missing in some rare instances
-    ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
-    ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
+
+    private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
 
     AppWidgetService(Context context) {
         mContext = context;
-        mPackageManager = context.getPackageManager();
-        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+        mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
+        AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0);
+        mAppWidgetServices.append(0, primary);
     }
 
     public void systemReady(boolean safeMode) {
         mSafeMode = safeMode;
 
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-        }
+        mAppWidgetServices.get(0).systemReady(safeMode);
 
         // Register for the boot completed broadcast, so we can send the
-        // ENABLE broacasts.  If we try to send them now, they time out,
+        // ENABLE broacasts. If we try to send them now, they time out,
         // because the system isn't ready to handle them yet.
         mContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
 
         // Register for configuration changes so we can update the names
         // of the widgets when the locale changes.
-        mContext.registerReceiver(mBroadcastReceiver,
-                new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
+        mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
+                Intent.ACTION_CONFIGURATION_CHANGED), null, null);
 
         // Register for broadcasts about package install, etc., so we can
         // update the provider list.
@@ -216,216 +172,24 @@
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
     }
 
-    private void ensureStateLoadedLocked() {
-        if (!mStateLoaded) {
-            loadAppWidgetList();
-            loadStateLocked();
-            mStateLoaded = true;
-        }
+    @Override
+    public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
+        return getImplForUser().allocateAppWidgetId(packageName, hostId);
     }
-
-    private void dumpProvider(Provider p, int index, PrintWriter pw) {
-        AppWidgetProviderInfo info = p.info;
-        pw.print("  ["); pw.print(index); pw.print("] provider ");
-                pw.print(info.provider.flattenToShortString());
-                pw.println(':');
-        pw.print("    min=("); pw.print(info.minWidth);
-                pw.print("x"); pw.print(info.minHeight);
-        pw.print(")   minResize=("); pw.print(info.minResizeWidth);
-                pw.print("x"); pw.print(info.minResizeHeight);
-                pw.print(") updatePeriodMillis=");
-                pw.print(info.updatePeriodMillis);
-                pw.print(" resizeMode=");
-                pw.print(info.resizeMode);
-                pw.print(" autoAdvanceViewId=");
-                pw.print(info.autoAdvanceViewId);
-                pw.print(" initialLayout=#");
-                pw.print(Integer.toHexString(info.initialLayout));
-                pw.print(" zombie="); pw.println(p.zombie);
-    }
-
-    private void dumpHost(Host host, int index, PrintWriter pw) {
-        pw.print("  ["); pw.print(index); pw.print("] hostId=");
-                pw.print(host.hostId); pw.print(' ');
-                pw.print(host.packageName); pw.print('/');
-        pw.print(host.uid); pw.println(':');
-        pw.print("    callbacks="); pw.println(host.callbacks);
-        pw.print("    instances.size="); pw.print(host.instances.size());
-                pw.print(" zombie="); pw.println(host.zombie);
-    }
-
-    private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
-        pw.print("  ["); pw.print(index); pw.print("] id=");
-                pw.println(id.appWidgetId);
-        pw.print("    hostId=");
-                pw.print(id.host.hostId); pw.print(' ');
-                pw.print(id.host.packageName); pw.print('/');
-                pw.println(id.host.uid);
-        if (id.provider != null) {
-            pw.print("    provider=");
-                    pw.println(id.provider.info.provider.flattenToShortString());
-        }
-        if (id.host != null) {
-            pw.print("    host.callbacks="); pw.println(id.host.callbacks);
-        }
-        if (id.views != null) {
-            pw.print("    views="); pw.println(id.views);
-        }
+    
+    @Override
+    public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
+        getImplForUser().deleteAppWidgetId(appWidgetId);
     }
 
     @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
-                != PackageManager.PERMISSION_GRANTED) {
-            pw.println("Permission Denial: can't dump from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-            return;
-        }
-
-        synchronized (mAppWidgetIds) {
-            int N = mInstalledProviders.size();
-            pw.println("Providers:");
-            for (int i=0; i<N; i++) {
-                dumpProvider(mInstalledProviders.get(i), i, pw);
-            }
-
-            N = mAppWidgetIds.size();
-            pw.println(" ");
-            pw.println("AppWidgetIds:");
-            for (int i=0; i<N; i++) {
-                dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
-            }
-
-            N = mHosts.size();
-            pw.println(" ");
-            pw.println("Hosts:");
-            for (int i=0; i<N; i++) {
-                dumpHost(mHosts.get(i), i, pw);
-            }
-
-            N = mDeletedProviders.size();
-            pw.println(" ");
-            pw.println("Deleted Providers:");
-            for (int i=0; i<N; i++) {
-                dumpProvider(mDeletedProviders.get(i), i, pw);
-            }
-
-            N = mDeletedHosts.size();
-            pw.println(" ");
-            pw.println("Deleted Hosts:");
-            for (int i=0; i<N; i++) {
-                dumpHost(mDeletedHosts.get(i), i, pw);
-            }
-        }
+    public void deleteHost(int hostId) throws RemoteException {
+        getImplForUser().deleteHost(hostId);
     }
 
-    public int allocateAppWidgetId(String packageName, int hostId) {
-        int callingUid = enforceCallingUid(packageName);
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            int appWidgetId = mNextAppWidgetId++;
-
-            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
-
-            AppWidgetId id = new AppWidgetId();
-            id.appWidgetId = appWidgetId;
-            id.host = host;
-
-            host.instances.add(id);
-            mAppWidgetIds.add(id);
-
-            saveStateLocked();
-
-            return appWidgetId;
-        }
-    }
-
-    public void deleteAppWidgetId(int appWidgetId) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id != null) {
-                deleteAppWidgetLocked(id);
-                saveStateLocked();
-            }
-        }
-    }
-
-    public void deleteHost(int hostId) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            int callingUid = getCallingUid();
-            Host host = lookupHostLocked(callingUid, hostId);
-            if (host != null) {
-                deleteHostLocked(host);
-                saveStateLocked();
-            }
-        }
-    }
-
-    public void deleteAllHosts() {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            int callingUid = getCallingUid();
-            final int N = mHosts.size();
-            boolean changed = false;
-            for (int i=N-1; i>=0; i--) {
-                Host host = mHosts.get(i);
-                if (host.uid == callingUid) {
-                    deleteHostLocked(host);
-                    changed = true;
-                }
-            }
-            if (changed) {
-                saveStateLocked();
-            }
-        }
-    }
-
-    void deleteHostLocked(Host host) {
-        final int N = host.instances.size();
-        for (int i=N-1; i>=0; i--) {
-            AppWidgetId id = host.instances.get(i);
-            deleteAppWidgetLocked(id);
-        }
-        host.instances.clear();
-        mHosts.remove(host);
-        mDeletedHosts.add(host);
-        // it's gone or going away, abruptly drop the callback connection
-        host.callbacks = null;
-    }
-
-    void deleteAppWidgetLocked(AppWidgetId id) {
-        // We first unbind all services that are bound to this id
-        unbindAppWidgetRemoteViewsServicesLocked(id);
-
-        Host host = id.host;
-        host.instances.remove(id);
-        pruneHostLocked(host);
-
-        mAppWidgetIds.remove(id);
-
-        Provider p = id.provider;
-        if (p != null) {
-            p.instances.remove(id);
-            if (!p.zombie) {
-                // send the broacast saying that this appWidgetId has been deleted
-                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
-                intent.setComponent(p.info.provider);
-                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
-                mContext.sendBroadcast(intent);
-                if (p.instances.size() == 0) {
-                    // cancel the future updates
-                    cancelBroadcasts(p);
-
-                    // send the broacast saying that the provider is not in use any more
-                    intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
-                    intent.setComponent(p.info.provider);
-                    mContext.sendBroadcast(intent);
-                }
-            }
-        }
+    @Override
+    public void deleteAllHosts() throws RemoteException {
+        getImplForUser().deleteAllHosts();
     }
 
     void cancelBroadcasts(Provider p) {
@@ -441,617 +205,58 @@
         }
     }
 
-    public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
-                "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
-        
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mAppWidgetIds) {
-                ensureStateLoadedLocked();
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-                if (id == null) {
-                    throw new IllegalArgumentException("bad appWidgetId");
-                }
-                if (id.provider != null) {
-                    throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
-                            + id.provider.info.provider);
-                }
-                Provider p = lookupProviderLocked(provider);
-                if (p == null) {
-                    throw new IllegalArgumentException("not a appwidget provider: " + provider);
-                }
-                if (p.zombie) {
-                    throw new IllegalArgumentException("can't bind to a 3rd party provider in"
-                            + " safe mode: " + provider);
-                }
-    
-                id.provider = p;
-                p.instances.add(id);
-                int instancesSize = p.instances.size();
-                if (instancesSize == 1) {
-                    // tell the provider that it's ready
-                    sendEnableIntentLocked(p);
-                }
-    
-                // send an update now -- We need this update now, and just for this appWidgetId.
-                // It's less critical when the next one happens, so when we schdule the next one,
-                // we add updatePeriodMillis to its start time.  That time will have some slop,
-                // but that's okay.
-                sendUpdateIntentLocked(p, new int[] { appWidgetId });
-    
-                // schedule the future updates
-                registerForBroadcastsLocked(p, getAppWidgetIds(p));
-                saveStateLocked();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+    @Override
+    public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
+        getImplForUser().bindAppWidgetId(appWidgetId, provider);
     }
 
-    // Binds to a specific RemoteViewsService
-    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id == null) {
-                throw new IllegalArgumentException("bad appWidgetId");
-            }
-            final ComponentName componentName = intent.getComponent();
-            try {
-                final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
-                        PackageManager.GET_PERMISSIONS);
-                if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
-                    throw new SecurityException("Selected service does not require "
-                            + android.Manifest.permission.BIND_REMOTEVIEWS
-                            + ": " + componentName);
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                throw new IllegalArgumentException("Unknown component " + componentName);
-            }
-
-            // If there is already a connection made for this service intent, then disconnect from
-            // that first.  (This does not allow multiple connections to the same service under
-            // the same key)
-            ServiceConnectionProxy conn = null;
-            FilterComparison fc = new FilterComparison(intent);
-            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
-            if (mBoundRemoteViewsServices.containsKey(key)) {
-                conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
-                conn.disconnect();
-                mContext.unbindService(conn);
-                mBoundRemoteViewsServices.remove(key);
-            }
-
-            // Bind to the RemoteViewsService (which will trigger a callback to the
-            // RemoteViewsAdapter.onServiceConnected())
-            final long token = Binder.clearCallingIdentity();
-            try {
-                conn = new ServiceConnectionProxy(key, connection);
-                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
-                mBoundRemoteViewsServices.put(key, conn);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-
-            // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
-            // when we can call back to the RemoteViewsService later to destroy associated
-            // factories.
-            incrementAppWidgetServiceRefCount(appWidgetId, fc);
-        }
+    @Override
+    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)
+            throws RemoteException {
+        getImplForUser().bindRemoteViewsService(appWidgetId, intent, connection);
     }
 
-    // Unbinds from a specific RemoteViewsService
-    public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            // Unbind from the RemoteViewsService (which will trigger a callback to the bound
-            // RemoteViewsAdapter)
-            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
-                    new FilterComparison(intent));
-            if (mBoundRemoteViewsServices.containsKey(key)) {
-                // We don't need to use the appWidgetId until after we are sure there is something
-                // to unbind.  Note that this may mask certain issues with apps calling unbind()
-                // more than necessary.
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-                if (id == null) {
-                    throw new IllegalArgumentException("bad appWidgetId");
-                }
-
-                ServiceConnectionProxy conn =
-                    (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
-                conn.disconnect();
-                mContext.unbindService(conn);
-                mBoundRemoteViewsServices.remove(key);
-            } else {
-                Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
-            }
-        }
+    @Override
+    public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
+            List<RemoteViews> updatedViews) throws RemoteException {
+        return getImplForUser().startListening(host, packageName, hostId, updatedViews);
     }
 
-    // Unbinds from a RemoteViewsService when we delete an app widget
-    private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
-        int appWidgetId = id.appWidgetId;
-        // Unbind all connections to Services bound to this AppWidgetId
-        Iterator<Pair<Integer, Intent.FilterComparison>> it =
-            mBoundRemoteViewsServices.keySet().iterator();
-        while (it.hasNext()) {
-            final Pair<Integer, Intent.FilterComparison> key = it.next();
-            if (key.first.intValue() == appWidgetId) {
-                final ServiceConnectionProxy conn = (ServiceConnectionProxy)
-                        mBoundRemoteViewsServices.get(key);
-                conn.disconnect();
-                mContext.unbindService(conn);
-                it.remove();
-            }
-        }
-
-        // Check if we need to destroy any services (if no other app widgets are
-        // referencing the same service)
-        decrementAppWidgetServiceRefCount(appWidgetId);
+    // TODO: Call this from PackageManagerService when a user is removed
+    public void removeUser(int userId) {
     }
 
-    // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
-    private void destroyRemoteViewsService(final Intent intent) {
-        final ServiceConnection conn = new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                final IRemoteViewsFactory cb =
-                    IRemoteViewsFactory.Stub.asInterface(service);
-                try {
-                    cb.onDestroy(intent);
-                } catch (RemoteException e) {
-                    e.printStackTrace();
-                } catch (RuntimeException e) {
-                    e.printStackTrace();
-                }
-                mContext.unbindService(this);
-            }
-            @Override
-            public void onServiceDisconnected(android.content.ComponentName name) {
-                // Do nothing
-            }
-        };
-
-        // Bind to the service and remove the static intent->factory mapping in the
-        // RemoteViewsService.
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
-        } finally {
-            Binder.restoreCallingIdentity(token);
+    private AppWidgetServiceImpl getImplForUser() {
+        final int userId = Binder.getOrigCallingUser();
+        AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
+        if (service == null) {
+            Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user");
+            // TODO: Verify that it's a valid user
+            service = new AppWidgetServiceImpl(mContext, userId);
+            service.systemReady(mSafeMode);
+            // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
+            service.sendInitialBroadcasts();
+            mAppWidgetServices.append(userId, service);
         }
+
+        return service;
     }
 
-    // Adds to the ref-count for a given RemoteViewsService intent
-    private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
-        HashSet<Integer> appWidgetIds = null;
-        if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
-            appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
-        } else {
-            appWidgetIds = new HashSet<Integer>();
-            mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
-        }
-        appWidgetIds.add(appWidgetId);
+    @Override
+    public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
+        return getImplForUser().getAppWidgetIds(provider);
     }
 
-    // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
-    // the ref-count reaches zero.
-    private void decrementAppWidgetServiceRefCount(int appWidgetId) {
-        Iterator<FilterComparison> it =
-            mRemoteViewsServicesAppWidgets.keySet().iterator();
-        while (it.hasNext()) {
-            final FilterComparison key = it.next();
-            final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
-            if (ids.remove(appWidgetId)) {
-                // If we have removed the last app widget referencing this service, then we
-                // should destroy it and remove it from this set
-                if (ids.isEmpty()) {
-                    destroyRemoteViewsService(key.getIntent());
-                    it.remove();
-                }
-            }
-        }
+    @Override
+    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
+        return getImplForUser().getAppWidgetInfo(appWidgetId);
     }
 
-    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id != null && id.provider != null && !id.provider.zombie) {
-                return id.provider.info;
-            }
-            return null;
-        }
+    @Override
+    public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
+        return getImplForUser().getAppWidgetViews(appWidgetId);
     }
 
-    public RemoteViews getAppWidgetViews(int appWidgetId) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id != null) {
-                return id.views;
-            }
-            return null;
-        }
-    }
-
-    public List<AppWidgetProviderInfo> getInstalledProviders() {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            final int N = mInstalledProviders.size();
-            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
-            for (int i=0; i<N; i++) {
-                Provider p = mInstalledProviders.get(i);
-                if (!p.zombie) {
-                    result.add(p.info);
-                }
-            }
-            return result;
-        }
-    }
-
-    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
-        if (appWidgetIds == null) {
-            return;
-        }
-        if (appWidgetIds.length == 0) {
-            return;
-        }
-        final int N = appWidgetIds.length;
-
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
-                updateAppWidgetInstanceLocked(id, views);
-            }
-        }
-    }
-
-    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
-        if (appWidgetIds == null) {
-            return;
-        }
-        if (appWidgetIds.length == 0) {
-            return;
-        }
-        final int N = appWidgetIds.length;
-
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
-                updateAppWidgetInstanceLocked(id, views, true);
-            }
-        }
-    }
-
-    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
-        if (appWidgetIds == null) {
-            return;
-        }
-        if (appWidgetIds.length == 0) {
-            return;
-        }
-        final int N = appWidgetIds.length;
-
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
-                notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
-            }
-        }
-    }
-
-    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            Provider p = lookupProviderLocked(provider);
-            if (p == null) {
-                Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
-                return;
-            }
-            ArrayList<AppWidgetId> instances = p.instances;
-            final int callingUid = getCallingUid();
-            final int N = instances.size();
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = instances.get(i);
-                if (canAccessAppWidgetId(id, callingUid)) {
-                    updateAppWidgetInstanceLocked(id, views);
-                }
-            }
-        }
-    }
-
-    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
-        updateAppWidgetInstanceLocked(id, views, false);
-    }
-
-    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
-        // allow for stale appWidgetIds and other badness
-        // lookup also checks that the calling process can access the appWidgetId
-        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
-        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
-
-            // We do not want to save this RemoteViews
-            if (!isPartialUpdate) id.views = views;
-
-            // is anyone listening?
-            if (id.host.callbacks != null) {
-                try {
-                    // the lock is held, but this is a oneway call
-                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
-                } catch (RemoteException e) {
-                    // It failed; remove the callback. No need to prune because
-                    // we know that this host is still referenced by this instance.
-                    id.host.callbacks = null;
-                }
-            }
-        }
-    }
-
-    void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
-        // allow for stale appWidgetIds and other badness
-        // lookup also checks that the calling process can access the appWidgetId
-        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
-        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
-            // is anyone listening?
-            if (id.host.callbacks != null) {
-                try {
-                    // the lock is held, but this is a oneway call
-                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
-                } catch (RemoteException e) {
-                    // It failed; remove the callback. No need to prune because
-                    // we know that this host is still referenced by this instance.
-                    id.host.callbacks = null;
-                }
-            }
-
-            // If the host is unavailable, then we call the associated
-            // RemoteViewsFactory.onDataSetChanged() directly
-            if (id.host.callbacks == null) {
-                Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
-                for (FilterComparison key : keys) {
-                    if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
-                        Intent intent = key.getIntent();
-
-                        final ServiceConnection conn = new ServiceConnection() {
-                            @Override
-                            public void onServiceConnected(ComponentName name, IBinder service) {
-                                IRemoteViewsFactory cb =
-                                    IRemoteViewsFactory.Stub.asInterface(service);
-                                try {
-                                    cb.onDataSetChangedAsync();
-                                } catch (RemoteException e) {
-                                    e.printStackTrace();
-                                } catch (RuntimeException e) {
-                                    e.printStackTrace();
-                                }
-                                mContext.unbindService(this);
-                            }
-                            @Override
-                            public void onServiceDisconnected(android.content.ComponentName name) {
-                                // Do nothing
-                            }
-                        };
-
-                        // Bind to the service and call onDataSetChanged()
-                        final long token = Binder.clearCallingIdentity();
-                        try {
-                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
-                        } finally {
-                            Binder.restoreCallingIdentity(token);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
-            List<RemoteViews> updatedViews) {
-        int callingUid = enforceCallingUid(packageName);
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
-            host.callbacks = callbacks;
-
-            updatedViews.clear();
-
-            ArrayList<AppWidgetId> instances = host.instances;
-            int N = instances.size();
-            int[] updatedIds = new int[N];
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = instances.get(i);
-                updatedIds[i] = id.appWidgetId;
-                updatedViews.add(id.views);
-            }
-            return updatedIds;
-        }
-    }
-
-    public void stopListening(int hostId) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            Host host = lookupHostLocked(getCallingUid(), hostId);
-            if (host != null) {
-                host.callbacks = null;
-                pruneHostLocked(host);
-            }
-        }
-    }
-
-    boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
-        if (id.host.uid == callingUid) {
-            // Apps hosting the AppWidget have access to it.
-            return true;
-        }
-        if (id.provider != null && id.provider.uid == callingUid) {
-            // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
-            return true;
-        }
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
-                == PackageManager.PERMISSION_GRANTED) {
-            // Apps that can bind have access to all appWidgetIds.
-            return true;
-        }
-        // Nobody else can access it.
-        return false;
-    }
-
-   AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
-        int callingUid = getCallingUid();
-        final int N = mAppWidgetIds.size();
-        for (int i=0; i<N; i++) {
-            AppWidgetId id = mAppWidgetIds.get(i);
-            if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
-                return id;
-            }
-        }
-        return null;
-    }
-
-    Provider lookupProviderLocked(ComponentName provider) {
-        final int N = mInstalledProviders.size();
-        for (int i=0; i<N; i++) {
-            Provider p = mInstalledProviders.get(i);
-            if (p.info.provider.equals(provider)) {
-                return p;
-            }
-        }
-        return null;
-    }
-
-    Host lookupHostLocked(int uid, int hostId) {
-        final int N = mHosts.size();
-        for (int i=0; i<N; i++) {
-            Host h = mHosts.get(i);
-            if (h.uid == uid && h.hostId == hostId) {
-                return h;
-            }
-        }
-        return null;
-    }
-
-    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
-        final int N = mHosts.size();
-        for (int i=0; i<N; i++) {
-            Host h = mHosts.get(i);
-            if (h.hostId == hostId && h.packageName.equals(packageName)) {
-                return h;
-            }
-        }
-        Host host = new Host();
-        host.packageName = packageName;
-        host.uid = uid;
-        host.hostId = hostId;
-        mHosts.add(host);
-        return host;
-    }
-
-    void pruneHostLocked(Host host) {
-        if (host.instances.size() == 0 && host.callbacks == null) {
-            mHosts.remove(host);
-        }
-    }
-
-    void loadAppWidgetList() {
-        PackageManager pm = mPackageManager;
-
-        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-        List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
-                PackageManager.GET_META_DATA);
-
-        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
-        for (int i=0; i<N; i++) {
-            ResolveInfo ri = broadcastReceivers.get(i);
-            addProviderLocked(ri);
-        }
-    }
-
-    boolean addProviderLocked(ResolveInfo ri) {
-        if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
-            return false;
-        }
-        if (!ri.activityInfo.isEnabled()) {
-            return false;
-        }
-        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
-                    ri.activityInfo.name), ri);
-        if (p != null) {
-            mInstalledProviders.add(p);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    void removeProviderLocked(int index, Provider p) {
-        int N = p.instances.size();
-        for (int i=0; i<N; i++) {
-            AppWidgetId id = p.instances.get(i);
-            // Call back with empty RemoteViews
-            updateAppWidgetInstanceLocked(id, null);
-            // Stop telling the host about updates for this from now on
-            cancelBroadcasts(p);
-            // clear out references to this appWidgetId
-            id.host.instances.remove(id);
-            mAppWidgetIds.remove(id);
-            id.provider = null;
-            pruneHostLocked(id.host);
-            id.host = null;
-        }
-        p.instances.clear();
-        mInstalledProviders.remove(index);
-        mDeletedProviders.add(p);
-        // no need to send the DISABLE broadcast, since the receiver is gone anyway
-        cancelBroadcasts(p);
-    }
-
-    void sendEnableIntentLocked(Provider p) {
-        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
-        intent.setComponent(p.info.provider);
-        mContext.sendBroadcast(intent);
-    }
-
-    void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
-        if (appWidgetIds != null && appWidgetIds.length > 0) {
-            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
-            intent.setComponent(p.info.provider);
-            mContext.sendBroadcast(intent);
-        }
-    }
-
-    void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
-        if (p.info.updatePeriodMillis > 0) {
-            // if this is the first instance, set the alarm.  otherwise,
-            // rely on the fact that we've already set it and that
-            // PendingIntent.getBroadcast will update the extras.
-            boolean alreadyRegistered = p.broadcast != null;
-            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
-            intent.setComponent(p.info.provider);
-            long token = Binder.clearCallingIdentity();
-            try {
-                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
-                        PendingIntent.FLAG_UPDATE_CURRENT);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            if (!alreadyRegistered) {
-                long period = p.info.updatePeriodMillis;
-                if (period < MIN_UPDATE_PERIOD) {
-                    period = MIN_UPDATE_PERIOD;
-                }
-                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                        SystemClock.elapsedRealtime() + period, period, p.broadcast);
-            }
-        }
-    }
-    
     static int[] getAppWidgetIds(Provider p) {
         int instancesSize = p.instances.size();
         int appWidgetIds[] = new int[instancesSize];
@@ -1060,570 +265,70 @@
         }
         return appWidgetIds;
     }
-    
-    public int[] getAppWidgetIds(ComponentName provider) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            Provider p = lookupProviderLocked(provider);
-            if (p != null && getCallingUid() == p.uid) {
-                return getAppWidgetIds(p);                
-            } else {
-                return new int[0];
-            }
-        }
+
+    @Override
+    public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
+        return getImplForUser().getInstalledProviders();
     }
 
-    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
-        Provider p = null;
-
-        ActivityInfo activityInfo = ri.activityInfo;
-        XmlResourceParser parser = null;
-        try {
-            parser = activityInfo.loadXmlMetaData(mPackageManager,
-                    AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
-            if (parser == null) {
-                Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
-                        + "AppWidget provider '" + component + '\'');
-                return null;
-            }
-
-            AttributeSet attrs = Xml.asAttributeSet(parser);
-
-            int type;
-            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
-                    && type != XmlPullParser.START_TAG) {
-                // drain whitespace, comments, etc.
-            }
-
-            String nodeName = parser.getName();
-            if (!"appwidget-provider".equals(nodeName)) {
-                Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
-                        + " AppWidget provider '" + component + '\'');
-                return null;
-            }
-
-            p = new Provider();
-            AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
-            info.provider = component;
-            p.uid = activityInfo.applicationInfo.uid;
-
-            Resources res = mPackageManager.getResourcesForApplication(
-                    activityInfo.applicationInfo);
-
-            TypedArray sa = res.obtainAttributes(attrs,
-                    com.android.internal.R.styleable.AppWidgetProviderInfo);
-
-            // These dimensions has to be resolved in the application's context.
-            // We simply send back the raw complex data, which will be
-            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
-            TypedValue value = sa.peekValue(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
-            info.minWidth = value != null ? value.data : 0; 
-            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
-            info.minHeight = value != null ? value.data : 0;
-            value = sa.peekValue(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
-            info.minResizeWidth = value != null ? value.data : info.minWidth;
-            value = sa.peekValue(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
-            info.minResizeHeight = value != null ? value.data : info.minHeight;
-
-            info.updatePeriodMillis = sa.getInt(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
-            info.initialLayout = sa.getResourceId(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
-            String className = sa.getString(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
-            if (className != null) {
-                info.configure = new ComponentName(component.getPackageName(), className);
-            }
-            info.label = activityInfo.loadLabel(mPackageManager).toString();
-            info.icon = ri.getIconResource();
-            info.previewImage = sa.getResourceId(
-            		com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
-            info.autoAdvanceViewId = sa.getResourceId(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
-            info.resizeMode = sa.getInt(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
-                    AppWidgetProviderInfo.RESIZE_NONE);
-
-            sa.recycle();
-        } catch (Exception e) {
-            // Ok to catch Exception here, because anything going wrong because
-            // of what a client process passes to us should not be fatal for the
-            // system process.
-            Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
-            return null;
-        } finally {
-            if (parser != null) parser.close();
-        }
-        return p;
+    @Override
+    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
+            throws RemoteException {
+        getImplForUser().notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
     }
 
-    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
-        PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
-        if (pkgInfo == null || pkgInfo.applicationInfo == null) {
-            throw new PackageManager.NameNotFoundException();
-        }
-        return pkgInfo.applicationInfo.uid;
+    @Override
+    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
+            throws RemoteException {
+        getImplForUser().partiallyUpdateAppWidgetIds(appWidgetIds, views);
     }
 
-    int enforceCallingUid(String packageName) throws IllegalArgumentException {
-        int callingUid = getCallingUid();
-        int packageUid;
-        try {
-            packageUid = getUidForPackage(packageName);
-        } catch (PackageManager.NameNotFoundException ex) {
-            throw new IllegalArgumentException("packageName and uid don't match packageName="
-                    + packageName);
-        }
-        if (callingUid != packageUid) {
-            throw new IllegalArgumentException("packageName and uid don't match packageName="
-                    + packageName);
-        }
-        return callingUid;
+    @Override
+    public void stopListening(int hostId) throws RemoteException {
+        getImplForUser().stopListening(hostId);
     }
 
-    void sendInitialBroadcasts() {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            final int N = mInstalledProviders.size();
-            for (int i=0; i<N; i++) {
-                Provider p = mInstalledProviders.get(i);
-                if (p.instances.size() > 0) {
-                    sendEnableIntentLocked(p);
-                    int[] appWidgetIds = getAppWidgetIds(p);
-                    sendUpdateIntentLocked(p, appWidgetIds);
-                    registerForBroadcastsLocked(p, appWidgetIds);
-                }
-            }
-        }
+    @Override
+    public void unbindRemoteViewsService(int appWidgetId, Intent intent) throws RemoteException {
+        getImplForUser().unbindRemoteViewsService(appWidgetId, intent);
     }
 
-    // only call from initialization -- it assumes that the data structures are all empty
-    void loadStateLocked() {
-        AtomicFile file = savedStateFile();
-        try {
-            FileInputStream stream = file.openRead();
-            readStateFromFileLocked(stream);
-
-            if (stream != null) {
-                try {
-                    stream.close();
-                } catch (IOException e) {
-                    Slog.w(TAG, "Failed to close state FileInputStream " + e);
-                }
-            }
-        } catch (FileNotFoundException e) {
-            Slog.w(TAG, "Failed to read state: " + e);
-        }
+    @Override
+    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
+        getImplForUser().updateAppWidgetIds(appWidgetIds, views);
     }
 
-    void saveStateLocked() {
-        AtomicFile file = savedStateFile();
-        FileOutputStream stream;
-        try {
-            stream = file.startWrite();
-            if (writeStateToFileLocked(stream)) {
-                file.finishWrite(stream);
-            } else {
-                file.failWrite(stream);
-                Slog.w(TAG, "Failed to save state, restoring backup.");
-            }
-        } catch (IOException e) {
-            Slog.w(TAG, "Failed open state file for write: " + e);
-        }
+    @Override
+    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
+            throws RemoteException {
+        getImplForUser().updateAppWidgetProvider(provider, views);
     }
 
-    boolean writeStateToFileLocked(FileOutputStream stream) {
-        int N;
-
-        try {
-            XmlSerializer out = new FastXmlSerializer();
-            out.setOutput(stream, "utf-8");
-            out.startDocument(null, true);
-            out.startTag(null, "gs");
-
-            int providerIndex = 0;
-            N = mInstalledProviders.size();
-            for (int i=0; i<N; i++) {
-                Provider p = mInstalledProviders.get(i);
-                if (p.instances.size() > 0) {
-                    out.startTag(null, "p");
-                    out.attribute(null, "pkg", p.info.provider.getPackageName());
-                    out.attribute(null, "cl", p.info.provider.getClassName());
-                    out.endTag(null, "p");
-                    p.tag = providerIndex;
-                    providerIndex++;
-                }
-            }
-
-            N = mHosts.size();
-            for (int i=0; i<N; i++) {
-                Host host = mHosts.get(i);
-                out.startTag(null, "h");
-                out.attribute(null, "pkg", host.packageName);
-                out.attribute(null, "id", Integer.toHexString(host.hostId));
-                out.endTag(null, "h");
-                host.tag = i;
-            }
-
-            N = mAppWidgetIds.size();
-            for (int i=0; i<N; i++) {
-                AppWidgetId id = mAppWidgetIds.get(i);
-                out.startTag(null, "g");
-                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
-                out.attribute(null, "h", Integer.toHexString(id.host.tag));
-                if (id.provider != null) {
-                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
-                }
-                out.endTag(null, "g");
-            }
-
-            out.endTag(null, "gs");
-
-            out.endDocument();
-            return true;
-        } catch (IOException e) {
-            Slog.w(TAG, "Failed to write state: " + e);
-            return false;
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        // Dump the state of all the app widget providers
+        for (int i = 0; i < mAppWidgetServices.size(); i++) {
+            AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+            service.dump(fd, pw, args);
         }
     }
 
-    void readStateFromFileLocked(FileInputStream stream) {
-        boolean success = false;
-
-        try {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(stream, null);
-
-            int type;
-            int providerIndex = 0;
-            HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
-            do {
-                type = parser.next();
-                if (type == XmlPullParser.START_TAG) {
-                    String tag = parser.getName();
-                    if ("p".equals(tag)) {
-                        // TODO: do we need to check that this package has the same signature
-                        // as before?
-                        String pkg = parser.getAttributeValue(null, "pkg");
-                        String cl = parser.getAttributeValue(null, "cl");
-
-                        final PackageManager packageManager = mContext.getPackageManager();
-                        try {
-                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
-                        } catch (PackageManager.NameNotFoundException e) {
-                            String[] pkgs = packageManager.currentToCanonicalPackageNames(
-                                    new String[] { pkg });
-                            pkg = pkgs[0];
-                        }
-
-                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
-                        if (p == null && mSafeMode) {
-                            // if we're in safe mode, make a temporary one
-                            p = new Provider();
-                            p.info = new AppWidgetProviderInfo();
-                            p.info.provider = new ComponentName(pkg, cl);
-                            p.zombie = true;
-                            mInstalledProviders.add(p);
-                        }
-                        if (p != null) {
-                            // if it wasn't uninstalled or something
-                            loadedProviders.put(providerIndex, p);
-                        }
-                        providerIndex++;
-                    }
-                    else if ("h".equals(tag)) {
-                        Host host = new Host();
-
-                        // TODO: do we need to check that this package has the same signature
-                        // as before?
-                        host.packageName = parser.getAttributeValue(null, "pkg");
-                        try {
-                            host.uid = getUidForPackage(host.packageName);
-                        } catch (PackageManager.NameNotFoundException ex) {
-                            host.zombie = true;
-                        }
-                        if (!host.zombie || mSafeMode) {
-                            // In safe mode, we don't discard the hosts we don't recognize
-                            // so that they're not pruned from our list.  Otherwise, we do.
-                            host.hostId = Integer.parseInt(
-                                    parser.getAttributeValue(null, "id"), 16);
-                            mHosts.add(host);
-                        }
-                    }
-                    else if ("g".equals(tag)) {
-                        AppWidgetId id = new AppWidgetId();
-                        id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
-                        if (id.appWidgetId >= mNextAppWidgetId) {
-                            mNextAppWidgetId = id.appWidgetId + 1;
-                        }
-
-                        String providerString = parser.getAttributeValue(null, "p");
-                        if (providerString != null) {
-                            // there's no provider if it hasn't been bound yet.
-                            // maybe we don't have to save this, but it brings the system
-                            // to the state it was in.
-                            int pIndex = Integer.parseInt(providerString, 16);
-                            id.provider = loadedProviders.get(pIndex);
-                            if (false) {
-                                Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
-                                        + pIndex + " which is " + id.provider);
-                            }
-                            if (id.provider == null) {
-                                // This provider is gone.  We just let the host figure out
-                                // that this happened when it fails to load it.
-                                continue;
-                            }
-                        }
-
-                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
-                        id.host = mHosts.get(hIndex);
-                        if (id.host == null) {
-                            // This host is gone.
-                            continue;
-                        }
-
-                        if (id.provider != null) {
-                            id.provider.instances.add(id);
-                        }
-                        id.host.instances.add(id);
-                        mAppWidgetIds.add(id);
-                    }
-                }
-            } while (type != XmlPullParser.END_DOCUMENT);
-            success = true;
-        } catch (NullPointerException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        } catch (NumberFormatException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        } catch (XmlPullParserException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        } catch (IOException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        } catch (IndexOutOfBoundsException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        }
-
-        if (success) {
-            // delete any hosts that didn't manage to get connected (should happen)
-            // if it matters, they'll be reconnected.
-            for (int i=mHosts.size()-1; i>=0; i--) {
-                pruneHostLocked(mHosts.get(i));
-            }
-        } else {
-            // failed reading, clean up
-            Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
-
-            mAppWidgetIds.clear();
-            mHosts.clear();
-            final int N = mInstalledProviders.size();
-            for (int i=0; i<N; i++) {
-                mInstalledProviders.get(i).instances.clear();
-            }
-        }
-    }
-
-    AtomicFile savedStateFile() {
-        return new AtomicFile(new File("/data/system/" + SETTINGS_FILENAME));
-    }
-
     BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            //Slog.d(TAG, "received " + action);
+            // Slog.d(TAG, "received " + action);
             if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-                sendInitialBroadcasts();
+                getImplForUser().sendInitialBroadcasts();
             } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
-                Locale revised = Locale.getDefault();
-                if (revised == null || mLocale == null ||
-                    !(revised.equals(mLocale))) {
-                    mLocale = revised;
-
-                    synchronized (mAppWidgetIds) {
-                        ensureStateLoadedLocked();
-                        int N = mInstalledProviders.size();
-                        for (int i=N-1; i>=0; i--) {
-                            Provider p = mInstalledProviders.get(i);
-                            String pkgName = p.info.provider.getPackageName();
-                            updateProvidersForPackageLocked(pkgName);
-                        }
-                        saveStateLocked();
-                    }
+                for (int i = 0; i < mAppWidgetServices.size(); i++) {
+                    AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+                    service.onConfigurationChanged();
                 }
             } else {
-                boolean added = false;
-                boolean changed = false;
-                String pkgList[] = null;
-                if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
-                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                    added = true;
-                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                    added = false;
-                } else  {
-                    Uri uri = intent.getData();
-                    if (uri == null) {
-                        return;
-                    }
-                    String pkgName = uri.getSchemeSpecificPart();
-                    if (pkgName == null) {
-                        return;
-                    }
-                    pkgList = new String[] { pkgName };
-                    added = Intent.ACTION_PACKAGE_ADDED.equals(action);
-                    changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
-                }
-                if (pkgList == null || pkgList.length == 0) {
-                    return;
-                }
-                if (added || changed) {
-                    synchronized (mAppWidgetIds) {
-                        ensureStateLoadedLocked();
-                        Bundle extras = intent.getExtras();
-                        if (changed || (extras != null &&
-                                    extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
-                            for (String pkgName : pkgList) {
-                                // The package was just upgraded
-                                updateProvidersForPackageLocked(pkgName);
-                            }
-                        } else {
-                            // The package was just added
-                            for (String pkgName : pkgList) {
-                                addProvidersForPackageLocked(pkgName);
-                            }
-                        }
-                        saveStateLocked();
-                    }
-                } else {
-                    Bundle extras = intent.getExtras();
-                    if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
-                        // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
-                    } else {
-                        synchronized (mAppWidgetIds) {
-                            ensureStateLoadedLocked();
-                            for (String pkgName : pkgList) {
-                                removeProvidersForPackageLocked(pkgName);
-                                saveStateLocked();
-                            }
-                        }
-                    }
-                }
+                // TODO: Verify that this only needs to be delivered for the related user and not
+                // all the users
+                getImplForUser().onBroadcastReceived(intent);
             }
         }
     };
-
-    void addProvidersForPackageLocked(String pkgName) {
-        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-        intent.setPackage(pkgName);
-        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
-                PackageManager.GET_META_DATA);
-
-        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
-        for (int i=0; i<N; i++) {
-            ResolveInfo ri = broadcastReceivers.get(i);
-            ActivityInfo ai = ri.activityInfo;
-            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
-                continue;
-            }
-            if (pkgName.equals(ai.packageName)) {
-                addProviderLocked(ri);
-            }
-        }
-    }
-
-    void updateProvidersForPackageLocked(String pkgName) {
-        HashSet<String> keep = new HashSet<String>();
-        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-        intent.setPackage(pkgName);
-        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
-                PackageManager.GET_META_DATA);
-
-        // add the missing ones and collect which ones to keep
-        int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
-        for (int i=0; i<N; i++) {
-            ResolveInfo ri = broadcastReceivers.get(i);
-            ActivityInfo ai = ri.activityInfo;
-            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
-                continue;
-            }
-            if (pkgName.equals(ai.packageName)) {
-                ComponentName component = new ComponentName(ai.packageName, ai.name);
-                Provider p = lookupProviderLocked(component);
-                if (p == null) {
-                    if (addProviderLocked(ri)) {
-                        keep.add(ai.name);
-                    }
-                } else {
-                    Provider parsed = parseProviderInfoXml(component, ri);
-                    if (parsed != null) {
-                        keep.add(ai.name);
-                        // Use the new AppWidgetProviderInfo.
-                        p.info = parsed.info;
-                        // If it's enabled
-                        final int M = p.instances.size();
-                        if (M > 0) {
-                            int[] appWidgetIds = getAppWidgetIds(p);
-                            // Reschedule for the new updatePeriodMillis (don't worry about handling
-                            // it specially if updatePeriodMillis didn't change because we just sent
-                            // an update, and the next one will be updatePeriodMillis from now).
-                            cancelBroadcasts(p);
-                            registerForBroadcastsLocked(p, appWidgetIds);
-                            // If it's currently showing, call back with the new AppWidgetProviderInfo.
-                            for (int j=0; j<M; j++) {
-                                AppWidgetId id = p.instances.get(j);
-                                id.views = null;
-                                if (id.host != null && id.host.callbacks != null) {
-                                    try {
-                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info);
-                                    } catch (RemoteException ex) {
-                                        // It failed; remove the callback. No need to prune because
-                                        // we know that this host is still referenced by this
-                                        // instance.
-                                        id.host.callbacks = null;
-                                    }
-                                }
-                            }
-                            // Now that we've told the host, push out an update.
-                            sendUpdateIntentLocked(p, appWidgetIds);
-                        }
-                    }
-                }
-            }
-        }
-
-        // prune the ones we don't want to keep
-        N = mInstalledProviders.size();
-        for (int i=N-1; i>=0; i--) {
-            Provider p = mInstalledProviders.get(i);
-            if (pkgName.equals(p.info.provider.getPackageName())
-                    && !keep.contains(p.info.provider.getClassName())) {
-                removeProviderLocked(i, p);
-            }
-        }
-    }
-
-    void removeProvidersForPackageLocked(String pkgName) {
-        int N = mInstalledProviders.size();
-        for (int i=N-1; i>=0; i--) {
-            Provider p = mInstalledProviders.get(i);
-            if (pkgName.equals(p.info.provider.getPackageName())) {
-                removeProviderLocked(i, p);
-            }
-        }
-
-        // Delete the hosts for this package too
-        //
-        // By now, we have removed any AppWidgets that were in any hosts here,
-        // so we don't need to worry about sending DISABLE broadcasts to them.
-        N = mHosts.size();
-        for (int i=N-1; i>=0; i--) {
-            Host host = mHosts.get(i);
-            if (pkgName.equals(host.packageName)) {
-                deleteHostLocked(host);
-            }
-        }
-    }
 }
-
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
new file mode 100644
index 0000000..250386f
--- /dev/null
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -0,0 +1,1606 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.Intent.FilterComparison;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserId;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.widget.RemoteViews;
+
+import com.android.internal.appwidget.IAppWidgetHost;
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.widget.IRemoteViewsAdapterConnection;
+import com.android.internal.widget.IRemoteViewsFactory;
+import com.android.server.am.ActivityManagerService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+class AppWidgetServiceImpl {
+
+    private static final String TAG = "AppWidgetServiceImpl";
+    private static final String SETTINGS_FILENAME = "appwidgets.xml";
+    private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
+
+    /*
+     * When identifying a Host or Provider based on the calling process, use the uid field. When
+     * identifying a Host or Provider based on a package manager broadcast, use the package given.
+     */
+
+    static class Provider {
+        int uid;
+        AppWidgetProviderInfo info;
+        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
+        PendingIntent broadcast;
+        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+        int tag; // for use while saving state (the index)
+    }
+
+    static class Host {
+        int uid;
+        int hostId;
+        String packageName;
+        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
+        IAppWidgetHost callbacks;
+        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+        int tag; // for use while saving state (the index)
+    }
+
+    static class AppWidgetId {
+        int appWidgetId;
+        Provider provider;
+        RemoteViews views;
+        Host host;
+    }
+
+    /**
+     * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
+     * needs to be a static inner class since a reference to the ServiceConnection is held globally
+     * and may lead us to leak AppWidgetService instances (if there were more than one).
+     */
+    static class ServiceConnectionProxy implements ServiceConnection {
+        private final IBinder mConnectionCb;
+
+        ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
+            mConnectionCb = connectionCb;
+        }
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
+                    .asInterface(mConnectionCb);
+            try {
+                cb.onServiceConnected(service);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            disconnect();
+        }
+
+        public void disconnect() {
+            final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
+                    .asInterface(mConnectionCb);
+            try {
+                cb.onServiceDisconnected();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    // Manages active connections to RemoteViewsServices
+    private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
+    // Manages persistent references to RemoteViewsServices from different App Widgets
+    private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
+
+    Context mContext;
+    Locale mLocale;
+    PackageManager mPackageManager;
+    AlarmManager mAlarmManager;
+    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
+    int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
+    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
+    ArrayList<Host> mHosts = new ArrayList<Host>();
+    boolean mSafeMode;
+    int mUserId;
+    boolean mStateLoaded;
+
+    // These are for debugging only -- widgets are going missing in some rare instances
+    ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
+    ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
+
+    AppWidgetServiceImpl(Context context, int userId) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        mUserId = userId;
+    }
+
+    public void systemReady(boolean safeMode) {
+        mSafeMode = safeMode;
+
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+        }
+    }
+
+    void onConfigurationChanged() {
+        Locale revised = Locale.getDefault();
+        if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
+            mLocale = revised;
+
+            synchronized (mAppWidgetIds) {
+                ensureStateLoadedLocked();
+                int N = mInstalledProviders.size();
+                for (int i = N - 1; i >= 0; i--) {
+                    Provider p = mInstalledProviders.get(i);
+                    String pkgName = p.info.provider.getPackageName();
+                    updateProvidersForPackageLocked(pkgName);
+                }
+                saveStateLocked();
+            }
+        }
+    }
+
+    void onBroadcastReceived(Intent intent) {
+        final String action = intent.getAction();
+        boolean added = false;
+        boolean changed = false;
+        String pkgList[] = null;
+        if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+            pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            added = true;
+        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+            pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            added = false;
+        } else {
+            Uri uri = intent.getData();
+            if (uri == null) {
+                return;
+            }
+            String pkgName = uri.getSchemeSpecificPart();
+            if (pkgName == null) {
+                return;
+            }
+            pkgList = new String[] { pkgName };
+            added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+            changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
+        }
+        if (pkgList == null || pkgList.length == 0) {
+            return;
+        }
+        if (added || changed) {
+            synchronized (mAppWidgetIds) {
+                ensureStateLoadedLocked();
+                Bundle extras = intent.getExtras();
+                if (changed
+                        || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
+                    for (String pkgName : pkgList) {
+                        // The package was just upgraded
+                        updateProvidersForPackageLocked(pkgName);
+                    }
+                } else {
+                    // The package was just added
+                    for (String pkgName : pkgList) {
+                        addProvidersForPackageLocked(pkgName);
+                    }
+                }
+                saveStateLocked();
+            }
+        } else {
+            Bundle extras = intent.getExtras();
+            if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+                // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
+            } else {
+                synchronized (mAppWidgetIds) {
+                    ensureStateLoadedLocked();
+                    for (String pkgName : pkgList) {
+                        removeProvidersForPackageLocked(pkgName);
+                        saveStateLocked();
+                    }
+                }
+            }
+        }
+    }
+
+    private void dumpProvider(Provider p, int index, PrintWriter pw) {
+        AppWidgetProviderInfo info = p.info;
+        pw.print("  ["); pw.print(index); pw.print("] provider ");
+                pw.print(info.provider.flattenToShortString());
+                pw.println(':');
+        pw.print("    min=("); pw.print(info.minWidth);
+                pw.print("x"); pw.print(info.minHeight);
+        pw.print(")   minResize=("); pw.print(info.minResizeWidth);
+                pw.print("x"); pw.print(info.minResizeHeight);
+                pw.print(") updatePeriodMillis=");
+                pw.print(info.updatePeriodMillis);
+                pw.print(" resizeMode=");
+                pw.print(info.resizeMode);
+                pw.print(" autoAdvanceViewId=");
+                pw.print(info.autoAdvanceViewId);
+                pw.print(" initialLayout=#");
+                pw.print(Integer.toHexString(info.initialLayout));
+                pw.print(" zombie="); pw.println(p.zombie);
+    }
+
+    private void dumpHost(Host host, int index, PrintWriter pw) {
+        pw.print("  ["); pw.print(index); pw.print("] hostId=");
+                pw.print(host.hostId); pw.print(' ');
+                pw.print(host.packageName); pw.print('/');
+        pw.print(host.uid); pw.println(':');
+        pw.print("    callbacks="); pw.println(host.callbacks);
+        pw.print("    instances.size="); pw.print(host.instances.size());
+                pw.print(" zombie="); pw.println(host.zombie);
+    }
+
+    private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
+        pw.print("  ["); pw.print(index); pw.print("] id=");
+                pw.println(id.appWidgetId);
+        pw.print("    hostId=");
+                pw.print(id.host.hostId); pw.print(' ');
+                pw.print(id.host.packageName); pw.print('/');
+                pw.println(id.host.uid);
+        if (id.provider != null) {
+            pw.print("    provider=");
+                    pw.println(id.provider.info.provider.flattenToShortString());
+        }
+        if (id.host != null) {
+            pw.print("    host.callbacks="); pw.println(id.host.callbacks);
+        }
+        if (id.views != null) {
+            pw.print("    views="); pw.println(id.views);
+        }
+    }
+
+    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        synchronized (mAppWidgetIds) {
+            int N = mInstalledProviders.size();
+            pw.println("Providers:");
+            for (int i=0; i<N; i++) {
+                dumpProvider(mInstalledProviders.get(i), i, pw);
+            }
+
+            N = mAppWidgetIds.size();
+            pw.println(" ");
+            pw.println("AppWidgetIds:");
+            for (int i=0; i<N; i++) {
+                dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
+            }
+
+            N = mHosts.size();
+            pw.println(" ");
+            pw.println("Hosts:");
+            for (int i=0; i<N; i++) {
+                dumpHost(mHosts.get(i), i, pw);
+            }
+
+            N = mDeletedProviders.size();
+            pw.println(" ");
+            pw.println("Deleted Providers:");
+            for (int i=0; i<N; i++) {
+                dumpProvider(mDeletedProviders.get(i), i, pw);
+            }
+
+            N = mDeletedHosts.size();
+            pw.println(" ");
+            pw.println("Deleted Hosts:");
+            for (int i=0; i<N; i++) {
+                dumpHost(mDeletedHosts.get(i), i, pw);
+            }
+        }
+    }
+
+    private void ensureStateLoadedLocked() {
+        if (!mStateLoaded) {
+            loadAppWidgetList();
+            loadStateLocked();
+            mStateLoaded = true;
+        }
+    }
+
+    public int allocateAppWidgetId(String packageName, int hostId) {
+        int callingUid = enforceCallingUid(packageName);
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            int appWidgetId = mNextAppWidgetId++;
+
+            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+
+            AppWidgetId id = new AppWidgetId();
+            id.appWidgetId = appWidgetId;
+            id.host = host;
+
+            host.instances.add(id);
+            mAppWidgetIds.add(id);
+
+            saveStateLocked();
+
+            return appWidgetId;
+        }
+    }
+
+    public void deleteAppWidgetId(int appWidgetId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+            if (id != null) {
+                deleteAppWidgetLocked(id);
+                saveStateLocked();
+            }
+        }
+    }
+
+    public void deleteHost(int hostId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            int callingUid = Binder.getCallingUid();
+            Host host = lookupHostLocked(callingUid, hostId);
+            if (host != null) {
+                deleteHostLocked(host);
+                saveStateLocked();
+            }
+        }
+    }
+
+    public void deleteAllHosts() {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            int callingUid = Binder.getCallingUid();
+            final int N = mHosts.size();
+            boolean changed = false;
+            for (int i = N - 1; i >= 0; i--) {
+                Host host = mHosts.get(i);
+                if (host.uid == callingUid) {
+                    deleteHostLocked(host);
+                    changed = true;
+                }
+            }
+            if (changed) {
+                saveStateLocked();
+            }
+        }
+    }
+
+    void deleteHostLocked(Host host) {
+        final int N = host.instances.size();
+        for (int i = N - 1; i >= 0; i--) {
+            AppWidgetId id = host.instances.get(i);
+            deleteAppWidgetLocked(id);
+        }
+        host.instances.clear();
+        mHosts.remove(host);
+        mDeletedHosts.add(host);
+        // it's gone or going away, abruptly drop the callback connection
+        host.callbacks = null;
+    }
+
+    void deleteAppWidgetLocked(AppWidgetId id) {
+        // We first unbind all services that are bound to this id
+        unbindAppWidgetRemoteViewsServicesLocked(id);
+
+        Host host = id.host;
+        host.instances.remove(id);
+        pruneHostLocked(host);
+
+        mAppWidgetIds.remove(id);
+
+        Provider p = id.provider;
+        if (p != null) {
+            p.instances.remove(id);
+            if (!p.zombie) {
+                // send the broacast saying that this appWidgetId has been deleted
+                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
+                intent.setComponent(p.info.provider);
+                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
+                mContext.sendBroadcast(intent);
+                if (p.instances.size() == 0) {
+                    // cancel the future updates
+                    cancelBroadcasts(p);
+
+                    // send the broacast saying that the provider is not in use any more
+                    intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
+                    intent.setComponent(p.info.provider);
+                    mContext.sendBroadcast(intent);
+                }
+            }
+        }
+    }
+
+    void cancelBroadcasts(Provider p) {
+        if (p.broadcast != null) {
+            mAlarmManager.cancel(p.broadcast);
+            long token = Binder.clearCallingIdentity();
+            try {
+                p.broadcast.cancel();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            p.broadcast = null;
+        }
+    }
+
+    public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
+                "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mAppWidgetIds) {
+                ensureStateLoadedLocked();
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+                if (id == null) {
+                    throw new IllegalArgumentException("bad appWidgetId");
+                }
+                if (id.provider != null) {
+                    throw new IllegalArgumentException("appWidgetId " + appWidgetId
+                            + " already bound to " + id.provider.info.provider);
+                }
+                Provider p = lookupProviderLocked(provider);
+                if (p == null) {
+                    throw new IllegalArgumentException("not a appwidget provider: " + provider);
+                }
+                if (p.zombie) {
+                    throw new IllegalArgumentException("can't bind to a 3rd party provider in"
+                            + " safe mode: " + provider);
+                }
+
+                Binder.restoreCallingIdentity(ident);
+
+                id.provider = p;
+                p.instances.add(id);
+                int instancesSize = p.instances.size();
+                if (instancesSize == 1) {
+                    // tell the provider that it's ready
+                    sendEnableIntentLocked(p);
+                }
+
+                // send an update now -- We need this update now, and just for this appWidgetId.
+                // It's less critical when the next one happens, so when we schedule the next one,
+                // we add updatePeriodMillis to its start time. That time will have some slop,
+                // but that's okay.
+                sendUpdateIntentLocked(p, new int[] { appWidgetId });
+
+                // schedule the future updates
+                registerForBroadcastsLocked(p, getAppWidgetIds(p));
+                saveStateLocked();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    // Binds to a specific RemoteViewsService
+    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+            if (id == null) {
+                throw new IllegalArgumentException("bad appWidgetId");
+            }
+            final ComponentName componentName = intent.getComponent();
+            try {
+                final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
+                        PackageManager.GET_PERMISSIONS);
+                if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
+                    throw new SecurityException("Selected service does not require "
+                            + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new IllegalArgumentException("Unknown component " + componentName);
+            }
+
+            // If there is already a connection made for this service intent, then disconnect from
+            // that first. (This does not allow multiple connections to the same service under
+            // the same key)
+            ServiceConnectionProxy conn = null;
+            FilterComparison fc = new FilterComparison(intent);
+            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
+            if (mBoundRemoteViewsServices.containsKey(key)) {
+                conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
+                conn.disconnect();
+                mContext.unbindService(conn);
+                mBoundRemoteViewsServices.remove(key);
+            }
+
+            // Bind to the RemoteViewsService (which will trigger a callback to the
+            // RemoteViewsAdapter.onServiceConnected())
+            final long token = Binder.clearCallingIdentity();
+            try {
+                conn = new ServiceConnectionProxy(key, connection);
+                mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+                mBoundRemoteViewsServices.put(key, conn);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
+            // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
+            // when we can call back to the RemoteViewsService later to destroy associated
+            // factories.
+            incrementAppWidgetServiceRefCount(appWidgetId, fc);
+        }
+    }
+
+    // Unbinds from a specific RemoteViewsService
+    public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            // Unbind from the RemoteViewsService (which will trigger a callback to the bound
+            // RemoteViewsAdapter)
+            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
+                    intent));
+            if (mBoundRemoteViewsServices.containsKey(key)) {
+                // We don't need to use the appWidgetId until after we are sure there is something
+                // to unbind. Note that this may mask certain issues with apps calling unbind()
+                // more than necessary.
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+                if (id == null) {
+                    throw new IllegalArgumentException("bad appWidgetId");
+                }
+
+                ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
+                        .get(key);
+                conn.disconnect();
+                mContext.unbindService(conn);
+                mBoundRemoteViewsServices.remove(key);
+            } else {
+                Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
+            }
+        }
+    }
+
+    // Unbinds from a RemoteViewsService when we delete an app widget
+    private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
+        int appWidgetId = id.appWidgetId;
+        // Unbind all connections to Services bound to this AppWidgetId
+        Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
+                .iterator();
+        while (it.hasNext()) {
+            final Pair<Integer, Intent.FilterComparison> key = it.next();
+            if (key.first.intValue() == appWidgetId) {
+                final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
+                        .get(key);
+                conn.disconnect();
+                mContext.unbindService(conn);
+                it.remove();
+            }
+        }
+
+        // Check if we need to destroy any services (if no other app widgets are
+        // referencing the same service)
+        decrementAppWidgetServiceRefCount(appWidgetId);
+    }
+
+    // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
+    private void destroyRemoteViewsService(final Intent intent) {
+        final ServiceConnection conn = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
+                try {
+                    cb.onDestroy(intent);
+                } catch (RemoteException e) {
+                    e.printStackTrace();
+                } catch (RuntimeException e) {
+                    e.printStackTrace();
+                }
+                mContext.unbindService(this);
+            }
+
+            @Override
+            public void onServiceDisconnected(android.content.ComponentName name) {
+                // Do nothing
+            }
+        };
+
+        // Bind to the service and remove the static intent->factory mapping in the
+        // RemoteViewsService.
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Adds to the ref-count for a given RemoteViewsService intent
+    private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
+        HashSet<Integer> appWidgetIds = null;
+        if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
+            appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
+        } else {
+            appWidgetIds = new HashSet<Integer>();
+            mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
+        }
+        appWidgetIds.add(appWidgetId);
+    }
+
+    // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
+    // the ref-count reaches zero.
+    private void decrementAppWidgetServiceRefCount(int appWidgetId) {
+        Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
+        while (it.hasNext()) {
+            final FilterComparison key = it.next();
+            final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
+            if (ids.remove(appWidgetId)) {
+                // If we have removed the last app widget referencing this service, then we
+                // should destroy it and remove it from this set
+                if (ids.isEmpty()) {
+                    destroyRemoteViewsService(key.getIntent());
+                    it.remove();
+                }
+            }
+        }
+    }
+
+    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+            if (id != null && id.provider != null && !id.provider.zombie) {
+                return id.provider.info;
+            }
+            return null;
+        }
+    }
+
+    public RemoteViews getAppWidgetViews(int appWidgetId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+            if (id != null) {
+                return id.views;
+            }
+            return null;
+        }
+    }
+
+    public List<AppWidgetProviderInfo> getInstalledProviders() {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            final int N = mInstalledProviders.size();
+            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
+            for (int i = 0; i < N; i++) {
+                Provider p = mInstalledProviders.get(i);
+                if (!p.zombie) {
+                    result.add(p.info);
+                }
+            }
+            return result;
+        }
+    }
+
+    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+        if (appWidgetIds == null) {
+            return;
+        }
+        if (appWidgetIds.length == 0) {
+            return;
+        }
+        final int N = appWidgetIds.length;
+
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+                updateAppWidgetInstanceLocked(id, views);
+            }
+        }
+    }
+
+    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+        if (appWidgetIds == null) {
+            return;
+        }
+        if (appWidgetIds.length == 0) {
+            return;
+        }
+        final int N = appWidgetIds.length;
+
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+                updateAppWidgetInstanceLocked(id, views, true);
+            }
+        }
+    }
+
+    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+        if (appWidgetIds == null) {
+            return;
+        }
+        if (appWidgetIds.length == 0) {
+            return;
+        }
+        final int N = appWidgetIds.length;
+
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+                notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
+            }
+        }
+    }
+
+    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            Provider p = lookupProviderLocked(provider);
+            if (p == null) {
+                Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
+                return;
+            }
+            ArrayList<AppWidgetId> instances = p.instances;
+            final int callingUid = Binder.getCallingUid();
+            final int N = instances.size();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = instances.get(i);
+                if (canAccessAppWidgetId(id, callingUid)) {
+                    updateAppWidgetInstanceLocked(id, views);
+                }
+            }
+        }
+    }
+
+    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
+        updateAppWidgetInstanceLocked(id, views, false);
+    }
+
+    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
+        // allow for stale appWidgetIds and other badness
+        // lookup also checks that the calling process can access the appWidgetId
+        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+
+            // We do not want to save this RemoteViews
+            if (!isPartialUpdate)
+                id.views = views;
+
+            // is anyone listening?
+            if (id.host.callbacks != null) {
+                try {
+                    // the lock is held, but this is a oneway call
+                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
+                } catch (RemoteException e) {
+                    // It failed; remove the callback. No need to prune because
+                    // we know that this host is still referenced by this instance.
+                    id.host.callbacks = null;
+                }
+            }
+        }
+    }
+
+    void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
+        // allow for stale appWidgetIds and other badness
+        // lookup also checks that the calling process can access the appWidgetId
+        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+            // is anyone listening?
+            if (id.host.callbacks != null) {
+                try {
+                    // the lock is held, but this is a oneway call
+                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
+                } catch (RemoteException e) {
+                    // It failed; remove the callback. No need to prune because
+                    // we know that this host is still referenced by this instance.
+                    id.host.callbacks = null;
+                }
+            }
+
+            // If the host is unavailable, then we call the associated
+            // RemoteViewsFactory.onDataSetChanged() directly
+            if (id.host.callbacks == null) {
+                Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
+                for (FilterComparison key : keys) {
+                    if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
+                        Intent intent = key.getIntent();
+
+                        final ServiceConnection conn = new ServiceConnection() {
+                            @Override
+                            public void onServiceConnected(ComponentName name, IBinder service) {
+                                IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
+                                        .asInterface(service);
+                                try {
+                                    cb.onDataSetChangedAsync();
+                                } catch (RemoteException e) {
+                                    e.printStackTrace();
+                                } catch (RuntimeException e) {
+                                    e.printStackTrace();
+                                }
+                                mContext.unbindService(this);
+                            }
+
+                            @Override
+                            public void onServiceDisconnected(android.content.ComponentName name) {
+                                // Do nothing
+                            }
+                        };
+
+                        // Bind to the service and call onDataSetChanged()
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
+            List<RemoteViews> updatedViews) {
+        int callingUid = enforceCallingUid(packageName);
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+            host.callbacks = callbacks;
+
+            updatedViews.clear();
+
+            ArrayList<AppWidgetId> instances = host.instances;
+            int N = instances.size();
+            int[] updatedIds = new int[N];
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = instances.get(i);
+                updatedIds[i] = id.appWidgetId;
+                updatedViews.add(id.views);
+            }
+            return updatedIds;
+        }
+    }
+
+    public void stopListening(int hostId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
+            if (host != null) {
+                host.callbacks = null;
+                pruneHostLocked(host);
+            }
+        }
+    }
+
+    boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
+        if (id.host.uid == callingUid) {
+            // Apps hosting the AppWidget have access to it.
+            return true;
+        }
+        if (id.provider != null && id.provider.uid == callingUid) {
+            // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
+            return true;
+        }
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
+            // Apps that can bind have access to all appWidgetIds.
+            return true;
+        }
+        // Nobody else can access it.
+        return false;
+    }
+
+    AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
+        int callingUid = Binder.getCallingUid();
+        final int N = mAppWidgetIds.size();
+        for (int i = 0; i < N; i++) {
+            AppWidgetId id = mAppWidgetIds.get(i);
+            if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
+                return id;
+            }
+        }
+        return null;
+    }
+
+    Provider lookupProviderLocked(ComponentName provider) {
+        final int N = mInstalledProviders.size();
+        for (int i = 0; i < N; i++) {
+            Provider p = mInstalledProviders.get(i);
+            if (p.info.provider.equals(provider)) {
+                return p;
+            }
+        }
+        return null;
+    }
+
+    Host lookupHostLocked(int uid, int hostId) {
+        final int N = mHosts.size();
+        for (int i = 0; i < N; i++) {
+            Host h = mHosts.get(i);
+            if (h.uid == uid && h.hostId == hostId) {
+                return h;
+            }
+        }
+        return null;
+    }
+
+    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
+        final int N = mHosts.size();
+        for (int i = 0; i < N; i++) {
+            Host h = mHosts.get(i);
+            if (h.hostId == hostId && h.packageName.equals(packageName)) {
+                return h;
+            }
+        }
+        Host host = new Host();
+        host.packageName = packageName;
+        host.uid = uid;
+        host.hostId = hostId;
+        mHosts.add(host);
+        return host;
+    }
+
+    void pruneHostLocked(Host host) {
+        if (host.instances.size() == 0 && host.callbacks == null) {
+            mHosts.remove(host);
+        }
+    }
+
+    void loadAppWidgetList() {
+        PackageManager pm = mPackageManager;
+
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+        List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
+                PackageManager.GET_META_DATA);
+
+        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+        for (int i = 0; i < N; i++) {
+            ResolveInfo ri = broadcastReceivers.get(i);
+            addProviderLocked(ri);
+        }
+    }
+
+    boolean addProviderLocked(ResolveInfo ri) {
+        if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+            return false;
+        }
+        if (!ri.activityInfo.isEnabled()) {
+            return false;
+        }
+        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
+                ri.activityInfo.name), ri);
+        if (p != null) {
+            mInstalledProviders.add(p);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    void removeProviderLocked(int index, Provider p) {
+        int N = p.instances.size();
+        for (int i = 0; i < N; i++) {
+            AppWidgetId id = p.instances.get(i);
+            // Call back with empty RemoteViews
+            updateAppWidgetInstanceLocked(id, null);
+            // Stop telling the host about updates for this from now on
+            cancelBroadcasts(p);
+            // clear out references to this appWidgetId
+            id.host.instances.remove(id);
+            mAppWidgetIds.remove(id);
+            id.provider = null;
+            pruneHostLocked(id.host);
+            id.host = null;
+        }
+        p.instances.clear();
+        mInstalledProviders.remove(index);
+        mDeletedProviders.add(p);
+        // no need to send the DISABLE broadcast, since the receiver is gone anyway
+        cancelBroadcasts(p);
+    }
+
+    void sendEnableIntentLocked(Provider p) {
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
+        intent.setComponent(p.info.provider);
+        mContext.sendBroadcast(intent);
+    }
+
+    void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
+        if (appWidgetIds != null && appWidgetIds.length > 0) {
+            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+            intent.setComponent(p.info.provider);
+            mContext.sendBroadcast(intent);
+        }
+    }
+
+    void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
+        if (p.info.updatePeriodMillis > 0) {
+            // if this is the first instance, set the alarm. otherwise,
+            // rely on the fact that we've already set it and that
+            // PendingIntent.getBroadcast will update the extras.
+            boolean alreadyRegistered = p.broadcast != null;
+            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+            intent.setComponent(p.info.provider);
+            long token = Binder.clearCallingIdentity();
+            try {
+                p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
+                        PendingIntent.FLAG_UPDATE_CURRENT);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            if (!alreadyRegistered) {
+                long period = p.info.updatePeriodMillis;
+                if (period < MIN_UPDATE_PERIOD) {
+                    period = MIN_UPDATE_PERIOD;
+                }
+                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
+                        .elapsedRealtime()
+                        + period, period, p.broadcast);
+            }
+        }
+    }
+
+    static int[] getAppWidgetIds(Provider p) {
+        int instancesSize = p.instances.size();
+        int appWidgetIds[] = new int[instancesSize];
+        for (int i = 0; i < instancesSize; i++) {
+            appWidgetIds[i] = p.instances.get(i).appWidgetId;
+        }
+        return appWidgetIds;
+    }
+
+    public int[] getAppWidgetIds(ComponentName provider) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            Provider p = lookupProviderLocked(provider);
+            if (p != null && Binder.getCallingUid() == p.uid) {
+                return getAppWidgetIds(p);
+            } else {
+                return new int[0];
+            }
+        }
+    }
+
+    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
+        Provider p = null;
+
+        ActivityInfo activityInfo = ri.activityInfo;
+        XmlResourceParser parser = null;
+        try {
+            parser = activityInfo.loadXmlMetaData(mPackageManager,
+                    AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
+            if (parser == null) {
+                Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
+                        + " meta-data for " + "AppWidget provider '" + component + '\'');
+                return null;
+            }
+
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+                // drain whitespace, comments, etc.
+            }
+
+            String nodeName = parser.getName();
+            if (!"appwidget-provider".equals(nodeName)) {
+                Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
+                        + " AppWidget provider '" + component + '\'');
+                return null;
+            }
+
+            p = new Provider();
+            AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
+            info.provider = component;
+            p.uid = activityInfo.applicationInfo.uid;
+
+            Resources res = mPackageManager
+                    .getResourcesForApplication(activityInfo.applicationInfo);
+
+            TypedArray sa = res.obtainAttributes(attrs,
+                    com.android.internal.R.styleable.AppWidgetProviderInfo);
+
+            // These dimensions has to be resolved in the application's context.
+            // We simply send back the raw complex data, which will be
+            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
+            TypedValue value = sa
+                    .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
+            info.minWidth = value != null ? value.data : 0;
+            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
+            info.minHeight = value != null ? value.data : 0;
+            value = sa.peekValue(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
+            info.minResizeWidth = value != null ? value.data : info.minWidth;
+            value = sa.peekValue(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
+            info.minResizeHeight = value != null ? value.data : info.minHeight;
+            info.updatePeriodMillis = sa.getInt(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
+            info.initialLayout = sa.getResourceId(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
+            String className = sa
+                    .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
+            if (className != null) {
+                info.configure = new ComponentName(component.getPackageName(), className);
+            }
+            info.label = activityInfo.loadLabel(mPackageManager).toString();
+            info.icon = ri.getIconResource();
+            info.previewImage = sa.getResourceId(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+            info.autoAdvanceViewId = sa.getResourceId(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
+            info.resizeMode = sa.getInt(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
+                    AppWidgetProviderInfo.RESIZE_NONE);
+
+            sa.recycle();
+        } catch (Exception e) {
+            // Ok to catch Exception here, because anything going wrong because
+            // of what a client process passes to us should not be fatal for the
+            // system process.
+            Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
+            return null;
+        } finally {
+            if (parser != null)
+                parser.close();
+        }
+        return p;
+    }
+
+    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
+        PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
+        if (pkgInfo == null || pkgInfo.applicationInfo == null) {
+            throw new PackageManager.NameNotFoundException();
+        }
+        return pkgInfo.applicationInfo.uid;
+    }
+
+    int enforceCallingUid(String packageName) throws IllegalArgumentException {
+        int callingUid = Binder.getCallingUid();
+        int packageUid;
+        try {
+            packageUid = getUidForPackage(packageName);
+        } catch (PackageManager.NameNotFoundException ex) {
+            throw new IllegalArgumentException("packageName and uid don't match packageName="
+                    + packageName);
+        }
+        if (!UserId.isSameApp(callingUid, packageUid)) {
+            throw new IllegalArgumentException("packageName and uid don't match packageName="
+                    + packageName);
+        }
+        return callingUid;
+    }
+
+    void sendInitialBroadcasts() {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            final int N = mInstalledProviders.size();
+            for (int i = 0; i < N; i++) {
+                Provider p = mInstalledProviders.get(i);
+                if (p.instances.size() > 0) {
+                    sendEnableIntentLocked(p);
+                    int[] appWidgetIds = getAppWidgetIds(p);
+                    sendUpdateIntentLocked(p, appWidgetIds);
+                    registerForBroadcastsLocked(p, appWidgetIds);
+                }
+            }
+        }
+    }
+
+    // only call from initialization -- it assumes that the data structures are all empty
+    void loadStateLocked() {
+        AtomicFile file = savedStateFile();
+        try {
+            FileInputStream stream = file.openRead();
+            readStateFromFileLocked(stream);
+
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    Slog.w(TAG, "Failed to close state FileInputStream " + e);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            Slog.w(TAG, "Failed to read state: " + e);
+        }
+    }
+
+    void saveStateLocked() {
+        AtomicFile file = savedStateFile();
+        FileOutputStream stream;
+        try {
+            stream = file.startWrite();
+            if (writeStateToFileLocked(stream)) {
+                file.finishWrite(stream);
+            } else {
+                file.failWrite(stream);
+                Slog.w(TAG, "Failed to save state, restoring backup.");
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed open state file for write: " + e);
+        }
+    }
+
+    boolean writeStateToFileLocked(FileOutputStream stream) {
+        int N;
+
+        try {
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(stream, "utf-8");
+            out.startDocument(null, true);
+            out.startTag(null, "gs");
+
+            int providerIndex = 0;
+            N = mInstalledProviders.size();
+            for (int i = 0; i < N; i++) {
+                Provider p = mInstalledProviders.get(i);
+                if (p.instances.size() > 0) {
+                    out.startTag(null, "p");
+                    out.attribute(null, "pkg", p.info.provider.getPackageName());
+                    out.attribute(null, "cl", p.info.provider.getClassName());
+                    out.endTag(null, "p");
+                    p.tag = providerIndex;
+                    providerIndex++;
+                }
+            }
+
+            N = mHosts.size();
+            for (int i = 0; i < N; i++) {
+                Host host = mHosts.get(i);
+                out.startTag(null, "h");
+                out.attribute(null, "pkg", host.packageName);
+                out.attribute(null, "id", Integer.toHexString(host.hostId));
+                out.endTag(null, "h");
+                host.tag = i;
+            }
+
+            N = mAppWidgetIds.size();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = mAppWidgetIds.get(i);
+                out.startTag(null, "g");
+                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
+                out.attribute(null, "h", Integer.toHexString(id.host.tag));
+                if (id.provider != null) {
+                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
+                }
+                out.endTag(null, "g");
+            }
+
+            out.endTag(null, "gs");
+
+            out.endDocument();
+            return true;
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to write state: " + e);
+            return false;
+        }
+    }
+
+    void readStateFromFileLocked(FileInputStream stream) {
+        boolean success = false;
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, null);
+
+            int type;
+            int providerIndex = 0;
+            HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
+            do {
+                type = parser.next();
+                if (type == XmlPullParser.START_TAG) {
+                    String tag = parser.getName();
+                    if ("p".equals(tag)) {
+                        // TODO: do we need to check that this package has the same signature
+                        // as before?
+                        String pkg = parser.getAttributeValue(null, "pkg");
+                        String cl = parser.getAttributeValue(null, "cl");
+
+                        final PackageManager packageManager = mContext.getPackageManager();
+                        try {
+                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
+                        } catch (PackageManager.NameNotFoundException e) {
+                            String[] pkgs = packageManager
+                                    .currentToCanonicalPackageNames(new String[] { pkg });
+                            pkg = pkgs[0];
+                        }
+
+                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
+                        if (p == null && mSafeMode) {
+                            // if we're in safe mode, make a temporary one
+                            p = new Provider();
+                            p.info = new AppWidgetProviderInfo();
+                            p.info.provider = new ComponentName(pkg, cl);
+                            p.zombie = true;
+                            mInstalledProviders.add(p);
+                        }
+                        if (p != null) {
+                            // if it wasn't uninstalled or something
+                            loadedProviders.put(providerIndex, p);
+                        }
+                        providerIndex++;
+                    } else if ("h".equals(tag)) {
+                        Host host = new Host();
+
+                        // TODO: do we need to check that this package has the same signature
+                        // as before?
+                        host.packageName = parser.getAttributeValue(null, "pkg");
+                        try {
+                            host.uid = getUidForPackage(host.packageName);
+                        } catch (PackageManager.NameNotFoundException ex) {
+                            host.zombie = true;
+                        }
+                        if (!host.zombie || mSafeMode) {
+                            // In safe mode, we don't discard the hosts we don't recognize
+                            // so that they're not pruned from our list. Otherwise, we do.
+                            host.hostId = Integer
+                                    .parseInt(parser.getAttributeValue(null, "id"), 16);
+                            mHosts.add(host);
+                        }
+                    } else if ("g".equals(tag)) {
+                        AppWidgetId id = new AppWidgetId();
+                        id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
+                        if (id.appWidgetId >= mNextAppWidgetId) {
+                            mNextAppWidgetId = id.appWidgetId + 1;
+                        }
+
+                        String providerString = parser.getAttributeValue(null, "p");
+                        if (providerString != null) {
+                            // there's no provider if it hasn't been bound yet.
+                            // maybe we don't have to save this, but it brings the system
+                            // to the state it was in.
+                            int pIndex = Integer.parseInt(providerString, 16);
+                            id.provider = loadedProviders.get(pIndex);
+                            if (false) {
+                                Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
+                                        + pIndex + " which is " + id.provider);
+                            }
+                            if (id.provider == null) {
+                                // This provider is gone. We just let the host figure out
+                                // that this happened when it fails to load it.
+                                continue;
+                            }
+                        }
+
+                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
+                        id.host = mHosts.get(hIndex);
+                        if (id.host == null) {
+                            // This host is gone.
+                            continue;
+                        }
+
+                        if (id.provider != null) {
+                            id.provider.instances.add(id);
+                        }
+                        id.host.instances.add(id);
+                        mAppWidgetIds.add(id);
+                    }
+                }
+            } while (type != XmlPullParser.END_DOCUMENT);
+            success = true;
+        } catch (NullPointerException e) {
+            Slog.w(TAG, "failed parsing " + e);
+        } catch (NumberFormatException e) {
+            Slog.w(TAG, "failed parsing " + e);
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "failed parsing " + e);
+        } catch (IOException e) {
+            Slog.w(TAG, "failed parsing " + e);
+        } catch (IndexOutOfBoundsException e) {
+            Slog.w(TAG, "failed parsing " + e);
+        }
+
+        if (success) {
+            // delete any hosts that didn't manage to get connected (should happen)
+            // if it matters, they'll be reconnected.
+            for (int i = mHosts.size() - 1; i >= 0; i--) {
+                pruneHostLocked(mHosts.get(i));
+            }
+        } else {
+            // failed reading, clean up
+            Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
+
+            mAppWidgetIds.clear();
+            mHosts.clear();
+            final int N = mInstalledProviders.size();
+            for (int i = 0; i < N; i++) {
+                mInstalledProviders.get(i).instances.clear();
+            }
+        }
+    }
+
+    AtomicFile savedStateFile() {
+        int userId = Binder.getOrigCallingUser();
+        File dir = new File("/data/system/users/" + userId);
+        File settingsFile = new File(dir, SETTINGS_FILENAME);
+        if (!dir.exists()) {
+            dir.mkdirs();
+            if (userId == 0) {
+                // Migrate old data
+                File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
+                // Method doesn't throw an exception on failure. Ignore any errors
+                // in moving the file (like non-existence)
+                oldFile.renameTo(settingsFile);
+            }
+        }
+        return new AtomicFile(settingsFile);
+    }
+
+    void addProvidersForPackageLocked(String pkgName) {
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+        intent.setPackage(pkgName);
+        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+                PackageManager.GET_META_DATA);
+
+        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+        for (int i = 0; i < N; i++) {
+            ResolveInfo ri = broadcastReceivers.get(i);
+            ActivityInfo ai = ri.activityInfo;
+            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+                continue;
+            }
+            if (pkgName.equals(ai.packageName)) {
+                addProviderLocked(ri);
+            }
+        }
+    }
+
+    void updateProvidersForPackageLocked(String pkgName) {
+        HashSet<String> keep = new HashSet<String>();
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+        intent.setPackage(pkgName);
+        List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+                PackageManager.GET_META_DATA);
+
+        // add the missing ones and collect which ones to keep
+        int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+        for (int i = 0; i < N; i++) {
+            ResolveInfo ri = broadcastReceivers.get(i);
+            ActivityInfo ai = ri.activityInfo;
+            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+                continue;
+            }
+            if (pkgName.equals(ai.packageName)) {
+                ComponentName component = new ComponentName(ai.packageName, ai.name);
+                Provider p = lookupProviderLocked(component);
+                if (p == null) {
+                    if (addProviderLocked(ri)) {
+                        keep.add(ai.name);
+                    }
+                } else {
+                    Provider parsed = parseProviderInfoXml(component, ri);
+                    if (parsed != null) {
+                        keep.add(ai.name);
+                        // Use the new AppWidgetProviderInfo.
+                        p.info = parsed.info;
+                        // If it's enabled
+                        final int M = p.instances.size();
+                        if (M > 0) {
+                            int[] appWidgetIds = getAppWidgetIds(p);
+                            // Reschedule for the new updatePeriodMillis (don't worry about handling
+                            // it specially if updatePeriodMillis didn't change because we just sent
+                            // an update, and the next one will be updatePeriodMillis from now).
+                            cancelBroadcasts(p);
+                            registerForBroadcastsLocked(p, appWidgetIds);
+                            // If it's currently showing, call back with the new
+                            // AppWidgetProviderInfo.
+                            for (int j = 0; j < M; j++) {
+                                AppWidgetId id = p.instances.get(j);
+                                id.views = null;
+                                if (id.host != null && id.host.callbacks != null) {
+                                    try {
+                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info);
+                                    } catch (RemoteException ex) {
+                                        // It failed; remove the callback. No need to prune because
+                                        // we know that this host is still referenced by this
+                                        // instance.
+                                        id.host.callbacks = null;
+                                    }
+                                }
+                            }
+                            // Now that we've told the host, push out an update.
+                            sendUpdateIntentLocked(p, appWidgetIds);
+                        }
+                    }
+                }
+            }
+        }
+
+        // prune the ones we don't want to keep
+        N = mInstalledProviders.size();
+        for (int i = N - 1; i >= 0; i--) {
+            Provider p = mInstalledProviders.get(i);
+            if (pkgName.equals(p.info.provider.getPackageName())
+                    && !keep.contains(p.info.provider.getClassName())) {
+                removeProviderLocked(i, p);
+            }
+        }
+    }
+
+    void removeProvidersForPackageLocked(String pkgName) {
+        int N = mInstalledProviders.size();
+        for (int i = N - 1; i >= 0; i--) {
+            Provider p = mInstalledProviders.get(i);
+            if (pkgName.equals(p.info.provider.getPackageName())) {
+                removeProviderLocked(i, p);
+            }
+        }
+
+        // Delete the hosts for this package too
+        //
+        // By now, we have removed any AppWidgets that were in any hosts here,
+        // so we don't need to worry about sending DISABLE broadcasts to them.
+        N = mHosts.size();
+        for (int i = N - 1; i >= 0; i--) {
+            Host host = mHosts.get(i);
+            if (pkgName.equals(host.packageName)) {
+                deleteHostLocked(host);
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 31515e1..a7b08f5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1675,7 +1675,8 @@
         synchronized(mClearDataLock) {
             mClearingData = true;
             try {
-                mActivityManager.clearApplicationUserData(packageName, observer);
+                mActivityManager.clearApplicationUserData(packageName, observer,
+                        Binder.getOrigCallingUser());
             } catch (RemoteException e) {
                 // can't happen because the activity manager is in this process
             }
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index f475dd6..308661f 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -153,6 +153,10 @@
                         start = i + 1;
                     }
                 }
+                if (start == 0) {
+                    final String rawEvent = new String(buffer, start, count, Charsets.UTF_8);
+                    log("RCV incomplete <- {" + rawEvent + "}");
+                }
 
                 // We should end at the amount we read. If not, compact then
                 // buffer and read again.
@@ -297,7 +301,11 @@
             throws NativeDaemonConnectorException {
         final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
 
-        mResponseQueue.clear();
+        while (mResponseQueue.size() > 0) {
+            try {
+                log("ignoring {" + mResponseQueue.take() + "}");
+            } catch (Exception e) {}
+        }
 
         final String sentCommand = sendCommandLocked(cmd, args);
 
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 5039294..34a8a02 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -45,6 +45,7 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserId;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
@@ -1034,7 +1035,7 @@
         try {
             ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
                     pkg, 0);
-            if (ai.uid != uid) {
+            if (!UserId.isSameApp(ai.uid, uid)) {
                 throw new SecurityException("Calling uid " + uid + " gave package"
                         + pkg + " which is owned by uid " + ai.uid);
             }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 3a6a3de..6fd5c07 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -75,6 +75,7 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -104,6 +105,7 @@
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserId;
 import android.provider.Settings;
 import android.util.EventLog;
 import android.util.Pair;
@@ -111,6 +113,7 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -135,6 +138,7 @@
 import java.lang.IllegalStateException;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -149,7 +153,9 @@
 
 public final class ActivityManagerService extends ActivityManagerNative
         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
+    private static final String USER_DATA_DIR = "/data/user/";
     static final String TAG = "ActivityManager";
+    static final String TAG_MU = "ActivityManagerServiceMU";
     static final boolean DEBUG = false;
     static final boolean localLOGV = DEBUG;
     static final boolean DEBUG_SWITCH = localLOGV || false;
@@ -172,6 +178,7 @@
     static final boolean DEBUG_CONFIGURATION = localLOGV || false;
     static final boolean DEBUG_POWER = localLOGV || false;
     static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
+    static final boolean DEBUG_MU = localLOGV || false;
     static final boolean VALIDATE_TOKENS = false;
     static final boolean SHOW_ACTIVITY_START_TIME = true;
     
@@ -781,7 +788,16 @@
                 r.curComponent = new ComponentName(
                         info.activityInfo.applicationInfo.packageName,
                         info.activityInfo.name);
+                if (r.callingUid != Process.SYSTEM_UID) {
+                    info.activityInfo = getActivityInfoForUser(info.activityInfo, UserId
+                            .getUserId(r.callingUid));
+                }
                 r.curReceiver = info.activityInfo;
+                if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
+                    Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+                            + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+                            + info.activityInfo.applicationInfo.uid);
+                }
 
                 // Broadcast is being executed, its package can't be stopped.
                 try {
@@ -1286,17 +1302,7 @@
     final HashMap<String, ArrayList<Intent>> mStickyBroadcasts =
             new HashMap<String, ArrayList<Intent>>();
 
-    /**
-     * All currently running services.
-     */
-    final HashMap<ComponentName, ServiceRecord> mServices =
-        new HashMap<ComponentName, ServiceRecord>();
-
-    /**
-     * All currently running services indexed by the Intent used to start them.
-     */
-    final HashMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent =
-        new HashMap<Intent.FilterComparison, ServiceRecord>();
+    final ServiceMap mServiceMap = new ServiceMap();
 
     /**
      * All currently bound service connections.  Keys are the IBinder of
@@ -1362,6 +1368,8 @@
     final HashMap<ComponentName, ContentProviderRecord> mProvidersByClass
             = new HashMap<ComponentName, ContentProviderRecord>();
 
+    final ProviderMap mProviderMap = new ProviderMap();
+
     /**
      * List of content providers who have clients waiting for them.  The
      * application is currently being launched and the provider will be
@@ -1392,6 +1400,7 @@
             uid = _uid;
         }
     }
+
     private static ThreadLocal<Identity> sCallerIdentity = new ThreadLocal<Identity>();
 
     /**
@@ -1688,7 +1697,7 @@
                     }
                     broadcastIntentLocked(null, null, intent,
                             null, null, 0, null, null, null,
-                            false, false, MY_PID, Process.SYSTEM_UID);
+                            false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
 
                     Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
                             mContext, proc, (ActivityRecord)data.get("activity"));
@@ -2587,6 +2596,7 @@
                     processName);
             return procs != null ? procs.valueAt(0) : null;
         }
+        // uid = applyUserId(uid);
         ProcessRecord proc = mProcessNames.get(processName, uid);
         return proc;
     }
@@ -2716,6 +2726,7 @@
         
         try {
             int uid = app.info.uid;
+
             int[] gids = null;
             try {
                 gids = mContext.getPackageManager().getPackageGids(
@@ -2827,7 +2838,7 @@
         }
     }
 
-    boolean startHomeActivityLocked() {
+    boolean startHomeActivityLocked(int userId) {
         if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
                 && mTopAction == null) {
             // We are running in factory test mode, but unable to find
@@ -2850,6 +2861,8 @@
                     aInfo.applicationInfo.packageName, aInfo.name));
             // Don't do this if the home app is currently being
             // instrumented.
+            aInfo = new ActivityInfo(aInfo);
+            aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
             ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                     aInfo.applicationInfo.uid);
             if (app == null || app.instrumentationClass == null) {
@@ -2858,11 +2871,10 @@
                         null, null, 0, 0, 0, false, false, null);
             }
         }
-        
-        
+
         return true;
     }
-    
+
     /**
      * Starts the "new version setup screen" if appropriate.
      */
@@ -3026,10 +3038,23 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
             String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+        int userId = 0;
+        if (intent.getCategories() != null && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
+            // Requesting home, set the identity to the current user
+            // HACK!
+            userId = mCurrentUserId;
+        } else {
+            // TODO: Fix this in a better way - calls coming from SystemUI should probably carry
+            // the current user's userId
+            if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
+                userId = 0;
+            } else {
+                userId = Binder.getOrigCallingUser();
+            }
+        }
         return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
-                grantedUriPermissions, grantedMode, resultTo, resultWho,
-                requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler,
-                null, null);
+                grantedUriPermissions, grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded,
+                debug, profileFile, profileFd, autoStopProfiler, null, null, userId);
     }
 
     public final WaitResult startActivityAndWait(IApplicationThread caller,
@@ -3038,10 +3063,11 @@
             String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
             String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
         WaitResult res = new WaitResult();
+        int userId = Binder.getOrigCallingUser();
         mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
                 requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler,
-                res, null);
+                res, null, userId);
         return res;
     }
     
@@ -3050,9 +3076,11 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug, Configuration config) {
-        return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
+        int ret = mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                 grantedUriPermissions, grantedMode, resultTo, resultWho,
-                requestCode, onlyIfNeeded, debug, null, null, false, null, config);
+                requestCode, onlyIfNeeded,
+                debug, null, null, false, null, config, Binder.getOrigCallingUser());
+        return ret;
     }
 
     public int startActivityIntentSender(IApplicationThread caller,
@@ -3080,9 +3108,9 @@
                 mAppSwitchesAllowedTime = 0;
             }
         }
-        
-        return pir.sendInner(0, fillInIntent, resolvedType, null,
+        int ret = pir.sendInner(0, fillInIntent, resolvedType, null,
                 null, resultTo, resultWho, requestCode, flagsMask, flagsValues);
+        return ret;
     }
     
     public boolean startNextMatchingActivity(IBinder callingActivity,
@@ -3185,20 +3213,24 @@
         
         // This is so super not safe, that only the system (or okay root)
         // can do it.
+        int userId = Binder.getOrigCallingUser();
         final int callingUid = Binder.getCallingUid();
         if (callingUid != 0 && callingUid != Process.myUid()) {
             throw new SecurityException(
                     "startActivityInPackage only available to the system");
         }
 
-        return mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
+        int ret = mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
                 null, 0, resultTo, resultWho, requestCode, onlyIfNeeded, false,
-                null, null, false, null, null);
+                null, null, false, null, null, userId);
+        return ret;
     }
 
     public final int startActivities(IApplicationThread caller,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
-        return mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo);
+        int ret = mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo,
+                Binder.getOrigCallingUser());
+        return ret;
     }
 
     public final int startActivitiesInPackage(int uid,
@@ -3211,8 +3243,9 @@
             throw new SecurityException(
                     "startActivityInPackage only available to the system");
         }
-
-        return mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo);
+        int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo,
+                UserId.getUserId(uid));
+        return ret;
     }
 
     final void addRecentTaskLocked(TaskRecord task) {
@@ -3224,8 +3257,9 @@
         // Remove any existing entries that are the same kind of task.
         for (int i=0; i<N; i++) {
             TaskRecord tr = mRecentTasks.get(i);
-            if ((task.affinity != null && task.affinity.equals(tr.affinity))
-                    || (task.intent != null && task.intent.filterEquals(tr.intent))) {
+            if (task.userId == tr.userId
+                    && ((task.affinity != null && task.affinity.equals(tr.affinity))
+                    || (task.intent != null && task.intent.filterEquals(tr.intent)))) {
                 mRecentTasks.remove(i);
                 i--;
                 N--;
@@ -3584,7 +3618,6 @@
 
     private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
         IBinder threadBinder = thread.asBinder();
-
         // Find the application record.
         for (int i=mLruProcesses.size()-1; i>=0; i--) {
             ProcessRecord rec = mLruProcesses.get(i);
@@ -3961,7 +3994,7 @@
     }
     
     public boolean clearApplicationUserData(final String packageName,
-            final IPackageDataObserver observer) {
+            final IPackageDataObserver observer, final int userId) {
         int uid = Binder.getCallingUid();
         int pid = Binder.getCallingPid();
         long callingId = Binder.clearCallingIdentity();
@@ -3996,7 +4029,7 @@
                         Uri.fromParts("package", packageName, null));
                 intent.putExtra(Intent.EXTRA_UID, pkgUid);
                 broadcastIntentInPackage("android", Process.SYSTEM_UID, intent,
-                        null, null, 0, null, null, null, false, false);
+                        null, null, 0, null, null, null, false, false, userId);
             } catch (RemoteException e) {
             }
         } finally {
@@ -4091,7 +4124,7 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
-        
+        final int userId = Binder.getOrigCallingUser();
         long callingId = Binder.clearCallingIdentity();
         try {
             IPackageManager pm = AppGlobals.getPackageManager();
@@ -4099,6 +4132,8 @@
             synchronized(this) {
                 try {
                     pkgUid = pm.getPackageUid(packageName);
+                    // Convert the uid to the one for the calling user
+                    pkgUid = UserId.getUid(userId, pkgUid);
                 } catch (RemoteException e) {
                 }
                 if (pkgUid == -1) {
@@ -4181,7 +4216,7 @@
             }
             
             broadcastIntentLocked(null, null, intent, null,
-                    null, 0, null, null, null, false, false, -1, uid);
+                    null, 0, null, null, null, false, false, -1, uid, 0 /* TODO: Verify */);
         }
         Binder.restoreCallingIdentity(origId);
     }
@@ -4241,7 +4276,8 @@
         intent.putExtra(Intent.EXTRA_UID, uid);
         broadcastIntentLocked(null, null, intent,
                 null, null, 0, null, null, null,
-                false, false, MY_PID, Process.SYSTEM_UID);
+                false, false,
+                MY_PID, Process.SYSTEM_UID, UserId.getUserId(uid));
     }
     
     private final boolean killPackageProcessesLocked(String packageName, int uid,
@@ -4345,7 +4381,8 @@
         }
 
         ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
-        for (ServiceRecord service : mServices.values()) {
+        int userId = UserId.getUserId(uid);
+        for (ServiceRecord service : mServiceMap.getAllServices(userId)) {
             if (service.packageName.equals(name)
                     && (service.app == null || evenPersistent || !service.app.persistent)) {
                 if (!doit) {
@@ -4809,11 +4846,12 @@
                 // Tell anyone interested that we are done booting!
                 SystemProperties.set("sys.boot_completed", "1");
                 SystemProperties.set("dev.bootcomplete", "1");
+                /* TODO: Send this to all users that are to be logged in on startup */
                 broadcastIntentLocked(null, null,
                         new Intent(Intent.ACTION_BOOT_COMPLETED, null),
                         null, null, 0, null, null,
                         android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
-                        false, false, MY_PID, Process.SYSTEM_UID);
+                        false, false, MY_PID, Process.SYSTEM_UID, Binder.getOrigCallingUser());
             }
         }
     }
@@ -4954,7 +4992,7 @@
                 if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
                     int uid = AppGlobals.getPackageManager()
                             .getPackageUid(packageName);
-                    if (uid != Binder.getCallingUid()) {
+                    if (UserId.getAppId(callingUid) != uid) {
                         String msg = "Permission Denial: getIntentSender() from pid="
                             + Binder.getCallingPid()
                             + ", uid=" + Binder.getCallingUid()
@@ -4965,7 +5003,10 @@
                     }
                 }
                 
-                return getIntentSenderLocked(type, packageName, callingUid,
+                if (DEBUG_MU)
+                    Slog.i(TAG_MU, "Getting intent sender for origCallingUid="
+                            + Binder.getOrigCallingUid());
+                return getIntentSenderLocked(type, packageName, Binder.getOrigCallingUid(),
                         token, resultWho, requestCode, intents, resolvedTypes, flags);
                 
             } catch (RemoteException e) {
@@ -4977,6 +5018,8 @@
     IIntentSender getIntentSenderLocked(int type,
             String packageName, int callingUid, IBinder token, String resultWho,
             int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
+        if (DEBUG_MU)
+            Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid);
         ActivityRecord activity = null;
         if (type == INTENT_SENDER_ACTIVITY_RESULT) {
             activity = mMainStack.isInStackLocked(token);
@@ -5220,7 +5263,7 @@
         }
         // If there is a uid that owns whatever is being accessed, it has
         // blanket access to it regardless of the permissions it requires.
-        if (owningUid >= 0 && uid == owningUid) {
+        if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
             return PackageManager.PERMISSION_GRANTED;
         }
         // If the target is not exported, then nobody else can get to it.
@@ -5254,7 +5297,7 @@
         if (permission == null) {
             return PackageManager.PERMISSION_DENIED;
         }
-        return checkComponentPermission(permission, pid, uid, -1, true);
+        return checkComponentPermission(permission, pid, UserId.getAppId(uid), -1, true);
     }
 
     /**
@@ -5264,7 +5307,7 @@
     int checkCallingPermission(String permission) {
         return checkPermission(permission,
                 Binder.getCallingPid(),
-                Binder.getCallingUid());
+                UserId.getAppId(Binder.getCallingUid()));
     }
 
     /**
@@ -5378,6 +5421,7 @@
             pid = tlsIdentity.pid;
         }
 
+        uid = UserId.getAppId(uid);
         // Our own process gets to do everything.
         if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
@@ -5420,7 +5464,8 @@
 
         String name = uri.getAuthority();
         ProviderInfo pi = null;
-        ContentProviderRecord cpr = mProvidersByName.get(name);
+        ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
+                UserId.getUserId(callingUid));
         if (cpr != null) {
             pi = cpr.info;
         } else {
@@ -5676,7 +5721,8 @@
 
         final String authority = uri.getAuthority();
         ProviderInfo pi = null;
-        ContentProviderRecord cpr = mProvidersByName.get(authority);
+        ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
+                UserId.getUserId(callingUid));
         if (cpr != null) {
             pi = cpr.info;
         } else {
@@ -5770,7 +5816,8 @@
 
             final String authority = uri.getAuthority();
             ProviderInfo pi = null;
-            ContentProviderRecord cpr = mProvidersByName.get(authority);
+            ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
+                    UserId.getUserId(r.info.uid));
             if (cpr != null) {
                 pi = cpr.info;
             } else {
@@ -6009,6 +6056,12 @@
 
     public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
             int flags) {
+        final int callingUid = Binder.getCallingUid();
+        // If it's the system uid asking, then use the current user id.
+        // TODO: Make sure that there aren't any other legitimate calls from the system uid that
+        // require the entire list.
+        final int callingUserId = callingUid == Process.SYSTEM_UID
+                ? mCurrentUserId : UserId.getUserId(callingUid);
         synchronized (this) {
             enforceCallingPermission(android.Manifest.permission.GET_TASKS,
                     "getRecentTasks()");
@@ -6021,11 +6074,14 @@
                             maxNum < N ? maxNum : N);
             for (int i=0; i<N && maxNum > 0; i++) {
                 TaskRecord tr = mRecentTasks.get(i);
+                // Only add calling user's recent tasks
+                if (tr.userId != callingUserId) continue;
                 // Return the entry if desired by the caller.  We always return
                 // the first entry, because callers always expect this to be the
-                // forground app.  We may filter others if the caller has
+                // foreground app.  We may filter others if the caller has
                 // not supplied RECENT_WITH_EXCLUDED and there is some reason
                 // we should exclude the entry.
+
                 if (i == 0
                         || ((flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0)
                         || (tr.intent == null)
@@ -6114,7 +6170,7 @@
 
         // Find any running services associated with this app.
         ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
-        for (ServiceRecord sr : mServices.values()) {
+        for (ServiceRecord sr : mServiceMap.getAllServices(root.userId)) {
             if (sr.packageName.equals(component.getPackageName())) {
                 services.add(sr);
             }
@@ -6485,17 +6541,23 @@
                         STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
         } catch (RemoteException ex) {
         }
+        if (DEBUG_MU)
+            Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.info.uid);
+        int userId = UserId.getUserId(app.info.uid);
         if (providers != null) {
             final int N = providers.size();
             for (int i=0; i<N; i++) {
                 ProviderInfo cpi =
                     (ProviderInfo)providers.get(i);
+
                 ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
-                ContentProviderRecord cpr = mProvidersByClass.get(comp);
+                ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
                 if (cpr == null) {
                     cpr = new ContentProviderRecord(cpi, app.info, comp);
-                    mProvidersByClass.put(comp, cpr);
+                    mProviderMap.putProviderByClass(comp, cpr);
                 }
+                if (DEBUG_MU)
+                    Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
                 app.pubProviders.put(cpi.name, cpr);
                 app.addPackage(cpi.applicationInfo.packageName);
                 ensurePackageDexOpt(cpi.applicationInfo.packageName);
@@ -6605,8 +6667,8 @@
         return false;
     }
 
-    private final ContentProviderHolder getContentProviderImpl(
-        IApplicationThread caller, String name) {
+    private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
+            String name) {
         ContentProviderRecord cpr;
         ProviderInfo cpi = null;
 
@@ -6623,7 +6685,8 @@
             }
 
             // First check if this content provider has been published...
-            cpr = mProvidersByName.get(name);
+            int userId = UserId.getUserId(r != null ? r.info.uid : Binder.getCallingUid());
+            cpr = mProviderMap.getProviderByName(name, userId);
             boolean providerRunning = cpr != null;
             if (providerRunning) {
                 cpi = cpr.info;
@@ -6708,6 +6771,9 @@
                     return null;
                 }
 
+                cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo,
+                        Binder.getOrigCallingUser());
+
                 String msg;
                 if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
                     throw new SecurityException(msg);
@@ -6723,7 +6789,7 @@
                 }
 
                 ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
-                cpr = mProvidersByClass.get(comp);
+                cpr = mProviderMap.getProviderByClass(comp, Binder.getOrigCallingUser());
                 final boolean firstClass = cpr == null;
                 if (firstClass) {
                     try {
@@ -6737,6 +6803,7 @@
                                     + cpi.name);
                             return null;
                         }
+                        ai = getAppInfoForUser(ai, Binder.getOrigCallingUser());
                         cpr = new ContentProviderRecord(cpi, ai, comp);
                     } catch (RemoteException ex) {
                         // pm is in same process, this will never happen.
@@ -6805,9 +6872,9 @@
                 // Make sure the provider is published (the same provider class
                 // may be published under multiple names).
                 if (firstClass) {
-                    mProvidersByClass.put(comp, cpr);
+                    mProviderMap.putProviderByClass(comp, cpr);
                 }
-                mProvidersByName.put(name, cpr);
+                mProviderMap.putProviderByName(name, cpr);
                 incProviderCount(r, cpr);
             }
         }
@@ -6826,6 +6893,10 @@
                     return null;
                 }
                 try {
+                    if (DEBUG_MU) {
+                        Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="
+                                + cpr.launchingApp);
+                    }
                     cpr.wait();
                 } catch (InterruptedException ex) {
                 }
@@ -6843,7 +6914,8 @@
             throw new SecurityException(msg);
         }
 
-        return getContentProviderImpl(caller, name);
+        ContentProviderHolder contentProvider = getContentProviderImpl(caller, name);
+        return contentProvider;
     }
 
     private ContentProviderHolder getContentProviderExternal(String name) {
@@ -6856,7 +6928,8 @@
      */
     public void removeContentProvider(IApplicationThread caller, String name) {
         synchronized (this) {
-            ContentProviderRecord cpr = mProvidersByName.get(name);
+            int userId = UserId.getUserId(Binder.getCallingUid());
+            ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
             if(cpr == null) {
                 // remove from mProvidersByClass
                 if (DEBUG_PROVIDER) Slog.v(TAG, name +
@@ -6871,8 +6944,11 @@
             }
             //update content provider record entry info
             ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
-            ContentProviderRecord localCpr = mProvidersByClass.get(comp);
-            if (localCpr.proc == r) {
+            ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
+            if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
+                    + r.info.processName + " from process "
+                    + localCpr.appInfo.processName);
+            if (localCpr.launchingApp == r) {
                 //should not happen. taken care of as a local provider
                 Slog.w(TAG, "removeContentProvider called on local provider: "
                         + cpr.info.name + " in process " + r.processName);
@@ -6887,7 +6963,8 @@
 
     private void removeContentProviderExternal(String name) {
         synchronized (this) {
-            ContentProviderRecord cpr = mProvidersByName.get(name);
+            ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
+                    Binder.getOrigCallingUser());
             if(cpr == null) {
                 //remove from mProvidersByClass
                 if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -6896,7 +6973,8 @@
 
             //update content provider record entry info
             ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
-            ContentProviderRecord localCpr = mProvidersByClass.get(comp);
+            ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp,
+                    Binder.getOrigCallingUser());
             localCpr.externals--;
             if (localCpr.externals < 0) {
                 Slog.e(TAG, "Externals < 0 for content provider " + localCpr);
@@ -6913,6 +6991,8 @@
 
         synchronized(this) {
             final ProcessRecord r = getRecordForAppLocked(caller);
+            if (DEBUG_MU)
+                Slog.v(TAG_MU, "ProcessRecord uid = " + r.info.uid);
             if (r == null) {
                 throw new SecurityException(
                         "Unable to find app for caller " + caller
@@ -6929,12 +7009,14 @@
                     continue;
                 }
                 ContentProviderRecord dst = r.pubProviders.get(src.info.name);
+                if (DEBUG_MU)
+                    Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
                 if (dst != null) {
                     ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
-                    mProvidersByClass.put(comp, dst);
+                    mProviderMap.putProviderByClass(comp, dst);
                     String names[] = dst.info.authority.split(";");
                     for (int j = 0; j < names.length; j++) {
-                        mProvidersByName.put(names[j], dst);
+                        mProviderMap.putProviderByName(names[j], dst);
                     }
 
                     int NL = mLaunchingProviders.size();
@@ -7679,8 +7761,10 @@
                             };
                         }
                         Slog.i(TAG, "Sending system update to: " + intent.getComponent());
+                        /* TODO: Send this to all users */
                         broadcastIntentLocked(null, null, intent, null, finisher,
-                                0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID);
+                                0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
+                                Process.SYSTEM_UID);
                         if (finisher != null) {
                             mWaitingUpdate = true;
                         }
@@ -9306,8 +9390,14 @@
 
         if ("all".equals(name)) {
             synchronized (this) {
-                for (ServiceRecord r1 : mServices.values()) {
-                    services.add(r1);
+                try {
+                    List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+                    for (UserInfo user : users) {
+                        for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+                            services.add(r1);
+                        }
+                    }
+                } catch (RemoteException re) {
                 }
             }
         } else {
@@ -9325,18 +9415,24 @@
             }
 
             synchronized (this) {
-                for (ServiceRecord r1 : mServices.values()) {
-                    if (componentName != null) {
-                        if (r1.name.equals(componentName)) {
-                            services.add(r1);
+                try {
+                    List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+                    for (UserInfo user : users) {
+                        for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+                            if (componentName != null) {
+                                if (r1.name.equals(componentName)) {
+                                    services.add(r1);
+                                }
+                            } else if (name != null) {
+                                if (r1.name.flattenToString().contains(name)) {
+                                    services.add(r1);
+                                }
+                            } else if (System.identityHashCode(r1) == objectId) {
+                                services.add(r1);
+                            }
                         }
-                    } else if (name != null) {
-                        if (r1.name.flattenToString().contains(name)) {
-                            services.add(r1);
-                        }
-                    } else if (System.identityHashCode(r1) == objectId) {
-                        services.add(r1);
                     }
+                } catch (RemoteException re) {
                 }
             }
         }
@@ -9778,75 +9874,87 @@
         matcher.build(args, opti);
 
         pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
-        if (mServices.size() > 0) {
-            boolean printed = false;
-            long nowReal = SystemClock.elapsedRealtime();
-            Iterator<ServiceRecord> it = mServices.values().iterator();
-            needSep = false;
-            while (it.hasNext()) {
-                ServiceRecord r = it.next();
-                if (!matcher.match(r, r.name)) {
-                    continue;
-                }
-                if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
-                    continue;
-                }
-                if (!printed) {
-                    pw.println("  Active services:");
-                    printed = true;
-                }
-                if (needSep) {
-                    pw.println();
-                }
-                pw.print("  * "); pw.println(r);
-                if (dumpAll) {
-                    r.dump(pw, "    ");
-                    needSep = true;
-                } else {
-                    pw.print("    app="); pw.println(r.app);
-                    pw.print("    created=");
-                        TimeUtils.formatDuration(r.createTime, nowReal, pw);
-                        pw.print(" started="); pw.print(r.startRequested);
-                        pw.print(" connections="); pw.println(r.connections.size());
-                    if (r.connections.size() > 0) {
-                        pw.println("    Connections:");
-                        for (ArrayList<ConnectionRecord> clist : r.connections.values()) {
-                            for (int i=0; i<clist.size(); i++) {
-                                ConnectionRecord conn = clist.get(i);
-                                pw.print("      ");
-                                pw.print(conn.binding.intent.intent.getIntent().toShortString(
-                                        false, false, false));
-                                pw.print(" -> ");
-                                ProcessRecord proc = conn.binding.client;
-                                pw.println(proc != null ? proc.toShortString() : "null");
+        try {
+            List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+            for (UserInfo user : users) {
+                if (mServiceMap.getAllServices(user.id).size() > 0) {
+                    boolean printed = false;
+                    long nowReal = SystemClock.elapsedRealtime();
+                    Iterator<ServiceRecord> it = mServiceMap.getAllServices(
+                            user.id).iterator();
+                    needSep = false;
+                    while (it.hasNext()) {
+                        ServiceRecord r = it.next();
+                        if (!matcher.match(r, r.name)) {
+                            continue;
+                        }
+                        if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+                            continue;
+                        }
+                        if (!printed) {
+                            pw.println("  Active services:");
+                            printed = true;
+                        }
+                        if (needSep) {
+                            pw.println();
+                        }
+                        pw.print("  * ");
+                        pw.println(r);
+                        if (dumpAll) {
+                            r.dump(pw, "    ");
+                            needSep = true;
+                        } else {
+                            pw.print("    app=");
+                            pw.println(r.app);
+                            pw.print("    created=");
+                            TimeUtils.formatDuration(r.createTime, nowReal, pw);
+                            pw.print(" started=");
+                            pw.print(r.startRequested);
+                            pw.print(" connections=");
+                            pw.println(r.connections.size());
+                            if (r.connections.size() > 0) {
+                                pw.println("    Connections:");
+                                for (ArrayList<ConnectionRecord> clist : r.connections.values()) {
+                                    for (int i = 0; i < clist.size(); i++) {
+                                        ConnectionRecord conn = clist.get(i);
+                                        pw.print("      ");
+                                        pw.print(conn.binding.intent.intent.getIntent()
+                                                .toShortString(false, false, false));
+                                        pw.print(" -> ");
+                                        ProcessRecord proc = conn.binding.client;
+                                        pw.println(proc != null ? proc.toShortString() : "null");
+                                    }
+                                }
                             }
                         }
-                    }
-                }
-                if (dumpClient && r.app != null && r.app.thread != null) {
-                    pw.println("    Client:");
-                    pw.flush();
-                    try {
-                        TransferPipe tp = new TransferPipe();
-                        try {
-                            r.app.thread.dumpService(
-                                    tp.getWriteFd().getFileDescriptor(), r, args);
-                            tp.setBufferPrefix("      ");
-                            // Short timeout, since blocking here can
-                            // deadlock with the application.
-                            tp.go(fd, 2000);
-                        } finally {
-                            tp.kill();
+                        if (dumpClient && r.app != null && r.app.thread != null) {
+                            pw.println("    Client:");
+                            pw.flush();
+                            try {
+                                TransferPipe tp = new TransferPipe();
+                                try {
+                                    r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(),
+                                            r, args);
+                                    tp.setBufferPrefix("      ");
+                                    // Short timeout, since blocking here can
+                                    // deadlock with the application.
+                                    tp.go(fd, 2000);
+                                } finally {
+                                    tp.kill();
+                                }
+                            } catch (IOException e) {
+                                pw.println("      Failure while dumping the service: " + e);
+                            } catch (RemoteException e) {
+                                pw.println("      Got a RemoteException while dumping the service");
+                            }
+                            needSep = true;
                         }
-                    } catch (IOException e) {
-                        pw.println("      Failure while dumping the service: " + e);
-                    } catch (RemoteException e) {
-                        pw.println("      Got a RemoteException while dumping the service");
                     }
-                    needSep = true;
+                    needSep = printed;
                 }
             }
-            needSep = printed;
+        } catch (RemoteException re) {
+
         }
 
         if (mPendingServices.size() > 0) {
@@ -9956,76 +10064,8 @@
         matcher.build(args, opti);
 
         pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)");
-        if (mProvidersByClass.size() > 0) {
-            boolean printed = false;
-            Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it
-                    = mProvidersByClass.entrySet().iterator();
-            while (it.hasNext()) {
-                Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
-                ContentProviderRecord r = e.getValue();
-                ComponentName comp = e.getKey();
-                String cls = comp.getClassName();
-                int end = cls.lastIndexOf('.');
-                if (end > 0 && end < (cls.length()-2)) {
-                    cls = cls.substring(end+1);
-                }
-                if (!matcher.match(r, comp)) {
-                    continue;
-                }
-                if (dumpPackage != null && !dumpPackage.equals(comp.getPackageName())) {
-                    continue;
-                }
-                if (!printed) {
-                    if (needSep) pw.println(" ");
-                    needSep = true;
-                    pw.println("  Published content providers (by class):");
-                    printed = true;
-                }
-                pw.print("  * "); pw.print(cls); pw.print(" (");
-                        pw.print(comp.flattenToShortString()); pw.println(")");
-                if (dumpAll) {
-                    r.dump(pw, "      ");
-                } else {
-                    if (r.proc != null) {
-                        pw.print("      "); pw.println(r.proc);
-                    } else {
-                        pw.println();
-                    }
-                    if (r.clients.size() > 0) {
-                        pw.println("      Clients:");
-                        for (ProcessRecord cproc : r.clients) {
-                            pw.print("        - "); pw.println(cproc);
-                        }
-                    }
-                }
-            }
-        }
-    
-        if (dumpAll) {
-            if (mProvidersByName.size() > 0) {
-                boolean printed = false;
-                Iterator<Map.Entry<String, ContentProviderRecord>> it
-                        = mProvidersByName.entrySet().iterator();
-                while (it.hasNext()) {
-                    Map.Entry<String, ContentProviderRecord> e = it.next();
-                    ContentProviderRecord r = e.getValue();
-                    if (!matcher.match(r, r.name)) {
-                        continue;
-                    }
-                    if (dumpPackage != null && !dumpPackage.equals(r.name.getPackageName())) {
-                        continue;
-                    }
-                    if (!printed) {
-                        if (needSep) pw.println(" ");
-                        needSep = true;
-                        pw.println("  Authority to provider mappings:");
-                        printed = true;
-                    }
-                    pw.print("  "); pw.print(e.getKey()); pw.println(":");
-                    pw.print("    "); pw.println(r);
-                }
-            }
-        }
+
+        mProviderMap.dumpProvidersLocked(pw, dumpAll);
 
         if (mLaunchingProviders.size() > 0) {
             boolean printed = false;
@@ -10911,10 +10951,10 @@
             cpr.notifyAll();
         }
         
-        mProvidersByClass.remove(cpr.name);
+        mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
         String names[] = cpr.info.authority.split(";");
         for (int j = 0; j < names.length; j++) {
-            mProvidersByName.remove(names[j]);
+            mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
         }
         
         Iterator<ProcessRecord> cit = cpr.clients.iterator();
@@ -11190,8 +11230,10 @@
             ArrayList<ActivityManager.RunningServiceInfo> res
                     = new ArrayList<ActivityManager.RunningServiceInfo>();
             
-            if (mServices.size() > 0) {
-                Iterator<ServiceRecord> it = mServices.values().iterator();
+            int userId = UserId.getUserId(Binder.getCallingUid());
+            if (mServiceMap.getAllServices(userId).size() > 0) {
+                Iterator<ServiceRecord> it
+                        = mServiceMap.getAllServices(userId).iterator();
                 while (it.hasNext() && res.size() < maxNum) {
                     res.add(makeRunningServiceInfoLocked(it.next()));
                 }
@@ -11211,7 +11253,8 @@
 
     public PendingIntent getRunningServiceControlPanel(ComponentName name) {
         synchronized (this) {
-            ServiceRecord r = mServices.get(name);
+            int userId = UserId.getUserId(Binder.getCallingUid());
+            ServiceRecord r = mServiceMap.getServiceByName(name, userId);
             if (r != null) {
                 for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
                     for (int i=0; i<conn.size(); i++) {
@@ -11227,7 +11270,7 @@
     
     private final ServiceRecord findServiceLocked(ComponentName name,
             IBinder token) {
-        ServiceRecord r = mServices.get(name);
+        ServiceRecord r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
         return r == token ? r : null;
     }
 
@@ -11245,11 +11288,11 @@
             String resolvedType) {
         ServiceRecord r = null;
         if (service.getComponent() != null) {
-            r = mServices.get(service.getComponent());
+            r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
         }
         if (r == null) {
             Intent.FilterComparison filter = new Intent.FilterComparison(service);
-            r = mServicesByIntent.get(filter);
+            r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
         }
 
         if (r == null) {
@@ -11265,7 +11308,7 @@
 
                 ComponentName name = new ComponentName(
                         sInfo.applicationInfo.packageName, sInfo.name);
-                r = mServices.get(name);
+                r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
             } catch (RemoteException ex) {
                 // pm is in same process, this will never happen.
             }
@@ -11312,11 +11355,17 @@
     private ServiceLookupResult retrieveServiceLocked(Intent service,
             String resolvedType, int callingPid, int callingUid) {
         ServiceRecord r = null;
+        if (DEBUG_SERVICE)
+            Slog.v(TAG, "retrieveServiceLocked: " + service + " type=" + resolvedType
+                    + " origCallingUid=" + callingUid);
+
         if (service.getComponent() != null) {
-            r = mServices.get(service.getComponent());
+            r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
         }
-        Intent.FilterComparison filter = new Intent.FilterComparison(service);
-        r = mServicesByIntent.get(filter);
+        if (r == null) {
+            Intent.FilterComparison filter = new Intent.FilterComparison(service);
+            r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
+        }
         if (r == null) {
             try {
                 ResolveInfo rInfo =
@@ -11329,12 +11378,16 @@
                           ": not found");
                     return null;
                 }
-
+                if (Binder.getOrigCallingUser() > 0) {
+                    sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo,
+                            Binder.getOrigCallingUser());
+                }
                 ComponentName name = new ComponentName(
                         sInfo.applicationInfo.packageName, sInfo.name);
-                r = mServices.get(name);
+                r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
                 if (r == null) {
-                    filter = new Intent.FilterComparison(service.cloneFilter());
+                    Intent.FilterComparison filter = new Intent.FilterComparison(
+                            service.cloneFilter());
                     ServiceRestarter res = new ServiceRestarter();
                     BatteryStatsImpl.Uid.Pkg.Serv ss = null;
                     BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
@@ -11345,8 +11398,8 @@
                     }
                     r = new ServiceRecord(this, ss, name, filter, sInfo, res);
                     res.setService(r);
-                    mServices.put(name, r);
-                    mServicesByIntent.put(filter, r);
+                    mServiceMap.putServiceByName(name, UserId.getUserId(r.appInfo.uid), r);
+                    mServiceMap.putServiceByIntent(filter, UserId.getUserId(r.appInfo.uid), r);
                     
                     // Make sure this component isn't in the pending list.
                     int N = mPendingServices.size();
@@ -11493,7 +11546,9 @@
         if (app.thread == null) {
             throw new RemoteException();
         }
-
+        if (DEBUG_MU)
+            Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+                    + ", ProcessRecord.uid = " + app.info.uid);
         r.app = app;
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
@@ -11690,6 +11745,8 @@
 
         final String appName = r.processName;
         ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid);
+        if (DEBUG_MU)
+            Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
         if (app != null && app.thread != null) {
             try {
                 app.addPackage(r.appInfo.packageName);
@@ -11794,8 +11851,8 @@
                 System.identityHashCode(r), r.shortName,
                 (r.app != null) ? r.app.pid : -1);
 
-        mServices.remove(r.name);
-        mServicesByIntent.remove(r.intent);
+        mServiceMap.removeServiceByName(r.name, r.userId);
+        mServiceMap.removeServiceByIntent(r.intent, r.userId);
         r.totalRestartCount = 0;
         unscheduleServiceRestartLocked(r);
 
@@ -11909,6 +11966,8 @@
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
+        if (DEBUG_SERVICE)
+            Slog.v(TAG, "startService: " + service + " type=" + resolvedType);
         synchronized(this) {
             final int callingPid = Binder.getCallingPid();
             final int callingUid = Binder.getCallingUid();
@@ -11923,6 +11982,8 @@
     ComponentName startServiceInPackage(int uid,
             Intent service, String resolvedType) {
         synchronized(this) {
+            if (DEBUG_SERVICE)
+                Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
             final long origId = Binder.clearCallingIdentity();
             ComponentName res = startServiceLocked(null, service,
                     resolvedType, -1, uid);
@@ -12126,6 +12187,9 @@
             if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
                     + " type=" + resolvedType + " conn=" + connection.asBinder()
                     + " flags=0x" + Integer.toHexString(flags));
+            if (DEBUG_MU)
+                Slog.i(TAG_MU, "bindService uid=" + Binder.getCallingUid() + " origUid="
+                        + Binder.getOrigCallingUid());
             final ProcessRecord callerApp = getRecordForAppLocked(caller);
             if (callerApp == null) {
                 throw new SecurityException(
@@ -12168,7 +12232,7 @@
             
             ServiceLookupResult res =
                 retrieveServiceLocked(service, resolvedType,
-                        Binder.getCallingPid(), Binder.getCallingUid());
+                        Binder.getCallingPid(), Binder.getOrigCallingUid());
             if (res == null) {
                 return 0;
             }
@@ -12514,7 +12578,9 @@
                         r.callStart = false;
                     }
                 }
-                
+                if (DEBUG_MU)
+                    Slog.v(TAG_MU, "before serviceDontExecutingLocked, uid="
+                            + Binder.getOrigCallingUid());
                 final long origId = Binder.clearCallingIdentity();
                 serviceDoneExecutingLocked(r, inStopping);
                 Binder.restoreCallingIdentity(origId);
@@ -12927,7 +12993,8 @@
             String callerPackage, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle map, String requiredPermission,
-            boolean ordered, boolean sticky, int callingPid, int callingUid) {
+            boolean ordered, boolean sticky, int callingPid, int callingUid,
+            int userId) {
         intent = new Intent(intent);
 
         // By default broadcasts do not go to stopped apps.
@@ -13102,10 +13169,11 @@
                 if (ai != null) {
                     receivers = new ArrayList();
                     ResolveInfo ri = new ResolveInfo();
-                    ri.activityInfo = ai;
+                    ri.activityInfo = getActivityInfoForUser(ai, userId);
                     receivers.add(ri);
                 }
             } else {
+                // TODO: Apply userId
                 // Need to resolve the intent to interested receivers...
                 if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                          == 0) {
@@ -13274,7 +13342,7 @@
     public final int broadcastIntent(IApplicationThread caller,
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle map,
-            String requiredPermission, boolean serialized, boolean sticky) {
+            String requiredPermission, boolean serialized, boolean sticky, int userId) {
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
             
@@ -13285,8 +13353,8 @@
             int res = broadcastIntentLocked(callerApp,
                     callerApp != null ? callerApp.info.packageName : null,
                     intent, resolvedType, resultTo,
-                    resultCode, resultData, map, requiredPermission, serialized,
-                    sticky, callingPid, callingUid);
+                    resultCode, resultData, map, requiredPermission, serialized, sticky,
+                    callingPid, callingUid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
@@ -13295,21 +13363,21 @@
     int broadcastIntentInPackage(String packageName, int uid,
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle map,
-            String requiredPermission, boolean serialized, boolean sticky) {
+            String requiredPermission, boolean serialized, boolean sticky, int userId) {
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
 
             final long origId = Binder.clearCallingIdentity();
             int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
                     resultTo, resultCode, resultData, map, requiredPermission,
-                    serialized, sticky, -1, uid);
+                    serialized, sticky, -1, uid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
     }
 
-    public final void unbroadcastIntent(IApplicationThread caller,
-            Intent intent) {
+    // TODO: Use the userId; maybe mStickyBroadcasts need to be tied to the user.
+    public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -13514,7 +13582,6 @@
         }
     }
 
-
     // =========================================================
     // INSTRUMENTATION
     // =========================================================
@@ -13786,12 +13853,12 @@
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                         | Intent.FLAG_RECEIVER_REPLACE_PENDING);
                 broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
-                        null, false, false, MY_PID, Process.SYSTEM_UID);
+                        null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
                 if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
                     broadcastIntentLocked(null, null,
                             new Intent(Intent.ACTION_LOCALE_CHANGED),
                             null, null, 0, null, null,
-                            null, false, false, MY_PID, Process.SYSTEM_UID);
+                            null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
                 }
             }
         }
@@ -15134,8 +15201,157 @@
 
     // Multi-user methods
 
-    public boolean switchUser(int userid) {
-        // TODO
+    private int mCurrentUserId;
+    private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
+
+    public boolean switchUser(int userId) {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != 0 && callingUid != Process.myUid()) {
+            Slog.e(TAG, "Trying to switch user from unauthorized app");
+            return false;
+        }
+        if (mCurrentUserId == userId)
+            return true;
+
+        synchronized (this) {
+            // Check if user is already logged in, otherwise check if user exists first before
+            // adding to the list of logged in users.
+            if (mLoggedInUsers.indexOfKey(userId) < 0) {
+                if (!userExists(userId)) {
+                    return false;
+                }
+                mLoggedInUsers.append(userId, userId);
+            }
+
+            mCurrentUserId = userId;
+            boolean haveActivities = mMainStack.switchUser(userId);
+            if (!haveActivities) {
+                startHomeActivityLocked(userId);
+            }
+        }
         return true;
     }
+
+    private boolean userExists(int userId) {
+        try {
+            List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+            for (UserInfo user : users) {
+                if (user.id == userId) {
+                    return true;
+                }
+            }
+        } catch (RemoteException re) {
+            // Won't happen, in same process
+        }
+
+        return false;
+    }
+
+
+    private int applyUserId(int uid, int userId) {
+        return UserId.getUid(userId, uid);
+    }
+
+    private ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
+        ApplicationInfo newInfo = new ApplicationInfo(info);
+        newInfo.uid = applyUserId(info.uid, userId);
+        if (newInfo.uid >= Process.FIRST_APPLICATION_UID) {
+            newInfo.dataDir = USER_DATA_DIR + userId + "/"
+                    + info.packageName;
+        }
+        return newInfo;
+    }
+
+    ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
+        if (aInfo.applicationInfo.uid < Process.FIRST_APPLICATION_UID
+                || userId < 1) {
+            return aInfo;
+        }
+
+        ActivityInfo info = new ActivityInfo(aInfo);
+        info.applicationInfo = getAppInfoForUser(info.applicationInfo, userId);
+        return info;
+    }
+
+    static class ServiceMap {
+
+        private final SparseArray<HashMap<ComponentName, ServiceRecord>> mServicesByNamePerUser
+                = new SparseArray<HashMap<ComponentName, ServiceRecord>>();
+        private final SparseArray<HashMap<Intent.FilterComparison, ServiceRecord>>
+                mServicesByIntentPerUser = new SparseArray<
+                    HashMap<Intent.FilterComparison, ServiceRecord>>();
+
+        ServiceRecord getServiceByName(ComponentName name, int callingUser) {
+            // TODO: Deal with global services
+            if (DEBUG_MU)
+                Slog.v(TAG_MU, "getServiceByName(" + name + "), callingUser = " + callingUser);
+            return getServices(callingUser).get(name);
+        }
+
+        ServiceRecord getServiceByName(ComponentName name) {
+            return getServiceByName(name, -1);
+        }
+
+        ServiceRecord getServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+            // TODO: Deal with global services
+            if (DEBUG_MU)
+                Slog.v(TAG_MU, "getServiceByIntent(" + filter + "), callingUser = " + callingUser);
+            return getServicesByIntent(callingUser).get(filter);
+        }
+
+        ServiceRecord getServiceByIntent(Intent.FilterComparison filter) {
+            return getServiceByIntent(filter, -1);
+        }
+
+        void putServiceByName(ComponentName name, int callingUser, ServiceRecord value) {
+            // TODO: Deal with global services
+            getServices(callingUser).put(name, value);
+        }
+
+        void putServiceByIntent(Intent.FilterComparison filter, int callingUser,
+                ServiceRecord value) {
+            // TODO: Deal with global services
+            getServicesByIntent(callingUser).put(filter, value);
+        }
+
+        void removeServiceByName(ComponentName name, int callingUser) {
+            // TODO: Deal with global services
+            ServiceRecord removed = getServices(callingUser).remove(name);
+            if (DEBUG_MU)
+                Slog.v(TAG, "removeServiceByName user=" + callingUser + " name=" + name
+                        + " removed=" + removed);
+        }
+
+        void removeServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+            // TODO: Deal with global services
+            ServiceRecord removed = getServicesByIntent(callingUser).remove(filter);
+            if (DEBUG_MU)
+                Slog.v(TAG_MU, "removeServiceByIntent user=" + callingUser + " intent=" + filter
+                        + " removed=" + removed);
+        }
+
+        Collection<ServiceRecord> getAllServices(int callingUser) {
+            // TODO: Deal with global services
+            return getServices(callingUser).values();
+        }
+
+        private HashMap<ComponentName, ServiceRecord> getServices(int callingUser) {
+            HashMap map = mServicesByNamePerUser.get(callingUser);
+            if (map == null) {
+                map = new HashMap<ComponentName, ServiceRecord>();
+                mServicesByNamePerUser.put(callingUser, map);
+            }
+            return map;
+        }
+
+        private HashMap<Intent.FilterComparison, ServiceRecord> getServicesByIntent(
+                int callingUser) {
+            HashMap map = mServicesByIntentPerUser.get(callingUser);
+            if (map == null) {
+                map = new HashMap<Intent.FilterComparison, ServiceRecord>();
+                mServicesByIntentPerUser.put(callingUser, map);
+            }
+            return map;
+        }
+    }
 }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index c819114..cdab6c6 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -25,6 +25,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.os.Build;
@@ -34,6 +35,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -55,6 +57,7 @@
     final IApplicationToken.Stub appToken; // window manager token
     final ActivityInfo info; // all about me
     final int launchedFromUid; // always the uid who started the activity.
+    final int userId;          // Which user is this running for?
     final Intent intent;    // the original intent that generated us
     final ComponentName realActivity;  // the intent component, or target of an alias.
     final String shortComponentName; // the short component name of the intent
@@ -124,6 +127,7 @@
         pw.print(prefix); pw.print("packageName="); pw.print(packageName);
                 pw.print(" processName="); pw.println(processName);
         pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
+        pw.print(prefix); pw.print("userId="); pw.print(userId);
                 pw.print(" app="); pw.println(app);
         pw.print(prefix); pw.println(intent.toInsecureString());
         pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask);
@@ -281,6 +285,7 @@
         appToken = new Token(this);
         info = aInfo;
         launchedFromUid = _launchedFromUid;
+        userId = UserId.getUserId(aInfo.applicationInfo.uid);
         intent = _intent;
         shortComponentName = _intent.getComponent().flattenToShortString();
         resolvedType = _resolvedType;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 6c11953..c3ae6a1 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -60,6 +60,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -274,6 +275,8 @@
     int mThumbnailWidth = -1;
     int mThumbnailHeight = -1;
 
+    private int mCurrentUser;
+
     static final int SLEEP_TIMEOUT_MSG = 8;
     static final int PAUSE_TIMEOUT_MSG = 9;
     static final int IDLE_TIMEOUT_MSG = 10;
@@ -365,6 +368,7 @@
     }
     
     final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+        // TODO: Don't look for any tasks from other users
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
@@ -377,6 +381,7 @@
     }
 
     final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+        // TODO: Don't look for any tasks from other users
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
@@ -398,6 +403,7 @@
      * @return Returns the HistoryRecord of the next activity on the stack.
      */
     final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
+        // TODO: Don't look for any tasks from other users
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
@@ -444,10 +450,11 @@
 
         TaskRecord cp = null;
 
+        final int userId = UserId.getUserId(info.applicationInfo.uid);
         final int N = mHistory.size();
         for (int i=(N-1); i>=0; i--) {
             ActivityRecord r = mHistory.get(i);
-            if (!r.finishing && r.task != cp
+            if (!r.finishing && r.task != cp && r.userId == userId
                     && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                 cp = r.task;
                 //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
@@ -487,12 +494,13 @@
         if (info.targetActivity != null) {
             cls = new ComponentName(info.packageName, info.targetActivity);
         }
+        final int userId = UserId.getUserId(info.applicationInfo.uid);
 
         final int N = mHistory.size();
         for (int i=(N-1); i>=0; i--) {
             ActivityRecord r = mHistory.get(i);
             if (!r.finishing) {
-                if (r.intent.getComponent().equals(cls)) {
+                if (r.intent.getComponent().equals(cls) && r.userId == userId) {
                     //Slog.i(TAG, "Found matching class!");
                     //dump();
                     //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
@@ -511,6 +519,43 @@
         mService.mHandler.sendMessage(msg);
     }
 
+    /*
+     * Move the activities around in the stack to bring a user to the foreground.
+     * @return whether there are any activities for the specified user.
+     */
+    final boolean switchUser(int userId) {
+        synchronized (mService) {
+            mCurrentUser = userId;
+
+            // Only one activity? Nothing to do...
+            if (mHistory.size() < 2)
+                return false;
+
+            boolean haveActivities = false;
+            // Check if the top activity is from the new user.
+            ActivityRecord top = mHistory.get(mHistory.size() - 1);
+            if (top.userId == userId) return true;
+            // Otherwise, move the user's activities to the top.
+            int N = mHistory.size();
+            int i = 0;
+            while (i < N) {
+                ActivityRecord r = mHistory.get(i);
+                if (r.userId == userId) {
+                    ActivityRecord moveToTop = mHistory.remove(i);
+                    mHistory.add(moveToTop);
+                    // No need to check the top one now
+                    N--;
+                    haveActivities = true;
+                } else {
+                    i++;
+                }
+            }
+            // Transition from the old top to the new top
+            resumeTopActivityLocked(top);
+            return haveActivities;
+        }
+    }
+
     final boolean realStartActivityLocked(ActivityRecord r,
             ProcessRecord app, boolean andResume, boolean checkConfig)
             throws RemoteException {
@@ -1272,7 +1317,7 @@
             // There are no more activities!  Let's just start up the
             // Launcher...
             if (mMainStack) {
-                return mService.startHomeActivityLocked();
+                return mService.startHomeActivityLocked(0);
             }
         }
 
@@ -1384,6 +1429,7 @@
         // Launching this app's activity, make sure the app is no longer
         // considered stopped.
         try {
+            // TODO: Apply to the correct userId
             AppGlobals.getPackageManager().setPackageStoppedState(
                     next.packageName, false);
         } catch (RemoteException e1) {
@@ -2354,7 +2400,7 @@
                 }
             }
         }
-        
+
         ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
                 intent, resolvedType, aInfo, mService.mConfiguration,
                 resultRecord, resultWho, requestCode, componentSpecified);
@@ -2420,7 +2466,8 @@
             int grantedMode, boolean onlyIfNeeded, boolean doResume) {
         final Intent intent = r.intent;
         final int callingUid = r.launchedFromUid;
-        
+        final int userId = r.userId;
+
         int launchFlags = intent.getFlags();
         
         // We'll invoke onUserLeaving before onPause only if the launching
@@ -2648,7 +2695,7 @@
             // once.
             ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
             if (top != null && r.resultTo == null) {
-                if (top.realActivity.equals(r.realActivity)) {
+                if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
                     if (top.app != null && top.app.thread != null) {
                         if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
                             || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
@@ -2821,12 +2868,12 @@
             int grantedMode, IBinder resultTo,
             String resultWho, int requestCode, boolean onlyIfNeeded,
             boolean debug, String profileFile, ParcelFileDescriptor profileFd,
-            boolean autoStopProfiler, WaitResult outResult, Configuration config) {
+            boolean autoStopProfiler,
+            WaitResult outResult, Configuration config, int userId) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
-
         boolean componentSpecified = intent.getComponent() != null;
 
         // Don't modify the client's object!
@@ -2835,6 +2882,7 @@
         // Collect information about the target of the Intent.
         ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug,
                 profileFile, profileFd, autoStopProfiler);
+        aInfo = mService.getActivityInfoForUser(aInfo, userId);
 
         synchronized (mService) {
             int callingPid;
@@ -2915,6 +2963,7 @@
                                         PackageManager.MATCH_DEFAULT_ONLY
                                         | ActivityManagerService.STOCK_PM_FLAGS);
                             aInfo = rInfo != null ? rInfo.activityInfo : null;
+                            aInfo = mService.getActivityInfoForUser(aInfo, userId);
                         } catch (RemoteException e) {
                             aInfo = null;
                         }
@@ -2977,7 +3026,8 @@
     }
     
     final int startActivities(IApplicationThread caller, int callingUid,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+            Intent[] intents,
+            String[] resolvedTypes, IBinder resultTo, int userId) {
         if (intents == null) {
             throw new NullPointerException("intents is null");
         }
@@ -3022,6 +3072,8 @@
                     // Collect information about the target of the Intent.
                     ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i], false,
                             null, null, false);
+                    // TODO: New, check if this is correct
+                    aInfo = mService.getActivityInfoForUser(aInfo, userId);
 
                     if (mMainStack && aInfo != null && (aInfo.applicationInfo.flags
                             & ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index abd2a1f..3b6a97c 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserId;
 import android.util.Slog;
 
 import java.io.PrintWriter;
@@ -248,7 +249,8 @@
                             owner.broadcastIntentInPackage(key.packageName, uid,
                                     finalIntent, resolvedType,
                                     finishedReceiver, code, null, null,
-                                    requiredPermission, (finishedReceiver != null), false);
+                                requiredPermission, (finishedReceiver != null), false, UserId
+                                        .getUserId(uid));
                             sendFinish = false;
                         } catch (RuntimeException e) {
                             Slog.w(ActivityManagerService.TAG,
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
new file mode 100644
index 0000000..44e7ecc
--- /dev/null
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.UserId;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Keeps track of content providers by authority (name) and class. It separates the mapping by
+ * user and ones that are not user-specific (system providers).
+ */
+public class ProviderMap {
+
+    private static final String TAG = "ProviderMap";
+
+    private static final boolean DBG = false;
+
+    private final HashMap<String, ContentProviderRecord> mGlobalByName
+            = new HashMap<String, ContentProviderRecord>();
+    private final HashMap<ComponentName, ContentProviderRecord> mGlobalByClass
+            = new HashMap<ComponentName, ContentProviderRecord>();
+
+    private final SparseArray<HashMap<String, ContentProviderRecord>> mProvidersByNamePerUser
+            = new SparseArray<HashMap<String, ContentProviderRecord>>();
+    private final SparseArray<HashMap<ComponentName, ContentProviderRecord>> mProvidersByClassPerUser
+            = new SparseArray<HashMap<ComponentName, ContentProviderRecord>>();
+
+    ContentProviderRecord getProviderByName(String name) {
+        return getProviderByName(name, -1);
+    }
+
+    ContentProviderRecord getProviderByName(String name, int userId) {
+        if (DBG) {
+            Slog.i(TAG, "getProviderByName: " + name + " , callingUid = " + Binder.getCallingUid());
+        }
+        // Try to find it in the global list
+        ContentProviderRecord record = mGlobalByName.get(name);
+        if (record != null) {
+            return record;
+        }
+
+        // Check the current user's list
+        return getProvidersByName(userId).get(name);
+    }
+
+    ContentProviderRecord getProviderByClass(ComponentName name) {
+        return getProviderByClass(name, -1);
+    }
+
+    ContentProviderRecord getProviderByClass(ComponentName name, int userId) {
+        if (DBG) {
+            Slog.i(TAG, "getProviderByClass: " + name + ", callingUid = " + Binder.getCallingUid());
+        }
+        // Try to find it in the global list
+        ContentProviderRecord record = mGlobalByClass.get(name);
+        if (record != null) {
+            return record;
+        }
+
+        // Check the current user's list
+        return getProvidersByClass(userId).get(name);
+    }
+
+    void putProviderByName(String name, ContentProviderRecord record) {
+        if (DBG) {
+            Slog.i(TAG, "putProviderByName: " + name + " , callingUid = " + Binder.getCallingUid()
+                + ", record uid = " + record.appInfo.uid);
+        }
+        if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
+            mGlobalByName.put(name, record);
+        } else {
+            final int userId = UserId.getUserId(record.appInfo.uid);
+            getProvidersByName(userId).put(name, record);
+        }
+    }
+
+    void putProviderByClass(ComponentName name, ContentProviderRecord record) {
+        if (DBG) {
+            Slog.i(TAG, "putProviderByClass: " + name + " , callingUid = " + Binder.getCallingUid()
+                + ", record uid = " + record.appInfo.uid);
+        }
+        if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
+            mGlobalByClass.put(name, record);
+        } else {
+            final int userId = UserId.getUserId(record.appInfo.uid);
+            getProvidersByClass(userId).put(name, record);
+        }
+    }
+
+    void removeProviderByName(String name, int optionalUserId) {
+        if (mGlobalByName.containsKey(name)) {
+            if (DBG)
+                Slog.i(TAG, "Removing from globalByName name=" + name);
+            mGlobalByName.remove(name);
+        } else {
+            // TODO: Verify this works, i.e., the caller happens to be from the correct user
+            if (DBG)
+                Slog.i(TAG,
+                        "Removing from providersByName name=" + name + " user="
+                        + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
+            getProvidersByName(optionalUserId).remove(name);
+        }
+    }
+
+    void removeProviderByClass(ComponentName name, int optionalUserId) {
+        if (mGlobalByClass.containsKey(name)) {
+            if (DBG)
+                Slog.i(TAG, "Removing from globalByClass name=" + name);
+            mGlobalByClass.remove(name);
+        } else {
+            if (DBG)
+                Slog.i(TAG,
+                        "Removing from providersByClass name=" + name + " user="
+                        + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
+            getProvidersByClass(optionalUserId).remove(name);
+        }
+    }
+
+    private HashMap<String, ContentProviderRecord> getProvidersByName(int optionalUserId) {
+        final int userId = optionalUserId >= 0
+                ? optionalUserId : Binder.getOrigCallingUser();
+        final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId);
+        if (map == null) {
+            HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>();
+            mProvidersByNamePerUser.put(userId, newMap);
+            return newMap;
+        } else {
+            return map;
+        }
+    }
+
+    private HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
+        final int userId = optionalUserId >= 0
+                ? optionalUserId : Binder.getOrigCallingUser();
+        final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId);
+        if (map == null) {
+            HashMap<ComponentName, ContentProviderRecord> newMap = new HashMap<ComponentName, ContentProviderRecord>();
+            mProvidersByClassPerUser.put(userId, newMap);
+            return newMap;
+        } else {
+            return map;
+        }
+    }
+
+    private void dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll,
+            HashMap<ComponentName, ContentProviderRecord> map) {
+        Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = map.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
+            ContentProviderRecord r = e.getValue();
+            if (dumpAll) {
+                pw.print("  * ");
+                pw.println(r);
+                r.dump(pw, "    ");
+            } else {
+                pw.print("  * ");
+                pw.print(r.name.toShortString());
+                /*
+                if (r.app != null) {
+                    pw.println(":");
+                    pw.print("      ");
+                    pw.println(r.app);
+                } else {
+                    pw.println();
+                }
+                */
+            }
+        }
+    }
+
+    private void dumpProvidersByNameLocked(PrintWriter pw,
+            HashMap<String, ContentProviderRecord> map) {
+        Iterator<Map.Entry<String, ContentProviderRecord>> it = map.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<String, ContentProviderRecord> e = it.next();
+            ContentProviderRecord r = e.getValue();
+            pw.print("  ");
+            pw.print(e.getKey());
+            pw.print(": ");
+            pw.println(r);
+        }
+    }
+
+    void dumpProvidersLocked(PrintWriter pw, boolean dumpAll) {
+        boolean needSep = false;
+        if (mGlobalByClass.size() > 0) {
+            if (needSep)
+                pw.println(" ");
+            pw.println("  Published content providers (by class):");
+            dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
+            pw.println(" ");
+        }
+
+        if (mProvidersByClassPerUser.size() > 1) {
+            for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
+                HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
+                pw.println("  User " + mProvidersByClassPerUser.keyAt(i) + ":");
+                dumpProvidersByClassLocked(pw, dumpAll, map);
+                pw.println(" ");
+            }
+        } else if (mProvidersByClassPerUser.size() == 1) {
+            HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(0);
+            dumpProvidersByClassLocked(pw, dumpAll, map);
+        }
+        needSep = true;
+
+        if (dumpAll) {
+            pw.println(" ");
+            pw.println("  Authority to provider mappings:");
+            dumpProvidersByNameLocked(pw, mGlobalByName);
+
+            for (int i = 0; i < mProvidersByNamePerUser.size(); i++) {
+                if (i > 0) {
+                    pw.println("  User " + mProvidersByNamePerUser.keyAt(i) + ":");
+                }
+                dumpProvidersByNameLocked(pw, mProvidersByNamePerUser.valueAt(i));
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 257113b..75ba947 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -25,11 +25,13 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.Slog;
 import android.util.TimeUtils;
 
@@ -60,6 +62,7 @@
                             // all information about the service.
     final ApplicationInfo appInfo;
                             // information about service's app.
+    final int userId;       // user that this service is running as
     final String packageName; // the package implementing intent's component
     final String processName; // process where this component wants to run
     final String permission;// permission needed to access service
@@ -289,6 +292,7 @@
         this.restarter = restarter;
         createTime = SystemClock.elapsedRealtime();
         lastActivity = SystemClock.uptimeMillis();
+        userId = UserId.getUserId(appInfo.uid);
     }
 
     public AppBindRecord retrieveAppBindingLocked(Intent intent,
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index de3129b..47ec218 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -19,7 +19,9 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
+import android.os.UserId;
 
 import java.io.PrintWriter;
 
@@ -37,6 +39,7 @@
     boolean askedCompatMode;// Have asked the user about compat mode for this task.
 
     String stringName;      // caching of toString() result.
+    int userId; // user for which this task was created
     
     TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
         taskId = _taskId;
@@ -84,13 +87,17 @@
                 origActivity = new ComponentName(info.packageName, info.name);
             }
         }
-        
+
         if (intent != null &&
                 (intent.getFlags()&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
             // Once we are set to an Intent with this flag, we count this
             // task as having a true root activity.
             rootWasReset = true;
         }
+
+        if (info.applicationInfo != null) {
+            userId = UserId.getUserId(info.applicationInfo.uid);
+        }
     }
     
     void dump(PrintWriter pw, String prefix) {
@@ -154,6 +161,8 @@
         } else {
             sb.append(" ??");
         }
+        sb.append(" U ");
+        sb.append(userId);
         sb.append('}');
         return stringName = sb.toString();
     }
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 5c9396f..9772d6a 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -48,6 +48,7 @@
 import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
 import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
@@ -156,10 +157,6 @@
     private static final int VERSION_ADDED_METERED = 4;
     private static final int VERSION_SPLIT_SNOOZE = 5;
 
-    private static final long KB_IN_BYTES = 1024;
-    private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
-    private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
-
     // @VisibleForTesting
     public static final int TYPE_WARNING = 0x1;
     public static final int TYPE_LIMIT = 0x2;
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 414ed1e..c9b67fc 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -34,6 +34,7 @@
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.NetworkTemplate.buildTemplateWifi;
+import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.provider.Settings.Secure.NETSTATS_DEV_BUCKET_DURATION;
 import static android.provider.Settings.Secure.NETSTATS_DEV_DELETE_AGE;
 import static android.provider.Settings.Secure.NETSTATS_DEV_PERSIST_BYTES;
@@ -153,10 +154,6 @@
 
     private PendingIntent mPollIntent;
 
-    private static final long KB_IN_BYTES = 1024;
-    private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
-    private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
-
     private static final String PREFIX_DEV = "dev";
     private static final String PREFIX_UID = "uid";
     private static final String PREFIX_UID_TAG = "uid_tag";
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 11ccd60..9b1973e 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -277,6 +277,27 @@
         return execute(builder.toString());
     }
 
+    /**
+     * Clone all the package data directories from srcUserId to targetUserId. If copyData is true,
+     * some of the data is also copied, otherwise just empty directories are created with the
+     * correct access rights.
+     * @param srcUserId user to copy the data directories from
+     * @param targetUserId user to copy the data directories to
+     * @param copyData whether the data itself is to be copied. If false, empty directories are
+     * created.
+     * @return success/error code
+     */
+    public int cloneUserData(int srcUserId, int targetUserId, boolean copyData) {
+        StringBuilder builder = new StringBuilder("cloneuserdata");
+        builder.append(' ');
+        builder.append(srcUserId);
+        builder.append(' ');
+        builder.append(targetUserId);
+        builder.append(' ');
+        builder.append(copyData ? '1' : '0');
+        return execute(builder.toString());
+    }
+
     public boolean ping() {
         if (execute("ping") < 0) {
             return false;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 090ca64..38c128c 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -92,6 +92,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserId;
 import android.security.SystemKeyStore;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -1743,12 +1744,16 @@
     }
 
     public ActivityInfo getActivityInfo(ComponentName component, int flags) {
+        return getActivityInfo(component, flags, Binder.getOrigCallingUser());
+    }
+
+    ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
         synchronized (mPackages) {
             PackageParser.Activity a = mActivities.mActivities.get(component);
 
             if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
             if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
-                return PackageParser.generateActivityInfo(a, flags);
+                return PackageParser.generateActivityInfo(a, flags, userId);
             }
             if (mResolveComponentName.equals(component)) {
                 return mResolveActivity;
@@ -1758,36 +1763,48 @@
     }
 
     public ActivityInfo getReceiverInfo(ComponentName component, int flags) {
+        return getReceiverInfo(component, flags, Binder.getOrigCallingUser());
+    }
+
+    ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
         synchronized (mPackages) {
             PackageParser.Activity a = mReceivers.mActivities.get(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
                 TAG, "getReceiverInfo " + component + ": " + a);
             if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
-                return PackageParser.generateActivityInfo(a, flags);
+                return PackageParser.generateActivityInfo(a, flags, userId);
             }
         }
         return null;
     }
 
     public ServiceInfo getServiceInfo(ComponentName component, int flags) {
+        return getServiceInfo(component, flags, Binder.getOrigCallingUser());
+    }
+
+    ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
         synchronized (mPackages) {
             PackageParser.Service s = mServices.mServices.get(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
                 TAG, "getServiceInfo " + component + ": " + s);
             if (s != null && mSettings.isEnabledLPr(s.info, flags)) {
-                return PackageParser.generateServiceInfo(s, flags);
+                return PackageParser.generateServiceInfo(s, flags, userId);
             }
         }
         return null;
     }
 
     public ProviderInfo getProviderInfo(ComponentName component, int flags) {
+        return getProviderInfo(component, flags, UserId.getUserId(Binder.getCallingUid()));
+    }
+
+    ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
         synchronized (mPackages) {
             PackageParser.Provider p = mProvidersByComponent.get(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
                 TAG, "getProviderInfo " + component + ": " + p);
             if (p != null && mSettings.isEnabledLPr(p.info, flags)) {
-                return PackageParser.generateProviderInfo(p, flags);
+                return PackageParser.generateProviderInfo(p, flags, userId);
             }
         }
         return null;
@@ -1850,7 +1867,7 @@
 
     public int checkUidPermission(String permName, int uid) {
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(uid);
+            Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
             if (obj != null) {
                 GrantedPermissions gp = (GrantedPermissions)obj;
                 if (gp.grantedPermissions.contains(permName)) {
@@ -1881,7 +1898,7 @@
         if (permName != null) {
             BasePermission bp = findPermissionTreeLP(permName);
             if (bp != null) {
-                if (bp.uid == Binder.getCallingUid()) {
+                if (bp.uid == UserId.getAppId(Binder.getCallingUid())) {
                     return bp;
                 }
                 throw new SecurityException("Calling uid "
@@ -2010,6 +2027,9 @@
     }
 
     public int checkUidSignatures(int uid1, int uid2) {
+        // Map to base uids.
+        uid1 = UserId.getAppId(uid1);
+        uid2 = UserId.getAppId(uid2);
         // reader
         synchronized (mPackages) {
             Signature[] s1;
@@ -2067,6 +2087,7 @@
     }
 
     public String[] getPackagesForUid(int uid) {
+        uid = UserId.getAppId(uid);
         // reader
         synchronized (mPackages) {
             Object obj = mSettings.getUserIdLPr(uid);
@@ -2091,7 +2112,7 @@
     public String getNameForUid(int uid) {
         // reader
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(uid);
+            Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.name + ":" + sus.userId;
@@ -2110,7 +2131,7 @@
         // reader
         synchronized (mPackages) {
             final SharedUserSetting suid = mSettings.getSharedUserLPw(sharedUserName, 0, false);
-            if(suid == null) {
+            if (suid == null) {
                 return -1;
             }
             return suid.userId;
@@ -2252,6 +2273,9 @@
                 comp = intent.getComponent();
             }
         }
+
+        final int userId = UserId.getUserId(Binder.getCallingUid());
+
         if (comp != null) {
             final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
             final ActivityInfo ai = getActivityInfo(comp, flags);
@@ -2603,6 +2627,7 @@
             Arrays.sort(keys);
             int i = getContinuationPoint(keys, lastRead);
             final int N = keys.length;
+            final int userId = UserId.getUserId(Binder.getCallingUid());
 
             while (i < N) {
                 final String packageName = keys[i++];
@@ -2616,7 +2641,7 @@
                 } else {
                     final PackageParser.Package p = mPackages.get(packageName);
                     if (p != null) {
-                        ai = PackageParser.generateApplicationInfo(p, flags);
+                        ai = PackageParser.generateApplicationInfo(p, flags, userId);
                     }
                 }
 
@@ -2639,12 +2664,13 @@
         // reader
         synchronized (mPackages) {
             final Iterator<PackageParser.Package> i = mPackages.values().iterator();
+            final int userId = UserId.getUserId(Binder.getCallingUid());
             while (i.hasNext()) {
                 final PackageParser.Package p = i.next();
                 if (p.applicationInfo != null
                         && (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
                         && (!mSafeMode || isSystemApp(p))) {
-                    finalList.add(PackageParser.generateApplicationInfo(p, flags));
+                    finalList.add(PackageParser.generateApplicationInfo(p, flags, userId));
                 }
             }
         }
@@ -2660,7 +2686,8 @@
                     && mSettings.isEnabledLPr(provider.info, flags)
                     && (!mSafeMode || (provider.info.applicationInfo.flags
                             &ApplicationInfo.FLAG_SYSTEM) != 0)
-                    ? PackageParser.generateProviderInfo(provider, flags)
+                    ? PackageParser.generateProviderInfo(provider, flags,
+                            UserId.getUserId(Binder.getCallingUid()))
                     : null;
         }
     }
@@ -2674,7 +2701,7 @@
         synchronized (mPackages) {
             final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
                     .iterator();
-
+            final int userId = UserId.getUserId(Binder.getCallingUid());
             while (i.hasNext()) {
                 Map.Entry<String, PackageParser.Provider> entry = i.next();
                 PackageParser.Provider p = entry.getValue();
@@ -2683,7 +2710,7 @@
                         && (!mSafeMode || (p.info.applicationInfo.flags
                                 &ApplicationInfo.FLAG_SYSTEM) != 0)) {
                     outNames.add(entry.getKey());
-                    outInfo.add(PackageParser.generateProviderInfo(p, 0));
+                    outInfo.add(PackageParser.generateProviderInfo(p, 0, userId));
                 }
             }
         }
@@ -2696,19 +2723,21 @@
         // reader
         synchronized (mPackages) {
             final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
+            final int userId = UserId.getUserId(Binder.getCallingUid());
             while (i.hasNext()) {
                 final PackageParser.Provider p = i.next();
                 if (p.info.authority != null
                         && (processName == null
                                 || (p.info.processName.equals(processName)
-                                        && p.info.applicationInfo.uid == uid))
+                                        && UserId.getAppId(p.info.applicationInfo.uid)
+                                            == UserId.getAppId(uid)))
                         && mSettings.isEnabledLPr(p.info, flags)
                         && (!mSafeMode
                                 || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
                     if (finalList == null) {
                         finalList = new ArrayList<ProviderInfo>(3);
                     }
-                    finalList.add(PackageParser.generateProviderInfo(p, flags));
+                    finalList.add(PackageParser.generateProviderInfo(p, flags, userId));
                 }
             }
         }
@@ -4461,8 +4490,8 @@
                 return null;
             }
             final ResolveInfo res = new ResolveInfo();
-            res.activityInfo = PackageParser.generateActivityInfo(activity,
-                    mFlags);
+            res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags,
+                    Binder.getOrigCallingUser());
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = info;
             }
@@ -4637,8 +4666,8 @@
                 return null;
             }
             final ResolveInfo res = new ResolveInfo();
-            res.serviceInfo = PackageParser.generateServiceInfo(service,
-                    mFlags);
+            res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags,
+                    Binder.getOrigCallingUser());
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = filter;
             }
@@ -4742,8 +4771,10 @@
                     intent.setPackage(targetPkg);
                 }
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                // TODO: Fix the userId argument
                 am.broadcastIntent(null, intent, null, finishedReceiver,
-                        0, null, null, null, finishedReceiver != null, false);
+                        0, null, null, null, finishedReceiver != null, false,
+                        Binder.getOrigCallingUser());
             } catch (RemoteException ex) {
             }
         }
@@ -7584,7 +7615,7 @@
                         "Unknown component: " + packageName
                         + "/" + className);
             }
-            if (!allowedByPermission && (uid != pkgSetting.userId)) {
+            if (!allowedByPermission && (!UserId.isSameApp(uid, pkgSetting.userId))) {
                 throw new SecurityException(
                         "Permission Denial: attempt to change component state from pid="
                         + Binder.getCallingPid()
@@ -8673,4 +8704,8 @@
             return mSettings.getVerifierDeviceIdentityLPw();
         }
     }
+
+    public List<UserInfo> getUsers() {
+        return mUserManager.getUsers();
+    }
 }
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
index 2687728..5eacf4a 100644
--- a/services/java/com/android/server/pm/UserManager.java
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -24,6 +24,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.SystemClock;
+import android.os.UserId;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -169,9 +170,10 @@
      * </user>
      */
     private void writeUser(UserInfo userInfo) {
+        FileOutputStream fos = null;
         try {
             final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
-            final FileOutputStream fos = new FileOutputStream(mUserFile);
+            fos = new FileOutputStream(mUserFile);
             final BufferedOutputStream bos = new BufferedOutputStream(fos);
 
             // XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -193,6 +195,13 @@
             serializer.endDocument();
         } catch (IOException ioe) {
             Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+        } finally {
+            if (fos != null) {
+                try {
+                    fos.close();
+                } catch (IOException ioe) {
+                }
+            }
         }
     }
 
@@ -205,8 +214,9 @@
      * </users>
      */
     private void writeUserList() {
+        FileOutputStream fos = null;
         try {
-            final FileOutputStream fos = new FileOutputStream(mUserListFile);
+            fos = new FileOutputStream(mUserListFile);
             final BufferedOutputStream bos = new BufferedOutputStream(fos);
 
             // XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -229,6 +239,13 @@
             serializer.endDocument();
         } catch (IOException ioe) {
             Slog.e(LOG_TAG, "Error writing user list");
+        } finally {
+            if (fos != null) {
+                try {
+                    fos.close();
+                } catch (IOException ioe) {
+                }
+            }
         }
     }
 
@@ -330,7 +347,7 @@
             // Don't do it for the primary user, it will become recursive.
             if (userId == 0)
                 continue;
-            mInstaller.createUserData(packageName, PackageManager.getUid(userId, uid),
+            mInstaller.createUserData(packageName, UserId.getUid(userId, uid),
                     userId);
         }
     }
@@ -367,6 +384,8 @@
 
     /**
      * Returns the next available user id, filling in any holes in the ids.
+     * TODO: May not be a good idea to recycle ids, in case it results in confusion
+     * for data and battery stats collection, or unexpected cross-talk.
      * @return
      */
     private int getNextAvailableId() {
@@ -390,14 +409,8 @@
         FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
                 | FileUtils.S_IXOTH, -1, -1);
 
-        // Create the individual data directories
-        for (ApplicationInfo app : apps) {
-            if (app.uid > android.os.Process.FIRST_APPLICATION_UID
-                    && app.uid < PackageManager.PER_USER_RANGE) {
-                mInstaller.createUserData(app.packageName,
-                        PackageManager.getUid(id, app.uid), id);
-            }
-        }
+        mInstaller.cloneUserData(0, id, false);
+
         final long stopTime = SystemClock.elapsedRealtime();
         Log.i(LOG_TAG,
                 "Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 2bf8b1c..e863f8b 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -28,6 +28,8 @@
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
 import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
+import static android.net.TrafficStats.KB_IN_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
@@ -100,10 +102,6 @@
     private static final long TEST_START = 1194220800000L;
     private static final String TEST_IFACE = "test0";
 
-    private static final long KB_IN_BYTES = 1024;
-    private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
-    private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
-
     private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi();
 
     private BroadcastInterceptingContext mServiceContext;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 8f5e77e..daf2018 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -31,6 +31,7 @@
 import static android.net.NetworkStatsHistory.FIELD_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.NetworkTemplate.buildTemplateWifi;
+import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.net.TrafficStats.UID_REMOVED;
 import static android.net.TrafficStats.UID_TETHERING;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -92,10 +93,6 @@
     private static final String IMSI_1 = "310004";
     private static final String IMSI_2 = "310260";
 
-    private static final long KB_IN_BYTES = 1024;
-    private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
-    private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
-
     private static NetworkTemplate sTemplateWifi = buildTemplateWifi();
     private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
     private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);