Merge "Fix Camera and GoogleNow launching in keyguard" into jb-mr1-dev
diff --git a/Android.mk b/Android.mk
index a637eb7..07500b1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -74,6 +74,7 @@
 	core/java/android/app/ISearchManager.aidl \
 	core/java/android/app/ISearchManagerCallback.aidl \
 	core/java/android/app/IServiceConnection.aidl \
+	core/java/android/app/IStopUserCallback.aidl \
 	core/java/android/app/IThumbnailReceiver.aidl \
 	core/java/android/app/IThumbnailRetriever.aidl \
 	core/java/android/app/ITransientNotification.aidl \
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 47d6a02..7f3dbe5 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -31,7 +31,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -141,6 +140,8 @@
             runToUri(true);
         } else if (op.equals("switch-user")) {
             runSwitchUser();
+        } else if (op.equals("stop-user")) {
+            runStopUser();
         } else {
             throw new IllegalArgumentException("Unknown command: " + op);
         }
@@ -323,7 +324,6 @@
                 mUserId = Integer.parseInt(nextArgRequired());
             } else {
                 System.err.println("Error: Unknown option: " + opt);
-                showUsage();
                 return null;
             }
         }
@@ -594,7 +594,6 @@
                 no_window_animation = true;
             } else {
                 System.err.println("Error: Unknown option: " + opt);
-                showUsage();
                 return;
             }
         }
@@ -738,7 +737,6 @@
                 persistent = true;
             } else {
                 System.err.println("Error: Unknown option: " + opt);
-                showUsage();
                 return;
             }
         }
@@ -752,13 +750,27 @@
     }
 
     private void runSwitchUser() throws Exception {
-        if (android.os.Process.myUid() != 0) {
-            throw new RuntimeException("switchuser can only be run as root");
-        }
         String user = nextArgRequired();
         mAm.switchUser(Integer.parseInt(user));
     }
 
+    private void runStopUser() throws Exception {
+        String user = nextArgRequired();
+        int res = mAm.stopUser(Integer.parseInt(user), null);
+        if (res != ActivityManager.USER_OP_SUCCESS) {
+            String txt = "";
+            switch (res) {
+                case ActivityManager.USER_OP_IS_CURRENT:
+                    txt = " (Can't stop current user)";
+                    break;
+                case ActivityManager.USER_OP_UNKNOWN_USER:
+                    txt = " (Unknown user " + user + ")";
+                    break;
+            }
+            System.err.println("Switch failed: " + res + txt);
+        }
+    }
+
     class MyActivityController extends IActivityController.Stub {
         final String mGdbPort;
 
@@ -1047,7 +1059,6 @@
                 gdbPort = nextArgRequired();
             } else {
                 System.err.println("Error: Unknown option: " + opt);
-                showUsage();
                 return;
             }
         }
@@ -1065,7 +1076,6 @@
             enabled = false;
         } else {
             System.err.println("Error: enabled mode must be 'on' or 'off' at " + mode);
-            showUsage();
             return;
         }
 
@@ -1090,7 +1100,6 @@
             int div = size.indexOf('x');
             if (div <= 0 || div >= (size.length()-1)) {
                 System.err.println("Error: bad size " + size);
-                showUsage();
                 return;
             }
             String mstr = size.substring(0, div);
@@ -1100,7 +1109,6 @@
                 n = Integer.parseInt(nstr);
             } catch (NumberFormatException e) {
                 System.err.println("Error: bad number " + e);
-                showUsage();
                 return;
             }
         }
@@ -1139,12 +1147,10 @@
                 density = Integer.parseInt(densityStr);
             } catch (NumberFormatException e) {
                 System.err.println("Error: bad number " + e);
-                showUsage();
                 return;
             }
             if (density < 72) {
                 System.err.println("Error: density must be >= 72");
-                showUsage();
                 return;
             }
         }
@@ -1345,6 +1351,7 @@
                 "       am to-uri [INTENT]\n" +
                 "       am to-intent-uri [INTENT]\n" +
                 "       am switch-user <USER_ID>\n" +
+                "       am stop-user <USER_ID>\n" +
                 "\n" +
                 "am start: start an Activity.  Options are:\n" +
                 "    -D: enable debugging\n" +
@@ -1403,6 +1410,12 @@
                 "\n" +
                 "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
                 "\n" +
+                "am switch-user: switch to put USER_ID in the foreground, starting" +
+                "  execution of that user if it is currently stopped.\n" +
+                "\n" +
+                "am stop-user: stop execution of USER_ID, not allowing it to run any" +
+                "  code until a later explicit switch to it.\n" +
+                "\n" +
                 "<INTENT> specifications include these flags and arguments:\n" +
                 "    [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
                 "    [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f8a9d75..26d8c17 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -211,6 +211,15 @@
      */
     public static final int INTENT_SENDER_SERVICE = 4;
 
+    /** @hide User operation call: success! */
+    public static final int USER_OP_SUCCESS = 0;
+
+    /** @hide User operation call: given user id is not known. */
+    public static final int USER_OP_UNKNOWN_USER = -1;
+
+    /** @hide User operation call: given user id is the current user, can't be stopped. */
+    public static final int USER_OP_IS_CURRENT = -2;
+
     /*package*/ ActivityManager(Context context, Handler handler) {
         mContext = context;
         mHandler = handler;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index adc9434..05c009f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1570,6 +1570,17 @@
             return true;
         }
 
+        case STOP_USER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int userid = data.readInt();
+            IStopUserCallback callback = IStopUserCallback.Stub.asInterface(
+                    data.readStrongBinder());
+            int result = stopUser(userid, callback);
+            reply.writeNoException();
+            reply.writeInt(result);
+            return true;
+        }
+
         case GET_CURRENT_USER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             UserInfo userInfo = getCurrentUser();
@@ -3756,11 +3767,25 @@
         return result;
     }
 
+    public int stopUser(int userid, IStopUserCallback callback) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(userid);
+        data.writeStrongInterface(callback);
+        mRemote.transact(STOP_USER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int result = reply.readInt();
+        reply.recycle();
+        data.recycle();
+        return result;
+    }
+
     public UserInfo getCurrentUser() throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        mRemote.transact(SWITCH_USER_TRANSACTION, data, reply, 0);
+        mRemote.transact(GET_CURRENT_USER_TRANSACTION, data, reply, 0);
         reply.readException();
         UserInfo userInfo = UserInfo.CREATOR.createFromParcel(reply);
         reply.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c3e911e..70d8445 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -331,6 +331,7 @@
 
     // Multi-user APIs
     public boolean switchUser(int userid) throws RemoteException;
+    public int stopUser(int userid, IStopUserCallback callback) throws RemoteException;
     public UserInfo getCurrentUser() throws RemoteException;
 
     public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException;
@@ -611,4 +612,5 @@
     int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+150;
     int IS_INTENT_SENDER_AN_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+151;
     int START_ACTIVITY_AS_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+152;
+    int STOP_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+153;
 }
diff --git a/core/java/android/app/IStopUserCallback.aidl b/core/java/android/app/IStopUserCallback.aidl
new file mode 100644
index 0000000..19ac1d5
--- /dev/null
+++ b/core/java/android/app/IStopUserCallback.aidl
@@ -0,0 +1,27 @@
+/*
+** 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.app;
+
+/**
+ * Callback to find out when we have finished stopping a user.
+ * {@hide}
+ */
+interface IStopUserCallback
+{
+    void userStopped(int userId);
+    void userStopAborted(int userId);
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 06edf32..53e0a75 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -27,7 +27,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
-import android.media.RemoteControlClient;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -2287,6 +2286,15 @@
             "android.intent.action.USER_ADDED";
 
     /**
+     * Broadcast sent to the system when a user is stopped. Carries an extra EXTRA_USER_HANDLE that has
+     * the userHandle of the user.  This is similar to {@link #ACTION_PACKAGE_RESTARTED},
+     * but for an entire user instead of a specific package.
+     * @hide
+     */
+    public static final String ACTION_USER_STOPPED =
+            "android.intent.action.USER_STOPPED";
+
+    /**
      * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has
      * the userHandle of the user.
      * @hide
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 650681a..3477a90 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -49,6 +49,7 @@
         sPackageFilt.addAction(Intent.ACTION_UID_REMOVED);
         sPackageFilt.addDataScheme("package");
         sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
+        sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
         sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
         sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
     }
@@ -136,6 +137,9 @@
     public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
         return false;
     }
+
+    public void onHandleUserStop(Intent intent, int userHandle) {
+    }
     
     public void onUidRemoved(int uid) {
     }
@@ -307,6 +311,10 @@
                     intent.getIntExtra(Intent.EXTRA_UID, 0), true);
         } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
             onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
+        } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+            if (intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
+                onHandleUserStop(intent, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+            }
         } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
             String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             mAppearingPackages = pkgList;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5829787..96eb54a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -62,6 +62,7 @@
     <protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
     <protected-broadcast android:name="android.intent.action.USER_ADDED" />
     <protected-broadcast android:name="android.intent.action.USER_REMOVED" />
+    <protected-broadcast android:name="android.intent.action.USER_STOPPED" />
     <protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
 
     <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
diff --git a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png
new file mode 100644
index 0000000..c30eb1c
--- /dev/null
+++ b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png
new file mode 100644
index 0000000..e5d5771
--- /dev/null
+++ b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png
Binary files differ
diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml
index ca0fa1d..b404155 100644
--- a/core/res/res/layout-land/keyguard_host_view.xml
+++ b/core/res/res/layout-land/keyguard_host_view.xml
@@ -26,7 +26,7 @@
     android:layout_height="match_parent"
     android:orientation="horizontal">
 
-    <com.android.internal.policy.impl.keyguard.KeyguardWidgetView
+    <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
         android:id="@+id/app_widget_container"
         android:layout_width="0dip"
         android:layout_height="match_parent"
@@ -35,8 +35,8 @@
 
         <!-- TODO: Remove this once supported as a widget -->
         <include layout="@layout/keyguard_status_view"/>
+    </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
 
-    </com.android.internal.policy.impl.keyguard.KeyguardWidgetView>
 
     <ViewFlipper
         android:id="@+id/view_flipper"
diff --git a/core/res/res/layout/keyguard_selector_view.xml b/core/res/res/layout/keyguard_selector_view.xml
index 6f6611d..d516369 100644
--- a/core/res/res/layout/keyguard_selector_view.xml
+++ b/core/res/res/layout/keyguard_selector_view.xml
@@ -26,7 +26,7 @@
     android:layout_height="match_parent"
     android:orientation="vertical">
 
-    <com.android.internal.policy.impl.keyguard.KeyguardWidgetView
+    <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
         android:id="@+id/app_widget_container"
         android:layout_width="match_parent"
         android:layout_height="0dip"
@@ -34,7 +34,7 @@
         android:visibility="gone">
             <!-- TODO: Remove this once supported as a widget -->
             <include layout="@layout/keyguard_status_view"/>
-    </com.android.internal.policy.impl.keyguard.KeyguardWidgetView>
+    </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
 
     <RelativeLayout
         android:layout_width="wrap_content"
diff --git a/core/res/res/layout/keyguard_status_view.xml b/core/res/res/layout/keyguard_status_view.xml
index 3560ea2..f8d05b7 100644
--- a/core/res/res/layout/keyguard_status_view.xml
+++ b/core/res/res/layout/keyguard_status_view.xml
@@ -18,67 +18,85 @@
 -->
 
 <!-- This is a view that shows general status information in Keyguard. -->
-<com.android.internal.policy.impl.keyguard.KeyguardStatusView
+<com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:gravity="center_horizontal">
 
-    <com.android.internal.widget.DigitalClock android:id="@+id/time"
-        android:layout_marginTop="@*android:dimen/keyguard_lockscreen_status_line_clockfont_top_margin"
-        android:layout_marginBottom="12dip"
-        android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
-        android:layout_gravity="end">
+    <com.android.internal.policy.impl.keyguard.KeyguardStatusView
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center_horizontal">
 
-        <!-- Because we can't have multi-tone fonts, we render two TextViews, one on
-        top of the other. Hence the redundant layout... -->
-        <TextView android:id="@*android:id/timeDisplayBackground"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:ellipsize="none"
-            android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:layout_marginBottom="6dip"
-            android:textColor="@*android:color/lockscreen_clock_background"
-            />
+        <com.android.internal.widget.DigitalClock android:id="@+id/time"
+            android:layout_marginTop="@*android:dimen/keyguard_lockscreen_status_line_clockfont_top_margin"
+            android:layout_marginBottom="12dip"
+            android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
+            android:layout_gravity="end">
 
-        <TextView android:id="@*android:id/timeDisplayForeground"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:ellipsize="none"
-            android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:layout_marginBottom="6dip"
-            android:textColor="@*android:color/lockscreen_clock_foreground"
-            android:layout_alignStart="@*android:id/timeDisplayBackground"
-            android:layout_alignTop="@*android:id/timeDisplayBackground"
-            />
+            <!-- Because we can't have multi-tone fonts, we render two TextViews, one on
+            top of the other. Hence the redundant layout... -->
+            <TextView android:id="@*android:id/timeDisplayBackground"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:ellipsize="none"
+                android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:layout_marginBottom="6dip"
+                android:textColor="@*android:color/lockscreen_clock_background"
+                />
 
-    </com.android.internal.widget.DigitalClock>
+            <TextView android:id="@*android:id/timeDisplayForeground"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:ellipsize="none"
+                android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:layout_marginBottom="6dip"
+                android:textColor="@*android:color/lockscreen_clock_foreground"
+                android:layout_alignStart="@*android:id/timeDisplayBackground"
+                android:layout_alignTop="@*android:id/timeDisplayBackground"
+                />
 
-    <LinearLayout
-        android:orientation="horizontal"
-        android:layout_gravity="end"
-        android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin">
+        </com.android.internal.widget.DigitalClock>
+
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_gravity="end"
+            android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin">
+
+            <TextView
+                android:id="@*android:id/date"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:ellipsize="marquee"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+                />
+
+            <TextView
+                android:id="@*android:id/alarm_status"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="16dip"
+                android:singleLine="true"
+                android:ellipsize="marquee"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+                android:drawablePadding="4dip"
+                />
+
+        </LinearLayout>
 
         <TextView
-            android:id="@*android:id/date"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
-            />
-
-        <TextView
-            android:id="@*android:id/alarm_status"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="16dip"
+            android:id="@*android:id/status1"
+            android:layout_gravity="end"
+            android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
             android:singleLine="true"
             android:ellipsize="marquee"
             android:textAppearance="?android:attr/textAppearanceMedium"
@@ -86,29 +104,16 @@
             android:drawablePadding="4dip"
             />
 
-    </LinearLayout>
+        <TextView
+            android:id="@*android:id/carrier"
+            android:layout_gravity="end"
+            android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+            android:textColor="?android:attr/textColorSecondary"
+            />
 
-    <TextView
-        android:id="@*android:id/status1"
-        android:layout_gravity="end"
-        android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
-        android:drawablePadding="4dip"
-        />
-
-    <TextView
-        android:id="@*android:id/carrier"
-        android:layout_gravity="end"
-        android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
-        android:textColor="?android:attr/textColorSecondary"
-        />
-
-</com.android.internal.policy.impl.keyguard.KeyguardStatusView>
-
+    </com.android.internal.policy.impl.keyguard.KeyguardStatusView>
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
\ No newline at end of file
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index cc4d05a..372a1ee 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -251,4 +251,6 @@
     <!-- Height of widget view in keyguard. -->
     <dimen name="kg_widget_view_height">0dp</dimen>
 
+    <!-- Padding surrounding each widget page -->
+    <dimen name="kg_widget_page_padding">10dp</dimen>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b110a40..6414df8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1237,6 +1237,7 @@
   <java-symbol type="dimen" name="navigation_bar_height_landscape" />
   <java-symbol type="dimen" name="navigation_bar_width" />
   <java-symbol type="dimen" name="status_bar_height" />
+  <java-symbol type="dimen" name="kg_widget_page_padding" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_off" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_on" />
   <java-symbol type="drawable" name="ic_jog_dial_unlock" />
@@ -1254,6 +1255,8 @@
   <java-symbol type="drawable" name="jog_tab_target_yellow" />
   <java-symbol type="drawable" name="menu_background" />
   <java-symbol type="drawable" name="stat_sys_secure" />
+  <java-symbol type="drawable" name="kg_widget_overscroll_layer_left" />
+  <java-symbol type="drawable" name="kg_widget_overscroll_layer_right" />
   <java-symbol type="id" name="action_mode_bar_stub" />
   <java-symbol type="id" name="alarm_status" />
   <java-symbol type="id" name="backspace" />
@@ -3749,5 +3752,5 @@
   <public type="attr" name="listPreferredItemPaddingStart" />
   <public type="attr" name="listPreferredItemPaddingEnd" />
   <public type="attr" name="singleUser" />
-  
+
 </resources>
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 1e425ea..d74a5e7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -70,7 +70,7 @@
     private static final int SECURITY_ACCOUNT_ID = R.id.keyguard_account_view;
 
     private AppWidgetHost mAppWidgetHost;
-    private ViewGroup mAppWidgetContainer;
+    private KeyguardWidgetPager mAppWidgetContainer;
     private ViewFlipper mViewFlipper;
     private Button mEmergencyDialerButton;
     private boolean mEnableMenuKey;
@@ -121,7 +121,7 @@
 
     @Override
     protected void onFinishInflate() {
-        mAppWidgetContainer = (ViewGroup) findViewById(R.id.app_widget_container);
+        mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
         mAppWidgetContainer.setVisibility(VISIBLE);
 
         // View Flipper
@@ -182,7 +182,7 @@
     }
 
     void addWidget(AppWidgetHostView view) {
-        mAppWidgetContainer.addView(view);
+        mAppWidgetContainer.addWidget(view);
     }
 
     private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
new file mode 100644
index 0000000..d778129
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
@@ -0,0 +1,96 @@
+/*
+ * 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.policy.impl.keyguard;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import com.android.internal.R;
+
+public class KeyguardWidgetFrame extends FrameLayout {
+    private final static PorterDuffXfermode sAddBlendMode =
+            new PorterDuffXfermode(PorterDuff.Mode.ADD);
+    private static int sWidgetPagePadding;
+    private static Drawable sLeftOverscrollDrawable;
+    private static Drawable sRightOverscrollDrawable;
+
+    private Drawable mForegroundDrawable;
+    private final Rect mForegroundRect = new Rect();
+    private int mForegroundAlpha = 0;
+
+    public KeyguardWidgetFrame(Context context) {
+        this(context, null, 0);
+    }
+
+    public KeyguardWidgetFrame(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        if (sLeftOverscrollDrawable == null) {
+            Resources res = context.getResources();
+            sLeftOverscrollDrawable = res.getDrawable(
+                    com.android.internal.R.drawable.kg_widget_overscroll_layer_left);
+            sRightOverscrollDrawable = res.getDrawable(
+                    com.android.internal.R.drawable.kg_widget_overscroll_layer_right);
+            sWidgetPagePadding =
+                    res.getDimensionPixelSize(com.android.internal.R.dimen.kg_widget_page_padding);
+        }
+        setPadding(sWidgetPagePadding, sWidgetPagePadding, sWidgetPagePadding, sWidgetPagePadding);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+        if (mForegroundAlpha > 0) {
+            mForegroundDrawable.setBounds(mForegroundRect);
+            Paint p = ((NinePatchDrawable) mForegroundDrawable).getPaint();
+            p.setXfermode(sAddBlendMode);
+            mForegroundDrawable.draw(canvas);
+            p.setXfermode(null);
+        }
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        mForegroundRect.set(sWidgetPagePadding, sWidgetPagePadding,
+                w - sWidgetPagePadding, h - sWidgetPagePadding);
+    }
+
+    void setOverScrollAmount(float r, boolean left) {
+        if (left && mForegroundDrawable != sLeftOverscrollDrawable) {
+            mForegroundDrawable = sLeftOverscrollDrawable;
+        } else if (!left && mForegroundDrawable != sRightOverscrollDrawable) {
+            mForegroundDrawable = sRightOverscrollDrawable;
+        }
+
+        mForegroundAlpha = (int) Math.round((r * 255));
+        mForegroundDrawable.setAlpha(mForegroundAlpha);
+        invalidate();
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
similarity index 76%
rename from policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetView.java
rename to policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index 120f8f8..7d077e2 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -16,39 +16,53 @@
 package com.android.internal.policy.impl.keyguard;
 
 import android.animation.TimeInterpolator;
+import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.Gravity;
 import android.view.View;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 
-public class KeyguardWidgetView extends PagedView {
+import android.widget.FrameLayout;
 
-    public KeyguardWidgetView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public KeyguardWidgetView(Context context) {
-        this(null, null, 0);
-    }
-
-    public KeyguardWidgetView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-    }
-
+public class KeyguardWidgetPager extends PagedView {
     ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
-    private static float CAMERA_DISTANCE = 1500;
+    private static float CAMERA_DISTANCE = 10000;
     private static float TRANSITION_SCALE_FACTOR = 0.74f;
     private static float TRANSITION_PIVOT = 0.65f;
     private static float TRANSITION_MAX_ROTATION = 30;
     private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
     private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
     private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
+
+    public KeyguardWidgetPager(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public KeyguardWidgetPager(Context context) {
+        this(null, null, 0);
+    }
+
+    public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    /*
+     * We wrap widgets in a special frame which handles drawing the overscroll foreground.
+     */
+    public void addWidget(AppWidgetHostView widget) {
+        KeyguardWidgetFrame frame = new KeyguardWidgetFrame(getContext());
+        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+                LayoutParams.MATCH_PARENT);
+        lp.gravity = Gravity.CENTER;
+        // The framework adds a default padding to AppWidgetHostView. We don't need this padding
+        // for the Keyguard, so we override it to be 0.
+        widget.setPadding(0,  0, 0, 0);
+        frame.addView(widget, lp);
+        addView(frame);
+    }
+
     /*
      * This interpolator emulates the rate at which the perceived scale of an object changes
      * as its distance from a camera increases. When this interpolator is applied to a scale
@@ -68,6 +82,11 @@
         }
     }
 
+    @Override
+    protected void overScroll(float amount) {
+        acceleratedOverScroll(amount);
+    }
+
     // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
     @Override
     protected void screenScrolled(int screenCenter) {
@@ -103,6 +122,10 @@
                         // Overscroll to the left
                         v.setPivotX(TRANSITION_PIVOT * pageWidth);
                         v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
+                        if (v instanceof KeyguardWidgetFrame) {
+                            ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
+                                    true);
+                        }
                         scale = 1.0f;
                         alpha = 1.0f;
                         // On the first page, we don't want the page to have any lateral motion
@@ -113,6 +136,10 @@
                         v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
                         scale = 1.0f;
                         alpha = 1.0f;
+                        if (v instanceof KeyguardWidgetFrame) {
+                            ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
+                                    false);
+                        }
                         // On the last page, we don't want the page to have any lateral motion.
                         translationX = 0;
                     } else {
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 32ac8e1..9b7be02 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -34,6 +34,7 @@
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.os.WorkSource;
 import android.text.TextUtils;
 import android.text.format.Time;
@@ -303,7 +304,7 @@
             }
         }
     }
-    
+
     public void removeLocked(String packageName) {
         removeLocked(mRtcWakeupAlarms, packageName);
         removeLocked(mRtcAlarms, packageName);
@@ -327,6 +328,29 @@
             }
         }
     }
+
+    public void removeUserLocked(int userHandle) {
+        removeUserLocked(mRtcWakeupAlarms, userHandle);
+        removeUserLocked(mRtcAlarms, userHandle);
+        removeUserLocked(mElapsedRealtimeWakeupAlarms, userHandle);
+        removeUserLocked(mElapsedRealtimeAlarms, userHandle);
+    }
+
+    private void removeUserLocked(ArrayList<Alarm> alarmList, int userHandle) {
+        if (alarmList.size() <= 0) {
+            return;
+        }
+
+        // iterator over the list removing any it where the intent match
+        Iterator<Alarm> it = alarmList.iterator();
+
+        while (it.hasNext()) {
+            Alarm alarm = it.next();
+            if (UserHandle.getUserId(alarm.operation.getTargetUid()) == userHandle) {
+                it.remove();
+            }
+        }
+    }
     
     public boolean lookForPackageLocked(String packageName) {
         return lookForPackageLocked(mRtcWakeupAlarms, packageName)
@@ -822,6 +846,7 @@
              // Register for events related to sdcard installation.
             IntentFilter sdFilter = new IntentFilter();
             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+            sdFilter.addAction(Intent.ACTION_USER_STOPPED);
             mContext.registerReceiver(this, sdFilter);
         }
         
@@ -841,6 +866,11 @@
                     return;
                 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+                    int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                    if (userHandle >= 0) {
+                        removeUserLocked(userHandle);
+                    }
                 } else {
                     if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
                             && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index d6fed39..9b61ec4 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -544,6 +544,11 @@
                 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
                         TelephonyManager.EXTRA_STATE_OFFHOOK));
                 updateNotificationPulse();
+            } else if (action.equals(Intent.ACTION_USER_STOPPED)) {
+                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                if (userHandle >= 0) {
+                    cancelAllNotificationsUser(userHandle);
+                }
             } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
                 // turn off LED when user passes through lock screen
                 mNotificationLight.turnOff();
@@ -619,6 +624,7 @@
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
         filter.addAction(Intent.ACTION_USER_PRESENT);
+        filter.addAction(Intent.ACTION_USER_STOPPED);
         mContext.registerReceiver(mIntentReceiver, filter);
         IntentFilter pkgFilter = new IntentFilter();
         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -1249,6 +1255,29 @@
         }
     }
 
+    /**
+     * Cancels all notifications from a given user.
+     */
+    boolean cancelAllNotificationsUser(int userHandle) {
+        synchronized (mNotificationList) {
+            final int N = mNotificationList.size();
+            boolean canceledSomething = false;
+            for (int i = N-1; i >= 0; --i) {
+                NotificationRecord r = mNotificationList.get(i);
+                if (UserHandle.getUserId(r.uid) != userHandle) {
+                    continue;
+                }
+                canceledSomething = true;
+                mNotificationList.remove(i);
+                cancelNotificationLocked(r, false);
+            }
+            if (canceledSomething) {
+                updateLightsLocked();
+            }
+            return canceledSomething;
+        }
+    }
+
     @Deprecated
     public void cancelNotification(String pkg, int id) {
         cancelNotificationWithTag(pkg, null /* tag */, id);
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index ca7faa2..b0dfa80 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -989,6 +989,17 @@
         // restarting state.
         mRestartingServices.remove(r);
 
+        // Make sure that the user who owns this service is started.  If not,
+        // we don't want to allow it to run.
+        if (mAm.mStartedUsers.get(r.userId) == null) {
+            Slog.w(TAG, "Unable to launch app "
+                    + r.appInfo.packageName + "/"
+                    + r.appInfo.uid + " for service "
+                    + r.intent.getIntent() + ": user " + r.userId + " is stopped");
+            bringDownServiceLocked(r, true);
+            return false;
+        }
+
         // Service is now being launched, its package can't be stopped.
         try {
             AppGlobals.getPackageManager().setPackageStoppedState(
@@ -1509,7 +1520,7 @@
         boolean didSomething = false;
         ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
         for (ServiceRecord service : mServiceMap.getAllServices(userId)) {
-            if (service.packageName.equals(name)
+            if ((name == null || service.packageName.equals(name))
                     && (service.app == null || evenPersistent || !service.app.persistent)) {
                 if (!doit) {
                     return true;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 368db26..d8c8dfa 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -46,6 +46,7 @@
 import android.app.INotificationManager;
 import android.app.IProcessObserver;
 import android.app.IServiceConnection;
+import android.app.IStopUserCallback;
 import android.app.IThumbnailReceiver;
 import android.app.Instrumentation;
 import android.app.Notification;
@@ -428,6 +429,11 @@
     long mPreviousProcessVisibleTime;
 
     /**
+     * Which uses have been started, so are allowed to run code.
+     */
+    final SparseArray<UserStartedState> mStartedUsers = new SparseArray<UserStartedState>();
+
+    /**
      * Packages that the user has asked to have run in screen size
      * compatibility mode instead of filling the screen.
      */
@@ -791,7 +797,6 @@
     static ActivityThread mSystemThread;
 
     private int mCurrentUserId;
-    private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
     private UserManager mUserManager;
 
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -1506,6 +1511,9 @@
                 systemDir, "usagestats").toString());
         mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
 
+        // User 0 is the first and only user that runs at boot.
+        mStartedUsers.put(0, new UserStartedState(new UserHandle(0), true));
+
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
             ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
 
@@ -2095,7 +2103,7 @@
         }
     }
 
-    boolean startHomeActivityLocked(int userId) {
+    boolean startHomeActivityLocked(int userId, UserStartedState startingUser) {
         if (mHeadless) {
             // Added because none of the other calls to ensureBootCompleted seem to fire
             // when running headless.
@@ -2135,6 +2143,9 @@
                         null, null, 0, 0, 0, 0, null, false, null);
             }
         }
+        if (startingUser != null) {
+            mMainStack.addStartingUserLocked(startingUser);
+        }
 
         return true;
     }
@@ -3454,7 +3465,7 @@
                     Slog.w(TAG, "Invalid packageName: " + packageName);
                     return;
                 }
-                killPackageProcessesLocked(packageName, pkgUid,
+                killPackageProcessesLocked(packageName, pkgUid, -1,
                         ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
             }
         } finally {
@@ -3650,7 +3661,8 @@
     }
 
     private void forceStopPackageLocked(final String packageName, int uid) {
-        forceStopPackageLocked(packageName, uid, false, false, true, false, UserHandle.getUserId(uid));
+        forceStopPackageLocked(packageName, uid, false, false, true, false,
+                UserHandle.getUserId(uid));
         Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
                 Uri.fromParts("package", packageName, null));
         if (!mProcessesReady) {
@@ -3662,16 +3674,27 @@
                 false, false,
                 MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
     }
-    
+
+    private void forceStopUserLocked(int userId) {
+        forceStopPackageLocked(null, -1, false, false, true, false, userId);
+        Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+        broadcastIntentLocked(null, null, intent,
+                null, null, 0, null, null, null,
+                false, false,
+                MY_PID, Process.SYSTEM_UID, userId);
+    }
+
     private final boolean killPackageProcessesLocked(String packageName, int uid,
-            int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit,
-            boolean evenPersistent, String reason) {
+            int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
+            boolean doit, boolean evenPersistent, String reason) {
         ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
 
         // Remove all processes this package may have touched: all with the
         // same UID (except for the system or root user), and all whose name
         // matches the package name.
-        final String procNamePrefix = packageName + ":";
+        final String procNamePrefix = packageName != null ? (packageName + ":") : null;
         for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) {
             final int NA = apps.size();
             for (int ia=0; ia<NA; ia++) {
@@ -3684,6 +3707,18 @@
                     if (doit) {
                         procs.add(app);
                     }
+                // If no package is specified, we call all processes under the
+                // give user id.
+                } else if (packageName == null) {
+                    if (app.userId == userId) {
+                        if (app.setAdj >= minOomAdj) {
+                            if (!doit) {
+                                return true;
+                            }
+                            app.removed = true;
+                            procs.add(app);
+                        }
+                    }
                 // If uid is specified and the uid and process name match
                 // Or, the uid is not specified and the process name matches
                 } else if (((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid)
@@ -3714,7 +3749,7 @@
         int i;
         int N;
 
-        if (uid < 0) {
+        if (uid < 0 && name != null) {
             try {
                 uid = AppGlobals.getPackageManager().getPackageUid(name, userId);
             } catch (RemoteException e) {
@@ -3722,24 +3757,45 @@
         }
 
         if (doit) {
-            Slog.i(TAG, "Force stopping package " + name + " uid=" + uid);
+            if (name != null) {
+                Slog.i(TAG, "Force stopping package " + name + " uid=" + uid);
+            } else {
+                Slog.i(TAG, "Force stopping user " + userId);
+            }
 
             Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator();
             while (badApps.hasNext()) {
                 SparseArray<Long> ba = badApps.next();
-                if (ba.get(uid) != null) {
+                for (i=ba.size()-1; i>=0; i--) {
+                    boolean remove = false;
+                    final int entUid = ba.keyAt(i);
+                    if (name != null) {
+                        if (entUid == uid) {
+                            remove = true;
+                        }
+                    } else if (UserHandle.getUserId(entUid) == userId) {
+                        remove = true;
+                    }
+                    if (remove) {
+                        ba.removeAt(i);
+                    }
+                }
+                if (ba.size() == 0) {
                     badApps.remove();
                 }
             }
         }
-        
-        boolean didSomething = killPackageProcessesLocked(name, uid, -100,
-                callerWillRestart, false, doit, evenPersistent, "force stop");
+
+        boolean didSomething = killPackageProcessesLocked(name, uid,
+                name == null ? userId : -1 , -100, callerWillRestart, false,
+                doit, evenPersistent,
+                name == null ? ("force stop user " + userId) : ("force stop " + name));
         
         TaskRecord lastTask = null;
         for (i=0; i<mMainStack.mHistory.size(); i++) {
             ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
-            final boolean samePackage = r.packageName.equals(name);
+            final boolean samePackage = r.packageName.equals(name)
+                    || (name == null && r.userId == userId);
             if (r.userId == userId
                     && (samePackage || r.task == lastTask)
                     && (r.app == null || evenPersistent || !r.app.persistent)) {
@@ -3776,7 +3832,7 @@
 
         ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
         for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(userId).values()) {
-            if (provider.info.packageName.equals(name)
+            if ((name == null || provider.info.packageName.equals(name))
                     && (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
                 if (!doit) {
                     return true;
@@ -3792,7 +3848,7 @@
         }
 
         if (doit) {
-            if (purgeCache) {
+            if (purgeCache && name != null) {
                 AttributeCache ac = AttributeCache.instance();
                 if (ac != null) {
                     ac.removePackage(name);
@@ -4197,15 +4253,6 @@
             }
         }, pkgFilter);
 
-        IntentFilter userFilter = new IntentFilter();
-        userFilter.addAction(Intent.ACTION_USER_REMOVED);
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                onUserRemoved(intent);
-            }
-        }, userFilter);
-
         synchronized (this) {
             // Ensure that any processes we had put on hold are now started
             // up.
@@ -4227,13 +4274,17 @@
                 // Tell anyone interested that we are done booting!
                 SystemProperties.set("sys.boot_completed", "1");
                 SystemProperties.set("dev.bootcomplete", "1");
-                List<UserInfo> users = getUserManager().getUsers();
-                for (UserInfo user : users) {
-                    broadcastIntentLocked(null, null,
-                            new Intent(Intent.ACTION_BOOT_COMPLETED, null),
-                            null, null, 0, null, null,
-                            android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
-                            false, false, MY_PID, Process.SYSTEM_UID, user.id);
+                for (int i=0; i<mStartedUsers.size(); i++) {
+                    UserStartedState uss = mStartedUsers.valueAt(i);
+                    if (uss.mState == UserStartedState.STATE_BOOTING) {
+                        uss.mState = UserStartedState.STATE_RUNNING;
+                        broadcastIntentLocked(null, null,
+                                new Intent(Intent.ACTION_BOOT_COMPLETED, null),
+                                null, null, 0, null, null,
+                                android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+                                false, false, MY_PID, Process.SYSTEM_UID,
+                                mStartedUsers.keyAt(i));
+                    }
                 }
             }
         }
@@ -6297,6 +6348,16 @@
                             "Attempt to launch content provider before system ready");
                 }
 
+                // Make sure that the user who owns this provider is started.  If not,
+                // we don't want to allow it to run.
+                if (mStartedUsers.get(userId) == null) {
+                    Slog.w(TAG, "Unable to launch app "
+                            + cpi.applicationInfo.packageName + "/"
+                            + cpi.applicationInfo.uid + " for provider "
+                            + name + ": user " + userId + " is stopped");
+                    return null;
+                }
+
                 ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
                 cpr = mProviderMap.getProviderByClass(comp, userId);
                 final boolean firstClass = cpr == null;
@@ -9048,6 +9109,13 @@
         }
 
         pw.println();
+        pw.println("  mStartedUsers:");
+        for (int i=0; i<mStartedUsers.size(); i++) {
+            UserStartedState uss = mStartedUsers.valueAt(i);
+            pw.print("    User #"); pw.print(uss.mHandle.getIdentifier());
+                    pw.println(":");
+            uss.dump("      ", pw);
+        }
         pw.println("  mHomeProcess: " + mHomeProcess);
         pw.println("  mPreviousProcess: " + mPreviousProcess);
         if (dumpAll) {
@@ -11107,6 +11175,14 @@
             }
         }
 
+        // Make sure that the user who is receiving this broadcast is started
+        // If not, we will just skip it.
+        if (mStartedUsers.get(userId) == null) {
+            Slog.w(TAG, "Skipping broadcast of " + intent
+                    + ": user " + userId + " is stopped");
+            return ActivityManager.BROADCAST_SUCCESS;
+        }
+
         // Handle special intents: if this broadcast is from the package
         // manager about a package being removed, we need to remove all of
         // its activities from the history stack.
@@ -11649,7 +11725,7 @@
         app.instrumentationProfileFile = null;
         app.instrumentationArguments = null;
 
-        forceStopPackageLocked(app.processName, -1, false, false, true, true, app.userId);
+        forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, app.userId);
     }
 
     public void finishInstrumentation(IApplicationThread target,
@@ -13491,73 +13567,172 @@
 
     // Multi-user methods
 
+    @Override
     public boolean switchUser(int userId) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != 0 && callingUid != Process.myUid()) {
-            Slog.e(TAG, "Trying to switch user from unauthorized app");
-            return false;
+        if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                != PackageManager.PERMISSION_GRANTED) {
+            String msg = "Permission Denial: switchUser() from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
         }
-        if (mCurrentUserId == userId)
-            return true;
-
         synchronized (this) {
-            // Check if user is already logged in, otherwise check if user exists first before
-            // adding to the list of logged in users.
-            if (mLoggedInUsers.indexOfKey(userId) < 0) {
-                if (!userExists(userId)) {
-                    return false;
-                }
-                mLoggedInUsers.append(userId, userId);
+            if (mCurrentUserId == userId) {
+                return true;
+            }
+
+            // If the user we are switching to is not currently started, then
+            // we need to start it now.
+            if (mStartedUsers.get(userId) == null) {
+                mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false));
             }
 
             mCurrentUserId = userId;
             boolean haveActivities = mMainStack.switchUser(userId);
             if (!haveActivities) {
-                startHomeActivityLocked(userId);
+                startHomeActivityLocked(userId, mStartedUsers.get(userId));
             }
-
         }
 
-        // Inform of user switch
-        Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
-        addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-        mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
+        long ident = Binder.clearCallingIdentity();
+        try {
+            // Inform of user switch
+            Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
+            addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+            mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
 
         return true;
     }
 
-    @Override
-    public UserInfo getCurrentUser() throws RemoteException {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != 0 && callingUid != Process.myUid()) {
-            Slog.e(TAG, "Trying to get user from unauthorized app");
-            return null;
+    void finishUserSwitch(UserStartedState uss) {
+        synchronized (this) {
+            if (uss.mState == UserStartedState.STATE_BOOTING
+                    && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
+                uss.mState = UserStartedState.STATE_RUNNING;
+                broadcastIntentLocked(null, null,
+                        new Intent(Intent.ACTION_BOOT_COMPLETED, null),
+                        null, null, 0, null, null,
+                        android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+                        false, false, MY_PID, Process.SYSTEM_UID, uss.mHandle.getIdentifier());
+            }
         }
-        return getUserManager().getUserInfo(mCurrentUserId);
     }
 
-    private void onUserRemoved(Intent intent) {
-        int extraUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-        if (extraUserId < 1) return;
-
-        // Kill all the processes for the user
-        ArrayList<Pair<String, Integer>> pkgAndUids = new ArrayList<Pair<String,Integer>>();
+    @Override
+    public int stopUser(final int userId, final IStopUserCallback callback) {
+        if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                != PackageManager.PERMISSION_GRANTED) {
+            String msg = "Permission Denial: switchUser() from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        if (userId <= 0) {
+            throw new IllegalArgumentException("Can't stop primary user " + userId);
+        }
         synchronized (this) {
-            HashMap<String,SparseArray<ProcessRecord>> map = mProcessNames.getMap();
-            for (Entry<String, SparseArray<ProcessRecord>> uidMap : map.entrySet()) {
-                SparseArray<ProcessRecord> uids = uidMap.getValue();
-                for (int i = 0; i < uids.size(); i++) {
-                    if (UserHandle.getUserId(uids.keyAt(i)) == extraUserId) {
-                        pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i)));
-                    }
+            if (mCurrentUserId == userId) {
+                return ActivityManager.USER_OP_IS_CURRENT;
+            }
+
+            final UserStartedState uss = mStartedUsers.get(userId);
+            if (uss == null) {
+                // User is not started, nothing to do...  but we do need to
+                // callback if requested.
+                if (callback != null) {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            try {
+                                callback.userStopped(userId);
+                            } catch (RemoteException e) {
+                            }
+                        }
+                    });
+                }
+                return ActivityManager.USER_OP_SUCCESS;
+            }
+
+            if (callback != null) {
+                uss.mStopCallbacks.add(callback);
+            }
+
+            if (uss.mState != UserStartedState.STATE_STOPPING) {
+                uss.mState = UserStartedState.STATE_STOPPING;
+
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    // Inform of user switch
+                    Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
+                    final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() {
+                        @Override
+                        public void performReceive(Intent intent, int resultCode, String data,
+                                Bundle extras, boolean ordered, boolean sticky) {
+                            finishUserStop(uss);
+                        }
+                    };
+                    broadcastIntentLocked(null, null, intent,
+                            null, resultReceiver, 0, null, null, null,
+                            true, false, MY_PID, Process.SYSTEM_UID, userId);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
                 }
             }
+        }
 
-            for (Pair<String,Integer> pkgAndUid : pkgAndUids) {
-                forceStopPackageLocked(pkgAndUid.first, pkgAndUid.second,
-                        false, false, true, true, extraUserId);
+        return ActivityManager.USER_OP_SUCCESS;
+    }
+
+    void finishUserStop(UserStartedState uss) {
+        final int userId = uss.mHandle.getIdentifier();
+        boolean stopped;
+        ArrayList<IStopUserCallback> callbacks;
+        synchronized (this) {
+            callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks);
+            if (uss.mState != UserStartedState.STATE_STOPPING
+                    || mStartedUsers.get(userId) != uss) {
+                stopped = false;
+            } else {
+                stopped = true;
+                // User can no longer run.
+                mStartedUsers.remove(userId);
+
+                // Clean up all state and processes associated with the user.
+                // Kill all the processes for the user.
+                forceStopUserLocked(userId);
             }
         }
+
+        for (int i=0; i<callbacks.size(); i++) {
+            try {
+                if (stopped) callbacks.get(i).userStopped(userId);
+                else callbacks.get(i).userStopAborted(userId);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    @Override
+    public UserInfo getCurrentUser() {
+        if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                != PackageManager.PERMISSION_GRANTED) {
+            String msg = "Permission Denial: getCurrentUser() from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        synchronized (this) {
+            return getUserManager().getUserInfo(mCurrentUserId);
+        }
     }
 
     private boolean userExists(int userId) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index ccea41a..a389edc2 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -211,7 +211,10 @@
      */
     final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible
             = new ArrayList<IActivityManager.WaitResult>();
-    
+
+    final ArrayList<UserStartedState> mStartingUsers
+            = new ArrayList<UserStartedState>();
+
     /**
      * Set when the system is going to sleep, until we have
      * successfully paused the current activity and released our wake lock.
@@ -1397,7 +1400,7 @@
             // Launcher...
             if (mMainStack) {
                 ActivityOptions.abort(options);
-                return mService.startHomeActivityLocked(0);
+                return mService.startHomeActivityLocked(0, null);
             }
         }
 
@@ -1427,7 +1430,16 @@
             ActivityOptions.abort(options);
             return false;
         }
-        
+
+        // Make sure that the user who owns this activity is started.  If not,
+        // we will just leave it as is because someone should be bringing
+        // another user's activities to the top of the stack.
+        if (mService.mStartedUsers.get(next.userId) == null) {
+            Slog.w(TAG, "Skipping resume of top activity " + next
+                    + ": user " + next.userId + " is stopped");
+            return false;
+        }
+
         // The activity may be waiting for stop, but that is no longer
         // appropriate for it.
         mStoppingActivities.remove(next);
@@ -1494,7 +1506,7 @@
                     Slog.d(TAG, "no-history finish of " + last + " on new resume");
                 }
                 requestFinishActivityLocked(last.appToken, Activity.RESULT_CANCELED, null,
-                "no-history");
+                        "no-history");
             }
         }
 
@@ -3414,6 +3426,7 @@
         ArrayList<ActivityRecord> stops = null;
         ArrayList<ActivityRecord> finishes = null;
         ArrayList<ActivityRecord> thumbnails = null;
+        ArrayList<UserStartedState> startingUsers = null;
         int NS = 0;
         int NF = 0;
         int NT = 0;
@@ -3495,6 +3508,10 @@
                 booting = mService.mBooting;
                 mService.mBooting = false;
             }
+            if (mStartingUsers.size() > 0) {
+                startingUsers = new ArrayList<UserStartedState>(mStartingUsers);
+                mStartingUsers.clear();
+            }
         }
 
         int i;
@@ -3539,6 +3556,10 @@
 
         if (booting) {
             mService.finishBooting();
+        } else if (startingUsers != null) {
+            for (i=0; i<startingUsers.size(); i++) {
+                mService.finishUserSwitch(startingUsers.get(i));
+            }
         }
 
         mService.trimApplications();
@@ -3556,6 +3577,10 @@
         return res;
     }
 
+    final void addStartingUserLocked(UserStartedState uss) {
+        mStartingUsers.add(uss);
+    }
+
     /**
      * @return Returns true if the activity is being finished, false if for
      * some reason it is being left as-is.
diff --git a/services/java/com/android/server/am/UserStartedState.java b/services/java/com/android/server/am/UserStartedState.java
new file mode 100644
index 0000000..3f3ed85
--- /dev/null
+++ b/services/java/com/android/server/am/UserStartedState.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 com.android.server.am;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import android.app.IStopUserCallback;
+import android.os.UserHandle;
+
+public class UserStartedState {
+    public final static int STATE_BOOTING = 0;
+    public final static int STATE_RUNNING = 1;
+    public final static int STATE_STOPPING = 2;
+
+    public final UserHandle mHandle;
+    public final ArrayList<IStopUserCallback> mStopCallbacks
+            = new ArrayList<IStopUserCallback>();
+
+    public int mState = STATE_BOOTING;
+
+    public UserStartedState(UserHandle handle, boolean initial) {
+        mHandle = handle;
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("mState="); pw.println(mState);
+    }
+}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 3c5eed7..48a4b74 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -1818,7 +1818,7 @@
                 pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
                 pkg.applicationInfo.sourceDir = ps.codePathString;
                 pkg.applicationInfo.dataDir =
-                        getDataPathForPackage(ps.pkg.packageName, 0).getPath();
+                        getDataPathForPackage(packageName, 0).getPath();
                 pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
             }
             // pkg.mSetEnabled = ps.getEnabled(userId);
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 750aa72..fb04d0f1 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -23,6 +23,8 @@
 import com.android.internal.util.FastXmlSerializer;
 
 import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.IStopUserCallback;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -33,6 +35,7 @@
 import android.os.IUserManager;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -549,13 +552,36 @@
      */
     public boolean removeUser(int userHandle) {
         checkManageUsersPermission("Only the system can remove users");
+        final UserInfo user;
+        synchronized (mPackagesLock) {
+            user = mUsers.get(userHandle);
+            if (userHandle == 0 || user == null) {
+                return false;
+            }
+        }
+
+        int res;
+        try {
+            res = ActivityManagerNative.getDefault().stopUser(userHandle,
+                    new IStopUserCallback.Stub() {
+                        @Override
+                        public void userStopped(int userId) {
+                            finishRemoveUser(userId);
+                        }
+                        @Override
+                        public void userStopAborted(int userId) {
+                        }
+            });
+        } catch (RemoteException e) {
+            return false;
+        }
+
+        return res == ActivityManager.USER_OP_SUCCESS;
+    }
+
+    void finishRemoveUser(int userHandle) {
         synchronized (mInstallLock) {
             synchronized (mPackagesLock) {
-                final UserInfo user = mUsers.get(userHandle);
-                if (userHandle == 0 || user == null) {
-                    return false;
-                }
-
                 // Cleanup package manager settings
                 mPm.cleanUpUserLILPw(userHandle);
 
@@ -574,7 +600,6 @@
         Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
         addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
         mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
-        return true;
     }
 
     @Override