Merge "Restore calling identity before checking permission" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index fac1d8c..64d6c79 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5454,6 +5454,7 @@
     method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException;
     method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException;
     method public void onRestoreFile(android.os.ParcelFileDescriptor, long, java.io.File, int, long, long) throws java.io.IOException;
+    method public void onRestoreFinished();
     field public static final int TYPE_DIRECTORY = 2; // 0x2
     field public static final int TYPE_FILE = 1; // 0x1
   }
@@ -8683,9 +8684,13 @@
     field public static final java.lang.String FEATURE_CAMERA = "android.hardware.camera";
     field public static final java.lang.String FEATURE_CAMERA_ANY = "android.hardware.camera.any";
     field public static final java.lang.String FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus";
+    field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING = "android.hardware.camera.capability.manual_post_processing";
+    field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR = "android.hardware.camera.capability.manual_sensor";
+    field public static final java.lang.String FEATURE_CAMERA_CAPABILITY_RAW = "android.hardware.camera.capability.raw";
     field public static final java.lang.String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external";
     field public static final java.lang.String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
     field public static final java.lang.String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
+    field public static final java.lang.String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
     field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
@@ -12763,9 +12768,9 @@
     field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
-    field public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
     field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
     field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
     field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
@@ -22310,9 +22315,10 @@
     field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
     field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
     field public static final java.lang.String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";
+    field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
     field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
     field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
-    field public static final java.lang.String DISALLOW_TELEPHONY = "no_telephony";
+    field public static final java.lang.String DISALLOW_SMS = "no_sms";
     field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
     field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
     field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -33523,6 +33529,7 @@
     method public boolean dispatchKeyEventPreIme(android.view.KeyEvent);
     method public boolean dispatchKeyShortcutEvent(android.view.KeyEvent);
     method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
     method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
     method public boolean dispatchNestedScroll(int, int, int, int, int[]);
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
@@ -34333,6 +34340,7 @@
     method public boolean onInterceptTouchEvent(android.view.MotionEvent);
     method protected abstract void onLayout(boolean, int, int, int, int);
     method public boolean onNestedFling(android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.view.View, float, float);
     method public void onNestedPreScroll(android.view.View, int, int, int[]);
     method public void onNestedScroll(android.view.View, int, int, int, int);
     method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
@@ -34478,6 +34486,7 @@
     method public abstract boolean isTextDirectionResolved();
     method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
     method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
+    method public abstract boolean onNestedPreFling(android.view.View, float, float);
     method public abstract void onNestedPreScroll(android.view.View, int, int, int[]);
     method public abstract void onNestedScroll(android.view.View, int, int, int, int);
     method public abstract void onNestedScrollAccepted(android.view.View, android.view.View, int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 15f3a75..48954f4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2610,15 +2610,7 @@
             return;
         }
 
-        if (mBackupAgents.get(packageName) != null) {
-            Slog.d(TAG, "BackupAgent " + "  for " + packageName
-                    + " already exists");
-            return;
-        }
-
-        BackupAgent agent = null;
         String classname = data.appInfo.backupAgentName;
-
         // full backup operation but no app-supplied agent?  use the default implementation
         if (classname == null && (data.backupMode == IApplicationThread.BACKUP_MODE_FULL
                 || data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL)) {
@@ -2627,29 +2619,38 @@
 
         try {
             IBinder binder = null;
-            try {
-                if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
-
-                java.lang.ClassLoader cl = packageInfo.getClassLoader();
-                agent = (BackupAgent) cl.loadClass(classname).newInstance();
-
-                // set up the agent's context
-                ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
-                context.setOuterContext(agent);
-                agent.attach(context);
-
-                agent.onCreate();
-                binder = agent.onBind();
-                mBackupAgents.put(packageName, agent);
-            } catch (Exception e) {
-                // If this is during restore, fail silently; otherwise go
-                // ahead and let the user see the crash.
-                Slog.e(TAG, "Agent threw during creation: " + e);
-                if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE
-                        && data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
-                    throw e;
+            BackupAgent agent = mBackupAgents.get(packageName);
+            if (agent != null) {
+                // reusing the existing instance
+                if (DEBUG_BACKUP) {
+                    Slog.v(TAG, "Reusing existing agent instance");
                 }
-                // falling through with 'binder' still null
+                binder = agent.onBind();
+            } else {
+                try {
+                    if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
+
+                    java.lang.ClassLoader cl = packageInfo.getClassLoader();
+                    agent = (BackupAgent) cl.loadClass(classname).newInstance();
+
+                    // set up the agent's context
+                    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+                    context.setOuterContext(agent);
+                    agent.attach(context);
+
+                    agent.onCreate();
+                    binder = agent.onBind();
+                    mBackupAgents.put(packageName, agent);
+                } catch (Exception e) {
+                    // If this is during restore, fail silently; otherwise go
+                    // ahead and let the user see the crash.
+                    Slog.e(TAG, "Agent threw during creation: " + e);
+                    if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE
+                            && data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
+                        throw e;
+                    }
+                    // falling through with 'binder' still null
+                }
             }
 
             // tell the OS that we're live now
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 7036aea..451af99 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -125,6 +125,21 @@
             int token, IBackupManager callbackBinder);
 
     /**
+     * Provide the app with a canonical "all data has been delivered" end-of-restore
+     * callback so that it can do any postprocessing of the restored data that might
+     * be appropriate.  This is issued after both key/value and full data restore
+     * operations have completed.
+     *
+     * @param token Opaque token identifying this transaction.  This must
+     *        be echoed back to the backup service binder once the agent is
+     *        finished restoring the application based on the restore data
+     *        contents.
+     * @param callbackBinder Binder on which to indicate operation completion,
+     *        passed here as a convenience to the agent.
+     */
+    void doRestoreFinished(int token, IBackupManager callbackBinder);
+
+    /**
      * Out of band: instruct the agent to crash within the client process.  This is used
      * when the backup infrastructure detects a semantic error post-hoc and needs to
      * pass the problem back to the app.
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index e2a86e8..87d785a 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -209,7 +209,7 @@
      *            output stream.
      */
     public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-             ParcelFileDescriptor newState) throws IOException;
+            ParcelFileDescriptor newState) throws IOException;
 
     /**
      * The application is being restored from backup and should replace any
@@ -243,8 +243,7 @@
      *            When a full-backup dataset is being restored, this will be <code>null</code>.
      */
     public abstract void onRestore(BackupDataInput data, int appVersionCode,
-            ParcelFileDescriptor newState)
-            throws IOException;
+            ParcelFileDescriptor newState) throws IOException;
 
     /**
      * The application is having its entire file system contents backed up.  {@code data}
@@ -575,6 +574,20 @@
         FullBackup.restoreFile(data, size, type, mode, mtime, null);
     }
 
+    /**
+     * The application's restore operation has completed.  This method is called after
+     * all available data has been delivered to the application for restore (via either
+     * the {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} or
+     * {@link #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) onRestoreFile()}
+     * callbacks).  This provides the app with a stable end-of-restore opportunity to
+     * perform any appropriate post-processing on the data that was just delivered.
+     *
+     * @see #onRestore(BackupDataInput, int, ParcelFileDescriptor)
+     * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
+     */
+    public void onRestoreFinished() {
+    }
+
     // ----- Core implementation -----
 
     /** @hide */
@@ -723,6 +736,24 @@
         }
 
         @Override
+        public void doRestoreFinished(int token, IBackupManager callbackBinder) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                BackupAgent.this.onRestoreFinished();
+            } finally {
+                // Ensure that any side-effect SharedPreferences writes have landed
+                waitForSharedPrefs();
+
+                Binder.restoreCallingIdentity(ident);
+                try {
+                    callbackBinder.opComplete(token);
+                } catch (RemoteException e) {
+                    // we'll time out anyway, so we're safe
+                }
+            }
+        }
+
+        @Override
         public void fail(String message) {
             getHandler().post(new FailRunnable(message));
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 37df29a..ee7810d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1033,6 +1033,45 @@
     public static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL full hardware}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR manual sensor}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR =
+            "android.hardware.camera.capability.manual_sensor";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING manual post-processing}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING =
+            "android.hardware.camera.capability.manual_post_processing";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: At least one
+     * of the cameras on the device supports the
+     * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}
+     * capability level.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_CAMERA_CAPABILITY_RAW =
+            "android.hardware.camera.capability.raw";
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device is capable of communicating with
      * consumer IR devices.
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 4976a48..ff86120 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -887,7 +887,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
-     * @see #REQUEST_AVAILABLE_CAPABILITIES_DNG
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_RAW
      */
     public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
             new Key<int[]>("android.request.availableCapabilities", int[].class);
@@ -1068,17 +1068,17 @@
      * <tr>
      * <td align="left">RAW_OPAQUE</td>
      * <td align="left">RAW16</td>
-     * <td align="left">DNG</td>
+     * <td align="left">RAW</td>
      * </tr>
      * <tr>
      * <td align="left">RAW16</td>
      * <td align="left">YUV_420_888</td>
-     * <td align="left">DNG</td>
+     * <td align="left">RAW</td>
      * </tr>
      * <tr>
      * <td align="left">RAW16</td>
      * <td align="left">JPEG</td>
-     * <td align="left">DNG</td>
+     * <td align="left">RAW</td>
      * </tr>
      * </tbody>
      * </table>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 36b1089..63130a7 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -387,8 +387,11 @@
     public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2;
 
     /**
-     * <p>The camera device supports outputting RAW buffers that can be
-     * saved offline into a DNG format.</p>
+     * <p>The camera device supports outputting RAW buffers and
+     * metadata for interpreting them.</p>
+     * <p>Devices supporting the RAW capability allow both for
+     * saving DNG files, and for direct application processing of
+     * raw sensor images.</p>
      * <ul>
      * <li>RAW_SENSOR is supported as an output format.</li>
      * <li>The maximum available resolution for RAW_SENSOR streams
@@ -403,7 +406,7 @@
      * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      */
-    public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 3;
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3;
 
     /**
      * <p>The camera device supports the Zero Shutter Lag use case.</p>
@@ -1186,8 +1189,7 @@
      * image while recording video) use case.</p>
      * <p>The camera device should take the highest-quality image
      * possible (given the other settings) without disrupting the
-     * frame rate of video recording.<br />
-     * </p>
+     * frame rate of video recording.  </p>
      * @see CaptureRequest#CONTROL_CAPTURE_INTENT
      */
     public static final int CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT = 4;
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index ebab563..ec1fc4a 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -475,7 +475,7 @@
                     @Override
                     @SuppressWarnings("unchecked")
                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
-                        return (T) metadata.getFaceRectangles();
+                        return (T) metadata.getFaces();
                     }
                 });
         sGetCommandMap.put(
@@ -483,7 +483,7 @@
                     @Override
                     @SuppressWarnings("unchecked")
                     public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
-                        return (T) metadata.getFaces();
+                        return (T) metadata.getFaceRectangles();
                     }
                 });
         sGetCommandMap.put(
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7bbac2c..d3aee50 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -271,15 +271,26 @@
     public static final String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
 
     /**
-     * Key for user restrictions. Specifies that the user is not allowed to send or receive
-     * phone calls or text messages. Emergency calls may still be permitted.
+     * Key for user restrictions. Specifies that the user is not allowed to make outgoing
+     * phone calls. Emergency calls are still permitted.
      * The default value is <code>false</code>.
      * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String DISALLOW_TELEPHONY = "no_telephony";
+    public static final String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
+
+    /**
+     * Key for user restrictions. Specifies that the user is not allowed to send or receive
+     * SMS messages.
+     * The default value is <code>false</code>.
+     * <p/>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_SMS = "no_sms";
 
     /**
      * Key for user restrictions. Specifies that windows besides app windows should not be
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a3e2c96..7bc8bc5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18445,6 +18445,43 @@
     }
 
     /**
+     * Dispatch a fling to a nested scrolling parent before it is processed by this view.
+     *
+     * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch
+     * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code>
+     * offsets an opportunity for the parent view in a nested fling to fully consume the fling
+     * before the child view consumes it. If this method returns <code>true</code>, a nested
+     * parent view consumed the fling and this view should not scroll as a result.</p>
+     *
+     * <p>For a better user experience, only one view in a nested scrolling chain should consume
+     * the fling at a time. If a parent view consumed the fling this method will return false.
+     * Custom view implementations should account for this in two ways:</p>
+     *
+     * <ul>
+     *     <li>If a custom view is paged and needs to settle to a fixed page-point, do not
+     *     call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid
+     *     position regardless.</li>
+     *     <li>If a nested parent does consume the fling, this view should not scroll at all,
+     *     even to settle back to a valid idle position.</li>
+     * </ul>
+     *
+     * <p>Views should also not offer fling velocities to nested parent views along an axis
+     * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
+     * should not offer a horizontal fling velocity to its parents since scrolling along that
+     * axis is not permitted and carrying velocity along that motion does not make sense.</p>
+     *
+     * @param velocityX Horizontal fling velocity in pixels per second
+     * @param velocityY Vertical fling velocity in pixels per second
+     * @return true if a nested scrolling parent consumed the fling
+     */
+    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
+        if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
+            return mNestedScrollingParent.onNestedPreFling(this, velocityX, velocityY);
+        }
+        return false;
+    }
+
+    /**
      * Gets a scale factor that determines the distance the view should scroll
      * vertically in response to {@link MotionEvent#ACTION_SCROLL}.
      * @return The vertical scroll scale factor.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ac70066..04c8b0b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -6066,6 +6066,14 @@
     }
 
     /**
+     * @inheritDoc
+     */
+    @Override
+    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+        return false;
+    }
+
+    /**
      * Return the current axes of nested scrolling for this ViewGroup.
      *
      * <p>A ViewGroup returning something other than {@link #SCROLL_AXIS_NONE} is currently
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 588b9cd..87a37f4 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -523,10 +523,32 @@
      * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
      *
      * @param target View that initiated the nested scroll
-     * @param velocityX Horizontal velocity in pixels per second.
+     * @param velocityX Horizontal velocity in pixels per second
      * @param velocityY Vertical velocity in pixels per second
      * @param consumed true if the child consumed the fling, false otherwise
      * @return true if this parent consumed or otherwise reacted to the fling
      */
     public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
+
+    /**
+     * React to a nested fling before the target view consumes it.
+     *
+     * <p>This method siginfies that a nested scrolling child has detected a fling with the given
+     * velocity along each axis. Generally this means that a touch scroll has ended with a
+     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
+     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
+     * along a scrollable axis.</p>
+     *
+     * <p>If a nested scrolling parent is consuming motion as part of a
+     * {@link #onNestedPreScroll(View, int, int, int[]) pre-scroll}, it may be appropriate for
+     * it to also consume the pre-fling to complete that same motion. By returning
+     * <code>true</code> from this method, the parent indicates that the child should not
+     * fling its own internal content as well.</p>
+     *
+     * @param target View that initiated the nested scroll
+     * @param velocityX Horizontal velocity in pixels per second
+     * @param velocityY Vertical velocity in pixels per second
+     * @return true if this parent consumed the fling ahead of the target view
+     */
+    public boolean onNestedPreFling(View target, float velocityX, float velocityY);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a8d3f99..5cad7db 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1052,25 +1052,24 @@
     }
 
     private boolean collectViewAttributes() {
-        final View.AttachInfo attachInfo = mAttachInfo;
-        if (attachInfo.mRecomputeGlobalAttributes) {
+        if (mAttachInfo.mRecomputeGlobalAttributes) {
             //Log.i(TAG, "Computing view hierarchy attributes!");
-            attachInfo.mRecomputeGlobalAttributes = false;
-            boolean oldScreenOn = attachInfo.mKeepScreenOn;
-            attachInfo.mKeepScreenOn = false;
-            attachInfo.mSystemUiVisibility = 0;
-            attachInfo.mHasSystemUiListeners = false;
-            mView.dispatchCollectViewAttributes(attachInfo, 0);
-            attachInfo.mSystemUiVisibility &= ~attachInfo.mDisabledSystemUiVisibility;
+            mAttachInfo.mRecomputeGlobalAttributes = false;
+            boolean oldScreenOn = mAttachInfo.mKeepScreenOn;
+            mAttachInfo.mKeepScreenOn = false;
+            mAttachInfo.mSystemUiVisibility = 0;
+            mAttachInfo.mHasSystemUiListeners = false;
+            mView.dispatchCollectViewAttributes(mAttachInfo, 0);
+            mAttachInfo.mSystemUiVisibility &= ~mAttachInfo.mDisabledSystemUiVisibility;
             WindowManager.LayoutParams params = mWindowAttributes;
-            attachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);
-            if (attachInfo.mKeepScreenOn != oldScreenOn
-                    || attachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
-                    || attachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
+            mAttachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);
+            if (mAttachInfo.mKeepScreenOn != oldScreenOn
+                    || mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
+                    || mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
                 applyKeepScreenOnFlag(params);
-                params.subtreeSystemUiVisibility = attachInfo.mSystemUiVisibility;
-                params.hasSystemUiListeners = attachInfo.mHasSystemUiListeners;
-                mView.dispatchWindowSystemUiVisiblityChanged(attachInfo.mSystemUiVisibility);
+                params.subtreeSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
+                params.hasSystemUiListeners = mAttachInfo.mHasSystemUiListeners;
+                mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility);
                 return true;
             }
         }
@@ -1162,8 +1161,7 @@
      * @param m input matrix to modify
      */
     void transformMatrixToGlobal(Matrix m) {
-        final View.AttachInfo attachInfo = mAttachInfo;
-        m.postTranslate(attachInfo.mWindowLeft, attachInfo.mWindowTop);
+        m.postTranslate(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
     }
 
     /**
@@ -1173,8 +1171,7 @@
      * @param m input matrix to modify
      */
     void transformMatrixToLocal(Matrix m) {
-        final View.AttachInfo attachInfo = mAttachInfo;
-        m.preTranslate(-attachInfo.mWindowLeft, -attachInfo.mWindowTop);
+        m.preTranslate(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop);
     }
 
     void dispatchApplyInsets(View host) {
@@ -1216,8 +1213,6 @@
         int desiredWindowWidth;
         int desiredWindowHeight;
 
-        final View.AttachInfo attachInfo = mAttachInfo;
-
         final int viewVisibility = getHostVisibility();
         boolean viewVisibilityChanged = mViewVisibility != viewVisibility
                 || mNewSurfaceNeeded;
@@ -1266,10 +1261,10 @@
             // We used to use the following condition to choose 32 bits drawing caches:
             // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888
             // However, windows are now always 32 bits by default, so choose 32 bits
-            attachInfo.mUse32BitDrawingCache = true;
-            attachInfo.mHasWindowFocus = false;
-            attachInfo.mWindowVisibility = viewVisibility;
-            attachInfo.mRecomputeGlobalAttributes = false;
+            mAttachInfo.mUse32BitDrawingCache = true;
+            mAttachInfo.mHasWindowFocus = false;
+            mAttachInfo.mWindowVisibility = viewVisibility;
+            mAttachInfo.mRecomputeGlobalAttributes = false;
             viewVisibilityChanged = false;
             mLastConfiguration.setTo(host.getResources().getConfiguration());
             mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
@@ -1277,8 +1272,8 @@
             if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
                 host.setLayoutDirection(mLastConfiguration.getLayoutDirection());
             }
-            host.dispatchAttachedToWindow(attachInfo, 0);
-            attachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
+            host.dispatchAttachedToWindow(mAttachInfo, 0);
+            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
             dispatchApplyInsets(host);
             //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
 
@@ -1295,7 +1290,7 @@
         }
 
         if (viewVisibilityChanged) {
-            attachInfo.mWindowVisibility = viewVisibility;
+            mAttachInfo.mWindowVisibility = viewVisibility;
             host.dispatchWindowVisibilityChanged(viewVisibility);
             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
                 destroyHardwareResources();
@@ -1308,7 +1303,7 @@
         }
 
         // Execute enqueued actions on every traversal in case a detached view enqueued an action
-        getRunQueue().executeActions(attachInfo.mHandler);
+        getRunQueue().executeActions(mAttachInfo.mHandler);
 
         boolean insetsChanged = false;
 
@@ -1364,21 +1359,21 @@
         if (collectViewAttributes()) {
             params = lp;
         }
-        if (attachInfo.mForceReportNewAttributes) {
-            attachInfo.mForceReportNewAttributes = false;
+        if (mAttachInfo.mForceReportNewAttributes) {
+            mAttachInfo.mForceReportNewAttributes = false;
             params = lp;
         }
 
-        if (mFirst || attachInfo.mViewVisibilityChanged) {
-            attachInfo.mViewVisibilityChanged = false;
+        if (mFirst || mAttachInfo.mViewVisibilityChanged) {
+            mAttachInfo.mViewVisibilityChanged = false;
             int resizeMode = mSoftInputMode &
                     WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
             // If we are in auto resize mode, then we need to determine
             // what mode to use now.
             if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
-                final int N = attachInfo.mScrollContainers.size();
+                final int N = mAttachInfo.mScrollContainers.size();
                 for (int i=0; i<N; i++) {
-                    if (attachInfo.mScrollContainers.get(i).isShown()) {
+                    if (mAttachInfo.mScrollContainers.get(i).isShown()) {
                         resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
                     }
                 }
@@ -1437,8 +1432,8 @@
         // If there are no inset listeners remaining then we may still need to compute
         // insets in case the old insets were non-empty and must be reset.
         final boolean computesInternalInsets =
-                attachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
-                || attachInfo.mHasNonEmptyGivenInternalInsets;
+                mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
+                || mAttachInfo.mHasNonEmptyGivenInternalInsets;
 
         boolean insetsPending = false;
         int relayoutResult = 0;
@@ -1659,8 +1654,8 @@
             if (DEBUG_ORIENTATION) Log.v(
                     TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);
 
-            attachInfo.mWindowLeft = frame.left;
-            attachInfo.mWindowTop = frame.top;
+            mAttachInfo.mWindowLeft = frame.left;
+            mAttachInfo.mWindowTop = frame.top;
 
             // !!FIXME!! This next section handles the case where we did not get the
             // window size we asked for. We should avoid this by getting a maximum size from
@@ -1799,20 +1794,20 @@
             // true since we are comparing a not translated value to a translated one.
             // This scenario is rare but we may want to fix that.
 
-            final boolean windowMoved = (attachInfo.mWindowLeft != frame.left
-                    || attachInfo.mWindowTop != frame.top);
+            final boolean windowMoved = (mAttachInfo.mWindowLeft != frame.left
+                    || mAttachInfo.mWindowTop != frame.top);
             if (windowMoved) {
                 if (mTranslator != null) {
                     mTranslator.translateRectInScreenToAppWinFrame(frame);
                 }
-                attachInfo.mWindowLeft = frame.left;
-                attachInfo.mWindowTop = frame.top;
+                mAttachInfo.mWindowLeft = frame.left;
+                mAttachInfo.mWindowTop = frame.top;
             }
         }
 
         final boolean didLayout = layoutRequested && !mStopped;
         boolean triggerGlobalLayoutListener = didLayout
-                || attachInfo.mRecomputeGlobalAttributes;
+                || mAttachInfo.mRecomputeGlobalAttributes;
         if (didLayout) {
             performLayout(lp, desiredWindowWidth, desiredWindowHeight);
 
@@ -1851,18 +1846,18 @@
         }
 
         if (triggerGlobalLayoutListener) {
-            attachInfo.mRecomputeGlobalAttributes = false;
-            attachInfo.mTreeObserver.dispatchOnGlobalLayout();
+            mAttachInfo.mRecomputeGlobalAttributes = false;
+            mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
         }
 
         if (computesInternalInsets) {
             // Clear the original insets.
-            final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
+            final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets;
             insets.reset();
 
             // Compute new insets in place.
-            attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
-            attachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty();
+            mAttachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
+            mAttachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty();
 
             // Tell the window manager.
             if (insetsPending || !mLastGivenInsets.equals(insets)) {
@@ -1941,7 +1936,7 @@
             mReportNextDraw = true;
         }
 
-        boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() ||
+        boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() ||
                 viewVisibility != View.VISIBLE;
 
         if (!cancelDraw && !newSurface) {
@@ -2379,10 +2374,9 @@
 
         scrollToRectOrFocus(null, false);
 
-        final AttachInfo attachInfo = mAttachInfo;
-        if (attachInfo.mViewScrollChanged) {
-            attachInfo.mViewScrollChanged = false;
-            attachInfo.mTreeObserver.dispatchOnScrollChanged();
+        if (mAttachInfo.mViewScrollChanged) {
+            mAttachInfo.mViewScrollChanged = false;
+            mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
         }
 
         boolean animating = mScroller != null && mScroller.computeScrollOffset();
@@ -2397,8 +2391,8 @@
             fullRedrawNeeded = true;
         }
 
-        final float appScale = attachInfo.mApplicationScale;
-        final boolean scalingRequired = attachInfo.mScalingRequired;
+        final float appScale = mAttachInfo.mApplicationScale;
+        final boolean scalingRequired = mAttachInfo.mScalingRequired;
 
         int resizeAlpha = 0;
         if (mResizeBuffer != null) {
@@ -2427,7 +2421,7 @@
         }
 
         if (fullRedrawNeeded) {
-            attachInfo.mIgnoreDirtyState = true;
+            mAttachInfo.mIgnoreDirtyState = true;
             dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
         }
 
@@ -2440,7 +2434,7 @@
                     appScale + ", width=" + mWidth + ", height=" + mHeight);
         }
 
-        attachInfo.mTreeObserver.dispatchOnDraw();
+        mAttachInfo.mTreeObserver.dispatchOnDraw();
 
         int xOffset = 0;
         int yOffset = curScrollY;
@@ -2455,7 +2449,7 @@
         }
 
         if (!dirty.isEmpty() || mIsAnimating) {
-            if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
+            if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                 // Draw with hardware renderer.
                 mIsAnimating = false;
                 if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
@@ -2468,7 +2462,7 @@
                 dirty.setEmpty();
 
                 mBlockResizeBuffer = false;
-                attachInfo.mHardwareRenderer.draw(mView, attachInfo, this);
+                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
             } else {
                 // If we get here with a disabled & requested hardware renderer, something went
                 // wrong (an invalidate posted right before we destroyed the hardware surface
@@ -2478,12 +2472,12 @@
                 // Before we request a new frame we must however attempt to reinitiliaze the
                 // hardware renderer if it's in requested state. This would happen after an
                 // eglTerminate() for instance.
-                if (attachInfo.mHardwareRenderer != null &&
-                        !attachInfo.mHardwareRenderer.isEnabled() &&
-                        attachInfo.mHardwareRenderer.isRequested()) {
+                if (mAttachInfo.mHardwareRenderer != null &&
+                        !mAttachInfo.mHardwareRenderer.isEnabled() &&
+                        mAttachInfo.mHardwareRenderer.isRequested()) {
 
                     try {
-                        attachInfo.mHardwareRenderer.initializeIfNeeded(
+                        mAttachInfo.mHardwareRenderer.initializeIfNeeded(
                                 mWidth, mHeight, mSurface, surfaceInsets);
                     } catch (OutOfResourcesException e) {
                         handleOutOfResourcesException(e);
@@ -2495,7 +2489,7 @@
                     return;
                 }
 
-                if (!drawSoftware(surface, attachInfo, xOffset, yOffset, scalingRequired, dirty)) {
+                if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                     return;
                 }
             }
@@ -2651,20 +2645,17 @@
     }
 
     private Drawable getAccessibilityFocusedDrawable() {
-        if (mAttachInfo != null) {
-            // Lazily load the accessibility focus drawable.
-            if (mAttachInfo.mAccessibilityFocusDrawable == null) {
-                TypedValue value = new TypedValue();
-                final boolean resolved = mView.mContext.getTheme().resolveAttribute(
-                        R.attr.accessibilityFocusedDrawable, value, true);
-                if (resolved) {
-                    mAttachInfo.mAccessibilityFocusDrawable =
+        // Lazily load the accessibility focus drawable.
+        if (mAttachInfo.mAccessibilityFocusDrawable == null) {
+            TypedValue value = new TypedValue();
+            final boolean resolved = mView.mContext.getTheme().resolveAttribute(
+                    R.attr.accessibilityFocusedDrawable, value, true);
+            if (resolved) {
+                mAttachInfo.mAccessibilityFocusDrawable =
                         mView.mContext.getDrawable(value.resourceId);
-                }
             }
-            return mAttachInfo.mAccessibilityFocusDrawable;
         }
-        return null;
+        return mAttachInfo.mAccessibilityFocusDrawable;
     }
 
     /**
@@ -2678,9 +2669,8 @@
     }
 
     boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
-        final View.AttachInfo attachInfo = mAttachInfo;
-        final Rect ci = attachInfo.mContentInsets;
-        final Rect vi = attachInfo.mVisibleInsets;
+        final Rect ci = mAttachInfo.mContentInsets;
+        final Rect vi = mAttachInfo.mVisibleInsets;
         int scrollY = 0;
         boolean handled = false;
 
@@ -5147,12 +5137,11 @@
         if (args.localChanges != 0) {
             mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges);
         }
-        if (mAttachInfo != null) {
-            int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS;
-            if (visibility != mAttachInfo.mGlobalSystemUiVisibility) {
-                mAttachInfo.mGlobalSystemUiVisibility = visibility;
-                mView.dispatchSystemUiVisibilityChanged(visibility);
-            }
+
+        int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS;
+        if (visibility != mAttachInfo.mGlobalSystemUiVisibility) {
+            mAttachInfo.mGlobalSystemUiVisibility = visibility;
+            mView.dispatchSystemUiVisibilityChanged(visibility);
         }
     }
 
@@ -5485,8 +5474,7 @@
     }
 
     private void destroyHardwareRenderer() {
-        AttachInfo attachInfo = mAttachInfo;
-        HardwareRenderer hardwareRenderer = attachInfo.mHardwareRenderer;
+        HardwareRenderer hardwareRenderer = mAttachInfo.mHardwareRenderer;
 
         if (hardwareRenderer != null) {
             if (mView != null) {
@@ -5495,8 +5483,8 @@
             hardwareRenderer.destroy();
             hardwareRenderer.setRequested(false);
 
-            attachInfo.mHardwareRenderer = null;
-            attachInfo.mHardwareAccelerated = false;
+            mAttachInfo.mHardwareRenderer = null;
+            mAttachInfo.mHardwareAccelerated = false;
         }
     }
 
@@ -6197,37 +6185,35 @@
     }
 
     private View getCommonPredecessor(View first, View second) {
-        if (mAttachInfo != null) {
-            if (mTempHashSet == null) {
-                mTempHashSet = new HashSet<View>();
-            }
-            HashSet<View> seen = mTempHashSet;
-            seen.clear();
-            View firstCurrent = first;
-            while (firstCurrent != null) {
-                seen.add(firstCurrent);
-                ViewParent firstCurrentParent = firstCurrent.mParent;
-                if (firstCurrentParent instanceof View) {
-                    firstCurrent = (View) firstCurrentParent;
-                } else {
-                    firstCurrent = null;
-                }
-            }
-            View secondCurrent = second;
-            while (secondCurrent != null) {
-                if (seen.contains(secondCurrent)) {
-                    seen.clear();
-                    return secondCurrent;
-                }
-                ViewParent secondCurrentParent = secondCurrent.mParent;
-                if (secondCurrentParent instanceof View) {
-                    secondCurrent = (View) secondCurrentParent;
-                } else {
-                    secondCurrent = null;
-                }
-            }
-            seen.clear();
+        if (mTempHashSet == null) {
+            mTempHashSet = new HashSet<View>();
         }
+        HashSet<View> seen = mTempHashSet;
+        seen.clear();
+        View firstCurrent = first;
+        while (firstCurrent != null) {
+            seen.add(firstCurrent);
+            ViewParent firstCurrentParent = firstCurrent.mParent;
+            if (firstCurrentParent instanceof View) {
+                firstCurrent = (View) firstCurrentParent;
+            } else {
+                firstCurrent = null;
+            }
+        }
+        View secondCurrent = second;
+        while (secondCurrent != null) {
+            if (seen.contains(secondCurrent)) {
+                seen.clear();
+                return secondCurrent;
+            }
+            ViewParent secondCurrentParent = secondCurrent.mParent;
+            if (secondCurrentParent instanceof View) {
+                secondCurrent = (View) secondCurrentParent;
+            } else {
+                secondCurrent = null;
+            }
+        }
+        seen.clear();
         return null;
     }
 
@@ -6291,6 +6277,11 @@
         return false;
     }
 
+    @Override
+    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+        return false;
+    }
+
     void changeCanvasOpacity(boolean opaque) {
         Log.d(TAG, "changeCanvasOpacity: opaque=" + opaque);
         if (mAttachInfo.mHardwareRenderer != null) {
@@ -6635,6 +6626,9 @@
 
             // Destroy Displaylists so they can be recreated with high contrast recordings
             destroyHardwareResources();
+
+            // Schedule redraw, which will rerecord + redraw all text
+            invalidate();
         }
     }
 
diff --git a/docs/html/preview/tv/adt-1/index.jd b/docs/html/preview/tv/adt-1/index.jd
index e09dcbf..882f421 100644
--- a/docs/html/preview/tv/adt-1/index.jd
+++ b/docs/html/preview/tv/adt-1/index.jd
@@ -139,15 +139,15 @@
   <li>YouTube</li>
   <li>Netflix</li>
   <li>Google+ Photos</li>
-  <li>Google Play Movies and TV (Android only)</li>
+  <li>Google Play Movies and TV (Android and iOS only)</li>
+  <li>Google Play Music</li>
+  <li>Mirror your Android device screen to ADT-1</li>
 </ul>
 
 <p>Coming soon:</p>
 
 <ul>
-  <li>Google Play Music</li>
-  <li>Google Play Movies and TV (iOS and Chrome)</li>
-  <li>Mirror you Android device screen to ADT-1</li>
+  <li>Google Play Movies and TV (Chrome)</li>
 </ul>
 
 <p class="note">
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 75d52b4..9f66904 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -21,6 +21,7 @@
     #define LOG_TAG "OpenGLRenderer"
 #endif
 
+#include <SkColor.h>
 #include <SkPath.h>
 #include <SkPathOps.h>
 #include <SkXfermode.h>
@@ -1315,7 +1316,7 @@
         FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
         fontRenderer.precache(paint, mText, mCount, SkMatrix::I());
 
-        deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
+        deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
                 DeferredDisplayList::kOpBatch_Text :
                 DeferredDisplayList::kOpBatch_ColorText;
     }
@@ -1385,17 +1386,19 @@
             fontRenderer.precache(paint, mText, mCount, transform);
             mPrecacheTransform = transform;
         }
-        deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
+        deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
                 DeferredDisplayList::kOpBatch_Text :
                 DeferredDisplayList::kOpBatch_ColorText;
 
         deferInfo.mergeId = reinterpret_cast<mergeid_t>(mPaint->getColor());
 
         // don't merge decorated text - the decorations won't draw in order
-        bool noDecorations = !(mPaint->getFlags() & (SkPaint::kUnderlineText_Flag |
-                        SkPaint::kStrikeThruText_Flag));
-        deferInfo.mergeable = state.mMatrix.isPureTranslate() && noDecorations &&
-                OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+        bool hasDecorations = mPaint->getFlags()
+                & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag);
+
+        deferInfo.mergeable = state.mMatrix.isPureTranslate()
+                && !hasDecorations
+                && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
     }
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7fa1975..e468a75 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 
 #include <SkCanvas.h>
+#include <SkColor.h>
 #include <SkShader.h>
 #include <SkTypeface.h>
 
@@ -1462,7 +1463,7 @@
             mSkipOutlineClip = true;
 
             SkPaint paint;
-            paint.setColor(0xff000000);
+            paint.setColor(SK_ColorBLACK);
             paint.setXfermodeMode(SkXfermode::kSrc_Mode);
 
             // NOTE: We could use the region contour path to generate a smaller mesh
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index ae48608..9ba8854 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -19,6 +19,7 @@
 
 #include <SkBitmap.h>
 #include <SkCanvas.h>
+#include <SkColor.h>
 #include <SkPaint.h>
 #include <SkPath.h>
 #include <SkRect.h>
@@ -111,7 +112,7 @@
 static void initPaint(SkPaint& paint) {
     // Make sure the paint is opaque, color, alpha, filter, etc.
     // will be applied later when compositing the alpha8 texture
-    paint.setColor(0xff000000);
+    paint.setColor(SK_ColorBLACK);
     paint.setAlpha(255);
     paint.setColorFilter(NULL);
     paint.setMaskFilter(NULL);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index a4ac262..2147810 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -185,7 +185,7 @@
     } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
         dirty.setEmpty();
     } else {
-        if (!dirty.intersect(0, 0, width, height)) {
+        if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
             ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
                     SK_RECT_ARGS(dirty), width, height);
             dirty.setEmpty();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b7210e1..5752646 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -70,6 +70,7 @@
     <uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
     <uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
     <uses-permission android:name="android.permission.START_ANY_ACTIVITY" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c8c8e9a..f8c9d4c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -703,4 +703,5 @@
 
     <!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
     <string name="notification_hidden_text">Contents hidden</string>
+    <string name="guest_exit_guest">Exit guest</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 3c647ed..67eef56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -81,13 +81,7 @@
             UserSwitcherController.UserRecord item = getItem(position);
             UserDetailItemView v = UserDetailItemView.convertOrInflate(
                     mContext, convertView, parent);
-            String name;
-            if (item.isGuest) {
-                name = mContext.getString(
-                        item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
-            } else {
-                name = item.info.name;
-            }
+            String name = getName(mContext, item);
             if (item.picture == null) {
                 v.bind(name, mContext.getDrawable(R.drawable.ic_account_circle_qs));
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 607e155..bd5df28 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.misc;
 
+import android.content.Intent;
 import android.graphics.Color;
 import android.graphics.Rect;
 import com.android.systemui.recents.RecentsConfiguration;
@@ -93,4 +94,10 @@
             throws IllegalAccessException, InvocationTargetException {
         sPropertyMethod.invoke(null, property, value);
     }
+
+    /** Returns whether the specified intent is a document. */
+    public static boolean isDocument(Intent intent) {
+        int flags = intent.getFlags();
+        return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
index 5f4fabe..bb4dc76 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
@@ -28,20 +28,20 @@
 public class KeyStoreLruCache<V> {
     // We keep a set of keys that are associated with the LRU cache, so that we can find out
     // information about the Task that was previously in the cache.
-    HashMap<Task.TaskKey, Task.TaskKey> mKeys = new HashMap<Task.TaskKey, Task.TaskKey>();
+    HashMap<Integer, Task.TaskKey> mTaskKeys = new HashMap<Integer, Task.TaskKey>();
     // The cache implementation
-    LruCache<Task.TaskKey, V> mCache;
+    LruCache<Integer, V> mCache;
 
     public KeyStoreLruCache(int cacheSize) {
-        mCache = new LruCache<Task.TaskKey, V>(cacheSize) {
+        mCache = new LruCache<Integer, V>(cacheSize) {
             @Override
-            protected int sizeOf(Task.TaskKey t, V v) {
+            protected int sizeOf(Integer taskId, V v) {
                 return computeSize(v);
             }
 
             @Override
-            protected void entryRemoved(boolean evicted, Task.TaskKey key, V oldV, V newV) {
-                mKeys.remove(key);
+            protected void entryRemoved(boolean evicted, Integer taskId, V oldV, V newV) {
+                mTaskKeys.remove(taskId);
             }
         };
     }
@@ -53,46 +53,41 @@
 
     /** Gets a specific entry in the cache. */
     final V get(Task.TaskKey key) {
-        return mCache.get(key);
+        return mCache.get(key.id);
     }
 
     /**
-     * Returns the value only if the last active time of the key currently in the lru cache is
-     * greater than or equal to the last active time of the key specified.
+     * Returns the value only if the Task has not updated since the last time it was in the cache.
      */
-    final V getCheckLastActiveTime(Task.TaskKey key) {
-        Task.TaskKey lruKey = mKeys.get(key);
-        if (lruKey != null && (lruKey.lastActiveTime < key.lastActiveTime)) {
-            // The task has changed (been made active since the last time it was put into the
+    final V getAndInvalidateIfModified(Task.TaskKey key) {
+        Task.TaskKey lastKey = mTaskKeys.get(key.id);
+        if (lastKey != null && (lastKey.lastActiveTime < key.lastActiveTime)) {
+            // The task has updated (been made active since the last time it was put into the
             // LRU cache) so invalidate that item in the cache
-            remove(lruKey);
+            remove(key);
             return null;
         }
         // Either the task does not exist in the cache, or the last active time is the same as
-        // the key specified
-        return mCache.get(key);
+        // the key specified, so return what is in the cache
+        return mCache.get(key.id);
     }
 
     /** Puts an entry in the cache for a specific key. */
     final void put(Task.TaskKey key, V value) {
-        mCache.put(key, value);
-        if (mKeys.containsKey(key)) {
-            mKeys.get(key).updateLastActiveTime(key.lastActiveTime);
-        } else {
-            mKeys.put(key, key);
-        }
+        mCache.put(key.id, value);
+        mTaskKeys.put(key.id, key);
     }
 
     /** Removes a cache entry for a specific key. */
     final void remove(Task.TaskKey key) {
-        mCache.remove(key);
-        mKeys.remove(key);
+        mCache.remove(key.id);
+        mTaskKeys.remove(key.id);
     }
 
     /** Removes all the entries in the cache. */
     final void evictAll() {
         mCache.evictAll();
-        mKeys.clear();
+        mTaskKeys.clear();
     }
 
     /** Returns the size of the cache. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 71979c4..adf0794 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -31,6 +31,7 @@
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -42,6 +43,18 @@
     ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
 
     /** Adds a new task to the load queue */
+    void addTasks(Collection<Task> tasks) {
+        for (Task t : tasks) {
+            if (!mQueue.contains(t)) {
+                mQueue.add(t);
+            }
+        }
+        synchronized(this) {
+            notifyAll();
+        }
+    }
+
+    /** Adds a new task to the load queue */
     void addTask(Task t) {
         if (!mQueue.contains(t)) {
             mQueue.add(t);
@@ -153,30 +166,30 @@
                 // Load the next item from the queue
                 final Task t = mLoadQueue.nextTask();
                 if (t != null) {
-                    Drawable cachedIcon = mApplicationIconCache.getCheckLastActiveTime(t.key);
-                    Bitmap cachedThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key);
+                    Drawable cachedIcon = mApplicationIconCache.get(t.key);
+                    Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
                     // Load the application icon if it is stale or we haven't cached one yet
                     if (cachedIcon == null) {
-                        Drawable icon = null;
                         ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(),
                                 t.userId);
                         if (info != null) {
-                            icon = ssp.getActivityIcon(info, t.userId);
+                            cachedIcon = ssp.getActivityIcon(info, t.userId);
                         }
-                        // If we can't load the icon, then set the default application icon into the
-                        // cache.  This will remain until the task's last active time is updated.
-                        cachedIcon = icon != null ? icon : mDefaultApplicationIcon;
+                        if (cachedIcon == null) {
+                            cachedIcon = mDefaultApplicationIcon;
+                        }
+                        // At this point, even if we can't load the icon, we will set the default
+                        // icon.
                         mApplicationIconCache.put(t.key, cachedIcon);
                     }
                     // Load the thumbnail if it is stale or we haven't cached one yet
                     if (cachedThumbnail == null) {
-                        Bitmap thumbnail = ssp.getTaskThumbnail(t.key.id);
-                        if (thumbnail != null) {
-                            thumbnail.setHasAlpha(false);
+                        cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
+                        if (cachedThumbnail != null) {
+                            cachedThumbnail.setHasAlpha(false);
+                        } else {
+                            cachedThumbnail = mDefaultThumbnail;
                         }
-                        // Even if we can't load the icon, we set the default thumbnail into the
-                        // cache.  This will remain until the task's last active time is updated.
-                        cachedThumbnail = thumbnail != null ? thumbnail : mDefaultThumbnail;
                         mThumbnailCache.put(t.key, cachedThumbnail);
                     }
                     if (!mCancelled) {
@@ -281,11 +294,11 @@
         return mSystemServicesProxy;
     }
 
+    /** Gets the list of recent tasks, ordered from back to front. */
     private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) {
         List<ActivityManager.RecentTaskInfo> tasks =
                 ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
         Collections.reverse(tasks);
-
         return tasks;
     }
 
@@ -302,7 +315,7 @@
         SystemServicesProxy ssp = mSystemServicesProxy;
         List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
 
-        // Add each task to the task stack
+        // From back to front, add each task to the task stack
         int taskCount = tasks.size();
         for (int i = 0; i < taskCount; i++) {
             ActivityManager.RecentTaskInfo t = tasks.get(i);
@@ -311,7 +324,7 @@
 
             ActivityManager.TaskDescription av = t.taskDescription;
             String activityLabel = null;
-            Drawable activityIcon = null;
+            Drawable activityIcon = mDefaultApplicationIcon;
             int activityColor = config.taskBarViewDefaultBackgroundColor;
             if (av != null) {
                 activityLabel = (av.getLabel() != null ? av.getLabel() : ssp.getActivityLabel(info));
@@ -323,7 +336,6 @@
             } else {
                 activityLabel = ssp.getActivityLabel(info);
             }
-            boolean isForemostTask = (i == (taskCount - 1));
 
             // Create a new task
             Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, t.affiliatedTaskId,
@@ -333,44 +345,35 @@
             // Preload the specified number of apps
             if (i >= (taskCount - preloadCount)) {
                 // Load the icon from the cache if possible
-                task.applicationIcon = mApplicationIconCache.getCheckLastActiveTime(task.key);
+                task.applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(task.key);
                 if (task.applicationIcon == null) {
-                    if (isForemostTask) {
-                        // We force loading the application icon for the foremost task
-                        task.applicationIcon = ssp.getActivityIcon(info, task.userId);
-                        if (task.applicationIcon == null) {
-                            task.applicationIcon = mDefaultApplicationIcon;
-                        }
-                        // Even if we can't load the icon we set the default application icon into
-                        // the cache.  This will remain until the task's last active time is updated.
+                    // Load the icon from the system
+                    task.applicationIcon = ssp.getActivityIcon(info, task.userId);
+                    if (task.applicationIcon != null) {
                         mApplicationIconCache.put(task.key, task.applicationIcon);
-                    } else {
-                        // Either the task has changed since the last active time, or it was not
-                        // previously cached, so try and load the task anew.
-                        tasksToLoad.add(task);
                     }
                 }
+                if (task.applicationIcon == null) {
+                    // Either the task has changed since the last active time, or it was not
+                    // previously cached, so try and load the task anew.
+                    tasksToLoad.add(task);
+                }
 
-                // Load the thumbnail (if possible and not the foremost task, from the cache)
-                task.thumbnail = mThumbnailCache.getCheckLastActiveTime(task.key);
+                // Load the thumbnail from the cache if possible
+                task.thumbnail = mThumbnailCache.getAndInvalidateIfModified(task.key);
                 if (task.thumbnail == null) {
-                    if (isForemostTask) {
-                        // We force loading the thumbnail icon for the foremost task
-                        task.thumbnail = ssp.getTaskThumbnail(task.key.id);
-                        if (task.thumbnail != null) {
-                            task.thumbnail.setHasAlpha(false);
-                        } else {
-                            task.thumbnail = mDefaultThumbnail;
-                        }
-                        // Even if we can't load the thumbnail we set the default thumbnail into
-                        // the cache.  This will remain until the task's last active time is updated.
+                    // Load the thumbnail from the system
+                    task.thumbnail = ssp.getTaskThumbnail(task.key.id);
+                    if (task.thumbnail != null) {
+                        task.thumbnail.setHasAlpha(false);
                         mThumbnailCache.put(task.key, task.thumbnail);
-                    } else {
-                        // Either the task has changed since the last active time, or it was not
-                        // previously cached, so try and load the task anew.
-                        tasksToLoad.add(task);
                     }
                 }
+                if (task.thumbnail == null) {
+                    // Either the task has changed since the last active time, or it was not
+                    // previously cached, so try and load the task anew.
+                    tasksToLoad.add(task);
+                }
             }
 
             // Add the task to the stack
@@ -380,13 +383,9 @@
         // Simulate the groupings that we describe
         stack.createAffiliatedGroupings();
 
-        // Start the task loader
+        // Start the task loader and add all the tasks we need to load
         mLoader.start(context);
-
-        // Add all the tasks that we are reloading
-        for (Task t : tasksToLoad) {
-            mLoadQueue.addTask(t);
-        }
+        mLoadQueue.addTasks(tasksToLoad);
 
         // Update the package monitor with the list of packages to listen for
         mPackageMonitor.setTasks(tasks);
@@ -414,18 +413,14 @@
 
     /** Acquires the task resource data directly from the pool. */
     public void loadTaskData(Task t) {
-        Drawable applicationIcon = mApplicationIconCache.get(t.key);
-        Bitmap thumbnail = mThumbnailCache.get(t.key);
+        Drawable applicationIcon = mApplicationIconCache.getAndInvalidateIfModified(t.key);
+        Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
 
-        boolean requiresLoad = false;
-        if (applicationIcon == null) {
-            applicationIcon = mDefaultApplicationIcon;
-            requiresLoad = true;
-        }
-        if (thumbnail == null) {
-            thumbnail = mLoadingThumbnail;
-            requiresLoad = true;
-        }
+        // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
+        // use the default assets in their place until they load
+        boolean requiresLoad = (applicationIcon == null) || (thumbnail == null);
+        applicationIcon = applicationIcon != null ? applicationIcon : mDefaultApplicationIcon;
+        thumbnail = thumbnail != null ? thumbnail : mDefaultThumbnail;
         if (requiresLoad) {
             mLoadQueue.addTask(t);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 1670735..2473352 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -51,10 +51,6 @@
             this.lastActiveTime = lastActiveTime;
         }
 
-        public void updateLastActiveTime(long lastActiveTime) {
-            this.lastActiveTime = lastActiveTime;
-        }
-
         @Override
         public boolean equals(Object o) {
             if (!(o instanceof TaskKey)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 78a99e0..e1c652e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -38,6 +38,7 @@
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.RecentsPackageMonitor;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.SpaceNode;
@@ -458,7 +459,7 @@
                     Intent i = new Intent(task.key.baseIntent);
                     i.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
                             | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
-                    if ((i.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == 0) {
+                    if (!Utilities.isDocument(i)) {
                         i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                     }
                     try {
@@ -510,11 +511,8 @@
         loader.deleteTaskData(t, false);
 
         // Remove the old task from activity manager
-        int flags = t.key.baseIntent.getFlags();
-        boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
-                Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
         RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id,
-                isDocument);
+                Utilities.isDocument(t.key.baseIntent));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 4640067..7cc8ed5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -30,8 +30,10 @@
 import android.graphics.Bitmap;
 import android.os.AsyncTask;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManagerGlobal;
@@ -64,15 +66,37 @@
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
         filter.addAction(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiver(mReceiver, filter);
-        refreshUsers();
+        filter.addAction(Intent.ACTION_USER_STOPPING);
+        mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter,
+                null /* permission */, null /* scheduler */);
+        refreshUsers(UserHandle.USER_NULL);
     }
 
-    private void refreshUsers() {
-        new AsyncTask<Void, Void, ArrayList<UserRecord>>() {
+    /**
+     * Refreshes users from UserManager.
+     *
+     * The pictures are only loaded if they have not been loaded yet.
+     *
+     * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
+     */
+    private void refreshUsers(int forcePictureLoadForId) {
 
+        SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
+        final int N = mUsers.size();
+        for (int i = 0; i < N; i++) {
+            UserRecord r = mUsers.get(i);
+            if (r == null || r.info == null
+                    || r.info.id == forcePictureLoadForId || r.picture == null) {
+                continue;
+            }
+            bitmaps.put(r.info.id, r.picture);
+        }
+
+        new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
+            @SuppressWarnings("unchecked")
             @Override
-            protected ArrayList<UserRecord> doInBackground(Void... params) {
+            protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
+                final SparseArray<Bitmap> bitmaps = params[0];
                 List<UserInfo> infos = mUserManager.getUsers(true);
                 if (infos == null) {
                     return null;
@@ -87,8 +111,11 @@
                         guestRecord = new UserRecord(info, null /* picture */,
                                 true /* isGuest */, isCurrent);
                     } else if (!info.isManagedProfile()) {
-                        records.add(new UserRecord(info, mUserManager.getUserIcon(info.id),
-                                false /* isGuest */, isCurrent));
+                        Bitmap picture = bitmaps.get(info.id);
+                        if (picture == null) {
+                            picture = mUserManager.getUserIcon(info.id);
+                        }
+                        records.add(new UserRecord(info, picture, false /* isGuest */, isCurrent));
                     }
                 }
 
@@ -109,7 +136,7 @@
                     notifyAdapters();
                 }
             }
-        }.execute((Void[])null);
+        }.execute((SparseArray)bitmaps);
     }
 
     private void notifyAdapters() {
@@ -134,9 +161,16 @@
         }
 
         if (ActivityManager.getCurrentUser() == id) {
+            if (record.isGuest) {
+                exitGuest(id);
+            }
             return;
         }
 
+        switchToUserId(id);
+    }
+
+    private void switchToUserId(int id) {
         try {
             WindowManagerGlobal.getWindowManagerService().lockNow(null);
             ActivityManagerNative.getDefault().switchUser(id);
@@ -145,6 +179,12 @@
         }
     }
 
+    private void exitGuest(int id) {
+        // TODO: show confirmation dialog
+        switchToUserId(UserHandle.USER_OWNER);
+        mUserManager.removeUser(id);
+    }
+
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -153,15 +193,20 @@
                 final int N = mUsers.size();
                 for (int i = 0; i < N; i++) {
                     UserRecord record = mUsers.get(i);
+                    if (record.info == null) continue;
                     boolean shouldBeCurrent = record.info.id == currentId;
                     if (record.isCurrent != shouldBeCurrent) {
                         mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
                     }
                 }
                 notifyAdapters();
-            } else {
-                refreshUsers();
             }
+            int forcePictureLoadForId = UserHandle.USER_NULL;
+            if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) {
+                forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                        UserHandle.USER_NULL);
+            }
+            refreshUsers(forcePictureLoadForId);
         }
     };
 
@@ -195,12 +240,25 @@
 
         @Override
         public long getItemId(int position) {
-            return mController.mUsers.get(position).info.id;
+            return position;
         }
 
         public void switchTo(UserRecord record) {
             mController.switchTo(record);
         }
+
+        public String getName(Context context, UserRecord item) {
+            if (item.isGuest) {
+                if (item.isCurrent) {
+                    return context.getString(R.string.guest_exit_guest);
+                } else {
+                    return context.getString(
+                            item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
+                }
+            } else {
+                return item.info.name;
+            }
+        }
     }
 
     public static final class UserRecord {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index abbaacc..da0bc30 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1729,6 +1729,7 @@
                         return true;
                     }
                 }
+                return false;
             }
 
             case KeyEvent.KEYCODE_MENU: {
@@ -1822,6 +1823,7 @@
                         return true;
                     }
                 }
+                return false;
             }
 
             case KeyEvent.KEYCODE_MENU: {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index e8e2813..f54f798 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -233,6 +233,7 @@
     static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
     static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
     static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
+    static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
 
     // User confirmation timeout for a full backup/restore operation.  It's this long in
     // order to give them time to enter the backup password.
@@ -6309,6 +6310,7 @@
         RUNNING_QUEUE,
         RESTORE_KEYVALUE,
         RESTORE_FULL,
+        RESTORE_FINISHED,
         FINAL
     }
 
@@ -6343,6 +6345,9 @@
         // Our bookkeeping about the ancestral dataset
         private PackageManagerBackupAgent mPmAgent;
 
+        // Currently-bound backup agent for restore + restoreFinished purposes
+        private IBackupAgent mAgent;
+
         // What sort of restore we're doing now
         private RestoreDescription mRestoreDescription;
 
@@ -6441,6 +6446,13 @@
                     // this one is always valid too
                 }
             }
+
+            if (MORE_DEBUG) {
+                Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
+                for (PackageInfo info : mAcceptSet) {
+                    Slog.v(TAG, "   " + info.packageName);
+                }
+            }
         }
 
         private String[] packagesToNames(List<PackageInfo> apps) {
@@ -6473,6 +6485,10 @@
                     restoreFull();
                     break;
 
+                case RESTORE_FINISHED:
+                    restoreFinished();
+                    break;
+
                 case FINAL:
                     if (!mFinished) finalizeRestore();
                     else {
@@ -6529,7 +6545,7 @@
         private  void startRestore() {
             sendStartRestore(mAcceptSet.size());
 
-            UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE;
+            UnifiedRestoreState nextState = UnifiedRestoreState.RESTORE_FINISHED;
             try {
                 // If we don't yet have PM metadata for this token, synthesize an
                 // entry for the PM pseudopackage and make it the first to be
@@ -6544,6 +6560,7 @@
                     mPmAgent = new PackageManagerBackupAgent(metadataFile);
                 } catch (IOException e) {
                     // Nope, we need to get it via restore
+                    if (MORE_DEBUG) Slog.v(TAG, "Need to restore @pm@");
                     PackageInfo pmPackage = new PackageInfo();
                     pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
                     mAcceptSet.add(0, pmPackage);
@@ -6581,8 +6598,11 @@
                     mCurrentPackage = new PackageInfo();
                     mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
                     mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
-                    initiateOneRestore(mCurrentPackage, 0,
-                            IBackupAgent.Stub.asInterface(mPmAgent.onBind()));
+                    mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
+                    if (MORE_DEBUG) {
+                        Slog.v(TAG, "initiating restore for PMBA");
+                    }
+                    initiateOneRestore(mCurrentPackage, 0);
                     // The PM agent called operationComplete() already, because our invocation
                     // of it is process-local and therefore synchronous.  That means that a
                     // RUNNING_QUEUE message is already enqueued.  Only if we're unable to
@@ -6621,6 +6641,11 @@
                         nextState = UnifiedRestoreState.FINAL;
                         return;
                     }
+                } else {
+                    // We have the PMBA already, so we can proceed directly to
+                    // the RUNNING_QUEUE state ourselves.
+                    if (MORE_DEBUG) Slog.v(TAG, "PMBA from cache; proceeding to run queue");
+                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
                 }
             } catch (RemoteException e) {
                 // If we lost the transport at any time, halt
@@ -6762,10 +6787,10 @@
             }
 
             // Good to go!  Set up and bind the agent...
-            IBackupAgent agent = bindToAgentSynchronous(
+            mAgent = bindToAgentSynchronous(
                     mCurrentPackage.applicationInfo,
                     IApplicationThread.BACKUP_MODE_INCREMENTAL);
-            if (agent == null) {
+            if (mAgent == null) {
                 Slog.w(TAG, "Can't find backup agent for " + packageName);
                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
                         "Restore agent missing");
@@ -6775,7 +6800,7 @@
 
             // And then finally start the restore on this agent
             try {
-                initiateOneRestore(mCurrentPackage, metaInfo.versionCode, agent);
+                initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
                 ++mCount;
             } catch (Exception e) {
                 Slog.e(TAG, "Error when attempting restore: " + e.toString());
@@ -6785,7 +6810,7 @@
         }
 
         // Guts of a key/value restore operation
-        void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
+        void initiateOneRestore(PackageInfo app, int appVersionCode) {
             final String packageName = app.packageName;
 
             if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
@@ -6881,7 +6906,7 @@
                 // the operationComplete() callback will schedule the next step,
                 // so we do not do that here.
                 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
-                agent.doRestore(mBackupData, appVersionCode, mNewState,
+                mAgent.doRestore(mBackupData, appVersionCode, mNewState,
                         token, mBackupManagerBinder);
             } catch (Exception e) {
                 Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
@@ -6926,6 +6951,20 @@
             }
         }
 
+        // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
+        private void restoreFinished() {
+            try {
+                final int token = generateToken();
+                prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this);
+                mAgent.doRestoreFinished(token, mBackupManagerBinder);
+                // If we get this far, the callback or timeout will schedule the
+                // next restore state, so we're done
+            } catch (Exception e) {
+                Slog.e(TAG, "Unable to finalize restore of " + mCurrentPackage.packageName);
+                executeNextState(UnifiedRestoreState.FINAL);
+            }
+        }
+
         class StreamFeederThread extends RestoreEngine implements Runnable {
             final String TAG = "StreamFeederThread";
             FullRestoreEngine mEngine;
@@ -7202,23 +7241,58 @@
 
         @Override
         public void operationComplete() {
-            int size = (int) mBackupDataName.length();
-            EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size);
-
-            // Just go back to running the restore queue
-            keyValueAgentCleanup();
-
-            // If there was widget state associated with this app, get the OS to
-            // incorporate it into current bookeeping and then pass that along to
-            // the app as part of the restore operation.
-            if (mWidgetData != null) {
-                restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
+            if (MORE_DEBUG) {
+                Slog.i(TAG, "operationComplete() during restore: target="
+                        + mCurrentPackage.packageName
+                        + " state=" + mState);
             }
 
-            executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
+            final UnifiedRestoreState nextState;
+            switch (mState) {
+                case RESTORE_KEYVALUE:
+                case RESTORE_FULL: {
+                    // Okay, we've just heard back from the agent that it's done with
+                    // the restore itself.  We now have to send the same agent its
+                    // doRestoreFinished() callback, so roll into that state.
+                    nextState = UnifiedRestoreState.RESTORE_FINISHED;
+                    break;
+                }
+
+                case RESTORE_FINISHED: {
+                    // Okay, we're done with this package.  Tidy up and go on to the next
+                    // app in the queue.
+                    int size = (int) mBackupDataName.length();
+                    EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
+                            mCurrentPackage.packageName, size);
+
+                    // Just go back to running the restore queue
+                    keyValueAgentCleanup();
+
+                    // If there was widget state associated with this app, get the OS to
+                    // incorporate it into current bookeeping and then pass that along to
+                    // the app as part of the restore-time work.
+                    if (mWidgetData != null) {
+                        restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
+                    }
+
+                    nextState = UnifiedRestoreState.RUNNING_QUEUE;
+                    break;
+                }
+
+                default: {
+                    // Some kind of horrible semantic error; we're in an unexpected state.
+                    // Back off hard and wind up.
+                    Slog.e(TAG, "Unexpected restore callback into state " + mState);
+                    keyValueAgentErrorCleanup();
+                    nextState = UnifiedRestoreState.FINAL;
+                    break;
+                }
+            }
+
+            executeNextState(nextState);
         }
 
-        // A call to agent.doRestore() has timed out
+        // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
         @Override
         public void handleTimeout() {
             Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8b7e0d6..31c1c6c 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -364,28 +364,6 @@
     // devices.
     private boolean mShowDialogs = true;
 
-    /**
-     * Description of a request to start a new activity, which has been held
-     * due to app switches being disabled.
-     */
-    static class PendingActivityLaunch {
-        final ActivityRecord r;
-        final ActivityRecord sourceRecord;
-        final int startFlags;
-        final ActivityStack stack;
-
-        PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
-                int _startFlags, ActivityStack _stack) {
-            r = _r;
-            sourceRecord = _sourceRecord;
-            startFlags = _startFlags;
-            stack = _stack;
-        }
-    }
-
-    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
-            = new ArrayList<PendingActivityLaunch>();
-
     BroadcastQueue mFgBroadcastQueue;
     BroadcastQueue mBgBroadcastQueue;
     // Convenient for easy iteration over the queues. Foreground is first
@@ -1438,7 +1416,7 @@
             } break;
             case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    doPendingActivityLaunchesLocked(true);
+                    mStackSupervisor.doPendingActivityLaunchesLocked(true);
                 }
             } break;
             case KILL_APPLICATION_MSG: {
@@ -3339,19 +3317,6 @@
         mProcessObservers.finishBroadcast();
     }
 
-    final void doPendingActivityLaunchesLocked(boolean doResume) {
-        final int N = mPendingActivityLaunches.size();
-        if (N <= 0) {
-            return;
-        }
-        for (int i=0; i<N; i++) {
-            PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
-            mStackSupervisor.startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
-                    doResume && i == (N-1), null);
-        }
-        mPendingActivityLaunches.clear();
-    }
-
     @Override
     public final int startActivity(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo,
@@ -9140,6 +9105,7 @@
         }
     }
 
+    @Override
     public void stopAppSwitches() {
         if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)
                 != PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 32f2624..91bc7e3 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2732,18 +2732,23 @@
         return r;
     }
 
-    void finishAllActivitiesLocked() {
+    void finishAllActivitiesLocked(boolean immediately) {
+        boolean noActivitiesInStack = true;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
-                if (r.finishing) {
+                noActivitiesInStack = false;
+                if (r.finishing && !immediately) {
                     continue;
                 }
-                Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r);
+                Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r + " immediately");
                 finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
             }
         }
+        if (noActivitiesInStack) {
+            mActivityContainer.onTaskListEmptyLocked();
+        }
     }
 
     final boolean navigateUpToLocked(IBinder token, Intent destIntent, int resultCode,
@@ -2858,6 +2863,7 @@
         // down to the max limit while they are still waiting to finish.
         mStackSupervisor.mFinishingActivities.remove(r);
         mStackSupervisor.mWaitingVisibleActivities.remove(r);
+        mStackSupervisor.removePendingActivityLaunchesLocked(r);
 
         // Remove any pending results.
         if (r.finishing && r.pendingResults != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0310962..4cfd042 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -98,7 +98,6 @@
 import com.android.internal.os.TransferPipe;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.server.LocalServices;
-import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.wm.WindowManagerService;
 
@@ -271,6 +270,28 @@
      */
     private LockTaskNotify mLockTaskNotify;
 
+    final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
+            = new ArrayList<PendingActivityLaunch>();
+
+    /**
+     * Description of a request to start a new activity, which has been held
+     * due to app switches being disabled.
+     */
+    static class PendingActivityLaunch {
+        final ActivityRecord r;
+        final ActivityRecord sourceRecord;
+        final int startFlags;
+        final ActivityStack stack;
+
+        PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
+                int _startFlags, ActivityStack _stack) {
+            r = _r;
+            sourceRecord = _sourceRecord;
+            startFlags = _startFlags;
+            stack = _stack;
+        }
+    }
+
     public ActivityStackSupervisor(ActivityManagerService service) {
         mService = service;
         mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
@@ -1421,7 +1442,7 @@
             if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
                 PendingActivityLaunch pal =
                         new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
-                mService.mPendingActivityLaunches.add(pal);
+                mPendingActivityLaunches.add(pal);
                 setDismissKeyguard(false);
                 ActivityOptions.abort(options);
                 return ActivityManager.START_SWITCHES_CANCELED;
@@ -1439,7 +1460,7 @@
             mService.mDidAppSwitch = true;
         }
 
-        mService.doPendingActivityLaunchesLocked(false);
+        doPendingActivityLaunchesLocked(false);
 
         err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
                 startFlags, true, options);
@@ -2008,6 +2029,23 @@
         return ActivityManager.START_SUCCESS;
     }
 
+    final void doPendingActivityLaunchesLocked(boolean doResume) {
+        while (!mPendingActivityLaunches.isEmpty()) {
+            PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
+            startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+                    doResume && mPendingActivityLaunches.isEmpty(), null);
+        }
+    }
+
+    void removePendingActivityLaunchesLocked(ActivityRecord r) {
+        for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) {
+            PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
+            if (pal.r == r) {
+                mPendingActivityLaunches.remove(palNdx);
+            }
+        }
+    }
+
     void acquireLaunchWakelock() {
         if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
             throw new IllegalStateException("Calling must be system uid");
@@ -3312,7 +3350,9 @@
                     synchronized (mService) {
                         Slog.w(TAG, "Timeout waiting for all activities in task to finish. " +
                                 msg.obj);
-                        ((ActivityContainer) msg.obj).onTaskListEmptyLocked();
+                        final ActivityContainer container = (ActivityContainer) msg.obj;
+                        container.mStack.finishAllActivitiesLocked(true);
+                        container.onTaskListEmptyLocked();
                     }
                 } break;
                 case LAUNCH_TASK_BEHIND_COMPLETE: {
@@ -3414,11 +3454,11 @@
 
                 final Message msg =
                         mHandler.obtainMessage(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this);
-                mHandler.sendMessageDelayed(msg, 1000);
+                mHandler.sendMessageDelayed(msg, 2000);
 
                 long origId = Binder.clearCallingIdentity();
                 try {
-                    mStack.finishAllActivitiesLocked();
+                    mStack.finishAllActivitiesLocked(false);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
                 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 727cff0..a70dd1b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6231,31 +6231,26 @@
         info.nativeLibraryDir = null;
         info.secondaryNativeLibraryDir = null;
 
-        if (bundledApp) {
-            // Monolithic bundled install
-            // TODO: support cluster bundled installs?
-
-            final boolean is64Bit = (info.primaryCpuAbi != null)
-                    && VMRuntime.is64BitAbi(info.primaryCpuAbi);
-
-            // This is a bundled system app so choose the path based on the ABI.
-            // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
-            // is just the default path.
-            final String apkName = deriveCodePathName(codePath);
-            final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
-            info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
-                    apkName).getAbsolutePath();
-            info.nativeLibraryRootRequiresIsa = false;
-
-            info.nativeLibraryDir = info.nativeLibraryRootDir;
-            if (info.secondaryCpuAbi != null) {
-                final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
-                info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
-                        secondaryLibDir, apkName).getAbsolutePath();
-            }
-        } else if (isApkFile(codeFile)) {
+        if (isApkFile(codeFile)) {
             // Monolithic install
-            if (asecApp) {
+            if (bundledApp) {
+                final boolean is64Bit = VMRuntime.is64BitInstructionSet(
+                        getPrimaryInstructionSet(info));
+
+                // This is a bundled system app so choose the path based on the ABI.
+                // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
+                // is just the default path.
+                final String apkName = deriveCodePathName(codePath);
+                final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
+                info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
+                        apkName).getAbsolutePath();
+
+                if (info.secondaryCpuAbi != null) {
+                    final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
+                    info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
+                            secondaryLibDir, apkName).getAbsolutePath();
+                }
+            } else if (asecApp) {
                 info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
                         .getAbsolutePath();
             } else {
@@ -6271,10 +6266,8 @@
             info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
             info.nativeLibraryRootRequiresIsa = true;
 
-            if (info.primaryCpuAbi != null) {
-                info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
-                        VMRuntime.getInstructionSet(info.primaryCpuAbi)).getAbsolutePath();
-            }
+            info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
+                    getPrimaryInstructionSet(info)).getAbsolutePath();
 
             if (info.secondaryCpuAbi != null) {
                 info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 43469ba..b4cc252 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -843,7 +843,8 @@
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_TELEPHONY);
+        writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
+        writeBoolean(serializer, restrictions, UserManager.DISALLOW_SMS);
         writeBoolean(serializer, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
         serializer.endTag(null, TAG_RESTRICTIONS);
     }
@@ -986,7 +987,8 @@
                 UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
         readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
         readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_TELEPHONY);
+        readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
+        readBoolean(parser, restrictions, UserManager.DISALLOW_SMS);
         readBoolean(parser, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
     }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 4a8623d..bed85fc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -94,7 +94,7 @@
     }
 
     public boolean addOrUpdateKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
-        SQLiteDatabase db = this.getWritableDatabase();
+        SQLiteDatabase db = getWritableDatabase();
         ContentValues values = new ContentValues();
         // Generate a random ID for the model.
         values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
@@ -105,7 +105,7 @@
         if (db.insertWithOnConflict(
                 SoundModelContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
             for (Keyphrase keyphrase : soundModel.keyphrases) {
-                status &= addKeyphrase(soundModel.uuid, keyphrase);
+                status &= addOrUpdateKeyphrase(soundModel.uuid, keyphrase);
             }
             return status;
         } else {
@@ -114,18 +114,16 @@
         }
     }
 
-    /**
-     * TODO(sansid): Change to addOrUpdate to handle changes here.
-     */
-    private boolean addKeyphrase(UUID modelId, Keyphrase keyphrase) {
-        SQLiteDatabase db = this.getWritableDatabase();
+    private boolean addOrUpdateKeyphrase(UUID modelId, Keyphrase keyphrase) {
+        SQLiteDatabase db = getWritableDatabase();
         ContentValues values = new ContentValues();
         values.put(KeyphraseContract.KEY_ID, keyphrase.id);
         values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModeFlags);
-        values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, keyphrase.id);
+        values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, modelId.toString());
         values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.hintText);
         values.put(KeyphraseContract.KEY_LOCALE, keyphrase.locale);
-        if (db.insert(KeyphraseContract.TABLE, null, values) != -1) {
+        if (db.insertWithOnConflict(
+                KeyphraseContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
             return true;
         } else {
             Slog.w(TAG, "Failed to persist keyphrase to database");
@@ -134,6 +132,26 @@
     }
 
     /**
+     * Deletes the sound model and associated keyphrases.
+     */
+    public boolean deleteKeyphraseSoundModel(UUID uuid) {
+        SQLiteDatabase db = getWritableDatabase();
+        String modelId = uuid.toString();
+        String soundModelClause = SoundModelContract.KEY_ID + "=" + modelId;
+        boolean status = true;
+        if (db.delete(SoundModelContract.TABLE, soundModelClause, null) == 0) {
+            Slog.w(TAG, "No sound models deleted from the database");
+            status = false;
+        }
+        String keyphraseClause = KeyphraseContract.KEY_SOUND_MODEL_ID + "=" + modelId;
+        if (db.delete(KeyphraseContract.TABLE, keyphraseClause, null) == 0) {
+            Slog.w(TAG, "No keyphrases deleted from the database");
+            status = false;
+        }
+        return status;
+    }
+
+    /**
      * Lists all the keyphrase sound models currently registered with the system.
      */
     public List<KeyphraseSoundModel> getKephraseSoundModels() {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 7204695..63702ba 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -281,12 +281,25 @@
                     throw new SecurityException("Caller does not hold the permission "
                             + Manifest.permission.MANAGE_VOICE_KEYPHRASES);
                 }
+                if (model == null) {
+                    throw new IllegalArgumentException("Model must not be null");
+                }
+
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    if (mDbHelper.addOrUpdateKeyphraseSoundModel(model)) {
-                        return STATUS_OK;
+                    // If the keyphrases are not present in the model, delete the model.
+                    if (model.keyphrases == null) {
+                        if (mDbHelper.deleteKeyphraseSoundModel(model.uuid)) {
+                            return STATUS_OK;
+                        } else {
+                            return STATUS_ERROR;
+                        }
                     } else {
-                        return STATUS_ERROR;
+                        if (mDbHelper.addOrUpdateKeyphraseSoundModel(model)) {
+                            return STATUS_OK;
+                        } else {
+                            return STATUS_ERROR;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(caller);