Merge "Remove unused IntentFilter in LocationManager" into jb-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index d053109..720681a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3012,6 +3012,7 @@
     method public android.app.AlertDialog.Builder setNeutralButton(int, android.content.DialogInterface.OnClickListener);
     method public android.app.AlertDialog.Builder setNeutralButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
     method public android.app.AlertDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
+    method public android.app.AlertDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
     method public android.app.AlertDialog.Builder setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener);
     method public android.app.AlertDialog.Builder setOnKeyListener(android.content.DialogInterface.OnKeyListener);
     method public android.app.AlertDialog.Builder setPositiveButton(int, android.content.DialogInterface.OnClickListener);
@@ -6293,6 +6294,7 @@
     field public static final int FLAG_EXTERNAL_STORAGE = 262144; // 0x40000
     field public static final int FLAG_FACTORY_TEST = 16; // 0x10
     field public static final int FLAG_HAS_CODE = 4; // 0x4
+    field public static final int FLAG_INSTALLED = 8388608; // 0x800000
     field public static final int FLAG_KILL_AFTER_RESTORE = 65536; // 0x10000
     field public static final int FLAG_LARGE_HEAP = 1048576; // 0x100000
     field public static final int FLAG_PERSISTENT = 8; // 0x8
@@ -6474,6 +6476,7 @@
     method public abstract int checkSignatures(int, int);
     method public abstract void clearPackagePreferredActivities(java.lang.String);
     method public abstract java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]);
+    method public abstract void extendVerificationTimeout(int, int, long);
     method public abstract android.graphics.drawable.Drawable getActivityIcon(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.graphics.drawable.Drawable getActivityIcon(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.ActivityInfo getActivityInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -6532,7 +6535,6 @@
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void verifyPendingInstall(int, int);
-    method public abstract void extendVerificationTimeout(int, int, long);
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
     field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
@@ -6593,6 +6595,7 @@
     field public static final int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
+    field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
     field public static final int PERMISSION_DENIED = -1; // 0xffffffff
     field public static final int PERMISSION_GRANTED = 0; // 0x0
     field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
@@ -6603,7 +6606,6 @@
     field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
-    field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80
   }
 
   public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
@@ -6629,6 +6631,17 @@
     field public java.lang.String packageName;
   }
 
+  public class PackageUserState {
+    ctor public PackageUserState();
+    ctor public PackageUserState(android.content.pm.PackageUserState);
+    field public java.util.HashSet disabledComponents;
+    field public int enabled;
+    field public java.util.HashSet enabledComponents;
+    field public boolean installed;
+    field public boolean notLaunched;
+    field public boolean stopped;
+  }
+
   public class PathPermission extends android.os.PatternMatcher {
     ctor public PathPermission(java.lang.String, int, java.lang.String, java.lang.String);
     ctor public PathPermission(android.os.Parcel);
@@ -11477,9 +11490,13 @@
     method public void setWakeMode(android.content.Context, int);
     method public void start() throws java.lang.IllegalStateException;
     method public void stop() throws java.lang.IllegalStateException;
+    field public static final int MEDIA_ERROR_IO = -1004; // 0xfffffc14
+    field public static final int MEDIA_ERROR_MALFORMED = -1007; // 0xfffffc11
     field public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200; // 0xc8
     field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
+    field public static final int MEDIA_ERROR_TIMED_OUT = -110; // 0xffffff92
     field public static final int MEDIA_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int MEDIA_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
     field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
     field public static final int MEDIA_INFO_BUFFERING_END = 702; // 0x2be
     field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
@@ -20057,8 +20074,8 @@
     method public void setContentView(android.view.View);
     method public void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
     method public void setInteractive(boolean);
-    field public static final java.lang.String SERVICE_INTERFACE = "android.service.dreams.Dream";
     field public static final java.lang.String METADATA_NAME_CONFIG_ACTIVITY = "android.service.dreams.config_activity";
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.dreams.Dream";
   }
 
 }
@@ -21316,6 +21333,7 @@
     method public int checkSignatures(int, int);
     method public void clearPackagePreferredActivities(java.lang.String);
     method public java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]);
+    method public void extendVerificationTimeout(int, int, long);
     method public android.graphics.drawable.Drawable getActivityIcon(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.graphics.drawable.Drawable getActivityIcon(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.ActivityInfo getActivityInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -21373,7 +21391,6 @@
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void verifyPendingInstall(int, int);
-    method public void extendVerificationTimeout(int, int, long);
   }
 
   public class MockResources extends android.content.res.Resources {
@@ -26928,6 +26945,7 @@
     method public synchronized int getDefaultFixedFontSize();
     method public synchronized int getDefaultFontSize();
     method public synchronized java.lang.String getDefaultTextEncodingName();
+    method public static java.lang.String getDefaultUserAgent(android.content.Context);
     method public android.webkit.WebSettings.ZoomDensity getDefaultZoom();
     method public boolean getDisplayZoomControls();
     method public synchronized boolean getDomStorageEnabled();
@@ -26958,7 +26976,6 @@
     method public synchronized boolean getUseWideViewPort();
     method public deprecated synchronized int getUserAgent();
     method public synchronized java.lang.String getUserAgentString();
-    method public static java.lang.String getDefaultUserAgent(android.content.Context);
     method public void setAllowContentAccess(boolean);
     method public void setAllowFileAccess(boolean);
     method public abstract void setAllowFileAccessFromFileURLs(boolean);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index a79eb14..47d6a02 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -393,7 +393,7 @@
     private void runStartService() throws Exception {
         Intent intent = makeIntent();
         System.out.println("Starting service: " + intent);
-        ComponentName cn = mAm.startService(null, intent, intent.getType());
+        ComponentName cn = mAm.startService(null, intent, intent.getType(), 0);
         if (cn == null) {
             System.err.println("Error: Not found; no service started.");
         }
@@ -1344,6 +1344,7 @@
                 "       am display-density [reset|DENSITY]\n" +
                 "       am to-uri [INTENT]\n" +
                 "       am to-intent-uri [INTENT]\n" +
+                "       am switch-user <USER_ID>\n" +
                 "\n" +
                 "am start: start an Activity.  Options are:\n" +
                 "    -D: enable debugging\n" +
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index c16e6fb..a52f74a 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -194,6 +194,11 @@
         ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
         return -errno;
     }
+    if (chmod(pkgdir, 0751) < 0) {
+        ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));
+        unlink(pkgdir);
+        return -errno;
+    }
     if (chown(pkgdir, uid, uid) < 0) {
         ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
         unlink(pkgdir);
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 7108d68..d51004a 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -384,6 +384,11 @@
         // Introducing multi-user, so migrate /data/media contents into /data/media/0
         ALOGD("Migrating /data/media for multi-user");
 
+        // Ensure /data/media
+        if (ensure_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+            goto fail;
+        }
+
         // /data/media.tmp
         char media_tmp_dir[PATH_MAX];
         snprintf(media_tmp_dir, PATH_MAX, "%smedia.tmp", android_data_dir.path);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index b34fd05..8cc4e69 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -776,7 +776,7 @@
     }
 
     private void runInstall() {
-        int installFlags = 0;
+        int installFlags = PackageManager.INSTALL_ALL_USERS;
         String installerPackageName = null;
 
         String opt;
@@ -811,6 +811,8 @@
             } else if (opt.equals("-f")) {
                 // Override if -s option is specified.
                 installFlags |= PackageManager.INSTALL_INTERNAL;
+            } else if (opt.equals("-d")) {
+                installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
             } else if (opt.equals("--algo")) {
                 algo = nextOptionData();
                 if (algo == null) {
@@ -1105,7 +1107,7 @@
 
         String opt = nextOption();
         if (opt != null && opt.equals("-k")) {
-            unInstallFlags = PackageManager.DONT_DELETE_DATA;
+            unInstallFlags = PackageManager.DELETE_KEEP_DATA;
         }
 
         String pkg = nextArg();
@@ -1473,21 +1475,24 @@
         System.err.println("       pm list instrumentation [-f] [TARGET-PACKAGE]");
         System.err.println("       pm list features");
         System.err.println("       pm list libraries");
+        System.err.println("       pm list users");
         System.err.println("       pm path PACKAGE");
         System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f]");
         System.err.println("                  [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>]");
         System.err.println("                  [--originating-uri <URI>] [--referrer <URI>] PATH");
         System.err.println("       pm uninstall [-k] PACKAGE");
         System.err.println("       pm clear PACKAGE");
-        System.err.println("       pm enable PACKAGE_OR_COMPONENT");
-        System.err.println("       pm disable PACKAGE_OR_COMPONENT");
-        System.err.println("       pm disable-user PACKAGE_OR_COMPONENT");
+        System.err.println("       pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
+        System.err.println("       pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
+        System.err.println("       pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT");
         System.err.println("       pm grant PACKAGE PERMISSION");
         System.err.println("       pm revoke PACKAGE PERMISSION");
         System.err.println("       pm set-install-location [0/auto] [1/internal] [2/external]");
         System.err.println("       pm get-install-location");
         System.err.println("       pm set-permission-enforced PERMISSION [true|false]");
         System.err.println("       pm trim-caches DESIRED_FREE_SPACE");
+        System.err.println("       pm create-user USER_NAME");
+        System.err.println("       pm remove-user 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:");
@@ -1525,6 +1530,7 @@
         System.err.println("    -i: specify the installer package name.");
         System.err.println("    -s: install package on sdcard.");
         System.err.println("    -f: install package on internal flash.");
+        System.err.println("    -d: allow version code downgrade.");
         System.err.println("");
         System.err.println("pm uninstall: removes a package from the system. Options:");
         System.err.println("    -k: keep the data and cache directories around after package removal.");
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 3197a63..adc9434 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -697,7 +697,8 @@
             IApplicationThread app = ApplicationThreadNative.asInterface(b);
             Intent service = Intent.CREATOR.createFromParcel(data);
             String resolvedType = data.readString();
-            ComponentName cn = startService(app, service, resolvedType);
+            int userId = data.readInt();
+            ComponentName cn = startService(app, service, resolvedType, userId);
             reply.writeNoException();
             ComponentName.writeToParcel(cn, reply);
             return true;
@@ -709,7 +710,8 @@
             IApplicationThread app = ApplicationThreadNative.asInterface(b);
             Intent service = Intent.CREATOR.createFromParcel(data);
             String resolvedType = data.readString();
-            int res = stopService(app, service, resolvedType);
+            int userId = data.readInt();
+            int res = stopService(app, service, resolvedType, userId);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -2523,7 +2525,7 @@
     }
     
     public ComponentName startService(IApplicationThread caller, Intent service,
-            String resolvedType) throws RemoteException
+            String resolvedType, int userId) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -2531,6 +2533,7 @@
         data.writeStrongBinder(caller != null ? caller.asBinder() : null);
         service.writeToParcel(data, 0);
         data.writeString(resolvedType);
+        data.writeInt(userId);
         mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
         reply.readException();
         ComponentName res = ComponentName.readFromParcel(reply);
@@ -2539,7 +2542,7 @@
         return res;
     }
     public int stopService(IApplicationThread caller, Intent service,
-            String resolvedType) throws RemoteException
+            String resolvedType, int userId) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -2547,6 +2550,7 @@
         data.writeStrongBinder(caller != null ? caller.asBinder() : null);
         service.writeToParcel(data, 0);
         data.writeString(resolvedType);
+        data.writeInt(userId);
         mRemote.transact(STOP_SERVICE_TRANSACTION, data, reply, 0);
         reply.readException();
         int res = reply.readInt();
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index e37b3fa..7809e73 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -566,7 +566,14 @@
         
         /**
          * Sets the callback that will be called if the dialog is canceled.
+         *
+         * <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than
+         * being canceled or one of the supplied choices being selected.
+         * If you are interested in listening for all cases where the dialog is dismissed
+         * and not just when it is canceled, see
+         * {@link #setOnDismissListener(android.content.DialogInterface.OnDismissListener) setOnDismissListener}.</p>
          * @see #setCancelable(boolean)
+         * @see #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
          *
          * @return This Builder object to allow for chaining of calls to set methods
          */
@@ -576,6 +583,16 @@
         }
         
         /**
+         * Sets the callback that will be called when the dialog is dismissed for any reason.
+         *
+         * @return This Builder object to allow for chaining of calls to set methods
+         */
+        public Builder setOnDismissListener(OnDismissListener onDismissListener) {
+            P.mOnDismissListener = onDismissListener;
+            return this;
+        }
+
+        /**
          * Sets the callback that will be called if a key is dispatched to the dialog.
          *
          * @return This Builder object to allow for chaining of calls to set methods
@@ -917,6 +934,7 @@
                 dialog.setCanceledOnTouchOutside(true);
             }
             dialog.setOnCancelListener(P.mOnCancelListener);
+            dialog.setOnDismissListener(P.mOnDismissListener);
             if (P.mOnKeyListener != null) {
                 dialog.setOnKeyListener(P.mOnKeyListener);
             }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f3f75ce..0f10c4f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -45,6 +45,7 @@
 import android.content.pm.UserInfo;
 import android.content.pm.VerificationParams;
 import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
@@ -997,6 +998,21 @@
     }
 
     @Override
+    public int installExistingPackage(String packageName)
+            throws NameNotFoundException {
+        try {
+            int res = mPM.installExistingPackage(packageName);
+            if (res == INSTALL_FAILED_INVALID_URI) {
+                throw new NameNotFoundException("Package " + packageName + " doesn't exist");
+            }
+            return res;
+        } catch (RemoteException e) {
+            // Should never happen!
+            throw new NameNotFoundException("Package " + packageName + " doesn't exist");
+        }
+    }
+
+    @Override
     public void verifyPendingInstall(int id, int response) {
         try {
             mPM.verifyPendingInstall(id, response);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 0ae4d06..32086d7 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1220,11 +1220,21 @@
 
     @Override
     public ComponentName startService(Intent service) {
+        return startServiceAsUser(service, Process.myUserHandle());
+    }
+
+    @Override
+    public boolean stopService(Intent service) {
+        return stopServiceAsUser(service, Process.myUserHandle());
+    }
+
+    @Override
+    public ComponentName startServiceAsUser(Intent service, UserHandle user) {
         try {
             service.setAllowFds(false);
             ComponentName cn = ActivityManagerNative.getDefault().startService(
                 mMainThread.getApplicationThread(), service,
-                service.resolveTypeIfNeeded(getContentResolver()));
+                service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
             if (cn != null && cn.getPackageName().equals("!")) {
                 throw new SecurityException(
                         "Not allowed to start service " + service
@@ -1237,12 +1247,12 @@
     }
 
     @Override
-    public boolean stopService(Intent service) {
+    public boolean stopServiceAsUser(Intent service, UserHandle user) {
         try {
             service.setAllowFds(false);
             int res = ActivityManagerNative.getDefault().stopService(
                 mMainThread.getApplicationThread(), service,
-                service.resolveTypeIfNeeded(getContentResolver()));
+                service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
             if (res < 0) {
                 throw new SecurityException(
                         "Not allowed to stop service " + service);
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 2cc3b02..16112cb3 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -1106,10 +1106,12 @@
 
     /**
      * Set a listener to be invoked when the dialog is canceled.
-     * <p>
-     * This will only be invoked when the dialog is canceled, if the creator
-     * needs to know when it is dismissed in general, use
-     * {@link #setOnDismissListener}.
+     *
+     * <p>This will only be invoked when the dialog is canceled.
+     * Cancel events alone will not capture all ways that
+     * the dialog might be dismissed. If the creator needs
+     * to know when a dialog is dismissed in general, use
+     * {@link #setOnDismissListener}.</p>
      * 
      * @param listener The {@link DialogInterface.OnCancelListener} to use.
      */
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index a6d1995..c3e911e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -133,9 +133,9 @@
     public PendingIntent getRunningServiceControlPanel(ComponentName service)
             throws RemoteException;
     public ComponentName startService(IApplicationThread caller, Intent service,
-            String resolvedType) throws RemoteException;
+            String resolvedType, int userId) throws RemoteException;
     public int stopService(IApplicationThread caller, Intent service,
-            String resolvedType) throws RemoteException;
+            String resolvedType, int userId) throws RemoteException;
     public boolean stopServiceToken(ComponentName className, IBinder token,
             int startId) throws RemoteException;
     public void setServiceForeground(ComponentName className, IBinder token,
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index bf83f5e..69c20b0 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -17,10 +17,9 @@
 package android.app;
 
 import android.content.Context;
-import android.os.Binder;
-import android.os.RemoteException;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
 
@@ -88,6 +87,11 @@
         mContext = context;
     }
 
+    /** {@hide} */
+    public static NotificationManager from(Context context) {
+        return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+    }
+
     /**
      * Post a notification to be shown in the status bar. If a notification with
      * the same id has already been posted by your application and has not yet been canceled, it
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1460bf5..dc6d93f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1414,6 +1414,16 @@
     public abstract boolean stopService(Intent service);
 
     /**
+     * @hide like {@link #startService(Intent)} but for a specific user.
+     */
+    public abstract ComponentName startServiceAsUser(Intent service, UserHandle user);
+
+    /**
+     * @hide like {@link #stopService(Intent)} but for a specific user.
+     */
+    public abstract boolean stopServiceAsUser(Intent service, UserHandle user);
+    
+    /**
      * Connect to an application service, creating it if needed.  This defines
      * a dependency between your application and the service.  The given
      * <var>conn</var> will receive the service object when it is created and be
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3a13725..4bbe44e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -410,6 +410,18 @@
         return mBase.stopService(name);
     }
 
+    /** @hide */
+    @Override
+    public ComponentName startServiceAsUser(Intent service, UserHandle user) {
+        return mBase.startServiceAsUser(service, user);
+    }
+
+    /** @hide */
+    @Override
+    public boolean stopServiceAsUser(Intent name, UserHandle user) {
+        return mBase.stopServiceAsUser(name, user);
+    }
+
     @Override
     public boolean bindService(Intent service, ServiceConnection conn,
             int flags) {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index cbabc7c..1a82d58 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -302,6 +302,12 @@
     public static final int FLAG_SUPPORTS_RTL = 1<<22;
 
     /**
+     * Value for {@link #flags}: true if the application is currently
+     * installed for the calling user.
+     */
+    public static final int FLAG_INSTALLED = 1<<23;
+
+    /**
      * Value for {@link #flags}: Set to true if the application has been
      * installed using the forward lock option.
      *
@@ -334,7 +340,8 @@
      * {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
      * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_SUPPORTS_XLARGE_SCREENS},
      * {@link #FLAG_RESIZEABLE_FOR_SCREENS},
-     * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}, {@link #FLAG_VM_SAFE_MODE}
+     * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}, {@link #FLAG_VM_SAFE_MODE},
+     * {@link #FLAG_INSTALLED}.
      */
     public int flags = 0;
     
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 0be8b83..0e1fe3e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -32,6 +32,7 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.ManifestDigest;
+import android.content.pm.PackageCleanItem;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ProviderInfo;
 import android.content.pm.PermissionGroupInfo;
@@ -351,7 +352,7 @@
      */
     void updateExternalMediaStatus(boolean mounted, boolean reportStatus);
 
-    String nextPackageToClean(String lastPackage);
+    PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);
 
     void movePackage(String packageName, IPackageMoveObserver observer, int flags);
     
@@ -369,6 +370,8 @@
             in VerificationParams verificationParams,
             in ContainerEncryptionParams encryptionParams);
 
+    int installExistingPackage(String packageName);
+
     void verifyPendingInstall(int id, int verificationCode);
     void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
 
diff --git a/core/java/android/content/pm/ManifestDigest.java b/core/java/android/content/pm/ManifestDigest.java
index f5e72e0..75505bc 100644
--- a/core/java/android/content/pm/ManifestDigest.java
+++ b/core/java/android/content/pm/ManifestDigest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2012 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.content.pm;
 
 import android.os.Parcel;
diff --git a/core/java/android/content/pm/PackageCleanItem.aidl b/core/java/android/content/pm/PackageCleanItem.aidl
new file mode 100644
index 0000000..9bb203e
--- /dev/null
+++ b/core/java/android/content/pm/PackageCleanItem.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2012, 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.content.pm;
+
+parcelable PackageCleanItem;
diff --git a/core/java/android/content/pm/PackageCleanItem.java b/core/java/android/content/pm/PackageCleanItem.java
new file mode 100644
index 0000000..eea3b9c
--- /dev/null
+++ b/core/java/android/content/pm/PackageCleanItem.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 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.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class PackageCleanItem {
+    public final String packageName;
+    public final boolean andCode;
+
+    public PackageCleanItem(String packageName, boolean andCode) {
+        this.packageName = packageName;
+        this.andCode = andCode;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        try {
+            if (obj != null) {
+                PackageCleanItem other = (PackageCleanItem)obj;
+                return packageName.equals(other.packageName) && andCode == other.andCode;
+            }
+        } catch (ClassCastException e) {
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + packageName.hashCode();
+        result = 31 * result + (andCode ? 1 : 0);
+        return result;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeString(packageName);
+        dest.writeInt(andCode ? 1 : 0);
+    }
+
+    public static final Parcelable.Creator<PackageCleanItem> CREATOR
+            = new Parcelable.Creator<PackageCleanItem>() {
+        public PackageCleanItem createFromParcel(Parcel source) {
+            return new PackageCleanItem(source);
+        }
+
+        public PackageCleanItem[] newArray(int size) {
+            return new PackageCleanItem[size];
+        }
+    };
+
+    private PackageCleanItem(Parcel source) {
+        packageName = source.readString();
+        andCode = source.readInt() != 0;
+    }
+}
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index 9625944..a1566da 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -32,6 +32,11 @@
     public String packageName;
 
     /**
+     * The android:versionCode of the package.
+     */
+    public int versionCode;
+
+    /**
      * Specifies the recommended install location. Can be one of
      * {@link #PackageHelper.RECOMMEND_INSTALL_INTERNAL} to install on internal storage
      * {@link #PackageHelper.RECOMMEND_INSTALL_EXTERNAL} to install on external media
@@ -58,6 +63,7 @@
 
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeString(packageName);
+        dest.writeInt(versionCode);
         dest.writeInt(recommendedInstallLocation);
         dest.writeInt(installLocation);
 
@@ -82,6 +88,7 @@
 
     private PackageInfoLite(Parcel source) {
         packageName = source.readString();
+        versionCode = source.readInt();
         recommendedInstallLocation = source.readInt();
         installLocation = source.readInt();
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b3e98e7..0d99d3f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -310,6 +310,23 @@
     public static final int INSTALL_FROM_ADB = 0x00000020;
 
     /**
+     * Flag parameter for {@link #installPackage} to indicate that this install
+     * should immediately be visible to all users.
+     *
+     * @hide
+     */
+    public static final int INSTALL_ALL_USERS = 0x00000040;
+
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that it is okay
+     * to install an update to an app where the newly installed app has a lower
+     * version code than the currently installed app.
+     *
+     * @hide
+     */
+    public static final int INSTALL_ALLOW_DOWNGRADE = 0x00000080;
+
+    /**
      * Flag parameter for
      * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
      * that you don't want to kill the app containing the component.  Be careful when you set this
@@ -529,6 +546,14 @@
     public static final int INSTALL_FAILED_UID_CHANGED = -24;
 
     /**
+     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+     * the new package has an older version code than the currently installed package.
+     * @hide
+     */
+    public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;
+
+    /**
      * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
      * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
      * if the parser was given a path that is not a file, or does not end with the expected
@@ -625,7 +650,15 @@
      *
      * @hide
      */
-    public static final int DONT_DELETE_DATA = 0x00000001;
+    public static final int DELETE_KEEP_DATA = 0x00000001;
+
+    /**
+     * Flag parameter for {@link #deletePackage} to indicate that you want the
+     * package deleted for all users.
+     *
+     * @hide
+     */
+    public static final int DELETE_ALL_USERS = 0x00000002;
 
     /**
      * Return code for when package deletion succeeds. This is passed to the
@@ -2175,8 +2208,8 @@
         if ((flags & GET_SIGNATURES) != 0) {
             packageParser.collectCertificates(pkg, 0);
         }
-        return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, false,
-                COMPONENT_ENABLED_STATE_DEFAULT);
+        PackageUserState state = new PackageUserState();
+        return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
     }
 
     /**
@@ -2267,6 +2300,14 @@
             ContainerEncryptionParams encryptionParams);
 
     /**
+     * If there is already an application with the given package name installed
+     * on the system for other users, also install it for the calling user.
+     * @hide
+     */
+    public abstract int installExistingPackage(String packageName)
+            throws NameNotFoundException;
+
+    /**
      * Allows a package listening to the
      * {@link Intent#ACTION_PACKAGE_NEEDS_VERIFICATION package verification
      * broadcast} to respond to the package manager. The response must include
@@ -2337,7 +2378,8 @@
      * @param observer An observer callback to get notified when the package deletion is
      * complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
      * called when that happens.  observer may be null to indicate that no callback is desired.
-     * @param flags - possible values: {@link #DONT_DELETE_DATA}
+     * @param flags - possible values: {@link #DELETE_KEEP_DATA},
+     * {@link #DELETE_ALL_USERS}.
      *
      * @hide
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ac75040..237f5c5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -203,11 +203,14 @@
      */
     public static class PackageLite {
         public final String packageName;
+        public final int versionCode;
         public final int installLocation;
         public final VerifierInfo[] verifiers;
 
-        public PackageLite(String packageName, int installLocation, List<VerifierInfo> verifiers) {
+        public PackageLite(String packageName, int versionCode,
+                int installLocation, List<VerifierInfo> verifiers) {
             this.packageName = packageName;
+            this.versionCode = versionCode;
             this.installLocation = installLocation;
             this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
         }
@@ -243,14 +246,15 @@
         return name.endsWith(".apk");
     }
 
+    /*
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
             int gids[], int flags, long firstInstallTime, long lastUpdateTime,
             HashSet<String> grantedPermissions) {
-
+        PackageUserState state = new PackageUserState();
         return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
-                grantedPermissions, false, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
-                UserHandle.getCallingUserId());
+                grantedPermissions, state, UserHandle.getCallingUserId());
     }
+    */
 
     /**
      * Generate and return the {@link PackageInfo} for a parsed package.
@@ -260,23 +264,30 @@
      */
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
             int gids[], int flags, long firstInstallTime, long lastUpdateTime,
-            HashSet<String> grantedPermissions, boolean stopped, int enabledState) {
+            HashSet<String> grantedPermissions, PackageUserState state) {
 
         return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
-                grantedPermissions, stopped, enabledState, UserHandle.getCallingUserId());
+                grantedPermissions, state, UserHandle.getCallingUserId());
+    }
+
+    private static boolean checkUseInstalled(int flags, PackageUserState state) {
+        return state.installed || ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0);
     }
 
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
             int gids[], int flags, long firstInstallTime, long lastUpdateTime,
-            HashSet<String> grantedPermissions, boolean stopped, int enabledState, int userId) {
+            HashSet<String> grantedPermissions, PackageUserState state, int userId) {
 
+        if (!checkUseInstalled(flags, state)) {
+            return null;
+        }
         PackageInfo pi = new PackageInfo();
         pi.packageName = p.packageName;
         pi.versionCode = p.mVersionCode;
         pi.versionName = p.mVersionName;
         pi.sharedUserId = p.mSharedUserId;
         pi.sharedUserLabel = p.mSharedUserLabel;
-        pi.applicationInfo = generateApplicationInfo(p, flags, stopped, enabledState, userId);
+        pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
         pi.installLocation = p.installLocation;
         pi.firstInstallTime = firstInstallTime;
         pi.lastUpdateTime = lastUpdateTime;
@@ -312,7 +323,7 @@
                     if (activity.info.enabled
                         || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
                         pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
-                                stopped, enabledState, userId);
+                                state, userId);
                     }
                 }
             }
@@ -334,7 +345,7 @@
                     if (activity.info.enabled
                         || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
                         pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
-                                stopped, enabledState, userId);
+                                state, userId);
                     }
                 }
             }
@@ -355,8 +366,8 @@
                     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, stopped,
-                                enabledState, userId);
+                        pi.services[j++] = generateServiceInfo(p.services.get(i), flags,
+                                state, userId);
                     }
                 }
             }
@@ -377,8 +388,8 @@
                     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, stopped,
-                                enabledState, userId);
+                        pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags,
+                                state, userId);
                     }
                 }
             }
@@ -840,11 +851,19 @@
             return null;
         }
         int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
+        int versionCode = 0;
+        int numFound = 0;
         for (int i = 0; i < attrs.getAttributeCount(); i++) {
             String attr = attrs.getAttributeName(i);
             if (attr.equals("installLocation")) {
                 installLocation = attrs.getAttributeIntValue(i,
                         PARSE_DEFAULT_INSTALL_LOCATION);
+                numFound++;
+            } else if (attr.equals("versionCode")) {
+                versionCode = attrs.getAttributeIntValue(i, 0);
+                numFound++;
+            }
+            if (numFound >= 2) {
                 break;
             }
         }
@@ -867,7 +886,7 @@
             }
         }
 
-        return new PackageLite(pkgName.intern(), installLocation, verifiers);
+        return new PackageLite(pkgName.intern(), versionCode, installLocation, verifiers);
     }
 
     /**
@@ -3458,13 +3477,25 @@
         }
     }
 
-    private static boolean copyNeeded(int flags, Package p, int enabledState, Bundle metaData) {
-        if (enabledState != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
-            boolean enabled = enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+    private static boolean copyNeeded(int flags, Package p,
+            PackageUserState state, Bundle metaData, int userId) {
+        if (userId != 0) {
+            // We always need to copy for other users, since we need
+            // to fix up the uid.
+            return true;
+        }
+        if (state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+            boolean enabled = state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
             if (p.applicationInfo.enabled != enabled) {
                 return true;
             }
         }
+        if (!state.installed) {
+            return true;
+        }
+        if (state.stopped) {
+            return true;
+        }
         if ((flags & PackageManager.GET_META_DATA) != 0
                 && (metaData != null || p.mAppMetaData != null)) {
             return true;
@@ -3476,32 +3507,34 @@
         return false;
     }
 
-    public static ApplicationInfo generateApplicationInfo(Package p, int flags, boolean stopped,
-            int enabledState) {
-        return generateApplicationInfo(p, flags, stopped, enabledState, UserHandle.getCallingUserId());
+    public static ApplicationInfo generateApplicationInfo(Package p, int flags,
+            PackageUserState state) {
+        return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId());
     }
 
     public static ApplicationInfo generateApplicationInfo(Package p, int flags,
-            boolean stopped, int enabledState, int userId) {
+            PackageUserState state, int userId) {
         if (p == null) return null;
-        if (!copyNeeded(flags, p, enabledState, null) && userId == 0) {
+        if (!checkUseInstalled(flags, state)) {
+            return null;
+        }
+        if (!copyNeeded(flags, p, state, null, userId)) {
             // CompatibilityMode is global state. It's safe to modify the instance
             // of the package.
             if (!sCompatibilityModeEnabled) {
                 p.applicationInfo.disableCompatibilityMode();
             }
-            if (stopped) {
-                p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
-            } else {
-                p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
-            }
-            if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+            // Make sure we report as installed.  Also safe to do, since the
+            // default state should be installed (we will always copy if we
+            // need to report it is not installed).
+            p.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+            if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
                 p.applicationInfo.enabled = true;
-            } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
-                    || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+            } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                    || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
                 p.applicationInfo.enabled = false;
             }
-            p.applicationInfo.enabledSetting = enabledState;
+            p.applicationInfo.enabledSetting = state.enabled;
             return p.applicationInfo;
         }
 
@@ -3520,18 +3553,23 @@
         if (!sCompatibilityModeEnabled) {
             ai.disableCompatibilityMode();
         }
-        if (stopped) {
+        if (state.stopped) {
             ai.flags |= ApplicationInfo.FLAG_STOPPED;
         } else {
             ai.flags &= ~ApplicationInfo.FLAG_STOPPED;
         }
-        if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+        if (state.installed) {
+            ai.flags |= ApplicationInfo.FLAG_INSTALLED;
+        } else {
+            ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+        }
+        if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
             ai.enabled = true;
-        } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
-                || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+        } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
             ai.enabled = false;
         }
-        ai.enabledSetting = enabledState;
+        ai.enabledSetting = state.enabled;
         return ai;
     }
 
@@ -3578,16 +3616,19 @@
         }
     }
 
-    public static final ActivityInfo generateActivityInfo(Activity a, int flags, boolean stopped,
-            int enabledState, int userId) {
+    public static final ActivityInfo generateActivityInfo(Activity a, int flags,
+            PackageUserState state, int userId) {
         if (a == null) return null;
-        if (!copyNeeded(flags, a.owner, enabledState, a.metaData) && userId == 0) {
+        if (!checkUseInstalled(flags, state)) {
+            return null;
+        }
+        if (!copyNeeded(flags, a.owner, state, a.metaData, userId)) {
             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, stopped, enabledState, userId);
+        ai.applicationInfo = generateApplicationInfo(a.owner, flags, state, userId);
         return ai;
     }
 
@@ -3612,17 +3653,19 @@
         }
     }
 
-    public static final ServiceInfo generateServiceInfo(Service s, int flags, boolean stopped,
-            int enabledState, int userId) {
+    public static final ServiceInfo generateServiceInfo(Service s, int flags,
+            PackageUserState state, int userId) {
         if (s == null) return null;
-        if (!copyNeeded(flags, s.owner, enabledState, s.metaData)
-                && userId == UserHandle.getUserId(s.info.applicationInfo.uid)) {
+        if (!checkUseInstalled(flags, state)) {
+            return null;
+        }
+        if (!copyNeeded(flags, s.owner, state, s.metaData, userId)) {
             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, stopped, enabledState, userId);
+        si.applicationInfo = generateApplicationInfo(s.owner, flags, state, userId);
         return si;
     }
 
@@ -3655,13 +3698,15 @@
         }
     }
 
-    public static final ProviderInfo generateProviderInfo(Provider p, int flags, boolean stopped,
-            int enabledState, int userId) {
+    public static final ProviderInfo generateProviderInfo(Provider p, int flags,
+            PackageUserState state, int userId) {
         if (p == null) return null;
-        if (!copyNeeded(flags, p.owner, enabledState, p.metaData)
+        if (!checkUseInstalled(flags, state)) {
+            return null;
+        }
+        if (!copyNeeded(flags, p.owner, state, p.metaData, userId)
                 && ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
-                        || p.info.uriPermissionPatterns == null)
-                && userId == 0) {
+                        || p.info.uriPermissionPatterns == null)) {
             return p.info;
         }
         // Make shallow copies so we can store the metadata safely
@@ -3670,7 +3715,7 @@
         if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
             pi.uriPermissionPatterns = null;
         }
-        pi.applicationInfo = generateApplicationInfo(p.owner, flags, stopped, enabledState, userId);
+        pi.applicationInfo = generateApplicationInfo(p.owner, flags, state, userId);
         return pi;
     }
 
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
new file mode 100644
index 0000000..1a71bfb
--- /dev/null
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 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.content.pm;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+
+import java.util.HashSet;
+
+/**
+ * Per-user state information about a package.
+ */
+public class PackageUserState {
+    public boolean stopped;
+    public boolean notLaunched;
+    public boolean installed;
+    public int enabled;
+
+    public HashSet<String> disabledComponents;
+    public HashSet<String> enabledComponents;
+
+    public PackageUserState() {
+        installed = true;
+        enabled = COMPONENT_ENABLED_STATE_DEFAULT;
+    }
+
+    public PackageUserState(PackageUserState o) {
+        installed = o.installed;
+        stopped = o.stopped;
+        notLaunched = o.notLaunched;
+        enabled = o.enabled;
+        disabledComponents = o.disabledComponents != null
+                ? new HashSet<String>(o.disabledComponents) : null;
+        enabledComponents = o.enabledComponents != null
+                ? new HashSet<String>(o.enabledComponents) : null;
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 0bc0f91..7642670 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -331,12 +331,16 @@
                 notifyListener(v1, true /* removed */);
             }
             if (changes.length() > 0) {
-                Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
-                        serviceInfos.size() + " services:\n" + changes);
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
+                            serviceInfos.size() + " services:\n" + changes);
+                }
                 writePersistentServicesLocked();
             } else {
-                Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
-                        serviceInfos.size() + " services unchanged");
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " +
+                            serviceInfos.size() + " services unchanged");
+                }
             }
             mPersistentServicesFileDidNotExist = false;
         }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index d30ef04..60bf4d68 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -912,4 +912,13 @@
             return false;
         }
     }
+
+    /** {@hide} */
+    public boolean updateLockdownVpn() {
+        try {
+            return mService.updateLockdownVpn();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 92aeff2..3614045 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -26,6 +26,7 @@
 
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
 
 /**
  * Interface that answers queries about, and allows changing, the
@@ -118,7 +119,9 @@
 
     ParcelFileDescriptor establishVpn(in VpnConfig config);
 
-    void startLegacyVpn(in VpnConfig config, in String[] racoon, in String[] mtpd);
+    void startLegacyVpn(in VpnProfile profile);
 
     LegacyVpnInfo getLegacyVpnInfo();
+
+    boolean updateLockdownVpn();
 }
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 7785368..4e2b5c0 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -101,37 +101,88 @@
     }
 
     /**
-     * Default constructor associates this handler with the queue for the
+     * Default constructor associates this handler with the {@link Looper} for the
      * current thread.
      *
-     * If there isn't one, this handler won't be able to receive messages.
+     * If this thread does not have a looper, this handler won't be able to receive messages
+     * so an exception is thrown.
      */
     public Handler() {
-        if (FIND_POTENTIAL_LEAKS) {
-            final Class<? extends Handler> klass = getClass();
-            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
-                    (klass.getModifiers() & Modifier.STATIC) == 0) {
-                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
-                    klass.getCanonicalName());
-            }
-        }
-
-        mLooper = Looper.myLooper();
-        if (mLooper == null) {
-            throw new RuntimeException(
-                "Can't create handler inside thread that has not called Looper.prepare()");
-        }
-        mQueue = mLooper.mQueue;
-        mCallback = null;
-        mAsynchronous = false;
+        this(null, false);
     }
 
     /**
-     * Constructor associates this handler with the queue for the
+     * Constructor associates this handler with the {@link Looper} for the
      * current thread and takes a callback interface in which you can handle
      * messages.
+     *
+     * If this thread does not have a looper, this handler won't be able to receive messages
+     * so an exception is thrown.
+     *
+     * @param callback The callback interface in which to handle messages, or null.
      */
     public Handler(Callback callback) {
+        this(callback, false);
+    }
+
+    /**
+     * Use the provided {@link Looper} instead of the default one.
+     *
+     * @param looper The looper, must not be null.
+     */
+    public Handler(Looper looper) {
+        this(looper, null, false);
+    }
+
+    /**
+     * Use the provided {@link Looper} instead of the default one and take a callback
+     * interface in which to handle messages.
+     *
+     * @param looper The looper, must not be null.
+     * @param callback The callback interface in which to handle messages, or null.
+     */
+    public Handler(Looper looper, Callback callback) {
+        this(looper, callback, false);
+    }
+
+    /**
+     * Use the {@link Looper} for the current thread
+     * and set whether the handler should be asynchronous.
+     *
+     * Handlers are synchronous by default unless this constructor is used to make
+     * one that is strictly asynchronous.
+     *
+     * Asynchronous messages represent interrupts or events that do not require global ordering
+     * with represent to synchronous messages.  Asynchronous messages are not subject to
+     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
+     *
+     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
+     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
+     *
+     * @hide
+     */
+    public Handler(boolean async) {
+        this(null, async);
+    }
+
+    /**
+     * Use the {@link Looper} for the current thread with the specified callback interface
+     * and set whether the handler should be asynchronous.
+     *
+     * Handlers are synchronous by default unless this constructor is used to make
+     * one that is strictly asynchronous.
+     *
+     * Asynchronous messages represent interrupts or events that do not require global ordering
+     * with represent to synchronous messages.  Asynchronous messages are not subject to
+     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
+     *
+     * @param callback The callback interface in which to handle messages, or null.
+     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
+     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
+     *
+     * @hide
+     */
+    public Handler(Callback callback, boolean async) {
         if (FIND_POTENTIAL_LEAKS) {
             final Class<? extends Handler> klass = getClass();
             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
@@ -148,32 +199,11 @@
         }
         mQueue = mLooper.mQueue;
         mCallback = callback;
-        mAsynchronous = false;
+        mAsynchronous = async;
     }
 
     /**
-     * Use the provided queue instead of the default one.
-     */
-    public Handler(Looper looper) {
-        mLooper = looper;
-        mQueue = looper.mQueue;
-        mCallback = null;
-        mAsynchronous = false;
-    }
-
-    /**
-     * Use the provided queue instead of the default one and take a callback
-     * interface in which to handle messages.
-     */
-    public Handler(Looper looper, Callback callback) {
-        mLooper = looper;
-        mQueue = looper.mQueue;
-        mCallback = callback;
-        mAsynchronous = false;
-    }
-
-    /**
-     * Use the provided queue instead of the default one and take a callback
+     * Use the provided {@link Looper} instead of the default one and take a callback
      * interface in which to handle messages.  Also set whether the handler
      * should be asynchronous.
      *
@@ -184,6 +214,8 @@
      * with represent to synchronous messages.  Asynchronous messages are not subject to
      * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
      *
+     * @param looper The looper, must not be null.
+     * @param callback The callback interface in which to handle messages, or null.
      * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
      * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
      *
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 20d3ec3..2179fa1 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -362,4 +362,11 @@
      * Flush the DNS cache associated with the specified interface.
      */
     void flushInterfaceDnsCache(String iface);
+
+    void setFirewallEnabled(boolean enabled);
+    boolean isFirewallEnabled();
+    void setFirewallInterfaceRule(String iface, boolean allow);
+    void setFirewallEgressSourceRule(String addr, boolean allow);
+    void setFirewallEgressDestRule(String addr, int port, boolean allow);
+    void setFirewallUidRule(int uid, boolean allow);
 }
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 0843d85..d33bd80 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -28,6 +28,9 @@
     /** @hide A user id to indicate all users on the device */
     public static final int USER_ALL = -1;
 
+    /** @hide A user handle to indicate all users on the device */
+    public static final UserHandle ALL = new UserHandle(USER_ALL);
+
     /** @hide A user id to indicate the currently active user */
     public static final int USER_CURRENT = -2;
 
diff --git a/core/java/android/service/dreams/DreamManagerService.java b/core/java/android/service/dreams/DreamManagerService.java
index 4b0f7c5..2cec6c3 100644
--- a/core/java/android/service/dreams/DreamManagerService.java
+++ b/core/java/android/service/dreams/DreamManagerService.java
@@ -105,7 +105,7 @@
     public ComponentName[] getDreamComponents() {
         // TODO(dsandler) don't load this every time, watch the value
         String names = Settings.Secure.getString(mContext.getContentResolver(), SCREENSAVER_COMPONENTS);
-        return componentsFromString(names);
+        return names == null ? null : componentsFromString(names);
     }
 
     // IDreamManager method
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index ac3dee4..9051285 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -357,11 +357,6 @@
 
                         // From the Unicode Line Breaking Algorithm (at least approximately)
                         boolean isLineBreak = isSpaceOrTab ||
-                                // .,:; are class IS breakpoints, except when adjacent to digits
-                                ((c == CHAR_DOT || c == CHAR_COMMA ||
-                                c == CHAR_COLON || c == CHAR_SEMICOLON) &&
-                                (j - 1 < here || !Character.isDigit(chs[j - 1 - paraStart])) &&
-                                (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) ||
                                 // / is class SY and - is class HY, except when followed by a digit
                                 ((c == CHAR_SLASH || c == CHAR_HYPHEN) &&
                                 (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) ||
@@ -959,10 +954,6 @@
     private static final char CHAR_NEW_LINE = '\n';
     private static final char CHAR_TAB = '\t';
     private static final char CHAR_SPACE = ' ';
-    private static final char CHAR_DOT = '.';
-    private static final char CHAR_COMMA = ',';
-    private static final char CHAR_COLON = ':';
-    private static final char CHAR_SEMICOLON = ';';
     private static final char CHAR_SLASH = '/';
     private static final char CHAR_HYPHEN = '-';
 
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index da10311..211453d 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -1618,7 +1618,7 @@
 
         String result;
         long now = System.currentTimeMillis();
-        long span = now - millis;
+        long span = Math.abs(now - millis);
 
         synchronized (DateUtils.class) {
             if (sNowTime == null) {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 61c942d..423135f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -973,6 +973,12 @@
 
         // Start selection mode if needed. We don't need to if we're unchecking something.
         if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
+            if (mMultiChoiceModeCallback == null ||
+                    !mMultiChoiceModeCallback.hasWrappedCallback()) {
+                throw new IllegalStateException("AbsListView: attempted to start selection mode " +
+                        "for CHOICE_MODE_MULTIPLE_MODAL but no choice mode callback was " +
+                        "supplied. Call setMultiChoiceModeListener to set a callback.");
+            }
             mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
         }
 
@@ -5945,6 +5951,10 @@
             mWrapped = wrapped;
         }
 
+        public boolean hasWrappedCallback() {
+            return mWrapped != null;
+        }
+
         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
             if (mWrapped.onCreateActionMode(mode, menu)) {
                 // Initialize checked graphic state?
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index f91201a..bc78adc 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -175,11 +175,7 @@
     @Override
     protected void internalSetPadding(int left, int top, int right, int bottom) {
         super.internalSetPadding(left, top, right, bottom);
-        if (isLayoutRtl()) {
-            mBasePadding = mUserPaddingLeft;
-        } else {
-            mBasePadding = mUserPaddingRight;
-        }
+        setBasePadding(isLayoutRtl());
     }
 
     @Override
@@ -201,13 +197,21 @@
     @Override
     public void setPadding(int left, int top, int right, int bottom) {
         super.setPadding(left, top, right, bottom);
-        mBasePadding = getPaddingEnd();
+        setBasePadding(isLayoutRtl());
     }
 
     @Override
     public void setPaddingRelative(int start, int top, int end, int bottom) {
         super.setPaddingRelative(start, top, end, bottom);
-        mBasePadding = getPaddingEnd();
+        setBasePadding(isLayoutRtl());
+    }
+
+    private void setBasePadding(boolean isLayoutRtl) {
+        if (isLayoutRtl) {
+            mBasePadding = mPaddingLeft;
+        } else {
+            mBasePadding = mPaddingRight;
+        }
     }
 
     @Override
@@ -237,11 +241,11 @@
             final int left;
             final int right;
             if (isLayoutRtl) {
-                right = getPaddingEnd();
-                left = right - mCheckMarkWidth;
-            } else {
-                left = width - getPaddingEnd();
+                left = mBasePadding;
                 right = left + mCheckMarkWidth;
+            } else {
+                right = width - mBasePadding;
+                left = right - mCheckMarkWidth;
             }
             checkMarkDrawable.setBounds( left, top, right, bottom);
             checkMarkDrawable.draw(canvas);
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index ff1dd11..7c8196d 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -284,6 +284,7 @@
                 mVideoHeight = mp.getVideoHeight();
                 if (mVideoWidth != 0 && mVideoHeight != 0) {
                     getHolder().setFixedSize(mVideoWidth, mVideoHeight);
+                    requestLayout();
                 }
             }
     };
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 2061c90..bcf0ea4 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -751,6 +751,7 @@
         public DialogInterface.OnClickListener mNeutralButtonListener;
         public boolean mCancelable;
         public DialogInterface.OnCancelListener mOnCancelListener;
+        public DialogInterface.OnDismissListener mOnDismissListener;
         public DialogInterface.OnKeyListener mOnKeyListener;
         public CharSequence[] mItems;
         public ListAdapter mAdapter;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 246b0c9..c5e7d9d 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -49,6 +49,7 @@
     public static final int RECOMMEND_FAILED_ALREADY_EXISTS = -4;
     public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5;
     public static final int RECOMMEND_FAILED_INVALID_URI = -6;
+    public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;
 
     private static final boolean localLOGV = true;
     private static final String TAG = "PackageHelper";
diff --git a/core/java/com/android/internal/net/VpnProfile.aidl b/core/java/com/android/internal/net/VpnProfile.aidl
new file mode 100644
index 0000000..a072160
--- /dev/null
+++ b/core/java/com/android/internal/net/VpnProfile.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2012 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.internal.net;
+
+parcelable VpnProfile;
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 154b16b..7287327 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -16,6 +16,12 @@
 
 package com.android.internal.net;
 
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.net.InetAddress;
 import java.nio.charset.Charsets;
 
 /**
@@ -27,7 +33,9 @@
  *
  * @hide
  */
-public class VpnProfile implements Cloneable {
+public class VpnProfile implements Cloneable, Parcelable {
+    private static final String TAG = "VpnProfile";
+
     // Match these constants with R.array.vpn_types.
     public static final int TYPE_PPTP = 0;
     public static final int TYPE_L2TP_IPSEC_PSK = 1;
@@ -120,4 +128,54 @@
         builder.append('\0').append(ipsecServerCert);
         return builder.toString().getBytes(Charsets.UTF_8);
     }
+
+    /**
+     * Test if profile is valid for lockdown, which requires IPv4 address for
+     * both server and DNS. Server hostnames would require using DNS before
+     * connection.
+     */
+    public boolean isValidLockdownProfile() {
+        try {
+            InetAddress.parseNumericAddress(server);
+
+            for (String dnsServer : dnsServers.split(" +")) {
+                InetAddress.parseNumericAddress(this.dnsServers);
+            }
+            if (TextUtils.isEmpty(dnsServers)) {
+                Log.w(TAG, "DNS required");
+                return false;
+            }
+
+            // Everything checked out above
+            return true;
+
+        } catch (IllegalArgumentException e) {
+            Log.w(TAG, "Invalid address", e);
+            return false;
+        }
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(key);
+        out.writeByteArray(encode());
+    }
+
+    public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() {
+        @Override
+        public VpnProfile createFromParcel(Parcel in) {
+            final String key = in.readString();
+            return decode(key, in.createByteArray());
+        }
+
+        @Override
+        public VpnProfile[] newArray(int size) {
+            return new VpnProfile[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
 }
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index a53a9c0..a327adc 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -54,4 +54,16 @@
         return reference;
     }
 
+    /**
+     * Ensures the truth of an expression involving the state of the calling
+     * instance, but not involving any parameters to the calling method.
+     *
+     * @param expression a boolean expression
+     * @throws IllegalStateException if {@code expression} is false
+     */
+    public static void checkState(boolean expression) {
+        if (!expression) {
+            throw new IllegalStateException();
+        }
+    }
 }
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index 08d9f49..b620568 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -23,7 +23,9 @@
 import android.app.ActionBar;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 import android.view.Gravity;
 import android.view.View;
@@ -38,6 +40,7 @@
 import android.widget.ListView;
 import android.widget.Spinner;
 import android.widget.TextView;
+import android.widget.Toast;
 
 /**
  * This widget implements the dynamic action bar tab behavior that can change
@@ -352,7 +355,7 @@
         tabView.getTab().select();
     }
 
-    private class TabView extends LinearLayout {
+    private class TabView extends LinearLayout implements OnLongClickListener {
         private ActionBar.Tab mTab;
         private TextView mTextView;
         private ImageView mIconView;
@@ -426,7 +429,8 @@
                     mIconView.setImageDrawable(null);
                 }
 
-                if (text != null) {
+                final boolean hasText = !TextUtils.isEmpty(text);
+                if (hasText) {
                     if (mTextView == null) {
                         TextView textView = new TextView(getContext(), null,
                                 com.android.internal.R.attr.actionBarTabTextStyle);
@@ -448,9 +452,35 @@
                 if (mIconView != null) {
                     mIconView.setContentDescription(tab.getContentDescription());
                 }
+
+                if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) {
+                    setOnLongClickListener(this);
+                } else {
+                    setOnLongClickListener(null);
+                    setLongClickable(false);
+                }
             }
         }
 
+        public boolean onLongClick(View v) {
+            final int[] screenPos = new int[2];
+            getLocationOnScreen(screenPos);
+
+            final Context context = getContext();
+            final int width = getWidth();
+            final int height = getHeight();
+            final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+
+            Toast cheatSheet = Toast.makeText(context, mTab.getContentDescription(),
+                    Toast.LENGTH_SHORT);
+            // Show under the tab
+            cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
+                    (screenPos[0] + width / 2) - screenWidth / 2, height);
+
+            cheatSheet.show();
+            return true;
+        }
+
         public ActionBar.Tab getTab() {
             return mTab;
         }
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c998ff6..5e63311 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -225,7 +225,7 @@
     <string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"licencia completa para interactuar con los usuarios"</string>
     <string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Permite que la aplicación interactúe con los usuarios."</string>
     <string name="permlab_manageUsers" msgid="1676150911672282428">"administrar usuarios"</string>
-    <string name="permdesc_manageUsers" msgid="8409306667645355638">"Permite a las aplicaciones administrar los usuarios del dispositivo, así como buscarlos, crearlos y eliminarlos."</string>
+    <string name="permdesc_manageUsers" msgid="8409306667645355638">"Permite a las aplicaciones administrar los usuarios del dispositivo, p. ej., buscarlos, crearlos y eliminarlos."</string>
     <string name="permlab_getDetailedTasks" msgid="6229468674753529501">"recuperar información de aplicaciones en ejecución"</string>
     <string name="permdesc_getDetailedTasks" msgid="153824741440717599">"Permite que la aplicación recupere información sobre tareas que se están ejecutando en este momento o que se han ejecutado recientemente. Las aplicaciones malintencionadas pueden usar este servicio para acceder a información privada sobre otras aplicaciones."</string>
     <string name="permlab_reorderTasks" msgid="2018575526934422779">"reorganizar aplicaciones en ejecución"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index cfc9b9b..b778d03 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -225,7 +225,7 @@
     <string name="permlab_interactAcrossUsersFull" msgid="2567734285545074105">"visa licencija, leidžianti sąveikauti su naudotojais"</string>
     <string name="permdesc_interactAcrossUsersFull" msgid="376841368395502366">"Leidžiama bet kokia sąveika tarp naudotojų."</string>
     <string name="permlab_manageUsers" msgid="1676150911672282428">"tvarkyti naudotojus"</string>
-    <string name="permdesc_manageUsers" msgid="8409306667645355638">"Leidžia tvarkyti įrenginio naudotojų duomenis, įskaitant užklausų teikimą, duomenų kūrimą ir ištrynimą."</string>
+    <string name="permdesc_manageUsers" msgid="8409306667645355638">"Leidžia programoms tvarkyti įrenginio naudotojų duomenis, įskaitant užklausų teikimą, duomenų kūrimą ir ištrynimą."</string>
     <string name="permlab_getDetailedTasks" msgid="6229468674753529501">"nuskaityti veikiančių programų išsamią informaciją"</string>
     <string name="permdesc_getDetailedTasks" msgid="153824741440717599">"Leidžiama programai nuskaityti išsamią informaciją apie šiuo ir pastaruoju metu vykdomas užduotis. Kenkėjiškos programos gali surasti privačios informacijos apie kitas programas."</string>
     <string name="permlab_reorderTasks" msgid="2018575526934422779">"pertvarkyti vykdomas programas"</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e17a05d..d761980 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1442,6 +1442,7 @@
   <java-symbol type="drawable" name="stat_sys_tether_usb" />
   <java-symbol type="drawable" name="stat_sys_throttled" />
   <java-symbol type="drawable" name="vpn_connected" />
+  <java-symbol type="drawable" name="vpn_disconnected" />
   <java-symbol type="id" name="ask_checkbox" />
   <java-symbol type="id" name="compat_checkbox" />
   <java-symbol type="id" name="original_app_icon" />
@@ -1557,6 +1558,10 @@
   <java-symbol type="string" name="vpn_text_long" />
   <java-symbol type="string" name="vpn_title" />
   <java-symbol type="string" name="vpn_title_long" />
+  <java-symbol type="string" name="vpn_lockdown_connecting" />
+  <java-symbol type="string" name="vpn_lockdown_connected" />
+  <java-symbol type="string" name="vpn_lockdown_error" />
+  <java-symbol type="string" name="vpn_lockdown_reset" />
   <java-symbol type="string" name="wallpaper_binding_label" />
   <java-symbol type="style" name="Theme.Dialog.AppError" />
   <java-symbol type="style" name="Theme.Toast" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b7ad1e9..e77dde7 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3272,6 +3272,15 @@
     <!-- The text of the notification when VPN is active with a session name. -->
     <string name="vpn_text_long">Connected to <xliff:g id="session" example="office">%s</xliff:g>. Touch to manage the network.</string>
 
+    <!-- Notification title when connecting to lockdown VPN. -->
+    <string name="vpn_lockdown_connecting">Always-on VPN connecting\u2026</string>
+    <!-- Notification title when connected to lockdown VPN. -->
+    <string name="vpn_lockdown_connected">Always-on VPN connected</string>
+    <!-- Notification title when error connecting to lockdown VPN. -->
+    <string name="vpn_lockdown_error">Always-on VPN error</string>
+    <!-- Notification body that indicates user can touch to cycle lockdown VPN connection. -->
+    <string name="vpn_lockdown_reset">Touch to reset connection</string>
+
     <!-- Localized strings for WebView -->
     <!-- Label for button in a WebView that will open a chooser to choose a file to upload -->
     <string name="upload_file">Choose file</string>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 6e1b9d6..a1fd14e 100755
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -864,7 +864,7 @@
 
     public void deleteFromRawResource(int iFlags, int dFlags) {
         InstallParams ip = sampleInstallFromRawResource(iFlags, false);
-        boolean retainData = ((dFlags & PackageManager.DONT_DELETE_DATA) != 0);
+        boolean retainData = ((dFlags & PackageManager.DELETE_KEEP_DATA) != 0);
         GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
         DeleteObserver observer = new DeleteObserver();
         try {
@@ -914,12 +914,12 @@
 
     @LargeTest
     public void testDeleteNormalInternalRetainData() {
-        deleteFromRawResource(0, PackageManager.DONT_DELETE_DATA);
+        deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA);
     }
 
     @LargeTest
     public void testDeleteFwdLockedInternalRetainData() {
-        deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, PackageManager.DONT_DELETE_DATA);
+        deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, PackageManager.DELETE_KEEP_DATA);
     }
 
     @LargeTest
@@ -929,7 +929,7 @@
             return;
         }
 
-        deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DONT_DELETE_DATA);
+        deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DELETE_KEEP_DATA);
     }
 
     /* sdcard mount/unmount tests ******/
@@ -1656,7 +1656,7 @@
                     false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
             // Delete the package now retaining data.
             GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
-            invokeDeletePackage(ip.pkg.packageName, PackageManager.DONT_DELETE_DATA, receiver);
+            invokeDeletePackage(ip.pkg.packageName, PackageManager.DELETE_KEEP_DATA, receiver);
             assertTrue(invokeMovePackageFail(ip.pkg.packageName, moveFlags, result));
         } catch (Exception e) {
             failStr(e);
@@ -2532,7 +2532,7 @@
             GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
 
             try {
-                invokeDeletePackage(ip.pkg.packageName, PackageManager.DONT_DELETE_DATA, receiver);
+                invokeDeletePackage(ip.pkg.packageName, PackageManager.DELETE_KEEP_DATA, receiver);
             } catch (Exception e) {
                 failStr(e);
             }
diff --git a/docs/downloads/design/Android_Design_Downloads_20120823.zip b/docs/downloads/design/Android_Design_Downloads_20120823.zip
new file mode 100644
index 0000000..6d31283
--- /dev/null
+++ b/docs/downloads/design/Android_Design_Downloads_20120823.zip
Binary files differ
diff --git a/docs/downloads/design/Roboto_Hinted_20120823.zip b/docs/downloads/design/Roboto_Hinted_20120823.zip
new file mode 100644
index 0000000..9ead4af
--- /dev/null
+++ b/docs/downloads/design/Roboto_Hinted_20120823.zip
Binary files differ
diff --git a/docs/html/design/downloads/index.jd b/docs/html/design/downloads/index.jd
index 4503098..5f78aea 100644
--- a/docs/html/design/downloads/index.jd
+++ b/docs/html/design/downloads/index.jd
@@ -12,7 +12,7 @@
   <div class="layout-content-col span-4">
 
 <p>
-  <a class="download-button" href="{@docRoot}downloads/design/Android_Design_Downloads_20120814.zip">Download All</a>
+  <a class="download-button" href="{@docRoot}downloads/design/Android_Design_Downloads_20120823.zip">Download All</a>
 </p>
 
   </div>
@@ -91,7 +91,7 @@
   <div class="layout-content-col span-4">
 
 <p>
-  <a class="download-button" href="{@docRoot}downloads/design/Roboto_Hinted_20111129.zip">Roboto</a>
+  <a class="download-button" href="{@docRoot}downloads/design/Roboto_Hinted_20120823.zip">Roboto</a>
   <a class="download-button" href="{@docRoot}downloads/design/Roboto_Specimen_Book_20111129.pdf">Specimen Book</a>
 </p>
 
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index f6bf432..b233ff6 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -61,6 +61,9 @@
     /** Key prefix for WIFI. */
     public static final String WIFI = "WIFI_";
 
+    /** Key containing suffix of lockdown VPN profile. */
+    public static final String LOCKDOWN_VPN = "LOCKDOWN_VPN";
+
     /** Data type for public keys. */
     public static final String EXTRA_PUBLIC_KEY = "KEY";
 
diff --git a/media/java/android/media/DataSource.java b/media/java/android/media/DataSource.java
new file mode 100644
index 0000000..347bd5f
--- /dev/null
+++ b/media/java/android/media/DataSource.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 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.media;
+
+import java.io.Closeable;
+
+/**
+ * An abstraction for a media data source, e.g. a file or an http stream
+ * {@hide}
+ */
+public interface DataSource extends Closeable {
+    /**
+     * Reads data from the data source at the requested position
+     *
+     * @param offset where in the source to read
+     * @param buffer the buffer to read the data into
+     * @param size how many bytes to read
+     * @return the number of bytes read, or -1 if there was an error
+     */
+    public int readAt(long offset, byte[] buffer, int size);
+
+    /**
+     * Gets the size of the data source.
+     *
+     * @return size of data source, or -1 if the length is unknown
+     */
+    public long getSize();
+}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 687d3a5..4b8d3cb 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -22,6 +22,7 @@
 import android.media.MediaCodec;
 import android.media.MediaFormat;
 import android.net.Uri;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -60,6 +61,12 @@
     }
 
     /**
+     * Sets the DataSource object to be used as the data source for this extractor
+     * {@hide}
+     */
+    public native final void setDataSource(DataSource source);
+
+    /**
      * Sets the data source as a content Uri.
      *
      * @param context the Context to use when resolving the Uri
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 1b98109..ef0da3a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2265,6 +2265,16 @@
      */
     public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
 
+    /** File or network related operation errors. */
+    public static final int MEDIA_ERROR_IO = -1004;
+    /** Bitstream is not conforming to the related coding standard or file spec. */
+    public static final int MEDIA_ERROR_MALFORMED = -1007;
+    /** Bitstream is conforming to the related coding standard or file spec, but
+     * the media framework does not support the feature. */
+    public static final int MEDIA_ERROR_UNSUPPORTED = -1010;
+    /** Some operation takes too long to complete, usually more than 3-5 seconds. */
+    public static final int MEDIA_ERROR_TIMED_OUT = -110;
+
     /**
      * Interface definition of a callback to be invoked when there
      * has been an error during an asynchronous operation (other errors
@@ -2283,6 +2293,12 @@
          * </ul>
          * @param extra an extra code, specific to the error. Typically
          * implementation dependent.
+         * <ul>
+         * <li>{@link #MEDIA_ERROR_IO}
+         * <li>{@link #MEDIA_ERROR_MALFORMED}
+         * <li>{@link #MEDIA_ERROR_UNSUPPORTED}
+         * <li>{@link #MEDIA_ERROR_TIMED_OUT}
+         * </ul>
          * @return True if the method handled the error, false if it didn't.
          * Returning false, or not having an OnErrorListener at all, will
          * cause the OnCompletionListener to be called.
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index ca2df47..2d1be02 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -351,12 +351,11 @@
      */
     public void setCaptureRate(double fps) {
         // Make sure that time lapse is enabled when this method is called.
-        setParameter(String.format("time-lapse-enable=1"));
+        setParameter("time-lapse-enable=1");
 
         double timeBetweenFrameCapture = 1 / fps;
         int timeBetweenFrameCaptureMs = (int) (1000 * timeBetweenFrameCapture);
-        setParameter(String.format("time-between-time-lapse-frame-capture=%d",
-                    timeBetweenFrameCaptureMs));
+        setParameter("time-between-time-lapse-frame-capture=" + timeBetweenFrameCaptureMs);
     }
 
     /**
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 351ff04..23949fa 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -44,6 +44,72 @@
 
 static fields_t gFields;
 
+class JavaDataSourceBridge : public DataSource {
+    jmethodID mReadMethod;
+    jmethodID mGetSizeMethod;
+    jmethodID mCloseMethod;
+    jobject   mDataSource;
+ public:
+    JavaDataSourceBridge(JNIEnv *env, jobject source) {
+        mDataSource = env->NewGlobalRef(source);
+
+        jclass datasourceclass = env->GetObjectClass(mDataSource);
+        CHECK(datasourceclass != NULL);
+
+        mReadMethod = env->GetMethodID(datasourceclass, "readAt", "(J[BI)I");
+        CHECK(mReadMethod != NULL);
+
+        mGetSizeMethod = env->GetMethodID(datasourceclass, "getSize", "()J");
+        CHECK(mGetSizeMethod != NULL);
+
+        mCloseMethod = env->GetMethodID(datasourceclass, "close", "()V");
+        CHECK(mCloseMethod != NULL);
+    }
+
+    ~JavaDataSourceBridge() {
+        JNIEnv *env = AndroidRuntime::getJNIEnv();
+        env->CallVoidMethod(mDataSource, mCloseMethod);
+        env->DeleteGlobalRef(mDataSource);
+    }
+
+    virtual status_t initCheck() const {
+        return OK;
+    }
+
+    virtual ssize_t readAt(off64_t offset, void* buffer, size_t size) {
+        JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+        // XXX could optimize this by reusing the same array
+        jbyteArray byteArrayObj = env->NewByteArray(size);
+        env->DeleteLocalRef(env->GetObjectClass(mDataSource));
+        env->DeleteLocalRef(env->GetObjectClass(byteArrayObj));
+        ssize_t numread = env->CallIntMethod(mDataSource, mReadMethod, offset, byteArrayObj, size);
+        env->GetByteArrayRegion(byteArrayObj, 0, size, (jbyte*) buffer);
+        env->DeleteLocalRef(byteArrayObj);
+        if (env->ExceptionCheck()) {
+            ALOGW("Exception occurred while reading %d at %lld", size, offset);
+            LOGW_EX(env);
+            env->ExceptionClear();
+            return -1;
+        }
+        return numread;
+    }
+
+    virtual status_t getSize(off64_t *size) {
+        JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+        CHECK(size != NULL);
+
+        int64_t len = env->CallLongMethod(mDataSource, mGetSizeMethod);
+        if (len < 0) {
+            *size = ERROR_UNSUPPORTED;
+        } else {
+            *size = len;
+        }
+        return OK;
+    }
+};
+
 ////////////////////////////////////////////////////////////////////////////////
 
 JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
@@ -76,6 +142,10 @@
     return mImpl->setDataSource(fd, offset, size);
 }
 
+status_t JMediaExtractor::setDataSource(const sp<DataSource> &datasource) {
+    return mImpl->setDataSource(datasource);
+}
+
 size_t JMediaExtractor::countTracks() const {
     return mImpl->countTracks();
 }
@@ -625,6 +695,33 @@
     }
 }
 
+static void android_media_MediaExtractor_setDataSourceCallback(
+        JNIEnv *env, jobject thiz,
+        jobject callbackObj) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    if (callbackObj == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    sp<JavaDataSourceBridge> bridge = new JavaDataSourceBridge(env, callbackObj);
+    status_t err = extractor->setDataSource(bridge);
+
+    if (err != OK) {
+        jniThrowException(
+                env,
+                "java/io/IOException",
+                "Failed to instantiate extractor.");
+        return;
+    }
+}
+
 static jlong android_media_MediaExtractor_getCachedDurationUs(
         JNIEnv *env, jobject thiz) {
     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
@@ -713,6 +810,9 @@
     { "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
       (void *)android_media_MediaExtractor_setDataSourceFd },
 
+    { "setDataSource", "(Landroid/media/DataSource;)V",
+      (void *)android_media_MediaExtractor_setDataSourceCallback },
+
     { "getCachedDuration", "()J",
       (void *)android_media_MediaExtractor_getCachedDurationUs },
 
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index 2d4627e..03900db 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -19,6 +19,7 @@
 
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/MediaSource.h>
+#include <media/stagefright/DataSource.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
@@ -39,6 +40,7 @@
             const KeyedVector<String8, String8> *headers);
 
     status_t setDataSource(int fd, off64_t offset, off64_t size);
+    status_t setDataSource(const sp<DataSource> &source);
 
     size_t countTracks() const;
     status_t getTrackFormat(size_t index, jobject *format) const;
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index a28b8a4..b36bd55 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -26,6 +26,7 @@
 import android.content.pm.ContainerEncryptionParams;
 import android.content.pm.IPackageManager;
 import android.content.pm.LimitedLengthInputStream;
+import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
@@ -181,6 +182,7 @@
             }
 
             ret.packageName = pkg.packageName;
+            ret.versionCode = pkg.versionCode;
             ret.installLocation = pkg.installLocation;
             ret.verifiers = pkg.verifiers;
 
@@ -268,12 +270,14 @@
         if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) {
             IPackageManager pm = IPackageManager.Stub.asInterface(
                     ServiceManager.getService("package"));
-            String pkg = null;
+            PackageCleanItem pkg = null;
             try {
                 while ((pkg=pm.nextPackageToClean(pkg)) != null) {
-                    eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg));
-                    eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg));
-                    eraseFiles(Environment.getExternalStorageAppObbDirectory(pkg));
+                    eraseFiles(Environment.getExternalStorageAppDataDirectory(pkg.packageName));
+                    eraseFiles(Environment.getExternalStorageAppMediaDirectory(pkg.packageName));
+                    if (pkg.andCode) {
+                        eraseFiles(Environment.getExternalStorageAppObbDirectory(pkg.packageName));
+                    }
                 }
             } catch (RemoteException e) {
             }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index c18f76c..b185471 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -179,5 +179,17 @@
 <!--            <category android:name="android.intent.category.LAUNCHER" />-->
             </intent-filter>
         </activity>
+
+        <!-- Beans in space -->
+        <service
+            android:name=".BeanBagDream"
+            android:exported="true"
+            android:label="Beans in space">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.DREAM" />
+            </intent-filter>
+        </service>
     </application>
 </manifest>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index b53fbe2..d435670 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -138,7 +138,7 @@
     <string name="data_usage_disabled_dialog_title" msgid="2086815304858964954">"Передача данных отключена"</string>
     <string name="data_usage_disabled_dialog" msgid="3853117269051806280">"Достигнут лимит трафика."\n\n"При восстановлении подключения оператор может взимать плату за передачу данных."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="7729772039208664606">"Восстановить подключение"</string>
-    <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Нет подключения к Интернету"</string>
+    <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Нет интернет-подключения"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi подключено"</string>
     <string name="gps_notification_searching_text" msgid="8574247005642736060">"Поиск GPS"</string>
     <string name="gps_notification_found_text" msgid="4619274244146446464">"Координаты по GPS"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/BeanBagDream.java b/packages/SystemUI/src/com/android/systemui/BeanBagDream.java
new file mode 100644
index 0000000..12e45c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/BeanBagDream.java
@@ -0,0 +1,31 @@
+package com.android.systemui;
+
+import android.service.dreams.Dream;
+
+import com.android.systemui.BeanBag.Board;
+
+public class BeanBagDream extends Dream {
+
+    private Board mBoard;
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        setInteractive(true);
+        mBoard = new Board(this, null);
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        setContentView(mBoard);
+        lightsOut();
+        mBoard.startAnimation();
+    }
+
+    @Override
+    public void finish() {
+        mBoard.stopAnimation();
+        super.finish();
+    }
+}
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index ff9bbc5..96de1b9 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -123,7 +123,11 @@
             if (which == DialogInterface.BUTTON_POSITIVE) {
                 mConfig.configureIntent.send();
             } else if (which == DialogInterface.BUTTON_NEUTRAL) {
-                mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
+                if (mConfig.legacy) {
+                    mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+                } else {
+                    mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
+                }
             }
         } catch (Exception e) {
             Log.e(TAG, "onClick", e);
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 3ea50201..236a4ea 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -971,7 +971,7 @@
      * interacts with the keyguard ui should be posted to this handler, rather
      * than called directly.
      */
-    private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
+    private Handler mHandler = new Handler(true /*async*/) {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index d0db0d2..776a1a4 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
 import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
@@ -32,10 +33,12 @@
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
 import android.bluetooth.BluetoothTetheringDataTracker;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -78,6 +81,8 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.security.Credentials;
+import android.security.KeyStore;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Slog;
@@ -85,12 +90,14 @@
 
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.Tethering;
 import com.android.server.connectivity.Vpn;
 import com.android.server.net.BaseNetworkObserver;
+import com.android.server.net.LockdownVpnTracker;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Sets;
 
@@ -137,9 +144,14 @@
     private Tethering mTethering;
     private boolean mTetheringConfigValid = false;
 
+    private KeyStore mKeyStore;
+
     private Vpn mVpn;
     private VpnCallback mVpnCallback = new VpnCallback();
 
+    private boolean mLockdownEnabled;
+    private LockdownVpnTracker mLockdownTracker;
+
     /** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */
     private Object mRulesLock = new Object();
     /** Currently active network rules by UID. */
@@ -269,6 +281,8 @@
      */
     private static final int EVENT_SET_POLICY_DATA_ENABLE = 13;
 
+    private static final int EVENT_VPN_STATE_CHANGED = 14;
+
     /** Handler used for internal events. */
     private InternalHandler mHandler;
     /** Handler used for incoming {@link NetworkStateTracker} events. */
@@ -371,6 +385,7 @@
         mContext = checkNotNull(context, "missing Context");
         mNetd = checkNotNull(netManager, "missing INetworkManagementService");
         mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
+        mKeyStore = KeyStore.getInstance();
 
         try {
             mPolicyManager.registerListener(mPolicyListener);
@@ -778,6 +793,9 @@
             info = new NetworkInfo(info);
             info.setDetailedState(DetailedState.BLOCKED, null, null);
         }
+        if (mLockdownTracker != null) {
+            info = mLockdownTracker.augmentNetworkInfo(info);
+        }
         return info;
     }
 
@@ -795,6 +813,17 @@
         return getNetworkInfo(mActiveDefaultNetwork, uid);
     }
 
+    public NetworkInfo getActiveNetworkInfoUnfiltered() {
+        enforceAccessPermission();
+        if (isNetworkTypeValid(mActiveDefaultNetwork)) {
+            final NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork];
+            if (tracker != null) {
+                return tracker.getNetworkInfo();
+            }
+        }
+        return null;
+    }
+
     @Override
     public NetworkInfo getActiveNetworkInfoForUid(int uid) {
         enforceConnectivityInternalPermission();
@@ -1054,6 +1083,12 @@
             // TODO - move this into individual networktrackers
             int usedNetworkType = convertFeatureToNetworkType(networkType, feature);
 
+            if (mLockdownEnabled) {
+                // Since carrier APNs usually aren't available from VPN
+                // endpoint, mark them as unavailable.
+                return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
+            }
+
             if (mProtectedNetworks.contains(usedNetworkType)) {
                 enforceConnectivityInternalPermission();
             }
@@ -1761,7 +1796,7 @@
         }
     }
 
-    private void sendConnectedBroadcast(NetworkInfo info) {
+    public void sendConnectedBroadcast(NetworkInfo info) {
         sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE);
         sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
     }
@@ -1776,6 +1811,10 @@
     }
 
     private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
+        if (mLockdownTracker != null) {
+            info = mLockdownTracker.augmentNetworkInfo(info);
+        }
+
         Intent intent = new Intent(bcastType);
         intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
         intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
@@ -1907,8 +1946,26 @@
         }
         // load the global proxy at startup
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY));
+
+        // Try bringing up tracker, but if KeyStore isn't ready yet, wait
+        // for user to unlock device.
+        if (!updateLockdownVpn()) {
+            final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT);
+            mContext.registerReceiver(mUserPresentReceiver, filter);
+        }
     }
 
+    private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // Try creating lockdown tracker, since user present usually means
+            // unlocked keystore.
+            if (updateLockdownVpn()) {
+                mContext.unregisterReceiver(this);
+            }
+        }
+    };
+
     private void handleConnect(NetworkInfo info) {
         final int type = info.getType();
 
@@ -2587,6 +2644,9 @@
                     } else if (state == NetworkInfo.State.CONNECTED) {
                         handleConnect(info);
                     }
+                    if (mLockdownTracker != null) {
+                        mLockdownTracker.onNetworkInfoChanged(info);
+                    }
                     break;
                 case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
                     info = (NetworkInfo) msg.obj;
@@ -2684,6 +2744,13 @@
                     final int networkType = msg.arg1;
                     final boolean enabled = msg.arg2 == ENABLED;
                     handleSetPolicyDataEnable(networkType, enabled);
+                    break;
+                }
+                case EVENT_VPN_STATE_CHANGED: {
+                    if (mLockdownTracker != null) {
+                        mLockdownTracker.onVpnStateChanged((NetworkInfo) msg.obj);
+                    }
+                    break;
                 }
             }
         }
@@ -3082,6 +3149,7 @@
      */
     @Override
     public boolean protectVpn(ParcelFileDescriptor socket) {
+        throwIfLockdownEnabled();
         try {
             int type = mActiveDefaultNetwork;
             if (ConnectivityManager.isNetworkTypeValid(type)) {
@@ -3108,6 +3176,7 @@
      */
     @Override
     public boolean prepareVpn(String oldPackage, String newPackage) {
+        throwIfLockdownEnabled();
         return mVpn.prepare(oldPackage, newPackage);
     }
 
@@ -3120,18 +3189,22 @@
      */
     @Override
     public ParcelFileDescriptor establishVpn(VpnConfig config) {
+        throwIfLockdownEnabled();
         return mVpn.establish(config);
     }
 
     /**
-     * Start legacy VPN and return an intent to VpnDialogs. This method is
-     * used by VpnSettings and not available in ConnectivityManager.
-     * Permissions are checked in Vpn class.
-     * @hide
+     * Start legacy VPN, controlling native daemons as needed. Creates a
+     * secondary thread to perform connection work, returning quickly.
      */
     @Override
-    public void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
-        mVpn.startLegacyVpn(config, racoon, mtpd);
+    public void startLegacyVpn(VpnProfile profile) {
+        throwIfLockdownEnabled();
+        final LinkProperties egress = getActiveLinkProperties();
+        if (egress == null) {
+            throw new IllegalStateException("Missing active network connection");
+        }
+        mVpn.startLegacyVpn(profile, mKeyStore, egress);
     }
 
     /**
@@ -3142,6 +3215,7 @@
      */
     @Override
     public LegacyVpnInfo getLegacyVpnInfo() {
+        throwIfLockdownEnabled();
         return mVpn.getLegacyVpnInfo();
     }
 
@@ -3160,8 +3234,7 @@
         }
 
         public void onStateChanged(NetworkInfo info) {
-            // TODO: if connected, release delayed broadcast
-            // TODO: if disconnected, consider kicking off reconnect
+            mHandler.obtainMessage(EVENT_VPN_STATE_CHANGED, info).sendToTarget();
         }
 
         public void override(List<String> dnsServers, List<String> searchDomains) {
@@ -3230,4 +3303,58 @@
             }
         }
     }
+
+    @Override
+    public boolean updateLockdownVpn() {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+        // Tear down existing lockdown if profile was removed
+        mLockdownEnabled = LockdownVpnTracker.isEnabled();
+        if (mLockdownEnabled) {
+            if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
+                Slog.w(TAG, "KeyStore locked; unable to create LockdownTracker");
+                return false;
+            }
+
+            final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN));
+            final VpnProfile profile = VpnProfile.decode(
+                    profileName, mKeyStore.get(Credentials.VPN + profileName));
+            setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpn, profile));
+        } else {
+            setLockdownTracker(null);
+        }
+
+        return true;
+    }
+
+    /**
+     * Internally set new {@link LockdownVpnTracker}, shutting down any existing
+     * {@link LockdownVpnTracker}. Can be {@code null} to disable lockdown.
+     */
+    private void setLockdownTracker(LockdownVpnTracker tracker) {
+        // Shutdown any existing tracker
+        final LockdownVpnTracker existing = mLockdownTracker;
+        mLockdownTracker = null;
+        if (existing != null) {
+            existing.shutdown();
+        }
+
+        try {
+            if (tracker != null) {
+                mNetd.setFirewallEnabled(true);
+                mLockdownTracker = tracker;
+                mLockdownTracker.init();
+            } else {
+                mNetd.setFirewallEnabled(false);
+            }
+        } catch (RemoteException e) {
+            // ignored; NMS lives inside system_server
+        }
+    }
+
+    private void throwIfLockdownEnabled() {
+        if (mLockdownEnabled) {
+            throw new IllegalStateException("Unavailable in lockdown mode");
+        }
+    }
 }
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 2bed957..f1ff27f 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -241,7 +241,7 @@
                 SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK) != 0;
     }
 
-    private final Handler mHandler = new Handler(Looper.myLooper(), null, true) {
+    private final Handler mHandler = new Handler(true /*async*/) {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 39e5186..efa16af 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -35,6 +35,7 @@
 import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
 import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
 
+import android.bluetooth.BluetoothTetheringDataTracker;
 import android.content.Context;
 import android.net.INetworkManagementEventObserver;
 import android.net.InterfaceConfiguration;
@@ -55,7 +56,9 @@
 import android.util.SparseBooleanArray;
 
 import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.util.Preconditions;
 import com.android.server.NativeDaemonConnector.Command;
+import com.android.server.net.LockdownVpnTracker;
 import com.google.android.collect.Maps;
 
 import java.io.BufferedReader;
@@ -78,7 +81,6 @@
 import java.util.NoSuchElementException;
 import java.util.StringTokenizer;
 import java.util.concurrent.CountDownLatch;
-import android.bluetooth.BluetoothTetheringDataTracker;
 
 /**
  * @hide
@@ -92,6 +94,9 @@
     private static final String ADD = "add";
     private static final String REMOVE = "remove";
 
+    private static final String ALLOW = "allow";
+    private static final String DENY = "deny";
+
     private static final String DEFAULT = "default";
     private static final String SECONDARY = "secondary";
 
@@ -169,6 +174,7 @@
     private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap();
 
     private volatile boolean mBandwidthControlEnabled;
+    private volatile boolean mFirewallEnabled;
 
     /**
      * Constructs a new NetworkManagementService instance
@@ -363,6 +369,9 @@
                 }
             }
         }
+
+        // TODO: Push any existing firewall state
+        setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
     }
 
     //
@@ -1425,7 +1434,72 @@
         }
     }
 
-    /** {@inheritDoc} */
+    @Override
+    public void setFirewallEnabled(boolean enabled) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        try {
+            mConnector.execute("firewall", enabled ? "enable" : "disable");
+            mFirewallEnabled = enabled;
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
+    public boolean isFirewallEnabled() {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        return mFirewallEnabled;
+    }
+
+    @Override
+    public void setFirewallInterfaceRule(String iface, boolean allow) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        Preconditions.checkState(mFirewallEnabled);
+        final String rule = allow ? ALLOW : DENY;
+        try {
+            mConnector.execute("firewall", "set_interface_rule", iface, rule);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
+    public void setFirewallEgressSourceRule(String addr, boolean allow) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        Preconditions.checkState(mFirewallEnabled);
+        final String rule = allow ? ALLOW : DENY;
+        try {
+            mConnector.execute("firewall", "set_egress_source_rule", addr, rule);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
+    public void setFirewallEgressDestRule(String addr, int port, boolean allow) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        Preconditions.checkState(mFirewallEnabled);
+        final String rule = allow ? ALLOW : DENY;
+        try {
+            mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
+    public void setFirewallUidRule(int uid, boolean allow) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        Preconditions.checkState(mFirewallEnabled);
+        final String rule = allow ? ALLOW : DENY;
+        try {
+            mConnector.execute("firewall", "set_uid_rule", uid, rule);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
     public void monitor() {
         if (mConnector != null) {
             mConnector.monitor();
@@ -1456,5 +1530,7 @@
             }
             pw.println("]");
         }
+
+        pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
     }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a6af144..7097891 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -197,7 +197,7 @@
             
             Slog.i(TAG, "User Service");
             ServiceManager.addService(Context.USER_SERVICE,
-                    UserManagerService.getInstance(context));
+                    UserManagerService.getInstance());
 
 
             mContentResolver = context.getContentResolver();
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index b609867..72fde11 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -441,7 +441,7 @@
 
         private void delay(long duration) {
             if (duration > 0) {
-                long bedtime = SystemClock.uptimeMillis();
+                long bedtime = duration + SystemClock.uptimeMillis();
                 do {
                     try {
                         this.wait(duration);
@@ -451,8 +451,7 @@
                     if (mDone) {
                         break;
                     }
-                    duration = duration
-                            - SystemClock.uptimeMillis() - bedtime;
+                    duration = bedtime - SystemClock.uptimeMillis();
                 } while (duration > 0);
             }
         }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index edbc624..f483576 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -959,10 +959,8 @@
      * an AsyncChannel communication with WifiService
      */
     public Messenger getWifiServiceMessenger() {
-         /* STOPSHIP: Fix this to have old sync API not need these new permissions
-          * enforceAccessPermission();
-          * enforceChangePermission();
-          */
+        enforceAccessPermission();
+        enforceChangePermission();
         return new Messenger(mAsyncServiceHandler);
     }
 
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index e222936..ca7faa2 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -210,7 +210,7 @@
 
     ComponentName startServiceLocked(IApplicationThread caller,
             Intent service, String resolvedType,
-            int callingPid, int callingUid) {
+            int callingPid, int callingUid, int userId) {
         if (DEBUG_SERVICE) Slog.v(TAG, "startService: " + service
                 + " type=" + resolvedType + " args=" + service.getExtras());
 
@@ -226,7 +226,7 @@
 
         ServiceLookupResult res =
             retrieveServiceLocked(service, resolvedType,
-                    callingPid, callingUid, UserHandle.getUserId(callingUid), true);
+                    callingPid, callingUid, userId, true);
         if (res == null) {
             return null;
         }
@@ -264,7 +264,7 @@
     }
 
     int stopServiceLocked(IApplicationThread caller, Intent service,
-            String resolvedType) {
+            String resolvedType, int userId) {
         if (DEBUG_SERVICE) Slog.v(TAG, "stopService: " + service
                 + " type=" + resolvedType);
 
@@ -278,9 +278,7 @@
 
         // If this service is active, make sure it is stopped.
         ServiceLookupResult r = retrieveServiceLocked(service, resolvedType,
-                Binder.getCallingPid(), Binder.getCallingUid(),
-                callerApp == null ? UserHandle.getCallingUserId() : callerApp.userId,
-                false);
+                Binder.getCallingPid(), Binder.getCallingUid(), userId, false);
         if (r != null) {
             if (r.record != null) {
                 final long origId = Binder.clearCallingIdentity();
@@ -731,7 +729,7 @@
                 ServiceInfo sInfo =
                     rInfo != null ? rInfo.serviceInfo : null;
                 if (sInfo == null) {
-                    Slog.w(TAG, "Unable to start service " + service +
+                    Slog.w(TAG, "Unable to start service " + service + " U=" + userId +
                           ": not found");
                     return null;
                 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8fc12d3..2b4f8b1 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -10554,7 +10554,7 @@
     }
     
     public ComponentName startService(IApplicationThread caller, Intent service,
-            String resolvedType) {
+            String resolvedType, int userId) {
         enforceNotIsolatedCaller("startService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
@@ -10566,9 +10566,10 @@
         synchronized(this) {
             final int callingPid = Binder.getCallingPid();
             final int callingUid = Binder.getCallingUid();
+            checkValidCaller(callingUid, userId);
             final long origId = Binder.clearCallingIdentity();
             ComponentName res = mServices.startServiceLocked(caller, service,
-                    resolvedType, callingPid, callingUid);
+                    resolvedType, callingPid, callingUid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
@@ -10581,22 +10582,24 @@
                 Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
             final long origId = Binder.clearCallingIdentity();
             ComponentName res = mServices.startServiceLocked(null, service,
-                    resolvedType, -1, uid);
+                    resolvedType, -1, uid, UserHandle.getUserId(uid));
             Binder.restoreCallingIdentity(origId);
             return res;
         }
     }
 
     public int stopService(IApplicationThread caller, Intent service,
-            String resolvedType) {
+            String resolvedType, int userId) {
         enforceNotIsolatedCaller("stopService");
         // Refuse possible leaked file descriptors
         if (service != null && service.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
+        checkValidCaller(Binder.getCallingUid(), userId);
+
         synchronized(this) {
-            return mServices.stopServiceLocked(caller, service, resolvedType);
+            return mServices.stopServiceLocked(caller, service, resolvedType, userId);
         }
     }
 
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index d490f24..b3cbb84 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -34,9 +34,11 @@
 import android.net.BaseNetworkStateTracker;
 import android.net.ConnectivityManager;
 import android.net.INetworkManagementEventObserver;
+import android.net.LinkProperties;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.net.NetworkInfo;
+import android.net.RouteInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.os.Binder;
 import android.os.FileUtils;
@@ -48,11 +50,15 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemService;
+import android.security.Credentials;
+import android.security.KeyStore;
 import android.util.Log;
+import android.widget.Toast;
 
 import com.android.internal.R;
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
 import com.android.internal.util.Preconditions;
 import com.android.server.ConnectivityService.VpnCallback;
 import com.android.server.net.BaseNetworkObserver;
@@ -60,6 +66,8 @@
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.Inet4Address;
+import java.net.InetAddress;
 import java.nio.charset.Charsets;
 import java.util.Arrays;
 
@@ -82,6 +90,7 @@
     private Connection mConnection;
     private LegacyVpnRunner mLegacyVpnRunner;
     private PendingIntent mStatusIntent;
+    private boolean mEnableNotif = true;
 
     public Vpn(Context context, VpnCallback callback, INetworkManagementService netService) {
         // TODO: create dedicated TYPE_VPN network type
@@ -96,6 +105,10 @@
         }
     }
 
+    public void setEnableNotifications(boolean enableNotif) {
+        mEnableNotif = enableNotif;
+    }
+
     @Override
     protected void startMonitoringInternal() {
         // Ignored; events are sent through callbacks for now
@@ -386,6 +399,7 @@
     }
 
     private void showNotification(VpnConfig config, String label, Bitmap icon) {
+        if (!mEnableNotif) return;
         mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, config);
 
         NotificationManager nm = (NotificationManager)
@@ -412,6 +426,7 @@
     }
 
     private void hideNotification() {
+        if (!mEnableNotif) return;
         mStatusIntent = null;
 
         NotificationManager nm = (NotificationManager)
@@ -430,20 +445,127 @@
     private native int jniCheck(String interfaze);
     private native void jniProtect(int socket, String interfaze);
 
-    /**
-     * Start legacy VPN. This method stops the daemons and restart them
-     * if arguments are not null. Heavy things are offloaded to another
-     * thread, so callers will not be blocked for a long time.
-     *
-     * @param config The parameters to configure the network.
-     * @param racoon The arguments to be passed to racoon.
-     * @param mtpd The arguments to be passed to mtpd.
-     */
-    public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
-        stopLegacyVpn();
+    private static String findLegacyVpnGateway(LinkProperties prop) {
+        for (RouteInfo route : prop.getRoutes()) {
+            // Currently legacy VPN only works on IPv4.
+            if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
+                return route.getGateway().getHostAddress();
+            }
+        }
 
-        // TODO: move legacy definition to settings
+        throw new IllegalStateException("Unable to find suitable gateway");
+    }
+
+    /**
+     * Start legacy VPN, controlling native daemons as needed. Creates a
+     * secondary thread to perform connection work, returning quickly.
+     */
+    public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
+        if (keyStore.state() != KeyStore.State.UNLOCKED) {
+            throw new IllegalStateException("KeyStore isn't unlocked");
+        }
+
+        final String iface = egress.getInterfaceName();
+        final String gateway = findLegacyVpnGateway(egress);
+
+        // Load certificates.
+        String privateKey = "";
+        String userCert = "";
+        String caCert = "";
+        String serverCert = "";
+        if (!profile.ipsecUserCert.isEmpty()) {
+            privateKey = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
+            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecUserCert);
+            userCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+        }
+        if (!profile.ipsecCaCert.isEmpty()) {
+            byte[] value = keyStore.get(Credentials.CA_CERTIFICATE + profile.ipsecCaCert);
+            caCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+        }
+        if (!profile.ipsecServerCert.isEmpty()) {
+            byte[] value = keyStore.get(Credentials.USER_CERTIFICATE + profile.ipsecServerCert);
+            serverCert = (value == null) ? null : new String(value, Charsets.UTF_8);
+        }
+        if (privateKey == null || userCert == null || caCert == null || serverCert == null) {
+            throw new IllegalStateException("Cannot load credentials");
+        }
+
+        // Prepare arguments for racoon.
+        String[] racoon = null;
+        switch (profile.type) {
+            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
+                racoon = new String[] {
+                    iface, profile.server, "udppsk", profile.ipsecIdentifier,
+                    profile.ipsecSecret, "1701",
+                };
+                break;
+            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
+                racoon = new String[] {
+                    iface, profile.server, "udprsa", privateKey, userCert,
+                    caCert, serverCert, "1701",
+                };
+                break;
+            case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
+                racoon = new String[] {
+                    iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
+                    profile.ipsecSecret, profile.username, profile.password, "", gateway,
+                };
+                break;
+            case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
+                racoon = new String[] {
+                    iface, profile.server, "xauthrsa", privateKey, userCert,
+                    caCert, serverCert, profile.username, profile.password, "", gateway,
+                };
+                break;
+            case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
+                racoon = new String[] {
+                    iface, profile.server, "hybridrsa",
+                    caCert, serverCert, profile.username, profile.password, "", gateway,
+                };
+                break;
+        }
+
+        // Prepare arguments for mtpd.
+        String[] mtpd = null;
+        switch (profile.type) {
+            case VpnProfile.TYPE_PPTP:
+                mtpd = new String[] {
+                    iface, "pptp", profile.server, "1723",
+                    "name", profile.username, "password", profile.password,
+                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
+                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+                    (profile.mppe ? "+mppe" : "nomppe"),
+                };
+                break;
+            case VpnProfile.TYPE_L2TP_IPSEC_PSK:
+            case VpnProfile.TYPE_L2TP_IPSEC_RSA:
+                mtpd = new String[] {
+                    iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
+                    "name", profile.username, "password", profile.password,
+                    "linkname", "vpn", "refuse-eap", "nodefaultroute",
+                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+                };
+                break;
+        }
+
+        VpnConfig config = new VpnConfig();
         config.legacy = true;
+        config.user = profile.key;
+        config.interfaze = iface;
+        config.session = profile.name;
+        config.routes = profile.routes;
+        if (!profile.dnsServers.isEmpty()) {
+            config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
+        }
+        if (!profile.searchDomains.isEmpty()) {
+            config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
+        }
+
+        startLegacyVpn(config, racoon, mtpd);
+    }
+
+    private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
+        stopLegacyVpn();
 
         // Prepare for the new request. This also checks the caller.
         prepare(null, VpnConfig.LEGACY_VPN);
@@ -483,6 +605,14 @@
         return info;
     }
 
+    public VpnConfig getLegacyVpnConfig() {
+        if (mLegacyVpnRunner != null) {
+            return mLegacyVpnRunner.mConfig;
+        } else {
+            return null;
+        }
+    }
+
     /**
      * Bringing up a VPN connection takes time, and that is all this thread
      * does. Here we have plenty of time. The only thing we need to take
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 0b8ff62..29c68eb 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -1438,6 +1438,10 @@
      * Private handler for the input manager.
      */
     private final class InputManagerHandler extends Handler {
+        public InputManagerHandler() {
+            super(true /*async*/);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 32b3597..c2c0a71 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -598,6 +598,8 @@
         }
         mInjectNtpTimePending = STATE_DOWNLOADING;
 
+        // hold wake lock while task runs
+        mWakeLock.acquire();
         AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
             @Override
             public void run() {
@@ -628,14 +630,16 @@
                     delay = RETRY_INTERVAL;
                 }
 
-                mHandler.sendMessage(Message.obtain(mHandler, INJECT_NTP_TIME_FINISHED));
+                sendMessage(INJECT_NTP_TIME_FINISHED, 0, null);
 
                 if (mPeriodicTimeInjection) {
                     // send delayed message for next NTP injection
                     // since this is delayed and not urgent we do not hold a wake lock here
-                    mHandler.removeMessages(INJECT_NTP_TIME);
-                    mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
+                    mHandler.sendEmptyMessageDelayed(INJECT_NTP_TIME, delay);
                 }
+
+                // release wake lock held by task
+                mWakeLock.release();
             }
         });
     }
@@ -652,6 +656,8 @@
         }
         mDownloadXtraDataPending = STATE_DOWNLOADING;
 
+        // hold wake lock while task runs
+        mWakeLock.acquire();
         AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
             @Override
             public void run() {
@@ -664,17 +670,17 @@
                     native_inject_xtra_data(data, data.length);
                 }
 
-                mHandler.sendMessage(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA_FINISHED));
+                sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
 
                 if (data == null) {
                     // try again later
                     // since this is delayed and not urgent we do not hold a wake lock here
-                    mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
-                    mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA),
-                            RETRY_INTERVAL);
+                    mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA, RETRY_INTERVAL);
                 }
-            }
 
+                // release wake lock held by task
+                mWakeLock.release();
+            }
         });
     }
 
@@ -1475,11 +1481,17 @@
 
     private void sendMessage(int message, int arg, Object obj) {
         // hold a wake lock until this message is delivered
+        // note that this assumes the message will not be removed from the queue before
+        // it is handled (otherwise the wake lock would be leaked).
         mWakeLock.acquire();
         mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
     }
 
     private final class ProviderHandler extends Handler {
+        public ProviderHandler() {
+            super(true /*async*/);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             int message = msg.what;
diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java
new file mode 100644
index 0000000..541650e
--- /dev/null
+++ b/services/java/com/android/server/net/LockdownVpnTracker.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2012 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.net;
+
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.Preconditions;
+import com.android.server.ConnectivityService;
+import com.android.server.connectivity.Vpn;
+
+/**
+ * State tracker for lockdown mode. Watches for normal {@link NetworkInfo} to be
+ * connected and kicks off VPN connection, managing any required {@code netd}
+ * firewall rules.
+ */
+public class LockdownVpnTracker {
+    private static final String TAG = "LockdownVpnTracker";
+
+    /** Number of VPN attempts before waiting for user intervention. */
+    private static final int MAX_ERROR_COUNT = 4;
+
+    private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
+
+    private final Context mContext;
+    private final INetworkManagementService mNetService;
+    private final ConnectivityService mConnService;
+    private final Vpn mVpn;
+    private final VpnProfile mProfile;
+
+    private final Object mStateLock = new Object();
+
+    private PendingIntent mResetIntent;
+
+    private String mAcceptedEgressIface;
+    private String mAcceptedIface;
+    private String mAcceptedSourceAddr;
+
+    private int mErrorCount;
+
+    public static boolean isEnabled() {
+        return KeyStore.getInstance().contains(Credentials.LOCKDOWN_VPN);
+    }
+
+    public LockdownVpnTracker(Context context, INetworkManagementService netService,
+            ConnectivityService connService, Vpn vpn, VpnProfile profile) {
+        mContext = Preconditions.checkNotNull(context);
+        mNetService = Preconditions.checkNotNull(netService);
+        mConnService = Preconditions.checkNotNull(connService);
+        mVpn = Preconditions.checkNotNull(vpn);
+        mProfile = Preconditions.checkNotNull(profile);
+
+        final Intent intent = new Intent(ACTION_LOCKDOWN_RESET);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mResetIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+    }
+
+    private BroadcastReceiver mResetReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            reset();
+        }
+    };
+
+    /**
+     * Watch for state changes to both active egress network, kicking off a VPN
+     * connection when ready, or setting firewall rules once VPN is connected.
+     */
+    private void handleStateChangedLocked() {
+        Slog.d(TAG, "handleStateChanged()");
+
+        final NetworkInfo egressInfo = mConnService.getActiveNetworkInfoUnfiltered();
+        final LinkProperties egressProp = mConnService.getActiveLinkProperties();
+
+        final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
+        final VpnConfig vpnConfig = mVpn.getLegacyVpnConfig();
+
+        // Restart VPN when egress network disconnected or changed
+        final boolean egressDisconnected = egressInfo == null
+                || State.DISCONNECTED.equals(egressInfo.getState());
+        final boolean egressChanged = egressProp == null
+                || !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName());
+        if (egressDisconnected || egressChanged) {
+            clearSourceRules();
+            mAcceptedEgressIface = null;
+            mVpn.stopLegacyVpn();
+        }
+        if (egressDisconnected) return;
+
+        if (mErrorCount > MAX_ERROR_COUNT) {
+            showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
+
+        } else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) {
+            if (mProfile.isValidLockdownProfile()) {
+                Slog.d(TAG, "Active network connected; starting VPN");
+                showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);
+
+                mAcceptedEgressIface = egressProp.getInterfaceName();
+                mVpn.startLegacyVpn(mProfile, KeyStore.getInstance(), egressProp);
+
+            } else {
+                Slog.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
+                showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
+            }
+
+        } else if (vpnInfo.isConnected() && vpnConfig != null) {
+            final String iface = vpnConfig.interfaze;
+            final String sourceAddr = vpnConfig.addresses;
+
+            if (TextUtils.equals(iface, mAcceptedIface)
+                    && TextUtils.equals(sourceAddr, mAcceptedSourceAddr)) {
+                return;
+            }
+
+            Slog.d(TAG, "VPN connected using iface=" + iface + ", sourceAddr=" + sourceAddr);
+            showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
+
+            try {
+                clearSourceRules();
+
+                mNetService.setFirewallInterfaceRule(iface, true);
+                mNetService.setFirewallEgressSourceRule(sourceAddr, true);
+
+                mErrorCount = 0;
+                mAcceptedIface = iface;
+                mAcceptedSourceAddr = sourceAddr;
+            } catch (RemoteException e) {
+                throw new RuntimeException("Problem setting firewall rules", e);
+            }
+
+            mConnService.sendConnectedBroadcast(augmentNetworkInfo(egressInfo));
+        }
+    }
+
+    public void init() {
+        Slog.d(TAG, "init()");
+
+        mVpn.setEnableNotifications(false);
+
+        final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET);
+        mContext.registerReceiver(mResetReceiver, resetFilter, CONNECTIVITY_INTERNAL, null);
+
+        try {
+            // TODO: support non-standard port numbers
+            mNetService.setFirewallEgressDestRule(mProfile.server, 500, true);
+            mNetService.setFirewallEgressDestRule(mProfile.server, 4500, true);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Problem setting firewall rules", e);
+        }
+
+        synchronized (mStateLock) {
+            handleStateChangedLocked();
+        }
+    }
+
+    public void shutdown() {
+        Slog.d(TAG, "shutdown()");
+
+        mAcceptedEgressIface = null;
+        mErrorCount = 0;
+
+        mVpn.stopLegacyVpn();
+        try {
+            mNetService.setFirewallEgressDestRule(mProfile.server, 500, false);
+            mNetService.setFirewallEgressDestRule(mProfile.server, 4500, false);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Problem setting firewall rules", e);
+        }
+        clearSourceRules();
+        hideNotification();
+
+        mContext.unregisterReceiver(mResetReceiver);
+        mVpn.setEnableNotifications(true);
+    }
+
+    public void reset() {
+        // cycle tracker, reset error count, and trigger retry
+        shutdown();
+        init();
+        synchronized (mStateLock) {
+            handleStateChangedLocked();
+        }
+    }
+
+    private void clearSourceRules() {
+        try {
+            if (mAcceptedIface != null) {
+                mNetService.setFirewallInterfaceRule(mAcceptedIface, false);
+                mAcceptedIface = null;
+            }
+            if (mAcceptedSourceAddr != null) {
+                mNetService.setFirewallEgressSourceRule(mAcceptedSourceAddr, false);
+                mAcceptedSourceAddr = null;
+            }
+        } catch (RemoteException e) {
+            throw new RuntimeException("Problem setting firewall rules", e);
+        }
+    }
+
+    public void onNetworkInfoChanged(NetworkInfo info) {
+        synchronized (mStateLock) {
+            handleStateChangedLocked();
+        }
+    }
+
+    public void onVpnStateChanged(NetworkInfo info) {
+        if (info.getDetailedState() == DetailedState.FAILED) {
+            mErrorCount++;
+        }
+        synchronized (mStateLock) {
+            handleStateChangedLocked();
+        }
+    }
+
+    public NetworkInfo augmentNetworkInfo(NetworkInfo info) {
+        final NetworkInfo vpnInfo = mVpn.getNetworkInfo();
+        info = new NetworkInfo(info);
+        info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null);
+        return info;
+    }
+
+    private void showNotification(int titleRes, int iconRes) {
+        final Notification.Builder builder = new Notification.Builder(mContext);
+        builder.setWhen(0);
+        builder.setSmallIcon(iconRes);
+        builder.setContentTitle(mContext.getString(titleRes));
+        builder.setContentText(mContext.getString(R.string.vpn_lockdown_reset));
+        builder.setContentIntent(mResetIntent);
+        builder.setPriority(Notification.PRIORITY_LOW);
+        builder.setOngoing(true);
+        NotificationManager.from(mContext).notify(TAG, 0, builder.build());
+    }
+
+    private void hideNotification() {
+        NotificationManager.from(mContext).cancel(TAG, 0);
+    }
+}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 4252b90..b84e25a 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -64,11 +64,12 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
-import android.content.pm.UserInfo;
+import android.content.pm.PackageUserState;
 import android.content.pm.PackageParser.ActivityIntentInfo;
 import android.content.pm.PackageStats;
 import android.content.pm.ParceledListSlice;
@@ -103,7 +104,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings.Secure;
 import android.security.SystemKeyStore;
 import android.util.DisplayMetrics;
@@ -168,6 +168,7 @@
     static final boolean DEBUG_UPGRADE = false;
     private static final boolean DEBUG_INSTALL = false;
     private static final boolean DEBUG_REMOVE = false;
+    private static final boolean DEBUG_BROADCASTS = false;
     private static final boolean DEBUG_SHOW_INFO = false;
     private static final boolean DEBUG_PACKAGE_INFO = false;
     private static final boolean DEBUG_INTENT_MATCHING = false;
@@ -400,6 +401,7 @@
     // package uri's from external media onto secure containers
     // or internal storage.
     private IMediaContainerService mContainerService = null;
+    private int mContainerServiceUserId;
 
     static final int SEND_PENDING_BROADCAST = 1;
     static final int MCS_BOUND = 3;
@@ -468,8 +470,15 @@
                     " DefaultContainerService");
             Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
             Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+            mContainerServiceUserId = 0;
+            if (mPendingInstalls.size() > 0) {
+                mContainerServiceUserId = mPendingInstalls.get(0).getUser().getIdentifier();
+                if (mContainerServiceUserId == UserHandle.USER_ALL) {
+                    mContainerServiceUserId = 0;
+                }
+            }
             if (mContext.bindService(service, mDefContainerConn,
-                    Context.BIND_AUTO_CREATE)) {
+                    Context.BIND_AUTO_CREATE, mContainerServiceUserId)) {
                 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                 mBound = true;
                 return true;
@@ -546,6 +555,15 @@
                     } else if (mPendingInstalls.size() > 0) {
                         HandlerParams params = mPendingInstalls.get(0);
                         if (params != null) {
+                            // Check if we're connected to the correct service, if it's an install
+                            // request.
+                            final int installFor = params.getUser().getIdentifier();
+                            if (installFor != mContainerServiceUserId
+                                    && (installFor == UserHandle.USER_ALL
+                                            && mContainerServiceUserId != 0)) {
+                                mHandler.sendEmptyMessage(MCS_RECONNECT);
+                                return;
+                            }
                             if (params.startCopy()) {
                                 // We are done...  look for more work or to
                                 // go idle.
@@ -662,15 +680,21 @@
                     break;
                 }
                 case START_CLEANING_PACKAGE: {
-                    String packageName = (String)msg.obj;
                     Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+                    PackageCleanItem item = new PackageCleanItem((String)msg.obj,
+                            msg.arg2 != 0);
                     synchronized (mPackages) {
-                        if (!mSettings.mPackagesToBeCleaned.contains(packageName)) {
-                            mSettings.mPackagesToBeCleaned.add(packageName);
+                        if (msg.arg1 == UserHandle.USER_ALL) {
+                            int[] users = sUserManager.getUserIds();
+                            for (int user : users) {
+                                mSettings.addPackageToCleanLPw(user, item);
+                            }
+                        } else {
+                            mSettings.addPackageToCleanLPw(msg.arg1, item);
                         }
                     }
                     Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                    startCleaningPackages();
+                    startCleaningPackages(-1);
                 } break;
                 case POST_INSTALL: {
                     if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
@@ -692,15 +716,14 @@
                             }
                             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                                     res.pkg.applicationInfo.packageName,
-                                    extras, null, null, UserHandle.USER_ALL);
+                                    extras, null, null, res.users);
                             if (update) {
                                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                                         res.pkg.applicationInfo.packageName,
-                                        extras, null, null, UserHandle.USER_ALL);
+                                        extras, null, null, res.users);
                                 sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                                         null, null,
-                                        res.pkg.applicationInfo.packageName, null,
-                                        UserHandle.USER_ALL);
+                                        res.pkg.applicationInfo.packageName, null, res.users);
                             }
                             if (res.removedInfo.args != null) {
                                 // Remove the replaced package's older resources safely now
@@ -944,8 +967,8 @@
             mUserAppDataDir = new File(dataDir, "user");
             mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
 
-            sUserManager = UserManagerService.getInstance(context);
-            sUserManager.setInstaller(this, mInstaller);
+            sUserManager = new UserManagerService(context, this,
+                    mInstallLock, mPackages);
 
             readPermissions();
 
@@ -1139,8 +1162,7 @@
                         String msg = "System package " + ps.name
                                 + " no longer exists; wiping its data";
                         reportSettingsProblem(Log.WARN, msg);
-                        mInstaller.remove(ps.name, 0);
-                        sUserManager.removePackageForAllUsers(ps.name);
+                        removeDataDirsLI(ps.name);
                     } else {
                         final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
                         if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
@@ -1189,9 +1211,7 @@
                     if (deletedPkg == null) {
                         msg = "Updated system package " + deletedAppName
                                 + " no longer exists; wiping its data";
-
-                        mInstaller.remove(deletedAppName, 0);
-                        sUserManager.removePackageForAllUsers(deletedAppName);
+                        removeDataDirsLI(deletedAppName);
                     } else {
                         msg = "Updated system app + " + deletedAppName
                                 + " no longer present; removing system privileges for "
@@ -1326,13 +1346,7 @@
 
     void cleanupInstallFailedPackage(PackageSetting ps) {
         Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
-        int retCode = mInstaller.remove(ps.name, 0);
-        if (retCode < 0) {
-            Slog.w(TAG, "Couldn't remove app data directory for package: "
-                       + ps.name + ", retcode=" + retCode);
-        } else {
-            sUserManager.removePackageForAllUsers(ps.name);
-        }
+        removeDataDirsLI(ps.name);
         if (ps.codePath != null) {
             if (!ps.codePath.delete()) {
                 Slog.w(TAG, "Unable to remove old code file: " + ps.codePath);
@@ -1562,19 +1576,17 @@
     PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
         PackageInfo pi;
-        if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
-            // The package has been uninstalled but has retained data and resources.
-            pi = PackageParser.generatePackageInfo(p, null, flags, 0, 0, null, false, 0, userId);
-        } else {
-            final PackageSetting ps = (PackageSetting) p.mExtras;
-            if (ps == null) {
-                return null;
-            }
-            final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
-            pi = PackageParser.generatePackageInfo(p, gp.gids, flags,
-                    ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
-                    ps.getStopped(userId), ps.getEnabled(userId), userId);
-            pi.applicationInfo.enabledSetting = ps.getEnabled(userId);
+        final PackageSetting ps = (PackageSetting) p.mExtras;
+        if (ps == null) {
+            return null;
+        }
+        final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+        final PackageUserState state = ps.readUserState(userId);
+        pi = PackageParser.generatePackageInfo(p, gp.gids, flags,
+                ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
+                state, userId);
+        if (pi != null) {
+            pi.applicationInfo.enabledSetting = state.enabled;
             pi.applicationInfo.enabled =
                     pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_DEFAULT
                     || pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
@@ -1741,14 +1753,15 @@
         PackageSetting ps = mSettings.mPackages.get(packageName);
         if (ps != null) {
             if (ps.pkg == null) {
-                PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName, flags, userId);
+                PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName,
+                        flags, userId);
                 if (pInfo != null) {
                     return pInfo.applicationInfo;
                 }
                 return null;
             }
-            return PackageParser.generateApplicationInfo(ps.pkg, flags, ps.getStopped(userId),
-                    ps.getEnabled(userId), userId);
+            return PackageParser.generateApplicationInfo(ps.pkg, flags,
+                    ps.readUserState(userId), userId);
         }
         return null;
     }
@@ -1758,20 +1771,23 @@
         if (!sUserManager.exists(userId)) return null;
         PackageSetting ps = mSettings.mPackages.get(packageName);
         if (ps != null) {
-            PackageParser.Package pkg = new PackageParser.Package(packageName);
-            if (ps.pkg == null) {
-                ps.pkg = new PackageParser.Package(packageName);
-                ps.pkg.applicationInfo.packageName = packageName;
-                ps.pkg.applicationInfo.flags = ps.pkgFlags;
-                ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
-                ps.pkg.applicationInfo.sourceDir = ps.codePathString;
-                ps.pkg.applicationInfo.dataDir =
+            PackageParser.Package pkg = ps.pkg;
+            if (pkg == null) {
+                if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
+                    return null;
+                }
+                pkg = new PackageParser.Package(packageName);
+                pkg.applicationInfo.packageName = packageName;
+                pkg.applicationInfo.flags = ps.pkgFlags;
+                pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
+                pkg.applicationInfo.sourceDir = ps.codePathString;
+                pkg.applicationInfo.dataDir =
                         getDataPathForPackage(ps.pkg.packageName, 0).getPath();
-                ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
+                pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
             }
-            // ps.pkg.mSetEnabled = ps.getEnabled(userId);
-            // ps.pkg.mSetStopped = ps.getStopped(userId);
-            return generatePackageInfo(ps.pkg, flags, userId);
+            // pkg.mSetEnabled = ps.getEnabled(userId);
+            // pkg.mSetStopped = ps.getStopped(userId);
+            return generatePackageInfo(pkg, flags, userId);
         }
         return null;
     }
@@ -1789,13 +1805,12 @@
                 PackageSetting ps = mSettings.mPackages.get(packageName);
                 if (ps == null) return null;
                 // Note: isEnabledLP() does not apply here - always return info
-                return PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
-                        ps.getEnabled(userId));
+                return PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId));
             }
             if ("android".equals(packageName)||"system".equals(packageName)) {
                 return mAndroidApplication;
             }
-            if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+            if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
                 return generateApplicationInfoFromSettingsLPw(packageName, flags, userId);
             }
         }
@@ -1811,9 +1826,11 @@
             public void run() {
                 mHandler.removeCallbacks(this);
                 int retCode = -1;
-                retCode = mInstaller.freeCache(freeStorageSize);
-                if (retCode < 0) {
-                    Slog.w(TAG, "Couldn't clear application caches");
+                synchronized (mInstallLock) {
+                    retCode = mInstaller.freeCache(freeStorageSize);
+                    if (retCode < 0) {
+                        Slog.w(TAG, "Couldn't clear application caches");
+                    }
                 }
                 if (observer != null) {
                     try {
@@ -1834,9 +1851,11 @@
             public void run() {
                 mHandler.removeCallbacks(this);
                 int retCode = -1;
-                retCode = mInstaller.freeCache(freeStorageSize);
-                if (retCode < 0) {
-                    Slog.w(TAG, "Couldn't clear application caches");
+                synchronized (mInstallLock) {
+                    retCode = mInstaller.freeCache(freeStorageSize);
+                    if (retCode < 0) {
+                        Slog.w(TAG, "Couldn't clear application caches");
+                    }
                 }
                 if(pi != null) {
                     try {
@@ -1862,8 +1881,8 @@
             if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
-                        ps.getEnabled(userId), userId);
+                return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
+                        userId);
             }
             if (mResolveComponentName.equals(component)) {
                 return mResolveActivity;
@@ -1882,8 +1901,8 @@
             if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
-                        ps.getEnabled(userId), userId);
+                return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
+                        userId);
             }
         }
         return null;
@@ -1899,8 +1918,8 @@
             if (s != null && mSettings.isEnabledLPr(s.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                return PackageParser.generateServiceInfo(s, flags, ps.getStopped(userId),
-                        ps.getEnabled(userId), userId);
+                return PackageParser.generateServiceInfo(s, flags, ps.readUserState(userId),
+                        userId);
             }
         }
         return null;
@@ -1916,8 +1935,8 @@
             if (p != null && mSettings.isEnabledLPr(p.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                return PackageParser.generateProviderInfo(p, flags, ps.getStopped(userId),
-                        ps.getEnabled(userId), userId);
+                return PackageParser.generateProviderInfo(p, flags, ps.readUserState(userId),
+                        userId);
             }
         }
         return null;
@@ -2870,8 +2889,8 @@
                 } else {
                     final PackageParser.Package p = mPackages.get(packageName);
                     if (p != null && ps != null) {
-                        ai = PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
-                                ps.getEnabled(userId), userId);
+                        ai = PackageParser.generateApplicationInfo(p, flags,
+                                ps.readUserState(userId), userId);
                     }
                 }
 
@@ -2901,10 +2920,13 @@
                         && (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
                         && (!mSafeMode || isSystemApp(p))) {
                     PackageSetting ps = mSettings.mPackages.get(p.packageName);
-                    finalList.add(PackageParser.generateApplicationInfo(p, flags,
-                            ps != null ? ps.getStopped(userId) : false,
-                            ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                            userId));
+                    if (ps != null) {
+                        ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
+                                ps.readUserState(userId), userId);
+                        if (ai != null) {
+                            finalList.add(ai);
+                        }
+                    }
                 }
             }
         }
@@ -2921,14 +2943,12 @@
             PackageSetting ps = provider != null
                     ? mSettings.mPackages.get(provider.owner.packageName)
                     : null;
-            return provider != null
+            return ps != null
                     && mSettings.isEnabledLPr(provider.info, flags, userId)
                     && (!mSafeMode || (provider.info.applicationInfo.flags
                             &ApplicationInfo.FLAG_SYSTEM) != 0)
                     ? PackageParser.generateProviderInfo(provider, flags,
-                            ps != null ? ps.getStopped(userId) : false,
-                            ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                            userId)
+                            ps.readUserState(userId), userId)
                     : null;
         }
     }
@@ -2948,14 +2968,15 @@
                 PackageParser.Provider p = entry.getValue();
                 PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
 
-                if (p.syncable
+                if (ps != null && p.syncable
                         && (!mSafeMode || (p.info.applicationInfo.flags
                                 &ApplicationInfo.FLAG_SYSTEM) != 0)) {
-                    outNames.add(entry.getKey());
-                    outInfo.add(PackageParser.generateProviderInfo(p, 0,
-                            ps != null ? ps.getStopped(userId) : false,
-                            ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                            userId));
+                    ProviderInfo info = PackageParser.generateProviderInfo(p, 0,
+                            ps.readUserState(userId), userId);
+                    if (info != null) {
+                        outNames.add(entry.getKey());
+                        outInfo.add(info);
+                    }
                 }
             }
         }
@@ -2973,7 +2994,7 @@
             while (i.hasNext()) {
                 final PackageParser.Provider p = i.next();
                 PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
-                if (p.info.authority != null
+                if (ps != null && p.info.authority != null
                         && (processName == null
                                 || (p.info.processName.equals(processName)
                                         && UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
@@ -2983,10 +3004,11 @@
                     if (finalList == null) {
                         finalList = new ArrayList<ProviderInfo>(3);
                     }
-                    finalList.add(PackageParser.generateProviderInfo(p, flags,
-                            ps != null ? ps.getStopped(userId) : false,
-                            ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                            userId));
+                    ProviderInfo info = PackageParser.generateProviderInfo(p, flags,
+                            ps.readUserState(userId), userId);
+                    if (info != null) {
+                        finalList.add(info);
+                    }
                 }
             }
         }
@@ -3019,8 +3041,11 @@
                 final PackageParser.Instrumentation p = i.next();
                 if (targetPackage == null
                         || targetPackage.equals(p.info.targetPackage)) {
-                    finalList.add(PackageParser.generateInstrumentationInfo(p,
-                            flags));
+                    InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p,
+                            flags);
+                    if (ii != null) {
+                        finalList.add(ii);
+                    }
                 }
             }
         }
@@ -3047,7 +3072,7 @@
                 continue;
             }
             PackageParser.Package pkg = scanPackageLI(file,
-                    flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime);
+                    flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
             // Don't mess around with apps in system partition.
             if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                     mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
@@ -3115,7 +3140,7 @@
      *  Returns null in case of errors and the error code is stored in mLastScanError
      */
     private PackageParser.Package scanPackageLI(File scanFile,
-            int parseFlags, int scanMode, long currentTime) {
+            int parseFlags, int scanMode, long currentTime, UserHandle user) {
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         String scanPath = scanFile.getPath();
         parseFlags |= mDefParseFlags;
@@ -3181,7 +3206,7 @@
 
                     InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
                             ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);
-                    synchronized (mInstaller) {
+                    synchronized (mInstallLock) {
                         args.cleanUpResourcesLI();
                     }
                     synchronized (mPackages) {
@@ -3215,7 +3240,7 @@
              */
             if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
                     != PackageManager.SIGNATURE_MATCH) {
-                deletePackageLI(pkg.packageName, true, 0, null, false);
+                deletePackageLI(pkg.packageName, null, true, 0, null, false);
                 ps = null;
             } else {
                 /*
@@ -3237,7 +3262,7 @@
                             + " better than installed " + ps.versionCode);
                     InstallArgs args = createInstallArgs(packageFlagsToInstallFlags(ps),
                             ps.codePathString, ps.resourcePathString, ps.nativeLibraryPathString);
-                    synchronized (mInstaller) {
+                    synchronized (mInstallLock) {
                         args.cleanUpResourcesLI();
                     }
                 }
@@ -3268,7 +3293,7 @@
         setApplicationInfoPaths(pkg, codePath, resPath);
         // Note that we invoke the following method only if we are about to unpack an application
         PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
-                | SCAN_UPDATE_SIGNATURE, currentTime);
+                | SCAN_UPDATE_SIGNATURE, currentTime, user);
 
         /*
          * If the system app should be overridden by a previously installed
@@ -3471,8 +3496,38 @@
         }
     }
 
+    private int createDataDirsLI(String packageName, int uid) {
+        int[] users = sUserManager.getUserIds();
+        int res = mInstaller.install(packageName, uid, uid);
+        if (res < 0) {
+            return res;
+        }
+        for (int user : users) {
+            if (user != 0) {
+                res = mInstaller.createUserData(packageName,
+                        UserHandle.getUid(user, uid), user);
+                if (res < 0) {
+                    return res;
+                }
+            }
+        }
+        return res;
+    }
+
+    private int removeDataDirsLI(String packageName) {
+        int[] users = sUserManager.getUserIds();
+        int res = 0;
+        for (int user : users) {
+            int resInner = mInstaller.remove(packageName, user);
+            if (resInner < 0) {
+                res = resInner;
+            }
+        }
+        return res;
+    }
+
     private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
-            int parseFlags, int scanMode, long currentTime) {
+            int parseFlags, int scanMode, long currentTime, UserHandle user) {
         File scanFile = new File(pkg.mScanPath);
         if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
                 pkg.applicationInfo.publicSourceDir == null) {
@@ -3664,7 +3719,7 @@
             // the PkgSetting exists already and doesn't have to be created.
             pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
                     destResourceFile, pkg.applicationInfo.nativeLibraryDir,
-                    pkg.applicationInfo.flags, true, false);
+                    pkg.applicationInfo.flags, user, false);
             if (pkgSetting == null) {
                 Slog.w(TAG, "Creating application package " + pkg.packageName + " failed");
                 mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -3823,11 +3878,9 @@
                             || (scanMode&SCAN_BOOTING) != 0)) {
                         // If this is a system app, we can at least delete its
                         // current data so the application will still work.
-                        int ret = mInstaller.remove(pkgName, 0);
+                        int ret = removeDataDirsLI(pkgName);
                         if (ret >= 0) {
                             // TODO: Kill the processes first
-                            // Remove the data directories for all users
-                            sUserManager.removePackageForAllUsers(pkgName);
                             // Old data gone!
                             String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
                                     ? "System package " : "Third party package ";
@@ -3839,8 +3892,7 @@
                             recovered = true;
 
                             // And now re-install the app.
-                            ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
-                                    pkg.applicationInfo.uid);
+                            ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
                             if (ret == -1) {
                                 // Ack should not happen!
                                 msg = prefix + pkg.packageName
@@ -3849,9 +3901,6 @@
                                 mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                                 return null;
                             }
-                            // Create data directories for all users
-                            sUserManager.installPackageForAllUsers(pkgName,
-                                    pkg.applicationInfo.uid);
                         }
                         if (!recovered) {
                             mHasSystemUidErrors = true;
@@ -3889,15 +3938,12 @@
                         Log.v(TAG, "Want this data dir: " + dataPath);
                 }
                 //invoke installer to do the actual installation
-                int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
-                        pkg.applicationInfo.uid);
+                int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
                 if (ret < 0) {
                     // Error from installer
                     mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                     return null;
                 }
-                // Create data directories for all users
-                sUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
 
                 if (dataPath.exists()) {
                     pkg.applicationInfo.dataDir = dataPath.getPath();
@@ -4024,7 +4070,9 @@
             // Add the new setting to mPackages
             mPackages.put(pkg.applicationInfo.packageName, pkg);
             // Make sure we don't accidentally delete its data.
-            mSettings.mPackagesToBeCleaned.remove(pkgName);
+            for (int i=0; i<mSettings.mPackagesToBeCleaned.size(); i++) {
+                mSettings.mPackagesToBeCleaned.valueAt(i).remove(pkgName);
+            }
             
             // Take care of first install / last update times.
             if (currentTime != 0) {
@@ -4840,7 +4888,8 @@
                     // System apps are never considered stopped for purposes of
                     // filtering, because there may be no way for the user to
                     // actually re-launch them.
-                    return ps.getStopped(userId) && (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0;
+                    return (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0
+                            && ps.getStopped(userId);
                 }
             }
             return false;
@@ -4863,12 +4912,17 @@
                     &ApplicationInfo.FLAG_SYSTEM) == 0) {
                 return null;
             }
-            final ResolveInfo res = new ResolveInfo();
             PackageSetting ps = (PackageSetting) activity.owner.mExtras;
-            res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags,
-                    ps != null ? ps.getStopped(userId) : false,
-                    ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                    userId);
+            if (ps == null) {
+                return null;
+            }
+            ActivityInfo ai = PackageParser.generateActivityInfo(activity, mFlags,
+                    ps.readUserState(userId), userId);
+            if (ai == null) {
+                return null;
+            }
+            final ResolveInfo res = new ResolveInfo();
+            res.activityInfo = ai;
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = info;
             }
@@ -5031,8 +5085,8 @@
                     // System apps are never considered stopped for purposes of
                     // filtering, because there may be no way for the user to
                     // actually re-launch them.
-                    return ps.getStopped(userId)
-                            && (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0;
+                    return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
+                            && ps.getStopped(userId);
                 }
             }
             return false;
@@ -5056,12 +5110,17 @@
                     &ApplicationInfo.FLAG_SYSTEM) == 0) {
                 return null;
             }
-            final ResolveInfo res = new ResolveInfo();
             PackageSetting ps = (PackageSetting) service.owner.mExtras;
-            res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags,
-                    ps != null ? ps.getStopped(userId) : false,
-                    ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
-                    userId);
+            if (ps == null) {
+                return null;
+            }
+            ServiceInfo si = PackageParser.generateServiceInfo(service, mFlags,
+                    ps.readUserState(userId), userId);
+            if (si == null) {
+                return null;
+            }
+            final ResolveInfo res = new ResolveInfo();
+            res.serviceInfo = si;
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = filter;
             }
@@ -5152,13 +5211,14 @@
     };
 
     static final void sendPackageBroadcast(String action, String pkg,
-            Bundle extras, String targetPkg, IIntentReceiver finishedReceiver, int userId) {
+            Bundle extras, String targetPkg, IIntentReceiver finishedReceiver,
+            int[] userIds) {
         IActivityManager am = ActivityManagerNative.getDefault();
         if (am != null) {
             try {
-                int[] userIds = userId == UserHandle.USER_ALL
-                        ? sUserManager.getUserIds() 
-                        : new int[] {userId};
+                if (userIds == null) {
+                    userIds = sUserManager.getUserIds();
+                }
                 for (int id : userIds) {
                     final Intent intent = new Intent(action,
                             pkg != null ? Uri.fromParts("package", pkg, null) : null);
@@ -5170,11 +5230,18 @@
                     }
                     // Modify the UID when posting to other users
                     int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
-                    if (uid > 0 && id > 0) {
+                    if (uid > 0 && UserHandle.getUserId(uid) != id) {
                         uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
                         intent.putExtra(Intent.EXTRA_UID, uid);
                     }
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                    if (DEBUG_BROADCASTS) {
+                        RuntimeException here = new RuntimeException("here");
+                        here.fillInStackTrace();
+                        Slog.d(TAG, "Sending to user " + id + ": "
+                                + intent.toShortString(false, true, false, false)
+                                + " " + intent.getExtras(), here);
+                    }
                     am.broadcastIntent(null, intent, null, finishedReceiver,
                             0, null, null, null, finishedReceiver != null, false, id);
                 }
@@ -5192,8 +5259,9 @@
         return mMediaMounted || Environment.isExternalStorageEmulated();
     }
 
-    public String nextPackageToClean(String lastPackage) {
+    public PackageCleanItem nextPackageToClean(PackageCleanItem lastPackage) {
         // writer
+        final int userId = UserHandle.getCallingUserId();
         synchronized (mPackages) {
             if (!isExternalMediaAvailable()) {
                 // If the external storage is no longer mounted at this point,
@@ -5201,34 +5269,65 @@
                 // packages files and can not delete any more.  Bail.
                 return null;
             }
-            if (lastPackage != null) {
-                mSettings.mPackagesToBeCleaned.remove(lastPackage);
+            ArrayList<PackageCleanItem> pkgs = mSettings.mPackagesToBeCleaned.get(userId);
+            if (pkgs != null) {
+                if (lastPackage != null) {
+                    pkgs.remove(lastPackage);
+                }
+                if (pkgs.size() > 0) {
+                    return pkgs.get(0);
+                }
             }
-            return mSettings.mPackagesToBeCleaned.size() > 0
-                    ? mSettings.mPackagesToBeCleaned.get(0) : null;
         }
+        // Move on to the next user to clean.
+        long ident = Binder.clearCallingIdentity();
+        try {
+            startCleaningPackages(userId);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return null;
     }
 
-    void schedulePackageCleaning(String packageName) {
-        mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE, packageName));
+    void schedulePackageCleaning(String packageName, int userId, boolean andCode) {
+        if (false) {
+            RuntimeException here = new RuntimeException("here");
+            here.fillInStackTrace();
+            Slog.d(TAG, "Schedule cleaning " + packageName + " user=" + userId
+                    + " andCode=" + andCode, here);
+        }
+        mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE,
+                userId, andCode ? 1 : 0, packageName));
     }
     
-    void startCleaningPackages() {
+    void startCleaningPackages(int lastUser) {
         // reader
+        int nextUser = -1;
         synchronized (mPackages) {
             if (!isExternalMediaAvailable()) {
                 return;
             }
-            if (mSettings.mPackagesToBeCleaned.size() <= 0) {
+            final int N = mSettings.mPackagesToBeCleaned.size();
+            if (N <= 0) {
                 return;
             }
+            for (int i=0; i<N; i++) {
+                int user = mSettings.mPackagesToBeCleaned.keyAt(i);
+                if (user > lastUser) {
+                    nextUser = user;
+                    break;
+                }
+            }
+            if (nextUser < 0) {
+                nextUser = mSettings.mPackagesToBeCleaned.keyAt(0);
+            }
         }
         Intent intent = new Intent(PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE);
         intent.setComponent(DEFAULT_CONTAINER_COMPONENT);
         IActivityManager am = ActivityManagerNative.getDefault();
         if (am != null) {
             try {
-                am.startService(null, intent, null);
+                am.startService(null, intent, null, nextUser);
             } catch (RemoteException e) {
             }
         }
@@ -5291,7 +5390,7 @@
                                 PackageParser.PARSE_CHATTY |
                                 PackageParser.PARSE_MUST_BE_APK,
                                 SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
-                                System.currentTimeMillis());
+                                System.currentTimeMillis(), null);
                         if (p != null) {
                             /*
                              * TODO this seems dangerous as the package may have
@@ -5320,13 +5419,13 @@
                 extras.putInt(Intent.EXTRA_UID, removedUid);
                 extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
-                        extras, null, null, UserHandle.USER_ALL);
+                        extras, null, null, null);
             }
             if (addedPackage != null) {
                 Bundle extras = new Bundle(1);
                 extras.putInt(Intent.EXTRA_UID, addedUid);
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
-                        extras, null, null, UserHandle.USER_ALL);
+                        extras, null, null, null);
             }
         }
 
@@ -5365,6 +5464,12 @@
                 null);
 
         final int uid = Binder.getCallingUid();
+        UserHandle user;
+        if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) {
+            user = UserHandle.ALL;
+        } else {
+            user = Process.myUserHandle();
+        }
 
         final int filteredFlags;
 
@@ -5379,10 +5484,51 @@
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
         msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
-                verificationParams, encryptionParams);
+                verificationParams, encryptionParams, user);
         mHandler.sendMessage(msg);
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public int installExistingPackage(String packageName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
+                null);
+        PackageSetting pkgSetting;
+        final int uid = Binder.getCallingUid();
+        final int userId = UserHandle.getUserId(uid);
+
+        long callingId = Binder.clearCallingIdentity();
+        try {
+            boolean sendAdded = false;
+            Bundle extras = new Bundle(1);
+
+            // writer
+            synchronized (mPackages) {
+                pkgSetting = mSettings.mPackages.get(packageName);
+                if (pkgSetting == null) {
+                    return PackageManager.INSTALL_FAILED_INVALID_URI;
+                }
+                if (!pkgSetting.getInstalled(userId)) {
+                    pkgSetting.setInstalled(true, userId);
+                    mSettings.writePackageRestrictionsLPr(userId);
+                    extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
+                    sendAdded = true;
+                }
+            }
+
+            if (sendAdded) {
+                sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+                        packageName, extras, null, null, new int[] {userId});
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+
+        return PackageManager.INSTALL_SUCCEEDED;
+    }
+    
     @Override
     public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
         final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
@@ -5721,6 +5867,17 @@
          */
         private int mRetries = 0;
 
+        /** User handle for the user requesting the information or installation. */
+        private final UserHandle mUser;
+
+        HandlerParams(UserHandle user) {
+            mUser = user;
+        }
+
+        UserHandle getUser() {
+            return mUser;
+        }
+
         final boolean startCopy() {
             boolean res;
             try {
@@ -5762,6 +5919,7 @@
         private final IPackageStatsObserver mObserver;
 
         public MeasureParams(PackageStats stats, IPackageStatsObserver observer) {
+            super(new UserHandle(stats.userHandle));
             mObserver = observer;
             mStats = stats;
         }
@@ -5844,7 +6002,8 @@
         InstallParams(Uri packageURI,
                 IPackageInstallObserver observer, int flags,
                 String installerPackageName, VerificationParams verificationParams,
-                ContainerEncryptionParams encryptionParams) {
+                ContainerEncryptionParams encryptionParams, UserHandle user) {
+            super(user);
             this.mPackageURI = packageURI;
             this.flags = flags;
             this.observer = observer;
@@ -5869,6 +6028,16 @@
                 PackageParser.Package pkg = mPackages.get(packageName);
                 if (pkg != null) {
                     if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+                        // Check for downgrading.
+                        if ((flags & PackageManager.INSTALL_ALLOW_DOWNGRADE) == 0) {
+                            if (pkgLite.versionCode < pkg.mVersionCode) {
+                                Slog.w(TAG, "Can't install update of " + packageName
+                                        + " update version " + pkgLite.versionCode
+                                        + " is older than installed version "
+                                        + pkg.mVersionCode);
+                                return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE;
+                            }
+                        }
                         // Check for updated system application.
                         if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                             if (onSd) {
@@ -5995,6 +6164,8 @@
                     ret = PackageManager.INSTALL_FAILED_INVALID_URI;
                 } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
                     ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
+                } else if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
+                    ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
                 } else {
                     // Override with defaults if needed.
                     loc = installLocationPolicy(pkgLite, flags);
@@ -6030,8 +6201,9 @@
                     verification.setDataAndType(getPackageUri(), PACKAGE_MIME_TYPE);
                     verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
-                    final List<ResolveInfo> receivers = queryIntentReceivers(verification, null,
-                            PackageManager.GET_DISABLED_COMPONENTS, 0 /* TODO: Which userId? */);
+                    final List<ResolveInfo> receivers = queryIntentReceivers(verification,
+                            PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS,
+                            0 /* TODO: Which userId? */);
 
                     if (DEBUG_VERIFY) {
                         Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
@@ -6185,7 +6357,8 @@
         int mRet;
 
         MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags,
-                String packageName, String dataDir, int uid) {
+                String packageName, String dataDir, int uid, UserHandle user) {
+            super(user);
             this.srcArgs = srcArgs;
             this.observer = observer;
             this.flags = flags;
@@ -6338,14 +6511,17 @@
         final Uri packageURI;
         final String installerPackageName;
         final ManifestDigest manifestDigest;
+        final UserHandle user;
 
         InstallArgs(Uri packageURI, IPackageInstallObserver observer, int flags,
-                String installerPackageName, ManifestDigest manifestDigest) {
+                String installerPackageName, ManifestDigest manifestDigest,
+                UserHandle user) {
             this.packageURI = packageURI;
             this.flags = flags;
             this.observer = observer;
             this.installerPackageName = installerPackageName;
             this.manifestDigest = manifestDigest;
+            this.user = user;
         }
 
         abstract void createCopyFile();
@@ -6396,11 +6572,12 @@
 
         FileInstallArgs(InstallParams params) {
             super(params.getPackageUri(), params.observer, params.flags,
-                    params.installerPackageName, params.getManifestDigest());
+                    params.installerPackageName, params.getManifestDigest(),
+                    params.getUser());
         }
 
         FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath) {
-            super(null, null, 0, null, null);
+            super(null, null, 0, null, null, null);
             File codeFile = new File(fullCodePath);
             installDir = codeFile.getParentFile();
             codeFileName = fullCodePath;
@@ -6409,7 +6586,7 @@
         }
 
         FileInstallArgs(Uri packageURI, String pkgName, String dataDir) {
-            super(packageURI, null, 0, null, null);
+            super(packageURI, null, 0, null, null, null);
             installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
             String apkName = getNextCodePath(null, pkgName, ".apk");
             codeFileName = new File(installDir, apkName + ".apk").getPath();
@@ -6683,13 +6860,15 @@
 
         AsecInstallArgs(InstallParams params) {
             super(params.getPackageUri(), params.observer, params.flags,
-                    params.installerPackageName, params.getManifestDigest());
+                    params.installerPackageName, params.getManifestDigest(),
+                    params.getUser());
         }
 
         AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
                 boolean isExternal, boolean isForwardLocked) {
             super(null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+                    null, null, null);
             // Extract cid from fullCodePath
             int eidx = fullCodePath.lastIndexOf("/");
             String subStr1 = fullCodePath.substring(0, eidx);
@@ -6700,14 +6879,16 @@
 
         AsecInstallArgs(String cid, boolean isForwardLocked) {
             super(null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+                    null, null, null);
             this.cid = cid;
             setCachePath(PackageHelper.getSdDir(cid));
         }
 
         AsecInstallArgs(Uri packageURI, String cid, boolean isExternal, boolean isForwardLocked) {
             super(packageURI, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), null, null);
+                    | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
+                    null, null, null);
             this.cid = cid;
         }
 
@@ -7031,6 +7212,7 @@
     class PackageInstalledInfo {
         String name;
         int uid;
+        int[] users;
         PackageParser.Package pkg;
         int returnCode;
         PackageRemovedInfo removedInfo;
@@ -7040,14 +7222,12 @@
      * Install a non-existing package.
      */
     private void installNewPackageLI(PackageParser.Package pkg,
-            int parseFlags,
-            int scanMode,
+            int parseFlags, int scanMode, UserHandle user,
             String installerPackageName, PackageInstalledInfo res) {
         // Remember this for later, in case we need to rollback this install
         String pkgName = pkg.packageName;
 
         boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
-        res.name = pkgName;
         synchronized(mPackages) {
             if (mSettings.mRenamedPackages.containsKey(pkgName)) {
                 // A package with the same name is already installed, though
@@ -7070,7 +7250,7 @@
         }
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
-                System.currentTimeMillis());
+                System.currentTimeMillis(), user);
         if (newPackage == null) {
             Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
             if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -7087,17 +7267,15 @@
                 // delete the package data and cache directories that it created in
                 // scanPackageLocked, unless those directories existed before we even tried to
                 // install.
-                deletePackageLI(
-                        pkgName, false,
-                        dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,
+                deletePackageLI(pkgName, UserHandle.ALL, false,
+                        dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
                                 res.removedInfo, true);
             }
         }
     }
 
     private void replacePackageLI(PackageParser.Package pkg,
-            int parseFlags,
-            int scanMode,
+            int parseFlags, int scanMode, UserHandle user,
             String installerPackageName, PackageInstalledInfo res) {
 
         PackageParser.Package oldPackage;
@@ -7114,15 +7292,16 @@
         }
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
-            replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
+            replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
+                    user, installerPackageName, res);
         } else {
-            replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, installerPackageName, res);
+            replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode,
+                    user, installerPackageName, res);
         }
     }
 
     private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
-            PackageParser.Package pkg,
-            int parseFlags, int scanMode,
+            PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
             String installerPackageName, PackageInstalledInfo res) {
         PackageParser.Package newPackage = null;
         String pkgName = deletedPackage.packageName;
@@ -7137,7 +7316,7 @@
         }
 
         // First delete the existing package while retaining the data directory
-        if (!deletePackageLI(pkgName, true, PackageManager.DONT_DELETE_DATA,
+        if (!deletePackageLI(pkgName, null, true, PackageManager.DELETE_KEEP_DATA,
                 res.removedInfo, true)) {
             // If the existing package wasn't successfully deleted
             res.returnCode = PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
@@ -7146,7 +7325,7 @@
             // Successfully deleted the old package. Now proceed with re-installation
             mLastScanError = PackageManager.INSTALL_SUCCEEDED;
             newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME,
-                    System.currentTimeMillis());
+                    System.currentTimeMillis(), user);
             if (newPackage == null) {
                 Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
                 if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -7167,8 +7346,8 @@
             // install.
             if(updatedSettings) {
                 deletePackageLI(
-                        pkgName, true,
-                        PackageManager.DONT_DELETE_DATA,
+                        pkgName, null, true,
+                        PackageManager.DELETE_KEEP_DATA,
                                 res.removedInfo, true);
             }
             // Since we failed to install the new package we need to restore the old
@@ -7183,7 +7362,7 @@
                 int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE
                         | SCAN_UPDATE_TIME;
                 if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode,
-                        origUpdateTime) == null) {
+                        origUpdateTime, user) == null) {
                     Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade");
                     return;
                 }
@@ -7201,8 +7380,7 @@
     }
 
     private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
-            PackageParser.Package pkg,
-            int parseFlags, int scanMode,
+            PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user,
             String installerPackageName, PackageInstalledInfo res) {
         PackageParser.Package newPackage = null;
         boolean updatedSettings = false;
@@ -7251,7 +7429,7 @@
         // Successfully disabled the old package. Now proceed with re-installation
         mLastScanError = PackageManager.INSTALL_SUCCEEDED;
         pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
-        newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0);
+        newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user);
         if (newPackage == null) {
             Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
             if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
@@ -7274,7 +7452,7 @@
                 removePackageLI(newPackage, true);
             }
             // Add back the old system package
-            scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0);
+            scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user);
             // Restore the old system information in Settings
             synchronized(mPackages) {
                 if (updatedSettings) {
@@ -7333,6 +7511,10 @@
                     UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
                             ? UPDATE_PERMISSIONS_ALL : 0));
             res.name = pkgName;
+            PackageSetting ps = mSettings.mPackages.get(pkgName);
+            if (ps != null) {
+                res.users = ps.getInstalledUsers(sUserManager.getUserIds());
+            }
             res.uid = newPackage.applicationInfo.uid;
             res.pkg = newPackage;
             mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
@@ -7448,12 +7630,18 @@
         setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
         pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
         if (replace) {
-            replacePackageLI(pkg, parseFlags, scanMode,
+            replacePackageLI(pkg, parseFlags, scanMode, args.user,
                     installerPackageName, res);
         } else {
-            installNewPackageLI(pkg, parseFlags, scanMode,
+            installNewPackageLI(pkg, parseFlags, scanMode, args.user,
                     installerPackageName,res);
         }
+        synchronized (mPackages) {
+            PackageSetting ps = mSettings.mPackages.get(pkgName);
+            if (ps != null) {
+                res.users = ps.getInstalledUsers(sUserManager.getUserIds());
+            }
+        }
     }
 
     private static boolean isForwardLocked(PackageParser.Package pkg) {
@@ -7544,10 +7732,11 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.DELETE_PACKAGES, null);
         // Queue up an async operation since the package deletion may take a little while.
+        final int uid = Binder.getCallingUid();
         mHandler.post(new Runnable() {
             public void run() {
                 mHandler.removeCallbacks(this);
-                final int returnCode = deletePackageX(packageName, true, true, flags);
+                final int returnCode = deletePackageX(packageName, uid, flags);
                 if (observer != null) {
                     try {
                         observer.packageDeleted(packageName, returnCode);
@@ -7573,8 +7762,7 @@
      *  persisting settings for later use
      *  sending a broadcast if necessary
      */
-    private int deletePackageX(String packageName, boolean sendBroadCast,
-                                   boolean deleteCodeAndResources, int flags) {
+    private int deletePackageX(String packageName, int uid, int flags) {
         final PackageRemovedInfo info = new PackageRemovedInfo();
         final boolean res;
 
@@ -7589,27 +7777,30 @@
         }
         
         synchronized (mInstallLock) {
-            res = deletePackageLI(packageName, deleteCodeAndResources,
-                    flags | REMOVE_CHATTY, info, true);
+            res = deletePackageLI(packageName,
+                    (flags & PackageManager.DELETE_ALL_USERS) != 0
+                            ? UserHandle.ALL : new UserHandle(UserHandle.getUserId(uid)),
+                    true, flags | REMOVE_CHATTY, info, true);
         }
 
-        if (res && sendBroadCast) {
+        if (res) {
             boolean systemUpdate = info.isRemovedPackageSystemUpdate;
-            info.sendBroadcast(deleteCodeAndResources, systemUpdate);
+            info.sendBroadcast(true, systemUpdate);
 
             // If the removed package was a system update, the old system packaged
             // was re-enabled; we need to broadcast this information
             if (systemUpdate) {
                 Bundle extras = new Bundle(1);
-                extras.putInt(Intent.EXTRA_UID, info.removedUid >= 0 ? info.removedUid : info.uid);
+                extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0
+                        ? info.removedAppId : info.uid);
                 extras.putBoolean(Intent.EXTRA_REPLACING, true);
 
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                        extras, null, null, UserHandle.USER_ALL);
+                        extras, null, null, null);
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
-                        extras, null, null, UserHandle.USER_ALL);
+                        extras, null, null, null);
                 sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
-                        null, packageName, null, UserHandle.USER_ALL);
+                        null, packageName, null, null);
             }
         }
         // Force a gc here.
@@ -7618,7 +7809,7 @@
         // other processes clean up before deleting resources.
         if (info.args != null) {
             synchronized (mInstallLock) {
-                info.args.doPostDeleteLI(deleteCodeAndResources);
+                info.args.doPostDeleteLI(true);
             }
         }
 
@@ -7628,29 +7819,30 @@
     static class PackageRemovedInfo {
         String removedPackage;
         int uid = -1;
-        int removedUid = -1;
+        int removedAppId = -1;
+        int[] removedUsers = null;
         boolean isRemovedPackageSystemUpdate = false;
         // Clean up resources deleted packages.
         InstallArgs args = null;
 
         void sendBroadcast(boolean fullRemove, boolean replacing) {
             Bundle extras = new Bundle(1);
-            extras.putInt(Intent.EXTRA_UID, removedUid >= 0 ? removedUid : uid);
+            extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
             extras.putBoolean(Intent.EXTRA_DATA_REMOVED, fullRemove);
             if (replacing) {
                 extras.putBoolean(Intent.EXTRA_REPLACING, true);
             }
             if (removedPackage != null) {
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
-                        extras, null, null, UserHandle.USER_ALL);
+                        extras, null, null, removedUsers);
                 if (fullRemove && !replacing) {
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
-                            extras, null, null, UserHandle.USER_ALL);
+                            extras, null, null, removedUsers);
                 }
             }
-            if (removedUid >= 0) {
+            if (removedAppId >= 0) {
                 sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
-                        UserHandle.getUserId(removedUid));
+                        removedUsers);
             }
         }
     }
@@ -7664,34 +7856,28 @@
     private void removePackageDataLI(PackageParser.Package p, PackageRemovedInfo outInfo,
             int flags, boolean writeSettings) {
         String packageName = p.packageName;
-        if (outInfo != null) {
-            outInfo.removedPackage = packageName;
-        }
         removePackageLI(p, (flags&REMOVE_CHATTY) != 0);
         // Retrieve object to delete permissions for shared user later on
         final PackageSetting deletedPs;
         // reader
         synchronized (mPackages) {
             deletedPs = mSettings.mPackages.get(packageName);
-        }
-        if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
-            int retCode = mInstaller.remove(packageName, 0);
-            if (retCode < 0) {
-                Slog.w(TAG, "Couldn't remove app data or cache directory for package: "
-                           + packageName + ", retcode=" + retCode);
-                // we don't consider this to be a failure of the core package deletion
-            } else {
-                // TODO: Kill the processes first
-                sUserManager.removePackageForAllUsers(packageName);
+            if (outInfo != null) {
+                outInfo.removedPackage = packageName;
+                outInfo.removedUsers = deletedPs != null
+                        ? deletedPs.getInstalledUsers(sUserManager.getUserIds()) : null;
             }
-            schedulePackageCleaning(packageName);
+        }
+        if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
+            removeDataDirsLI(packageName);
+            schedulePackageCleaning(packageName, UserHandle.USER_ALL, true);
         }
         // writer
         synchronized (mPackages) {
             if (deletedPs != null) {
-                if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
+                if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
                     if (outInfo != null) {
-                        outInfo.removedUid = mSettings.removePackageLPw(packageName);
+                        outInfo.removedAppId = mSettings.removePackageLPw(packageName);
                     }
                     if (deletedPs != null) {
                         updatePermissionsLPw(deletedPs.name, null, 0);
@@ -7740,10 +7926,10 @@
         outInfo.isRemovedPackageSystemUpdate = true;
         if (ps.versionCode < p.mVersionCode) {
             // Delete data for downgrades
-            flags &= ~PackageManager.DONT_DELETE_DATA;
+            flags &= ~PackageManager.DELETE_KEEP_DATA;
         } else {
             // Preserve data by setting flag
-            flags |= PackageManager.DONT_DELETE_DATA;
+            flags |= PackageManager.DELETE_KEEP_DATA;
         }
         boolean ret = deleteInstalledPackageLI(p, true, flags, outInfo,
                 writeSettings);
@@ -7760,7 +7946,7 @@
         // Install the system package
         PackageParser.Package newPkg = scanPackageLI(ps.codePath,
                 PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM,
-                SCAN_MONITOR | SCAN_NO_PATHS, 0);
+                SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
 
         if (newPkg == null) {
             Slog.w(TAG, "Failed to restore system package:"+p.packageName+" with error:" + mLastScanError);
@@ -7807,7 +7993,7 @@
     /*
      * This method handles package deletion in general
      */
-    private boolean deletePackageLI(String packageName,
+    private boolean deletePackageLI(String packageName, UserHandle user,
             boolean deleteCodeAndResources, int flags, PackageRemovedInfo outInfo,
             boolean writeSettings) {
         if (packageName == null) {
@@ -7816,6 +8002,8 @@
         }
         PackageParser.Package p;
         boolean dataOnly = false;
+        int removeUser = -1;
+        int appId = -1;
         synchronized (mPackages) {
             p = mPackages.get(packageName);
             if (p == null) {
@@ -7823,15 +8011,53 @@
                 dataOnly = true;
                 PackageSetting ps = mSettings.mPackages.get(packageName);
                 if (ps == null) {
-                    Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
+                    Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
                     return false;
                 }
                 p = ps.pkg;
             }
+            if (p == null) {
+                Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
+                return false;
+            }
+            final PackageSetting ps = (PackageSetting)p.mExtras;
+            if (!isSystemApp(p) && ps != null && user != null
+                    && user.getIdentifier() != UserHandle.USER_ALL) {
+                // The caller is asking that the package only be deleted for a single
+                // user.  To do this, we just mark its uninstalled state and delete
+                // its data.
+                ps.setUserState(user.getIdentifier(),
+                        COMPONENT_ENABLED_STATE_DEFAULT,
+                        false, //installed
+                        true,  //stopped
+                        true,  //notLaunched
+                        null, null);
+                if (ps.isAnyInstalled(sUserManager.getUserIds())) {
+                    // Other user still have this package installed, so all
+                    // we need to do is clear this user's data and save that
+                    // it is uninstalled.
+                    removeUser = user.getIdentifier();
+                    appId = ps.appId;
+                    mSettings.writePackageRestrictionsLPr(removeUser);
+                } else {
+                    // We need to set it back to 'installed' so the uninstall
+                    // broadcasts will be sent correctly.
+                    ps.setInstalled(true, user.getIdentifier());
+                }
+            }
         }
-        if (p == null) {
-            Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
-            return false;
+
+        if (removeUser >= 0) {
+            // From above, we determined that we are deleting this only
+            // for a single user.  Continue the work here.
+            if (outInfo != null) {
+                outInfo.removedPackage = packageName;
+                outInfo.removedAppId = appId;
+                outInfo.removedUsers = new int[] {removeUser};
+            }
+            mInstaller.clearUserData(packageName, removeUser);
+            schedulePackageCleaning(packageName, removeUser, false);
+            return true;
         }
 
         if (dataOnly) {
@@ -7876,7 +8102,7 @@
         }
     }
 
-    private void clearExternalStorageDataSync(String packageName, boolean allData) {
+    private void clearExternalStorageDataSync(String packageName, int userId, boolean allData) {
         final boolean mounted;
         if (Environment.isExternalStorageEmulated()) {
             mounted = true;
@@ -7892,44 +8118,52 @@
         }
 
         final Intent containerIntent = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
-        ClearStorageConnection conn = new ClearStorageConnection();
-        if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE)) {
-            try {
-                long timeout = SystemClock.uptimeMillis() + 5000;
-                synchronized (conn) {
-                    long now = SystemClock.uptimeMillis();
-                    while (conn.mContainerService == null && now < timeout) {
-                        try {
-                            conn.wait(timeout - now);
-                        } catch (InterruptedException e) {
+        int[] users;
+        if (userId == UserHandle.USER_ALL) {
+            users = sUserManager.getUserIds();
+        } else {
+            users = new int[] { userId };
+        }
+        for (int curUser : users) {
+            ClearStorageConnection conn = new ClearStorageConnection();
+            if (mContext.bindService(containerIntent, conn, Context.BIND_AUTO_CREATE, curUser)) {
+                try {
+                    long timeout = SystemClock.uptimeMillis() + 5000;
+                    synchronized (conn) {
+                        long now = SystemClock.uptimeMillis();
+                        while (conn.mContainerService == null && now < timeout) {
+                            try {
+                                conn.wait(timeout - now);
+                            } catch (InterruptedException e) {
+                            }
                         }
                     }
-                }
-                if (conn.mContainerService == null) {
-                    return;
-                }
-                final File externalCacheDir = Environment
-                        .getExternalStorageAppCacheDirectory(packageName);
-                try {
-                    conn.mContainerService.clearDirectory(externalCacheDir.toString());
-                } catch (RemoteException e) {
-                }
-                if (allData) {
-                    final File externalDataDir = Environment
-                            .getExternalStorageAppDataDirectory(packageName);
+                    if (conn.mContainerService == null) {
+                        return;
+                    }
+                    final File externalCacheDir = Environment
+                            .getExternalStorageAppCacheDirectory(packageName);
                     try {
-                        conn.mContainerService.clearDirectory(externalDataDir.toString());
+                        conn.mContainerService.clearDirectory(externalCacheDir.toString());
                     } catch (RemoteException e) {
                     }
-                    final File externalMediaDir = Environment
-                            .getExternalStorageAppMediaDirectory(packageName);
-                    try {
-                        conn.mContainerService.clearDirectory(externalMediaDir.toString());
-                    } catch (RemoteException e) {
+                    if (allData) {
+                        final File externalDataDir = Environment
+                                .getExternalStorageAppDataDirectory(packageName);
+                        try {
+                            conn.mContainerService.clearDirectory(externalDataDir.toString());
+                        } catch (RemoteException e) {
+                        }
+                        final File externalMediaDir = Environment
+                                .getExternalStorageAppMediaDirectory(packageName);
+                        try {
+                            conn.mContainerService.clearDirectory(externalMediaDir.toString());
+                        } catch (RemoteException e) {
+                        }
                     }
+                } finally {
+                    mContext.unbindService(conn);
                 }
-            } finally {
-                mContext.unbindService(conn);
             }
         }
     }
@@ -7948,7 +8182,7 @@
                 synchronized (mInstallLock) {
                     succeeded = clearApplicationUserDataLI(packageName, userId);
                 }
-                clearExternalStorageDataSync(packageName, true);
+                clearExternalStorageDataSync(packageName, userId, true);
                 if (succeeded) {
                     // invoke DeviceStorageMonitor's update method to clear any notifications
                     DeviceStorageMonitorService dsm = (DeviceStorageMonitorService)
@@ -8022,7 +8256,7 @@
                 synchronized (mInstallLock) {
                     succeded = deleteApplicationCacheFilesLI(packageName, userId);
                 }
-                clearExternalStorageDataSync(packageName, false);
+                clearExternalStorageDataSync(packageName, userId, false);
                 if(observer != null) {
                     try {
                         observer.onRemoveCompleted(packageName, succeded);
@@ -8480,7 +8714,7 @@
         extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
         extras.putInt(Intent.EXTRA_UID, packageUid);
         sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED,  packageName, extras, null, null,
-                UserHandle.getUserId(packageUid));
+                new int[] {UserHandle.getUserId(packageUid)});
     }
 
     public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
@@ -9085,7 +9319,7 @@
             if (DEBUG_SD_INSTALL)
                 Log.i(TAG, "Loading packages");
             loadMediaPackages(processCids, uidArr, removeCids);
-            startCleaningPackages();
+            startCleaningPackages(-1);
         } else {
             if (DEBUG_SD_INSTALL)
                 Log.i(TAG, "Unloading packages");
@@ -9106,7 +9340,7 @@
             }
             String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
                     : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
-            sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserHandle.USER_ALL);
+            sendPackageBroadcast(action, null, extras, null, finishedReceiver, null);
         }
     }
 
@@ -9151,7 +9385,7 @@
                 doGc = true;
                 synchronized (mInstallLock) {
                     final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags,
-                            0, 0);
+                            0, 0, null);
                     // Scan the package
                     if (pkg != null) {
                         /*
@@ -9260,8 +9494,8 @@
             // Delete package internally
             PackageRemovedInfo outInfo = new PackageRemovedInfo();
             synchronized (mInstallLock) {
-                boolean res = deletePackageLI(pkgName, false, PackageManager.DONT_DELETE_DATA,
-                        outInfo, false);
+                boolean res = deletePackageLI(pkgName, null, false,
+                        PackageManager.DELETE_KEEP_DATA, outInfo, false);
                 if (res) {
                     pkgList.add(pkgName);
                 } else {
@@ -9298,9 +9532,12 @@
         }
     }
 
+    /** Binder call */
+    @Override
     public void movePackage(final String packageName, final IPackageMoveObserver observer,
             final int flags) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
+        UserHandle user = new UserHandle(UserHandle.getCallingUserId());
         int returnCode = PackageManager.MOVE_SUCCEEDED;
         int currFlags = 0;
         int newFlags = 0;
@@ -9351,14 +9588,15 @@
              * anyway.
              */
             if (returnCode != PackageManager.MOVE_SUCCEEDED) {
-                processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1),
+                processPendingMove(new MoveParams(null, observer, 0, packageName,
+                        null, -1, user),
                         returnCode);
             } else {
                 Message msg = mHandler.obtainMessage(INIT_COPY);
                 InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
                         pkg.applicationInfo.publicSourceDir, pkg.applicationInfo.nativeLibraryDir);
                 MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
-                        pkg.applicationInfo.dataDir, pkg.applicationInfo.uid);
+                        pkg.applicationInfo.dataDir, pkg.applicationInfo.uid, user);
                 msg.obj = mp;
                 mHandler.sendMessage(msg);
             }
@@ -9550,15 +9788,36 @@
     }
 
     /** Called by UserManagerService */
-    void cleanUpUser(int userHandle) {
+    void cleanUpUserLILPw(int userHandle) {
         // Disable all the packages for the user first
-        synchronized (mPackages) {
-            Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet();
-            for (Entry<String, PackageSetting> entry : entries) {
-                entry.getValue().removeUser(userHandle);
+        Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet();
+        for (Entry<String, PackageSetting> entry : entries) {
+            entry.getValue().removeUser(userHandle);
+        }
+        if (mDirtyUsers.remove(userHandle));
+        mSettings.removeUserLPr(userHandle);
+        if (mInstaller != null) {
+            // Technically, we shouldn't be doing this with the package lock
+            // held.  However, this is very rare, and there is already so much
+            // other disk I/O going on, that we'll let it slide for now.
+            mInstaller.removeUserDataDirs(userHandle);
+        }
+    }
+
+    /** Called by UserManagerService */
+    void createNewUserLILPw(int userHandle, File path) {
+        if (mInstaller != null) {
+            path.mkdir();
+            FileUtils.setPermissions(path.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+                    | FileUtils.S_IXOTH, -1, -1);
+            for (PackageSetting ps : mSettings.mPackages.values()) {
+                // Only system apps are initially installed.
+                ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle);
+                // Need to create a data directory for all apps under this user.
+                mInstaller.createUserData(ps.name,
+                        UserHandle.getUid(userHandle, ps.appId), userHandle);
             }
-            if (mDirtyUsers.remove(userHandle));
-            mSettings.removeUserLPr(userHandle);
+            mSettings.writePackageRestrictionsLPr(userHandle);
         }
     }
 
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index 56f2166..6d31f0e 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -20,11 +20,13 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 
+import android.content.pm.PackageUserState;
+import android.content.pm.UserInfo;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 
 import java.io.File;
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * Settings base class for pending and resolved classes.
@@ -62,19 +64,11 @@
     boolean permissionsFixed;
     boolean haveGids;
 
+    private static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();
+
     // Whether this package is currently stopped, thus can not be
     // started until explicitly launched by the user.
-    private SparseArray<Boolean> stopped = new SparseArray<Boolean>();
-
-    // Set to true if we have never launched this app.
-    private SparseArray<Boolean> notLaunched = new SparseArray<Boolean>();
-
-    /* Explicitly disabled components */
-    private SparseArray<HashSet<String>> disabledComponents = new SparseArray<HashSet<String>>();
-    /* Explicitly enabled components */
-    private SparseArray<HashSet<String>> enabledComponents = new SparseArray<HashSet<String>>();
-    /* Enabled state */
-    private SparseIntArray enabled = new SparseIntArray();
+    private final SparseArray<PackageUserState> userState = new SparseArray<PackageUserState>();
 
     int installStatus = PKG_INSTALL_COMPLETE;
 
@@ -115,12 +109,11 @@
 
         permissionsFixed = base.permissionsFixed;
         haveGids = base.haveGids;
-        notLaunched = base.notLaunched;
-
-        disabledComponents = (SparseArray<HashSet<String>>) base.disabledComponents.clone();
-        enabledComponents = (SparseArray<HashSet<String>>) base.enabledComponents.clone();
-        enabled = (SparseIntArray) base.enabled.clone();
-        stopped = (SparseArray<Boolean>) base.stopped.clone();
+        userState.clear();
+        for (int i=0; i<base.userState.size(); i++) {
+            userState.put(base.userState.keyAt(i),
+                    new PackageUserState(base.userState.valueAt(i)));
+        }
         installStatus = base.installStatus;
 
         origPackage = base.origPackage;
@@ -171,103 +164,174 @@
         signatures = base.signatures;
         permissionsFixed = base.permissionsFixed;
         haveGids = base.haveGids;
-        stopped = base.stopped;
-        notLaunched = base.notLaunched;
-        disabledComponents = base.disabledComponents;
-        enabledComponents = base.enabledComponents;
-        enabled = base.enabled;
+        userState.clear();
+        for (int i=0; i<base.userState.size(); i++) {
+            userState.put(base.userState.keyAt(i), base.userState.valueAt(i));
+        }
         installStatus = base.installStatus;
     }
 
+    private PackageUserState modifyUserState(int userId) {
+        PackageUserState state = userState.get(userId);
+        if (state == null) {
+            state = new PackageUserState();
+            userState.put(userId, state);
+        }
+        return state;
+    }
+
+    public PackageUserState readUserState(int userId) {
+        PackageUserState state = userState.get(userId);
+        return state != null ? state : DEFAULT_USER_STATE;
+    }
+
     void setEnabled(int state, int userId) {
-        enabled.put(userId, state);
+        modifyUserState(userId).enabled = state;
     }
 
     int getEnabled(int userId) {
-        return enabled.get(userId, COMPONENT_ENABLED_STATE_DEFAULT);
+        return readUserState(userId).enabled;
+    }
+
+    void setInstalled(boolean inst, int userId) {
+        modifyUserState(userId).installed = inst;
+    }
+
+    boolean getInstalled(int userId) {
+        return readUserState(userId).installed;
+    }
+
+    boolean isAnyInstalled(int[] users) {
+        for (int user: users) {
+            if (readUserState(user).installed) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    int[] getInstalledUsers(int[] users) {
+        int num = 0;
+        for (int user : users) {
+            if (getInstalled(user)) {
+                num++;
+            }
+        }
+        int[] res = new int[num];
+        num = 0;
+        for (int user : users) {
+            if (getInstalled(user)) {
+                res[num] = user;
+                num++;
+            }
+        }
+        return res;
     }
 
     boolean getStopped(int userId) {
-        return stopped.get(userId, false);
+        return readUserState(userId).stopped;
     }
 
     void setStopped(boolean stop, int userId) {
-        stopped.put(userId, stop);
+        modifyUserState(userId).stopped = stop;
     }
 
     boolean getNotLaunched(int userId) {
-        return notLaunched.get(userId, false);
+        return readUserState(userId).notLaunched;
     }
 
     void setNotLaunched(boolean stop, int userId) {
-        notLaunched.put(userId, stop);
+        modifyUserState(userId).notLaunched = stop;
+    }
+
+    void setUserState(int userId, int enabled, boolean installed, boolean stopped,
+            boolean notLaunched, HashSet<String> enabledComponents,
+            HashSet<String> disabledComponents) {
+        PackageUserState state = modifyUserState(userId);
+        state.enabled = enabled;
+        state.installed = installed;
+        state.stopped = stopped;
+        state.notLaunched = notLaunched;
+        state.enabledComponents = enabledComponents;
+        state.disabledComponents = disabledComponents;
     }
 
     HashSet<String> getEnabledComponents(int userId) {
-        return getComponentHashSet(enabledComponents, userId);
+        return readUserState(userId).enabledComponents;
     }
 
     HashSet<String> getDisabledComponents(int userId) {
-        return getComponentHashSet(disabledComponents, userId);
+        return readUserState(userId).disabledComponents;
     }
 
     void setEnabledComponents(HashSet<String> components, int userId) {
-        enabledComponents.put(userId, components);
+        modifyUserState(userId).enabledComponents = components;
     }
 
     void setDisabledComponents(HashSet<String> components, int userId) {
-        disabledComponents.put(userId, components);
+        modifyUserState(userId).disabledComponents = components;
     }
 
-    private HashSet<String> getComponentHashSet(SparseArray<HashSet<String>> setArray, int userId) {
-        HashSet<String> set = setArray.get(userId);
-        if (set == null) {
-            set = new HashSet<String>(1);
-            setArray.put(userId, set);
+    void setEnabledComponentsCopy(HashSet<String> components, int userId) {
+        modifyUserState(userId).enabledComponents = components != null
+                ? new HashSet<String>(components) : null;
+    }
+
+    void setDisabledComponentsCopy(HashSet<String> components, int userId) {
+        modifyUserState(userId).disabledComponents = components != null
+                ? new HashSet<String>(components) : null;
+    }
+
+    PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) {
+        PackageUserState state = modifyUserState(userId);
+        if (disabled && state.disabledComponents == null) {
+            state.disabledComponents = new HashSet<String>(1);
         }
-        return set;
+        if (enabled && state.enabledComponents == null) {
+            state.enabledComponents = new HashSet<String>(1);
+        }
+        return state;
     }
 
     void addDisabledComponent(String componentClassName, int userId) {
-        HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
-        disabled.add(componentClassName);
+        modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
     }
 
     void addEnabledComponent(String componentClassName, int userId) {
-        HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
-        enabled.add(componentClassName);
+        modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName);
     }
 
     boolean enableComponentLPw(String componentClassName, int userId) {
-        HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
-        HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
-        boolean changed = disabled.remove(componentClassName);
-        changed |= enabled.add(componentClassName);
+        PackageUserState state = modifyUserStateComponents(userId, false, true);
+        boolean changed = state.disabledComponents != null
+                ? state.disabledComponents.remove(componentClassName) : false;
+        changed |= state.enabledComponents.add(componentClassName);
         return changed;
     }
 
     boolean disableComponentLPw(String componentClassName, int userId) {
-        HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
-        HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
-        boolean changed = enabled.remove(componentClassName);
-        changed |= disabled.add(componentClassName);
+        PackageUserState state = modifyUserStateComponents(userId, true, false);
+        boolean changed = state.enabledComponents != null
+                ? state.enabledComponents.remove(componentClassName) : false;
+        changed |= state.disabledComponents.add(componentClassName);
         return changed;
     }
 
     boolean restoreComponentLPw(String componentClassName, int userId) {
-        HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
-        HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
-        boolean changed = enabled.remove(componentClassName);
-        changed |= disabled.remove(componentClassName);
+        PackageUserState state = modifyUserStateComponents(userId, true, true);
+        boolean changed = state.disabledComponents != null
+                ? state.disabledComponents.remove(componentClassName) : false;
+        changed |= state.enabledComponents != null
+                ? state.enabledComponents.remove(componentClassName) : false;
         return changed;
     }
 
     int getCurrentEnabledStateLPr(String componentName, int userId) {
-        HashSet<String> disabled = getComponentHashSet(disabledComponents, userId);
-        HashSet<String> enabled = getComponentHashSet(enabledComponents, userId);
-        if (enabled.contains(componentName)) {
+        PackageUserState state = readUserState(userId);
+        if (state.enabledComponents != null && state.enabledComponents.contains(componentName)) {
             return COMPONENT_ENABLED_STATE_ENABLED;
-        } else if (disabled.contains(componentName)) {
+        } else if (state.disabledComponents != null
+                && state.disabledComponents.contains(componentName)) {
             return COMPONENT_ENABLED_STATE_DISABLED;
         } else {
             return COMPONENT_ENABLED_STATE_DEFAULT;
@@ -275,11 +339,6 @@
     }
 
     void removeUser(int userId) {
-        enabled.delete(userId);
-        stopped.delete(userId);
-        enabledComponents.delete(userId);
-        disabledComponents.delete(userId);
-        notLaunched.delete(userId);
+        userState.delete(userId);
     }
-
 }
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index cfc0f5c..68b594a 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -38,11 +38,13 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ComponentInfo;
+import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PermissionInfo;
 import android.content.pm.Signature;
 import android.content.pm.UserInfo;
+import android.content.pm.PackageUserState;
 import android.content.pm.VerifierDeviceIdentity;
 import android.os.Binder;
 import android.os.Environment;
@@ -92,9 +94,12 @@
     private static final String TAG_PACKAGE = "pkg";
 
     private static final String ATTR_NAME = "name";
+    private static final String ATTR_USER = "user";
+    private static final String ATTR_CODE = "code";
     private static final String ATTR_NOT_LAUNCHED = "nl";
     private static final String ATTR_ENABLED = "enabled";
     private static final String ATTR_STOPPED = "stopped";
+    private static final String ATTR_INSTALLED = "inst";
 
     private final File mSettingsFilename;
     private final File mBackupSettingsFilename;
@@ -156,7 +161,8 @@
 
     // Packages that have been uninstalled and still need their external
     // storage data deleted.
-    final ArrayList<String> mPackagesToBeCleaned = new ArrayList<String>();
+    final SparseArray<ArrayList<PackageCleanItem>> mPackagesToBeCleaned
+            = new SparseArray<ArrayList<PackageCleanItem>>();
     
     // Packages that have been renamed since they were first installed.
     // Keys are the new names of the packages, values are the original
@@ -200,10 +206,11 @@
 
     PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
             String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
-            String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) {
+            String nativeLibraryPathString, int pkgFlags, UserHandle user, boolean add) {
         final String name = pkg.packageName;
         PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
-                resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add);
+                resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags,
+                user, add);
         return p;
     }
 
@@ -364,7 +371,8 @@
 
     private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
             String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
-            String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) {
+            String nativeLibraryPathString, int vc, int pkgFlags,
+            UserHandle installUser, boolean add) {
         PackageSetting p = mPackages.get(name);
         if (p != null) {
             if (!p.codePath.equals(codePath)) {
@@ -407,11 +415,6 @@
             }
         }
         if (p == null) {
-            // Create a new PackageSettings entry. this can end up here because
-            // of code path mismatch or user id mismatch of an updated system partition
-            if (!create) {
-                return null;
-            }
             if (origPackage != null) {
                 // We are consuming the data from an existing package.
                 p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
@@ -445,8 +448,20 @@
                     List<UserInfo> users = getAllUsers();
                     if (users != null) {
                         for (UserInfo user : users) {
-                            p.setStopped(true, user.id);
-                            p.setNotLaunched(true, user.id);
+                            // By default we consider this app to be installed
+                            // for the user if no user has been specified (which
+                            // means to leave it at its original value, and the
+                            // original default value is true), or we are being
+                            // asked to install for all users, or this is the
+                            // user we are installing for.
+                            final boolean installed = installUser == null
+                                    || installUser.getIdentifier() == UserHandle.USER_ALL
+                                    || installUser.getIdentifier() == user.id;
+                            p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT,
+                                    installed,
+                                    true, // stopped,
+                                    true, // notLaunched
+                                    null, null);
                             writePackageRestrictionsLPr(user.id);
                         }
                     }
@@ -472,12 +487,10 @@
                         if (users != null) {
                             for (UserInfo user : users) {
                                 int userId = user.id;
-                                p.setDisabledComponents(
-                                        new HashSet<String>(dis.getDisabledComponents(userId)),
-                                        userId);
-                                p.setEnabledComponents(
-                                        new HashSet<String>(dis.getEnabledComponents(userId)),
-                                        userId);
+                                p.setDisabledComponentsCopy(
+                                        dis.getDisabledComponents(userId), userId);
+                                p.setEnabledComponentsCopy(
+                                        dis.getEnabledComponents(userId), userId);
                             }
                         }
                         // Add new setting to list of user ids
@@ -498,6 +511,25 @@
                 // user preferences
                 addPackageSettingLPw(p, name, sharedUser);
             }
+        } else {
+            if (installUser != null) {
+                // The caller has explicitly specified the user they want this
+                // package installed for, and the package already exists.
+                // Make sure it conforms to the new request.
+                List<UserInfo> users = getAllUsers();
+                if (users != null) {
+                    for (UserInfo user : users) {
+                        if (installUser.getIdentifier() == UserHandle.USER_ALL
+                                || installUser.getIdentifier() == user.id) {
+                            boolean installed = p.getInstalled(user.id);
+                            if (!installed) {
+                                p.setInstalled(true, user.id);
+                                writePackageRestrictionsLPr(user.id);
+                            }
+                        }
+                    }
+                }
+            }
         }
         return p;
     }
@@ -778,10 +810,14 @@
                             + "assuming all started");
                     // At first boot, make sure no packages are stopped.
                     // We usually want to have third party apps initialize
-                    // in the stopped state, but not at first boot.
+                    // in the stopped state, but not at first boot.  Also
+                    // consider all applications to be installed.
                     for (PackageSetting pkg : mPackages.values()) {
-                        pkg.setStopped(false, userId);
-                        pkg.setNotLaunched(false, userId);
+                        pkg.setUserState(userId, COMPONENT_ENABLED_STATE_DEFAULT,
+                                true,   // installed
+                                false,  // stopped
+                                false,  // notLaunched
+                                null, null);
                     }
                     return;
                 }
@@ -823,17 +859,21 @@
                         XmlUtils.skipCurrentTag(parser);
                         continue;
                     }
-                    String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
-                    int enabled = enabledStr == null ? COMPONENT_ENABLED_STATE_DEFAULT
-                            : Integer.parseInt(enabledStr);
-                    ps.setEnabled(enabled, userId);
-                    String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
-                    boolean stopped = stoppedStr == null ? false : Boolean.parseBoolean(stoppedStr);
-                    ps.setStopped(stopped, userId);
-                    String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
-                    boolean notLaunched = stoppedStr == null ? false
-                            : Boolean.parseBoolean(notLaunchedStr);
-                    ps.setNotLaunched(notLaunched, userId);
+                    final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
+                    final int enabled = enabledStr == null
+                            ? COMPONENT_ENABLED_STATE_DEFAULT : Integer.parseInt(enabledStr);
+                    final String installedStr = parser.getAttributeValue(null, ATTR_INSTALLED);
+                    final boolean installed = installedStr == null
+                            ? true : Boolean.parseBoolean(installedStr);
+                    final String stoppedStr = parser.getAttributeValue(null, ATTR_STOPPED);
+                    final boolean stopped = stoppedStr == null
+                            ? false : Boolean.parseBoolean(stoppedStr);
+                    final String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
+                    final boolean notLaunched = stoppedStr == null
+                            ? false : Boolean.parseBoolean(notLaunchedStr);
+
+                    HashSet<String> enabledComponents = null;
+                    HashSet<String> disabledComponents = null;
 
                     int packageDepth = parser.getDepth();
                     while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -845,13 +885,14 @@
                         }
                         tagName = parser.getName();
                         if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
-                            HashSet<String> components = readComponentsLPr(parser);
-                            ps.setEnabledComponents(components, userId);
+                            enabledComponents = readComponentsLPr(parser);
                         } else if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
-                            HashSet<String> components = readComponentsLPr(parser);
-                            ps.setDisabledComponents(components, userId);
+                            disabledComponents = readComponentsLPr(parser);
                         }
                     }
+
+                    ps.setUserState(userId, enabled, installed, stopped, notLaunched,
+                            enabledComponents, disabledComponents);
                 } else {
                     Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
                           + parser.getName());
@@ -876,7 +917,7 @@
 
     private HashSet<String> readComponentsLPr(XmlPullParser parser)
             throws IOException, XmlPullParserException {
-        HashSet<String> components = new HashSet<String>();
+        HashSet<String> components = null;
         int type;
         int outerDepth = parser.getDepth();
         String tagName;
@@ -891,6 +932,9 @@
             if (tagName.equals(TAG_ITEM)) {
                 String componentName = parser.getAttributeValue(null, ATTR_NAME);
                 if (componentName != null) {
+                    if (components == null) {
+                        components = new HashSet<String>();
+                    }
                     components.add(componentName);
                 }
             }
@@ -936,41 +980,44 @@
             serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS);
 
             for (final PackageSetting pkg : mPackages.values()) {
-                if (pkg.getStopped(userId)
-                        || pkg.getNotLaunched(userId)
-                        || pkg.getEnabled(userId) != COMPONENT_ENABLED_STATE_DEFAULT
-                        || pkg.getEnabledComponents(userId).size() > 0
-                        || pkg.getDisabledComponents(userId).size() > 0) {
+                PackageUserState ustate = pkg.readUserState(userId);
+                if (ustate.stopped || ustate.notLaunched || !ustate.installed
+                        || ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT
+                        || (ustate.enabledComponents != null
+                                && ustate.enabledComponents.size() > 0)
+                        || (ustate.disabledComponents != null
+                                && ustate.disabledComponents.size() > 0)) {
                     serializer.startTag(null, TAG_PACKAGE);
                     serializer.attribute(null, ATTR_NAME, pkg.name);
-                    boolean stopped = pkg.getStopped(userId);
-                    boolean notLaunched = pkg.getNotLaunched(userId);
-                    int enabled = pkg.getEnabled(userId);
-                    if (DEBUG_MU) Log.i(TAG, "  pkg=" + pkg.name + ", state=" + enabled);
-                    HashSet<String> enabledComponents = pkg.getEnabledComponents(userId);
-                    HashSet<String> disabledComponents = pkg.getDisabledComponents(userId);
+                    if (DEBUG_MU) Log.i(TAG, "  pkg=" + pkg.name + ", state=" + ustate.enabled);
 
-                    if (stopped) {
+                    if (!ustate.installed) {
+                        serializer.attribute(null, ATTR_INSTALLED, "false");
+                    }
+                    if (ustate.stopped) {
                         serializer.attribute(null, ATTR_STOPPED, "true");
                     }
-                    if (notLaunched) {
+                    if (ustate.notLaunched) {
                         serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");
                     }
-                    if (enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
-                        serializer.attribute(null, ATTR_ENABLED, Integer.toString(enabled));
+                    if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
+                        serializer.attribute(null, ATTR_ENABLED,
+                                Integer.toString(ustate.enabled));
                     }
-                    if (enabledComponents.size() > 0) {
+                    if (ustate.enabledComponents != null
+                            && ustate.enabledComponents.size() > 0) {
                         serializer.startTag(null, TAG_ENABLED_COMPONENTS);
-                        for (final String name : enabledComponents) {
+                        for (final String name : ustate.enabledComponents) {
                             serializer.startTag(null, TAG_ITEM);
                             serializer.attribute(null, ATTR_NAME, name);
                             serializer.endTag(null, TAG_ITEM);
                         }
                         serializer.endTag(null, TAG_ENABLED_COMPONENTS);
                     }
-                    if (disabledComponents.size() > 0) {
+                    if (ustate.disabledComponents != null
+                            && ustate.disabledComponents.size() > 0) {
                         serializer.startTag(null, TAG_DISABLED_COMPONENTS);
-                        for (final String name : disabledComponents) {
+                        for (final String name : ustate.disabledComponents) {
                             serializer.startTag(null, TAG_ITEM);
                             serializer.attribute(null, ATTR_NAME, name);
                             serializer.endTag(null, TAG_ITEM);
@@ -1210,9 +1257,17 @@
 
             if (mPackagesToBeCleaned.size() > 0) {
                 for (int i=0; i<mPackagesToBeCleaned.size(); i++) {
-                    serializer.startTag(null, "cleaning-package");
-                    serializer.attribute(null, ATTR_NAME, mPackagesToBeCleaned.get(i));
-                    serializer.endTag(null, "cleaning-package");
+                    final int userId = mPackagesToBeCleaned.keyAt(i);
+                    final String userStr = Integer.toString(userId);
+                    final ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.valueAt(i);
+                    for (int j=0; j<pkgs.size(); j++) {
+                        serializer.startTag(null, "cleaning-package");
+                        PackageCleanItem item = pkgs.get(i);
+                        serializer.attribute(null, ATTR_NAME, item.packageName);
+                        serializer.attribute(null, ATTR_CODE, item.andCode ? "true" : "false");
+                        serializer.attribute(null, ATTR_USER, userStr);
+                        serializer.endTag(null, "cleaning-package");
+                    }
                 }
             }
             
@@ -1468,6 +1523,17 @@
         return ret;
     }
 
+    void addPackageToCleanLPw(int userId, PackageCleanItem pkg) {
+        ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.get(userId);
+        if (pkgs == null) {
+            pkgs = new ArrayList<PackageCleanItem>();
+            mPackagesToBeCleaned.put(userId, pkgs);
+        }
+        if (!pkgs.contains(pkg)) {
+            pkgs.add(pkg);
+        }
+    }
+
     boolean readLPw(List<UserInfo> users) {
         FileInputStream str = null;
         if (mBackupSettingsFilename.exists()) {
@@ -1545,8 +1611,21 @@
                     readDisabledSysPackageLPw(parser);
                 } else if (tagName.equals("cleaning-package")) {
                     String name = parser.getAttributeValue(null, ATTR_NAME);
+                    String userStr = parser.getAttributeValue(null, ATTR_USER);
+                    String codeStr = parser.getAttributeValue(null, ATTR_CODE);
                     if (name != null) {
-                        mPackagesToBeCleaned.add(name);
+                        int user = 0;
+                        boolean andCode = true;
+                        try {
+                            if (userStr != null) {
+                                user = Integer.parseInt(userStr);
+                            }
+                        } catch (NumberFormatException e) {
+                        }
+                        if (codeStr != null) {
+                            andCode = Boolean.parseBoolean(codeStr);
+                        }
+                        addPackageToCleanLPw(user, new PackageCleanItem(name, andCode));
                     }
                 } else if (tagName.equals("renamed-package")) {
                     String nname = parser.getAttributeValue(null, "new");
@@ -1623,7 +1702,8 @@
             if (idObj != null && idObj instanceof SharedUserSetting) {
                 PackageSetting p = getPackageLPw(pp.name, null, pp.realName,
                         (SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
-                        pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true);
+                        pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags,
+                        UserHandle.ALL, true);
                 if (p == null) {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unable to create application package for " + pp.name);
@@ -2292,6 +2372,10 @@
         return ps;
     }
 
+    private String compToString(HashSet<String> cmp) {
+        return cmp != null ? Arrays.toString(cmp.toArray()) : "[]";
+    }
+ 
     boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) {
         if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
             return true;
@@ -2302,24 +2386,26 @@
             Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = "
                     + componentInfo.packageName + " componentName = " + componentInfo.name);
             Log.v(PackageManagerService.TAG, "enabledComponents: "
-                    + Arrays.toString(packageSettings.getEnabledComponents(userId).toArray()));
+                    + compToString(packageSettings.getEnabledComponents(userId)));
             Log.v(PackageManagerService.TAG, "disabledComponents: "
-                    + Arrays.toString(packageSettings.getDisabledComponents(userId).toArray()));
+                    + compToString(packageSettings.getDisabledComponents(userId)));
         }
         if (packageSettings == null) {
             return false;
         }
-        final int enabled = packageSettings.getEnabled(userId);
-        if (enabled == COMPONENT_ENABLED_STATE_DISABLED
-                || enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
+        PackageUserState ustate = packageSettings.readUserState(userId);
+        if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED
+                || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
                 || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
-                    && enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
+                    && ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
             return false;
         }
-        if (packageSettings.getEnabledComponents(userId).contains(componentInfo.name)) {
+        if (ustate.enabledComponents != null
+                && ustate.enabledComponents.contains(componentInfo.name)) {
             return true;
         }
-        if (packageSettings.getDisabledComponents(userId).contains(componentInfo.name)) {
+        if (ustate.disabledComponents != null
+                && ustate.disabledComponents.contains(componentInfo.name)) {
             return false;
         }
         return componentInfo.enabled;
@@ -2378,7 +2464,7 @@
                 if (pkgSetting.installerPackageName != null) {
                     PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
                             pkgSetting.name, null,
-                            pkgSetting.installerPackageName, null, userId);
+                            pkgSetting.installerPackageName, null, new int[] {userId});
                 }
                 pkgSetting.setNotLaunched(false, userId);
             }
@@ -2390,7 +2476,7 @@
     private List<UserInfo> getAllUsers() {
         long id = Binder.clearCallingIdentity();
         try {
-            return UserManagerService.getInstance(mContext).getUsers();
+            return UserManagerService.getInstance().getUsers();
         } catch (NullPointerException npe) {
             // packagemanager not yet initialized
         } finally {
@@ -2427,7 +2513,6 @@
         ApplicationInfo.FLAG_RESTORE_ANY_VERSION, "RESTORE_ANY_VERSION",
         ApplicationInfo.FLAG_EXTERNAL_STORAGE, "EXTERNAL_STORAGE",
         ApplicationInfo.FLAG_LARGE_HEAP, "LARGE_HEAP",
-        ApplicationInfo.FLAG_STOPPED, "STOPPED",
         ApplicationInfo.FLAG_FORWARD_LOCK, "FORWARD_LOCK",
         ApplicationInfo.FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
     };
@@ -2537,22 +2622,28 @@
             pw.print("    permissionsFixed="); pw.print(ps.permissionsFixed);
             pw.print(" haveGids="); pw.println(ps.haveGids);
             pw.print("    pkgFlags=0x"); pw.print(Integer.toHexString(ps.pkgFlags));
-            pw.print(" installStatus="); pw.print(ps.installStatus);
+            pw.print(" installStatus="); pw.println(ps.installStatus);
             for (UserInfo user : users) {
-                pw.print(" User "); pw.print(user.id); pw.print(": ");
+                pw.print("    User "); pw.print(user.id); pw.print(": ");
+                pw.print(" installed=");
+                pw.print(ps.getInstalled(user.id));
                 pw.print(" stopped=");
                 pw.print(ps.getStopped(user.id));
+                pw.print(" notLaunched=");
+                pw.print(ps.getNotLaunched(user.id));
                 pw.print(" enabled=");
                 pw.println(ps.getEnabled(user.id));
-                if (ps.getDisabledComponents(user.id).size() > 0) {
-                    pw.println("    disabledComponents:");
-                    for (String s : ps.getDisabledComponents(user.id)) {
+                HashSet<String> cmp = ps.getDisabledComponents(user.id);
+                if (cmp != null && cmp.size() > 0) {
+                    pw.println("      disabledComponents:");
+                    for (String s : cmp) {
                         pw.print("      "); pw.println(s);
                     }
                 }
-                if (ps.getEnabledComponents(user.id).size() > 0) {
-                    pw.println("    enabledComponents:");
-                    for (String s : ps.getEnabledComponents(user.id)) {
+                cmp = ps.getEnabledComponents(user.id);
+                if (cmp != null && cmp.size() > 0) {
+                    pw.println("      enabledComponents:");
+                    for (String s : cmp) {
                         pw.print("      "); pw.println(s);
                     }
                 }
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 6a76c5d..750aa72 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -25,7 +25,6 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.Binder;
@@ -34,10 +33,8 @@
 import android.os.IUserManager;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.AtomicFile;
-import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.Xml;
@@ -72,59 +69,79 @@
     private static final String USER_LIST_FILENAME = "userlist.xml";
     private static final String USER_PHOTO_FILENAME = "photo.png";
 
-    private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
+    private final Context mContext;
+    private final PackageManagerService mPm;
+    private final Object mInstallLock;
+    private final Object mPackagesLock;
 
     private final File mUsersDir;
     private final File mUserListFile;
+    private final File mBaseUserPath;
+
+    private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
+
     private int[] mUserIds;
     private boolean mGuestEnabled;
     private int mNextSerialNumber;
 
-    private Installer mInstaller;
-    private File mBaseUserPath;
-    private Context mContext;
     private static UserManagerService sInstance;
-    private PackageManagerService mPm;
 
-    public synchronized static UserManagerService getInstance(Context context) {
-        if (sInstance == null) {
-            sInstance = new UserManagerService(context);
+    public static UserManagerService getInstance() {
+        synchronized (UserManagerService.class) {
+            return sInstance;
         }
-        return sInstance;
     }
 
     /**
      * Available for testing purposes.
      */
     UserManagerService(File dataDir, File baseUserPath) {
-        mUsersDir = new File(dataDir, USER_INFO_DIR);
-        mUsersDir.mkdirs();
-        // Make zeroth user directory, for services to migrate their files to that location
-        File userZeroDir = new File(mUsersDir, "0");
-        userZeroDir.mkdirs();
-        mBaseUserPath = baseUserPath;
-        FileUtils.setPermissions(mUsersDir.toString(),
-                FileUtils.S_IRWXU|FileUtils.S_IRWXG
-                |FileUtils.S_IROTH|FileUtils.S_IXOTH,
-                -1, -1);
-        mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
-        readUserList();
+        this(null, null, new Object(), new Object(), dataDir, baseUserPath);
     }
 
-    public UserManagerService(Context context) {
-        this(Environment.getDataDirectory(), new File(Environment.getDataDirectory(), "user"));
-        mContext = context;
+    /**
+     * Called by package manager to create the service.  This is closely
+     * associated with the package manager, and the given lock is the
+     * package manager's own lock.
+     */
+    UserManagerService(Context context, PackageManagerService pm,
+            Object installLock, Object packagesLock) {
+        this(context, pm, installLock, packagesLock,
+                Environment.getDataDirectory(),
+                new File(Environment.getDataDirectory(), "user"));
     }
 
-    void setInstaller(PackageManagerService pm, Installer installer) {
-        mInstaller = installer;
-        mPm = pm;
+    /**
+     * Available for testing purposes.
+     */
+    private UserManagerService(Context context, PackageManagerService pm,
+            Object installLock, Object packagesLock,
+            File dataDir, File baseUserPath) {
+        synchronized (UserManagerService.class) {
+            mContext = context;
+            mPm = pm;
+            mInstallLock = installLock;
+            mPackagesLock = packagesLock;
+            mUsersDir = new File(dataDir, USER_INFO_DIR);
+            mUsersDir.mkdirs();
+            // Make zeroth user directory, for services to migrate their files to that location
+            File userZeroDir = new File(mUsersDir, "0");
+            userZeroDir.mkdirs();
+            mBaseUserPath = baseUserPath;
+            FileUtils.setPermissions(mUsersDir.toString(),
+                    FileUtils.S_IRWXU|FileUtils.S_IRWXG
+                    |FileUtils.S_IROTH|FileUtils.S_IXOTH,
+                    -1, -1);
+            mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
+            readUserList();
+            sInstance = this;
+        }
     }
 
     @Override
     public List<UserInfo> getUsers() {
         checkManageUsersPermission("query users");
-        synchronized (mUsers) {
+        synchronized (mPackagesLock) {
             ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
             for (int i = 0; i < mUsers.size(); i++) {
                 users.add(mUsers.valueAt(i));
@@ -136,7 +153,7 @@
     @Override
     public UserInfo getUserInfo(int userId) {
         checkManageUsersPermission("query user");
-        synchronized (mUsers) {
+        synchronized (mPackagesLock) {
             return getUserInfoLocked(userId);
         }
     }
@@ -149,7 +166,7 @@
     }
 
     public boolean exists(int userId) {
-        synchronized (mUsers) {
+        synchronized (mPackagesLock) {
             return ArrayUtils.contains(mUserIds, userId);
         }
     }
@@ -157,7 +174,7 @@
     @Override
     public void setUserName(int userId, String name) {
         checkManageUsersPermission("rename users");
-        synchronized (mUsers) {
+        synchronized (mPackagesLock) {
             UserInfo info = mUsers.get(userId);
             if (name != null && !name.equals(info.name)) {
                 info.name = name;
@@ -169,7 +186,7 @@
     @Override
     public ParcelFileDescriptor setUserIcon(int userId) {
         checkManageUsersPermission("update users");
-        synchronized (mUsers) {
+        synchronized (mPackagesLock) {
             UserInfo info = mUsers.get(userId);
             if (info == null) return null;
             ParcelFileDescriptor fd = updateIconBitmapLocked(info);
@@ -183,7 +200,7 @@
     @Override
     public void setGuestEnabled(boolean enable) {
         checkManageUsersPermission("enable guest users");
-        synchronized (mUsers) {
+        synchronized (mPackagesLock) {
             if (mGuestEnabled != enable) {
                 mGuestEnabled = enable;
                 // Erase any guest user that currently exists
@@ -206,7 +223,7 @@
 
     @Override
     public boolean isGuestEnabled() {
-        synchronized (mUsers) {
+        synchronized (mPackagesLock) {
             return mGuestEnabled;
         }
     }
@@ -262,11 +279,17 @@
      * @return the array of user ids.
      */
     int[] getUserIds() {
+        synchronized (mPackagesLock) {
+            return mUserIds;
+        }
+    }
+
+    int[] getUserIdsLPr() {
         return mUserIds;
     }
 
     private void readUserList() {
-        synchronized (mUsers) {
+        synchronized (mPackagesLock) {
             readUserListLocked();
         }
     }
@@ -499,15 +522,15 @@
         int userId = getNextAvailableId();
         UserInfo userInfo = new UserInfo(userId, name, null, flags);
         File userPath = new File(mBaseUserPath, Integer.toString(userId));
-        if (!createPackageFolders(userId, userPath)) {
-            return null;
-        }
-        synchronized (mUsers) {
-            userInfo.serialNumber = mNextSerialNumber++;
-            mUsers.put(userId, userInfo);
-            writeUserListLocked();
-            writeUserLocked(userInfo);
-            updateUserIdsLocked();
+        synchronized (mInstallLock) {
+            synchronized (mPackagesLock) {
+                userInfo.serialNumber = mNextSerialNumber++;
+                mUsers.put(userId, userInfo);
+                writeUserListLocked();
+                writeUserLocked(userInfo);
+                updateUserIdsLocked();
+                mPm.createNewUserLILPw(userId, userPath);
+            }
         }
         if (userInfo != null) {
             Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
@@ -526,24 +549,37 @@
      */
     public boolean removeUser(int userHandle) {
         checkManageUsersPermission("Only the system can remove users");
-        boolean result;
-        synchronized (mUsers) {
-            result = removeUserLocked(userHandle);
-        }
+        synchronized (mInstallLock) {
+            synchronized (mPackagesLock) {
+                final UserInfo user = mUsers.get(userHandle);
+                if (userHandle == 0 || user == null) {
+                    return false;
+                }
 
-        // Cleanup package manager settings
-        mPm.cleanUpUser(userHandle);
+                // Cleanup package manager settings
+                mPm.cleanUpUserLILPw(userHandle);
+
+                // Remove this user from the list
+                mUsers.remove(userHandle);
+                // Remove user file
+                AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
+                userFile.delete();
+                // Update the user list
+                writeUserListLocked();
+                updateUserIdsLocked();
+            }
+        }
 
         // Let other services shutdown any activity
         Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
         mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
-        return result;
+        return true;
     }
 
     @Override
     public int getUserSerialNumber(int userHandle) {
-        synchronized (mUsers) {
+        synchronized (mPackagesLock) {
             if (!exists(userHandle)) return -1;
             return getUserInfoLocked(userHandle).serialNumber;
         }
@@ -551,7 +587,7 @@
 
     @Override
     public int getUserHandle(int userSerialNumber) {
-        synchronized (mUsers) {
+        synchronized (mPackagesLock) {
             for (int userId : mUserIds) {
                 if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId;
             }
@@ -560,63 +596,15 @@
         }
     }
 
-    private boolean removeUserLocked(int userHandle) {
-        final UserInfo user = mUsers.get(userHandle);
-        if (userHandle == 0 || user == null) {
-            return false;
-        }
-
-        // Remove this user from the list
-        mUsers.remove(userHandle);
-        // Remove user file
-        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
-        userFile.delete();
-        // Update the user list
-        writeUserListLocked();
-        updateUserIdsLocked();
-
-        removePackageFolders(userHandle);
-        return true;
-    }
-
-    public void installPackageForAllUsers(String packageName, int uid) {
-        for (int userId : mUserIds) {
-            // Don't do it for the primary user, it will become recursive.
-            if (userId == 0)
-                continue;
-            mInstaller.createUserData(packageName, UserHandle.getUid(userId, uid),
-                    userId);
-        }
-    }
-
-    public void clearUserDataForAllUsers(String packageName) {
-        for (int userId : mUserIds) {
-            // Don't do it for the primary user, it will become recursive.
-            if (userId == 0)
-                continue;
-            mInstaller.clearUserData(packageName, userId);
-        }
-    }
-
-    public void removePackageForAllUsers(String packageName) {
-        for (int userId : mUserIds) {
-            // Don't do it for the primary user, it will become recursive.
-            if (userId == 0)
-                continue;
-            mInstaller.remove(packageName, userId);
-        }
-    }
-
     /**
      * Caches the list of user ids in an array, adjusting the array size when necessary.
      */
     private void updateUserIdsLocked() {
-        if (mUserIds == null || mUserIds.length != mUsers.size()) {
-            mUserIds = new int[mUsers.size()];
-        }
+        int[] newUsers = new int[mUsers.size()];
         for (int i = 0; i < mUsers.size(); i++) {
-            mUserIds[i] = mUsers.keyAt(i);
+            newUsers[i] = mUsers.keyAt(i);
         }
+        mUserIds = newUsers;
     }
 
     /**
@@ -626,7 +614,7 @@
      * @return
      */
     private int getNextAvailableId() {
-        synchronized (mUsers) {
+        synchronized (mPackagesLock) {
             int i = 0;
             while (i < Integer.MAX_VALUE) {
                 if (mUsers.indexOfKey(i) < 0) {
@@ -637,26 +625,4 @@
             return i;
         }
     }
-
-    private boolean createPackageFolders(int id, File userPath) {
-        // mInstaller may not be available for unit-tests.
-        if (mInstaller == null) return true;
-
-        // Create the user path
-        userPath.mkdir();
-        FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
-                | FileUtils.S_IXOTH, -1, -1);
-
-        mInstaller.cloneUserData(0, id, false);
-
-        return true;
-    }
-
-    boolean removePackageFolders(int id) {
-        // mInstaller may not be available for unit-tests.
-        if (mInstaller == null) return true;
-
-        mInstaller.removeUserDataDirs(id);
-        return true;
-    }
 }
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 5f917af..cd211da 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -1112,7 +1112,7 @@
 
     private final class DisplayControllerHandler extends Handler {
         public DisplayControllerHandler(Looper looper) {
-            super(looper);
+            super(looper, null, true /*async*/);
         }
 
         @Override
diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java
index 64a0462..3524a08 100644
--- a/services/java/com/android/server/power/DisplayPowerState.java
+++ b/services/java/com/android/server/power/DisplayPowerState.java
@@ -49,8 +49,6 @@
     private static final int DIRTY_ELECTRON_BEAM = 1 << 1;
     private static final int DIRTY_BRIGHTNESS = 1 << 2;
 
-    private static final int DIRTY_ALL = 0xffffffff;
-
     private final Choreographer mChoreographer;
     private final ElectronBeam mElectronBeam;
     private final PhotonicModulator mScreenBrightnessModulator;
@@ -68,10 +66,16 @@
         mElectronBeam = electronBean;
         mScreenBrightnessModulator = screenBrightnessModulator;
 
+        // At boot time, we know that the screen is on and the electron beam
+        // animation is not playing.  We don't know the screen's brightness though,
+        // so prepare to set it to a known state when the state is next applied.
+        // Although we set the brightness to full on here, the display power controller
+        // will reset the brightness to a new level immediately before the changes
+        // actually have a chance to be applied.
         mScreenOn = true;
         mElectronBeamLevel = 1.0f;
         mScreenBrightness = PowerManager.BRIGHTNESS_ON;
-        invalidate(DIRTY_ALL);
+        invalidate(DIRTY_BRIGHTNESS);
     }
 
     public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL =
diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java
index 9d928e5..75f8445 100644
--- a/services/java/com/android/server/power/Notifier.java
+++ b/services/java/com/android/server/power/Notifier.java
@@ -422,7 +422,7 @@
 
     private final class NotifierHandler extends Handler {
         public NotifierHandler(Looper looper) {
-            super(looper);
+            super(looper, null, true /*async*/);
         }
 
         @Override
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 9f2b247..6d68104 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -296,6 +296,7 @@
         }
 
         nativeInit();
+        nativeSetPowerState(true, true);
     }
 
     /**
@@ -305,6 +306,14 @@
     public void init(Context context, LightsService ls,
             ActivityManagerService am, BatteryService bs, IBatteryStats bss,
             DisplayManagerService dm) {
+        // Forcibly turn the screen on at boot so that it is in a known power state.
+        // We do this in init() rather than in the constructor because setting the
+        // screen state requires a call into surface flinger which then needs to call back
+        // into the activity manager to check permissions.  Unfortunately the
+        // activity manager is not running when the constructor is called, so we
+        // have to defer setting the screen state until this point.
+        nativeSetScreenState(true);
+
         mContext = context;
         mLightsService = ls;
         mBatteryService = bs;
@@ -1952,7 +1961,7 @@
      */
     private final class PowerManagerHandler extends Handler {
         public PowerManagerHandler(Looper looper) {
-            super(looper);
+            super(looper, null, true /*async*/);
         }
 
         @Override
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index 26ea7c1..3fcf680 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -71,12 +71,6 @@
         mFlags = flags;
         mLocalWin = localWin;
         mNotifiedWindows = new ArrayList<WindowState>();
-        WindowState win = service.mWindowMap.get(token);
-        if (win != null) {
-            mDisplayContent = win.mDisplayContent;
-        } else {
-            Slog.e(WindowManagerService.TAG, "No window associated with token");
-        }
     }
 
     void reset() {
@@ -92,7 +86,11 @@
         mNotifiedWindows = null;
     }
 
-    void register() {
+    /**
+     * @param displayContent The display parameters associated with the window being dragged.
+     */
+    void register(DisplayContent displayContent) {
+        mDisplayContent = displayContent;
         if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel");
         if (mClientChannel != null) {
             Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel");
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index d44b170..1ffbecc 100644
--- a/services/java/com/android/server/wm/Session.java
+++ b/services/java/com/android/server/wm/Session.java
@@ -280,7 +280,7 @@
             // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
             // the actual drag event dispatch stuff in the dragstate
 
-            mService.mDragState.register();
+            mService.mDragState.register(callingWin.mDisplayContent);
             mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
             if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
                     mService.mDragState.mServerChannel)) {
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 12ad4fea..6047cda 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -360,6 +360,18 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public ComponentName startServiceAsUser(Intent service, UserHandle user) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
+    public boolean stopServiceAsUser(Intent service, UserHandle user) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public boolean bindService(Intent service, ServiceConnection conn, int flags) {
         throw new UnsupportedOperationException();
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index ac40fc6b..562f286 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -41,6 +41,7 @@
 import android.content.pm.UserInfo;
 import android.content.pm.VerificationParams;
 import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
@@ -531,6 +532,15 @@
         throw new UnsupportedOperationException();
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public int installExistingPackage(String packageName)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public void verifyPendingInstall(int id, int verificationCode) {
         throw new UnsupportedOperationException();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 3d45bff..76033d4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1281,6 +1281,18 @@
     }
 
     @Override
+    public ComponentName startServiceAsUser(Intent arg0, UserHandle arg1) {
+        // pass
+        return null;
+    }
+
+    @Override
+    public boolean stopServiceAsUser(Intent arg0, UserHandle arg1) {
+        // pass
+        return false;
+    }
+
+    @Override
     public void unbindService(ServiceConnection arg0) {
         // pass
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 3579b86..284bee8 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1358,7 +1358,11 @@
 
     private void init() {
         mWifiServiceMessenger = getWifiServiceMessenger();
-        if (mWifiServiceMessenger == null) throw new RuntimeException("Failed to initialize");
+        if (mWifiServiceMessenger == null) {
+            mAsyncChannel = null;
+            return;
+        }
+
         HandlerThread t = new HandlerThread("WifiManager");
         t.start();
         mHandler = new ServiceHandler(t.getLooper());
@@ -1372,7 +1376,7 @@
 
     private void validateChannel() {
         if (mAsyncChannel == null) throw new IllegalStateException(
-                "Bad WifiManager instance state, re-initialize");
+                "No permission to access and change wifi or a bad initialization");
     }
 
     /**
@@ -1517,6 +1521,8 @@
             return mService.getWifiServiceMessenger();
         } catch (RemoteException e) {
             return null;
+        } catch (SecurityException e) {
+            return null;
         }
     }