Merge "Cleanup USER_OWNER in pm"
diff --git a/api/current.txt b/api/current.txt
index 2d133aa..c3a4a0c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4016,6 +4016,23 @@
     field public java.lang.String serviceDetails;
   }
 
+  public class AutomaticZenRule implements android.os.Parcelable {
+    ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
+    ctor public AutomaticZenRule(android.os.Parcel);
+    method public int describeContents();
+    method public android.net.Uri getConditionId();
+    method public int getInterruptionFilter();
+    method public java.lang.String getName();
+    method public android.content.ComponentName getOwner();
+    method public boolean isEnabled();
+    method public void setConditionId(android.net.Uri);
+    method public void setEnabled(boolean);
+    method public void setInterruptionFilter(int);
+    method public void setName(java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR;
+  }
+
   public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
     ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
     ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
@@ -5061,15 +5078,20 @@
   }
 
   public class NotificationManager {
+    method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule);
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
+    method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
+    method public java.util.List<android.app.AutomaticZenRule> getAutomaticZenRules();
     method public final int getCurrentInterruptionFilter();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
     method public boolean isNotificationPolicyAccessGranted();
     method public void notify(int, android.app.Notification);
     method public void notify(java.lang.String, int, android.app.Notification);
+    method public boolean removeAutomaticZenRule(java.lang.String);
+    method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
@@ -9213,6 +9235,7 @@
     method public void setAppLabel(java.lang.CharSequence);
     method public void setAppPackageName(java.lang.String);
     method public void setInstallLocation(int);
+    method public void setOriginatingUid(int);
     method public void setOriginatingUri(android.net.Uri);
     method public void setReferrerUri(android.net.Uri);
     method public void setSize(long);
@@ -23567,7 +23590,7 @@
     method public static final int myPid();
     method public static final int myTid();
     method public static final int myUid();
-    method public static final android.os.UserHandle myUserHandle();
+    method public static android.os.UserHandle myUserHandle();
     method public static final void sendSignal(int, int);
     method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
     method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
@@ -24939,6 +24962,7 @@
   public static class CallLog.Calls implements android.provider.BaseColumns {
     ctor public CallLog.Calls();
     method public static java.lang.String getLastOutgoingCall(android.content.Context);
+    field public static final int BLOCKED_TYPE = 6; // 0x6
     field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number";
     field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri";
     field public static final java.lang.String CACHED_MATCHED_NUMBER = "matched_number";
@@ -24977,6 +25001,7 @@
     field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
     field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
+    field public static final int REJECTED_TYPE = 5; // 0x5
     field public static final java.lang.String TRANSCRIPTION = "transcription";
     field public static final java.lang.String TYPE = "type";
     field public static final int VOICEMAIL_TYPE = 4; // 0x4
@@ -40917,6 +40942,7 @@
     method public int getInputMethodMode();
     method public int getMaxAvailableHeight(android.view.View);
     method public int getMaxAvailableHeight(android.view.View, int);
+    method public int getMaxAvailableHeight(android.view.View, int, boolean);
     method public boolean getOverlapAnchor();
     method public int getSoftInputMode();
     method public int getWidth();
@@ -45360,7 +45386,10 @@
   }
 
   public final class Constructor extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+    method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
+    method public java.lang.annotation.Annotation[] getAnnotations();
+    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<T> getDeclaringClass();
     method public java.lang.Class<?>[] getExceptionTypes();
     method public java.lang.reflect.Type[] getGenericExceptionTypes();
@@ -45370,6 +45399,7 @@
     method public java.lang.annotation.Annotation[][] getParameterAnnotations();
     method public java.lang.Class<?>[] getParameterTypes();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Constructor<T>>[] getTypeParameters();
+    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isSynthetic();
     method public boolean isVarArgs();
     method public T newInstance(java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException;
@@ -45443,7 +45473,10 @@
   }
 
   public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+    method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
+    method public java.lang.annotation.Annotation[] getAnnotations();
+    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<?> getDeclaringClass();
     method public java.lang.Object getDefaultValue();
     method public java.lang.Class<?>[] getExceptionTypes();
@@ -45457,6 +45490,7 @@
     method public java.lang.Class<?> getReturnType();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
     method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
+    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isBridge();
     method public boolean isSynthetic();
     method public boolean isVarArgs();
diff --git a/api/system-current.txt b/api/system-current.txt
index 93551da..325aa28 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4127,6 +4127,23 @@
     field public java.lang.String serviceDetails;
   }
 
+  public class AutomaticZenRule implements android.os.Parcelable {
+    ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
+    ctor public AutomaticZenRule(android.os.Parcel);
+    method public int describeContents();
+    method public android.net.Uri getConditionId();
+    method public int getInterruptionFilter();
+    method public java.lang.String getName();
+    method public android.content.ComponentName getOwner();
+    method public boolean isEnabled();
+    method public void setConditionId(android.net.Uri);
+    method public void setEnabled(boolean);
+    method public void setInterruptionFilter(int);
+    method public void setName(java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR;
+  }
+
   public class BroadcastOptions {
     method public static android.app.BroadcastOptions makeBasic();
     method public void setTemporaryAppWhitelistDuration(long);
@@ -5178,15 +5195,20 @@
   }
 
   public class NotificationManager {
+    method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule);
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
+    method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
+    method public java.util.List<android.app.AutomaticZenRule> getAutomaticZenRules();
     method public final int getCurrentInterruptionFilter();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
     method public boolean isNotificationPolicyAccessGranted();
     method public void notify(int, android.app.Notification);
     method public void notify(java.lang.String, int, android.app.Notification);
+    method public boolean removeAutomaticZenRule(java.lang.String);
+    method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
@@ -9496,6 +9518,7 @@
     method public void setAppPackageName(java.lang.String);
     method public void setGrantedRuntimePermissions(java.lang.String[]);
     method public void setInstallLocation(int);
+    method public void setOriginatingUid(int);
     method public void setOriginatingUri(android.net.Uri);
     method public void setReferrerUri(android.net.Uri);
     method public void setSize(long);
@@ -25519,7 +25542,7 @@
     method public static final int myPid();
     method public static final int myTid();
     method public static final int myUid();
-    method public static final android.os.UserHandle myUserHandle();
+    method public static android.os.UserHandle myUserHandle();
     method public static final void sendSignal(int, int);
     method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
     method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
@@ -26895,6 +26918,7 @@
   public static class CallLog.Calls implements android.provider.BaseColumns {
     ctor public CallLog.Calls();
     method public static java.lang.String getLastOutgoingCall(android.content.Context);
+    field public static final int BLOCKED_TYPE = 6; // 0x6
     field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number";
     field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri";
     field public static final java.lang.String CACHED_MATCHED_NUMBER = "matched_number";
@@ -26933,6 +26957,7 @@
     field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
     field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
+    field public static final int REJECTED_TYPE = 5; // 0x5
     field public static final java.lang.String TRANSCRIPTION = "transcription";
     field public static final java.lang.String TYPE = "type";
     field public static final int VOICEMAIL_TYPE = 4; // 0x4
@@ -43526,6 +43551,7 @@
     method public int getInputMethodMode();
     method public int getMaxAvailableHeight(android.view.View);
     method public int getMaxAvailableHeight(android.view.View, int);
+    method public int getMaxAvailableHeight(android.view.View, int, boolean);
     method public boolean getOverlapAnchor();
     method public int getSoftInputMode();
     method public int getWidth();
@@ -47969,7 +47995,10 @@
   }
 
   public final class Constructor extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+    method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
+    method public java.lang.annotation.Annotation[] getAnnotations();
+    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<T> getDeclaringClass();
     method public java.lang.Class<?>[] getExceptionTypes();
     method public java.lang.reflect.Type[] getGenericExceptionTypes();
@@ -47979,6 +48008,7 @@
     method public java.lang.annotation.Annotation[][] getParameterAnnotations();
     method public java.lang.Class<?>[] getParameterTypes();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Constructor<T>>[] getTypeParameters();
+    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isSynthetic();
     method public boolean isVarArgs();
     method public T newInstance(java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException;
@@ -48052,7 +48082,10 @@
   }
 
   public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+    method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
+    method public java.lang.annotation.Annotation[] getAnnotations();
+    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<?> getDeclaringClass();
     method public java.lang.Object getDefaultValue();
     method public java.lang.Class<?>[] getExceptionTypes();
@@ -48066,6 +48099,7 @@
     method public java.lang.Class<?> getReturnType();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
     method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
+    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isBridge();
     method public boolean isSynthetic();
     method public boolean isVarArgs();
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 16f825d..d444638 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -603,7 +603,17 @@
         createDependencyGraph();
 
         // Now that all dependencies are set up, start the animations that should be started.
-        start(mRootNode);
+        boolean setIsEmpty = false;
+        if (mStartDelay > 0) {
+            start(mRootNode);
+        } else if (mNodes.size() > 1) {
+            // No delay, but there are other animators in the set
+            onChildAnimatorEnded(mDelayAnim);
+        } else {
+            // Set is empty, no delay, no other animation. Skip to end in this case
+            setIsEmpty = true;
+        }
+
         if (mListeners != null) {
             ArrayList<AnimatorListener> tmpListeners =
                     (ArrayList<AnimatorListener>) mListeners.clone();
@@ -612,18 +622,9 @@
                 tmpListeners.get(i).onAnimationStart(this);
             }
         }
-        if (mNodes.size() == 0 && mStartDelay == 0) {
-            // Handle unusual case where empty AnimatorSet is started - should send out
-            // end event immediately since the event will not be sent out at all otherwise
-            mStarted = false;
-            if (mListeners != null) {
-                ArrayList<AnimatorListener> tmpListeners =
-                        (ArrayList<AnimatorListener>) mListeners.clone();
-                int numListeners = tmpListeners.size();
-                for (int i = 0; i < numListeners; ++i) {
-                    tmpListeners.get(i).onAnimationEnd(this);
-                }
-            }
+        if (setIsEmpty) {
+            // In the case of empty AnimatorSet, we will trigger the onAnimationEnd() right away.
+            onChildAnimatorEnded(mDelayAnim);
         }
     }
 
@@ -751,44 +752,7 @@
         public void onAnimationEnd(Animator animation) {
             animation.removeListener(this);
             mAnimatorSet.mPlayingSet.remove(animation);
-            Node animNode = mAnimatorSet.mNodeMap.get(animation);
-            animNode.mEnded = true;
-
-            if (!mAnimatorSet.mTerminated) {
-                List<Node> children = animNode.mChildNodes;
-                // Start children animations, if any.
-                int childrenSize = children == null ? 0 : children.size();
-                for (int i = 0; i < childrenSize; i++) {
-                    if (children.get(i).mLatestParent == animNode) {
-                        mAnimatorSet.start(children.get(i));
-                    }
-                }
-                // Listeners are already notified of the AnimatorSet ending in cancel() or
-                // end(); the logic below only kicks in when animations end normally
-                boolean allDone = true;
-                // Traverse the tree and find if there's any unfinished node
-                int size = mAnimatorSet.mNodes.size();
-                for (int i = 0; i < size; i++) {
-                    if (!mAnimatorSet.mNodes.get(i).mEnded) {
-                        allDone = false;
-                        break;
-                    }
-                }
-                if (allDone) {
-                    // If this was the last child animation to end, then notify listeners that this
-                    // AnimatorSet has ended
-                    if (mAnimatorSet.mListeners != null) {
-                        ArrayList<AnimatorListener> tmpListeners =
-                                (ArrayList<AnimatorListener>) mAnimatorSet.mListeners.clone();
-                        int numListeners = tmpListeners.size();
-                        for (int i = 0; i < numListeners; ++i) {
-                            tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
-                        }
-                    }
-                    mAnimatorSet.mStarted = false;
-                    mAnimatorSet.mPaused = false;
-                }
-            }
+            mAnimatorSet.onChildAnimatorEnded(animation);
         }
 
         // Nothing to do
@@ -801,6 +765,47 @@
 
     }
 
+    private void onChildAnimatorEnded(Animator animation) {
+        Node animNode = mNodeMap.get(animation);
+        animNode.mEnded = true;
+
+        if (!mTerminated) {
+            List<Node> children = animNode.mChildNodes;
+            // Start children animations, if any.
+            int childrenSize = children == null ? 0 : children.size();
+            for (int i = 0; i < childrenSize; i++) {
+                if (children.get(i).mLatestParent == animNode) {
+                    start(children.get(i));
+                }
+            }
+            // Listeners are already notified of the AnimatorSet ending in cancel() or
+            // end(); the logic below only kicks in when animations end normally
+            boolean allDone = true;
+            // Traverse the tree and find if there's any unfinished node
+            int size = mNodes.size();
+            for (int i = 0; i < size; i++) {
+                if (!mNodes.get(i).mEnded) {
+                    allDone = false;
+                    break;
+                }
+            }
+            if (allDone) {
+                // If this was the last child animation to end, then notify listeners that this
+                // AnimatorSet has ended
+                if (mListeners != null) {
+                    ArrayList<AnimatorListener> tmpListeners =
+                            (ArrayList<AnimatorListener>) mListeners.clone();
+                    int numListeners = tmpListeners.size();
+                    for (int i = 0; i < numListeners; ++i) {
+                        tmpListeners.get(i).onAnimationEnd(this);
+                    }
+                }
+                mStarted = false;
+                mPaused = false;
+            }
+        }
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index da9a8c8..1995ef5 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -669,6 +669,10 @@
      * The frame delay may be ignored when the animation system uses an external timing
      * source, such as the display refresh rate (vsync), to govern animations.
      *
+     * Note that this method should be called from the same thread that {@link #start()} is
+     * called in order to check the frame delay for that animation. A runtime exception will be
+     * thrown if the calling thread does not have a Looper.
+     *
      * @return the requested time between frames, in milliseconds
      */
     public static long getFrameDelay() {
@@ -685,6 +689,10 @@
      * The frame delay may be ignored when the animation system uses an external timing
      * source, such as the display refresh rate (vsync), to govern animations.
      *
+     * Note that this method should be called from the same thread that {@link #start()} is
+     * called in order to have the new frame delay take effect on that animation. A runtime
+     * exception will be thrown if the calling thread does not have a Looper.
+     *
      * @param frameDelay the requested time between frames, in milliseconds
      */
     public static void setFrameDelay(long frameDelay) {
@@ -924,6 +932,13 @@
         updateScaledDuration(); // in case the scale factor has changed since creation time
         AnimationHandler animationHandler = AnimationHandler.getInstance();
         animationHandler.addAnimationFrameCallback(this, mStartDelay);
+
+        if (mStartDelay == 0) {
+            // If there's no start delay, init the animation and notify start listeners right away
+            // Otherwise, postpone this until the first frame after the start delay.
+            startAnimation();
+            setCurrentFraction(mSeekFraction == -1 ? 0 : mSeekFraction);
+        }
     }
 
     @Override
@@ -1067,6 +1082,7 @@
         mStartListenersCalled = false;
         mPlayingBackwards = false;
         mReversing = false;
+        mLastFrameTime = 0;
         mCurrentIteration = 0;
         if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
             Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
@@ -1176,12 +1192,13 @@
      * @hide
      */
     public final void doAnimationFrame(long frameTime) {
-        mLastFrameTime = frameTime;
         AnimationHandler handler = AnimationHandler.getInstance();
-        if (!mRunning) {
+        if (mLastFrameTime == 0) {
             // First frame
             handler.addOneShotCommitCallback(this);
-            startAnimation();
+            if (mStartDelay > 0) {
+                startAnimation();
+            }
             if (mSeekFraction < 0) {
                 mStartTime = frameTime;
             } else {
@@ -1191,6 +1208,7 @@
             }
             mStartTimeCommitted = false; // allow start time to be compensated for jank
         }
+        mLastFrameTime = frameTime;
         if (mPaused) {
             if (mPauseTime < 0) {
                 mPauseTime = frameTime;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4997dc7..f60250c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3742,6 +3742,12 @@
     }
 
     @Override
+    public void setTheme(int resid) {
+        super.setTheme(resid);
+        mWindow.setTheme(resid);
+    }
+
+    @Override
     protected void onApplyThemeResource(Resources.Theme theme, @StyleRes int resid,
             boolean first) {
         if (mParent == null) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4191dce..61a9a84 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -468,28 +468,51 @@
      */
     public static final int DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
 
-
     /**
      * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
-     * that the resize is from the window manager (instead of the user).
+     * that the resize doesn't need to preserve the window, and can be skipped if bounds
+     * is unchanged. This mode is used by window manager in most cases.
      * @hide
      */
     public static final int RESIZE_MODE_SYSTEM = 0;
 
     /**
      * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
-     * that the resize is initiated by the user (most likely via a drag action on the
-     * window's edge or corner).
+     * that the resize should preserve the window if possible.
      * @hide
      */
-    public static final int RESIZE_MODE_USER   = 1;
+    public static final int RESIZE_MODE_PRESERVE_WINDOW   = (0x1 << 0);
 
     /**
      * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
      * that the resize should be performed even if the bounds appears unchanged.
      * @hide
      */
-    public static final int RESIZE_MODE_FORCED = 2;
+    public static final int RESIZE_MODE_FORCED = (0x1 << 1);
+
+    /**
+     * Input parameter to {@link android.app.IActivityManager#resizeTask} used by window
+     * manager during a screen rotation.
+     * @hide
+     */
+    public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = RESIZE_MODE_PRESERVE_WINDOW;
+
+    /**
+     * Input parameter to {@link android.app.IActivityManager#resizeTask} used when the
+     * resize is due to a drag action.
+     * @hide
+     */
+    public static final int RESIZE_MODE_USER = RESIZE_MODE_PRESERVE_WINDOW;
+
+    /**
+     * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
+     * that the resize should preserve the window if possible, and should not be skipped
+     * even if the bounds is unchanged. Usually used to force a resizing when a drag action
+     * is ending.
+     * @hide
+     */
+    public static final int RESIZE_MODE_USER_FORCED =
+            RESIZE_MODE_PRESERVE_WINDOW | RESIZE_MODE_FORCED;
 
     /** @hide */
     public int getFrontActivityScreenCompatMode() {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 40eb799..373a23f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -55,4 +55,13 @@
      * @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
      */
     public abstract ComponentName getHomeActivityForUser(int userId);
+
+    /**
+     * Called when a user has been deleted. This can happen during normal device usage
+     * or just at startup, when partially removed users are purged. Any state persisted by the
+     * ActivityManager should be purged now.
+     *
+     * @param userId The user being cleaned up.
+     */
+    public abstract void onUserRemoved(int userId);
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0d53dbe..f29dba2 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1344,7 +1344,7 @@
         final VerificationParams verificationParams = new VerificationParams(null, null,
                 null, VerificationParams.NO_UID, null);
         installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
-                installerPackageName, verificationParams, null, UserHandle.myUserId());
+                installerPackageName, verificationParams, null, mContext.getUserId());
     }
 
     @Override
@@ -1354,7 +1354,7 @@
         final VerificationParams verificationParams = new VerificationParams(verificationURI, null,
                 null, VerificationParams.NO_UID, manifestDigest);
         installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
-                installerPackageName, verificationParams, encryptionParams, UserHandle.myUserId());
+                installerPackageName, verificationParams, encryptionParams, mContext.getUserId());
     }
 
     @Override
@@ -1362,14 +1362,14 @@
             IPackageInstallObserver observer, int flags, String installerPackageName,
             VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
         installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
-                installerPackageName, verificationParams, encryptionParams, UserHandle.myUserId());
+                installerPackageName, verificationParams, encryptionParams, mContext.getUserId());
     }
 
     @Override
     public void installPackage(Uri packageURI, PackageInstallObserver observer,
             int flags, String installerPackageName) {
         installPackageAsUser(packageURI, observer, flags, installerPackageName,
-                UserHandle.myUserId());
+                mContext.getUserId());
     }
 
     @Override
@@ -1389,7 +1389,7 @@
         final VerificationParams verificationParams = new VerificationParams(verificationURI, null,
                 null, VerificationParams.NO_UID, manifestDigest);
         installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
-                encryptionParams, UserHandle.myUserId());
+                encryptionParams, mContext.getUserId());
     }
 
     @Override
@@ -1397,7 +1397,7 @@
             PackageInstallObserver observer, int flags, String installerPackageName,
             VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
         installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
-                encryptionParams, UserHandle.myUserId());
+                encryptionParams, mContext.getUserId());
     }
 
     private void installCommon(Uri packageURI,
@@ -1421,7 +1421,7 @@
 
     @Override
     public int installExistingPackage(String packageName) throws NameNotFoundException {
-        return installExistingPackageAsUser(packageName, UserHandle.myUserId());
+        return installExistingPackageAsUser(packageName, mContext.getUserId());
     }
 
     @Override
@@ -1726,7 +1726,7 @@
 
     @Override
     public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
-        deletePackageAsUser(packageName, observer, flags, UserHandle.myUserId());
+        deletePackageAsUser(packageName, observer, flags, mContext.getUserId());
     }
 
     @Override
@@ -1838,7 +1838,7 @@
     public void replacePreferredActivity(IntentFilter filter,
                                          int match, ComponentName[] set, ComponentName activity) {
         try {
-            mPM.replacePreferredActivity(filter, match, set, activity, UserHandle.myUserId());
+            mPM.replacePreferredActivity(filter, match, set, activity, mContext.getUserId());
         } catch (RemoteException e) {
             // Should never happen!
         }
@@ -2169,7 +2169,7 @@
     }
 
     private UserInfo getUserIfProfile(int userHandle) {
-        List<UserInfo> userProfiles = getUserManager().getProfiles(UserHandle.myUserId());
+        List<UserInfo> userProfiles = getUserManager().getProfiles(mContext.getUserId());
         for (UserInfo user : userProfiles) {
             if (user.id == userHandle) {
                 return user;
diff --git a/core/java/android/app/AutomaticZenRule.aidl b/core/java/android/app/AutomaticZenRule.aidl
new file mode 100644
index 0000000..feb21d6
--- /dev/null
+++ b/core/java/android/app/AutomaticZenRule.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+parcelable AutomaticZenRule;
\ No newline at end of file
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
new file mode 100644
index 0000000..fea5624
--- /dev/null
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -0,0 +1,190 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Rule instance information for zen mode.
+ */
+public class AutomaticZenRule implements Parcelable {
+
+    private boolean enabled = false;
+    private String name;
+    private int interruptionFilter;
+    private Uri conditionId;
+    private ComponentName owner;
+
+    /**
+     * Creates an automatic zen rule.
+     *
+     * @param name The name of the rule.
+     * @param owner The Condition Provider service that owns this rule.
+     * @param conditionId A representation of the state that should cause the Condition Provider
+     *                    service to apply the interruption filter.
+     * @param interruptionFilter The interruption filter defines which notifications are allowed to
+     *                           interrupt the user (e.g. via sound &amp; vibration) while this rule
+     *                           is active.
+     * @param enabled Whether the rule is enabled.
+     */
+    public AutomaticZenRule(String name, ComponentName owner, Uri conditionId,
+            int interruptionFilter, boolean enabled) {
+        this.name = name;
+        this.owner = owner;
+        this.conditionId = conditionId;
+        this.interruptionFilter = interruptionFilter;
+        this.enabled = enabled;
+    }
+
+    public AutomaticZenRule(Parcel source) {
+        enabled = source.readInt() == 1;
+        if (source.readInt() == 1) {
+            name = source.readString();
+        }
+        interruptionFilter = source.readInt();
+        conditionId = source.readParcelable(null);
+        owner = source.readParcelable(null);
+    }
+
+    /**
+     * Returns the {@link ComponentName} of the condition provider service that owns this rule.
+     */
+    public ComponentName getOwner() {
+        return owner;
+    }
+
+    /**
+     * Returns the representation of the state that causes this rule to become active.
+     */
+    public Uri getConditionId() {
+        return conditionId;
+    }
+
+    /**
+     * Returns the interruption filter that is applied when this rule is active.
+     */
+    public int getInterruptionFilter() {
+        return interruptionFilter;
+    }
+
+    /**
+     * Returns the name of this rule.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns whether this rule is enabled.
+     */
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    /**
+     * Sets the representation of the state that causes this rule to become active.
+     */
+    public void setConditionId(Uri conditionId) {
+        this.conditionId = conditionId;
+    }
+
+    /**
+     * Sets the interruption filter that is applied when this rule is active.
+     * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants in NotificationManager.
+     */
+    public void setInterruptionFilter(int interruptionFilter) {
+        this.interruptionFilter = interruptionFilter;
+    }
+
+    /**
+     * Sets the name of this rule.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Enables this rule.
+     */
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(enabled ? 1 : 0);
+        if (name != null) {
+            dest.writeInt(1);
+            dest.writeString(name);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(interruptionFilter);
+        dest.writeParcelable(conditionId, 0);
+        dest.writeParcelable(owner, 0);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(AutomaticZenRule.class.getSimpleName()).append('[')
+                .append("enabled=").append(enabled)
+                .append(",name=").append(name)
+                .append(",interruptionFilter=").append(interruptionFilter)
+                .append(",conditionId=").append(conditionId)
+                .append(",owner=").append(owner)
+                .append(']').toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof AutomaticZenRule)) return false;
+        if (o == this) return true;
+        final AutomaticZenRule other = (AutomaticZenRule) o;
+        return other.enabled == enabled
+                && Objects.equals(other.name, name)
+                && other.interruptionFilter == interruptionFilter
+                && Objects.equals(other.conditionId, conditionId)
+                && Objects.equals(other.owner, owner);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(enabled, name, interruptionFilter, conditionId, owner);
+    }
+
+    public static final Parcelable.Creator<AutomaticZenRule> CREATOR
+            = new Parcelable.Creator<AutomaticZenRule>() {
+        @Override
+        public AutomaticZenRule createFromParcel(Parcel source) {
+            return new AutomaticZenRule(source);
+        }
+        @Override
+        public AutomaticZenRule[] newArray(int size) {
+            return new AutomaticZenRule[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index f78fb47..920fbe9 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -30,6 +30,7 @@
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
 import android.service.notification.StatusBarNotification;
+import android.app.AutomaticZenRule;
 import android.service.notification.ZenModeConfig;
 
 /** {@hide} */
@@ -92,6 +93,11 @@
     String[] getPackagesRequestingNotificationPolicyAccess();
     boolean isNotificationPolicyAccessGrantedForPackage(String pkg);
     void setNotificationPolicyAccessGranted(String pkg, boolean granted);
+    AutomaticZenRule getAutomaticZenRule(String name);
+    List<AutomaticZenRule> getAutomaticZenRules();
+    boolean addOrUpdateAutomaticZenRule(in AutomaticZenRule automaticZenRule);
+    boolean renameAutomaticZenRule(String oldName, String newName);
+    boolean removeAutomaticZenRule(String name);
 
     byte[] getBackupPayload(int user);
     void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 605c006..cbf198b 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -420,6 +420,106 @@
     }
 
     /**
+     * Returns AutomaticZenRules owned by the caller.
+     *
+     * <p>
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
+     */
+    public List<AutomaticZenRule> getAutomaticZenRules() {
+        INotificationManager service = getService();
+        try {
+            return service.getAutomaticZenRules();
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
+
+    /**
+     * Returns the AutomaticZenRule with the given name, if it exists and the caller has access.
+     *
+     * <p>
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
+     *
+     * <p>
+     * Returns null if there are no zen rules that match the given name, or if the calling package
+     * doesn't own the matching rule. See {@link AutomaticZenRule#getOwner}.
+     */
+    public AutomaticZenRule getAutomaticZenRule(String name) {
+        INotificationManager service = getService();
+        try {
+            return service.getAutomaticZenRule(name);
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
+
+    /**
+     * Creates or updates the given zen rule.
+     *
+     * <p>
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
+     *
+     * <p>
+     * Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
+     * @param automaticZenRule the rule to create or update.
+     * @return Whether the rule was successfully created or updated.
+     */
+    public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule) {
+        INotificationManager service = getService();
+        try {
+            return service.addOrUpdateAutomaticZenRule(automaticZenRule);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
+     * Renames a zen rule.
+     *
+     * <p>
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
+     *
+     * <p>
+     * Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
+     * @param oldName The name of the rule to update.
+     * @param newName The new name for the rule.
+     * @return Whether the rule was successfully updated.
+     */
+    public boolean renameAutomaticZenRule(String oldName, String newName) {
+        INotificationManager service = getService();
+        try {
+            return service.renameAutomaticZenRule(oldName, newName);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
+     * Deletes the automatic zen rule with the given name.
+     *
+     * <p>
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
+     *
+     * <p>
+     * Callers can only delete rules that they own. See {@link AutomaticZenRule#getOwner}.
+     * @param name the name of the rule to delete.
+     * @return Whether the rule was successfully deleted.
+     */
+    public boolean removeAutomaticZenRule(String name) {
+        INotificationManager service = getService();
+        try {
+            return service.removeAutomaticZenRule(name);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
      * Checks the ability to read/modify notification policy for the calling package.
      *
      * <p>
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e6484e9..a118f16 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2438,6 +2438,17 @@
     }
 
     /**
+     * Determine whether or not creating a guest user has been disabled for the device
+     *
+     * @hide
+     */
+    public boolean getGuestUserDisabled(@Nullable ComponentName admin) {
+        // Currently guest users can always be created if multi-user is enabled
+        // TODO introduce a policy for guest user creation
+        return false;
+    }
+
+    /**
      * Called by a device/profile owner to set whether the screen capture is disabled. Disabling
      * screen capture also prevents the content from being shown on display devices that do not have
      * a secure video output. See {@link android.view.Display#FLAG_SECURE} for more details about
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 9341be1..3283005 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -868,6 +868,9 @@
         public static final int MODE_INHERIT_EXISTING = 2;
 
         /** {@hide} */
+        public static final int UID_UNKNOWN = -1;
+
+        /** {@hide} */
         public int mode = MODE_INVALID;
         /** {@hide} */
         public int installFlags;
@@ -886,6 +889,8 @@
         /** {@hide} */
         public Uri originatingUri;
         /** {@hide} */
+        public int originatingUid = UID_UNKNOWN;
+        /** {@hide} */
         public Uri referrerUri;
         /** {@hide} */
         public String abiOverride;
@@ -915,6 +920,7 @@
             appIcon = source.readParcelable(null);
             appLabel = source.readString();
             originatingUri = source.readParcelable(null);
+            originatingUid = source.readInt();
             referrerUri = source.readParcelable(null);
             abiOverride = source.readString();
             volumeUuid = source.readString();
@@ -983,6 +989,15 @@
         }
 
         /**
+         * Sets the UID that initiated package installation. Used for verification purposes.
+         *
+         * @see PackageManager#EXTRA_VERIFICATION_INSTALLER_UID
+         */
+        public void setOriginatingUid(int originatingUid) {
+            this.originatingUid = originatingUid;
+        }
+
+        /**
          * Optionally set the URI that referred you to install this package. Used
          * for verification purposes.
          *
@@ -1022,6 +1037,11 @@
         }
 
         /** {@hide} */
+        public void setInstallFlagsForcePermissionPrompt() {
+            installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
+        }
+
+        /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
             pw.printHexPair("installFlags", installFlags);
@@ -1031,6 +1051,7 @@
             pw.printPair("appIcon", (appIcon != null));
             pw.printPair("appLabel", appLabel);
             pw.printPair("originatingUri", originatingUri);
+            pw.printPair("originatingUid", originatingUid);
             pw.printPair("referrerUri", referrerUri);
             pw.printPair("abiOverride", abiOverride);
             pw.printPair("volumeUuid", volumeUuid);
@@ -1053,6 +1074,7 @@
             dest.writeParcelable(appIcon, flags);
             dest.writeString(appLabel);
             dest.writeParcelable(originatingUri, flags);
+            dest.writeInt(originatingUid);
             dest.writeParcelable(referrerUri, flags);
             dest.writeString(abiOverride);
             dest.writeString(volumeUuid);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 697b946..82cbbbe 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -422,6 +422,15 @@
     public static final int INSTALL_FORCE_VOLUME_UUID = 0x00000200;
 
     /**
+     * Flag parameter for {@link #installPackage} to indicate that we always want to force
+     * the prompt for permission approval. This overrides any special behaviour for internal
+     * components.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FORCE_PERMISSION_PROMPT = 0x00000400;
+
+    /**
      * Flag parameter for
      * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
      * that you don't want to kill the app containing the component.  Be careful when you set this
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index e29bd2c..0606e35 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -16,24 +16,14 @@
 
 package android.content.res;
 
-import android.annotation.AttrRes;
-import android.annotation.ColorInt;
-import android.annotation.StyleRes;
-import android.annotation.StyleableRes;
-import android.graphics.drawable.DrawableInflater;
-import android.icu.text.PluralRules;
-import com.android.internal.util.GrowingArrayUtils;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.animation.Animator;
 import android.animation.StateListAnimator;
 import android.annotation.AnimRes;
 import android.annotation.AnyRes;
 import android.annotation.ArrayRes;
+import android.annotation.AttrRes;
 import android.annotation.BoolRes;
+import android.annotation.ColorInt;
 import android.annotation.ColorRes;
 import android.annotation.DimenRes;
 import android.annotation.DrawableRes;
@@ -45,18 +35,23 @@
 import android.annotation.PluralsRes;
 import android.annotation.RawRes;
 import android.annotation.StringRes;
+import android.annotation.StyleRes;
+import android.annotation.StyleableRes;
 import android.annotation.XmlRes;
 import android.content.pm.ActivityInfo;
 import android.graphics.Movie;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Drawable.ConstantState;
+import android.graphics.drawable.DrawableInflater;
+import android.icu.text.PluralRules;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.LocaleList;
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.Pools.SynchronizedPool;
@@ -65,6 +60,12 @@
 import android.view.ViewDebug;
 import android.view.ViewHierarchyEncoder;
 
+import com.android.internal.util.GrowingArrayUtils;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
@@ -372,7 +373,7 @@
     private PluralRules getPluralRule() {
         synchronized (sSync) {
             if (mPluralRule == null) {
-                mPluralRule = PluralRules.forLocale(mConfiguration.locale);
+                mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary());
             }
             return mPluralRule;
         }
@@ -435,7 +436,7 @@
     @NonNull
     public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException {
         final String raw = getString(id);
-        return String.format(mConfiguration.locale, raw, formatArgs);
+        return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs);
     }
 
     /**
@@ -466,7 +467,7 @@
     public String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs)
             throws NotFoundException {
         String raw = getQuantityText(id, quantity).toString();
-        return String.format(mConfiguration.locale, raw, formatArgs);
+        return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs);
     }
 
     /**
@@ -1971,9 +1972,10 @@
             mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
 
             final int configChanges = calcConfigChanges(config);
-            if (mConfiguration.locale == null) {
-                mConfiguration.locale = Locale.getDefault();
-                mConfiguration.setLayoutDirection(mConfiguration.locale);
+            LocaleList locales = mConfiguration.getLocales();
+            if (locales.isEmpty()) {
+                locales = LocaleList.getDefault();
+                mConfiguration.setLocales(locales);
             }
             if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
                 mMetrics.densityDpi = mConfiguration.densityDpi;
@@ -1981,11 +1983,6 @@
             }
             mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
 
-            String locale = null;
-            if (mConfiguration.locale != null) {
-                locale = adjustLanguageTag(mConfiguration.locale.toLanguageTag());
-            }
-
             final int width, height;
             if (mMetrics.widthPixels >= mMetrics.heightPixels) {
                 width = mMetrics.widthPixels;
@@ -2005,8 +2002,10 @@
                 keyboardHidden = mConfiguration.keyboardHidden;
             }
 
+            // TODO: Pass the whole locale list to setConfiguration()
             mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
-                    locale, mConfiguration.orientation,
+                    adjustLanguageTag(locales.getPrimary().toLanguageTag()),
+                    mConfiguration.orientation,
                     mConfiguration.touchscreen,
                     mConfiguration.densityDpi, mConfiguration.keyboard,
                     keyboardHidden, mConfiguration.navigation, width, height,
@@ -2030,7 +2029,7 @@
         }
         synchronized (sSync) {
             if (mPluralRule != null) {
-                mPluralRule = PluralRules.forLocale(config.locale);
+                mPluralRule = PluralRules.forLocale(config.getLocales().getPrimary());
             }
         }
     }
@@ -2049,9 +2048,8 @@
 
             mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
 
-            if (mTmpConfig.locale == null) {
-                mTmpConfig.locale = Locale.getDefault();
-                mTmpConfig.setLayoutDirection(mTmpConfig.locale);
+            if (mTmpConfig.getLocales().isEmpty()) {
+                mTmpConfig.setLocales(LocaleList.getDefault());
             }
             configChanges = mConfiguration.updateFrom(mTmpConfig);
             configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index e965d65..3f566eb 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1545,7 +1545,7 @@
     /**
      * <p>Whether video stabilization is
      * active.</p>
-     * <p>Video stabilization automatically translates and scales images from
+     * <p>Video stabilization automatically warps images from
      * the camera in order to stabilize motion between consecutive frames.</p>
      * <p>If enabled, video stabilization can modify the
      * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream stabilized.</p>
@@ -1555,6 +1555,15 @@
      * the video stabilization modes in the first several capture results may
      * still be "OFF", and it will become "ON" when the initialization is
      * done.</p>
+     * <p>In addition, not all recording sizes or frame rates may be supported for
+     * stabilization by a device that reports stabilization support. It is guaranteed
+     * that an output targeting a MediaRecorder or MediaCodec will be stabilized if
+     * the recording resolution is less than or equal to 1920 x 1080 (width less than
+     * or equal to 1920, height less than or equal to 1080), and the recording
+     * frame rate is less than or equal to 30fps.  At other sizes, the CaptureResult
+     * {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
+     * OFF if the recording output is not stabilized, or if there are no output
+     * Surface types that can be stabilized.</p>
      * <p>If a camera device supports both this mode and OIS
      * ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
      * produce undesirable interaction, so it is recommended not to enable
@@ -1566,6 +1575,7 @@
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
+     * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
      * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 46eddb3..b3acf2b 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2056,7 +2056,7 @@
     /**
      * <p>Whether video stabilization is
      * active.</p>
-     * <p>Video stabilization automatically translates and scales images from
+     * <p>Video stabilization automatically warps images from
      * the camera in order to stabilize motion between consecutive frames.</p>
      * <p>If enabled, video stabilization can modify the
      * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream stabilized.</p>
@@ -2066,6 +2066,15 @@
      * the video stabilization modes in the first several capture results may
      * still be "OFF", and it will become "ON" when the initialization is
      * done.</p>
+     * <p>In addition, not all recording sizes or frame rates may be supported for
+     * stabilization by a device that reports stabilization support. It is guaranteed
+     * that an output targeting a MediaRecorder or MediaCodec will be stabilized if
+     * the recording resolution is less than or equal to 1920 x 1080 (width less than
+     * or equal to 1920, height less than or equal to 1080), and the recording
+     * frame rate is less than or equal to 30fps.  At other sizes, the CaptureResult
+     * {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
+     * OFF if the recording output is not stabilized, or if there are no output
+     * Surface types that can be stabilized.</p>
      * <p>If a camera device supports both this mode and OIS
      * ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
      * produce undesirable interaction, so it is recommended not to enable
@@ -2077,6 +2086,7 @@
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
+     * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
      * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index c373308..cd483b1 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.MathUtils;
 
 import java.io.Serializable;
 import java.util.ArrayList;
@@ -1345,18 +1346,19 @@
      */
     void readFromParcelInner(Parcel parcel) {
         int length = parcel.readInt();
-        if (length < 0) {
-            throw new RuntimeException("Bad length in parcel: " + length);
-        }
         readFromParcelInner(parcel, length);
     }
 
     private void readFromParcelInner(Parcel parcel, int length) {
-        if (length == 0) {
+        if (length < 0) {
+            throw new RuntimeException("Bad length in parcel: " + length);
+
+        } else if (length == 0) {
             // Empty Bundle or end of data.
             mParcelledData = EMPTY_PARCEL;
             return;
         }
+
         int magic = parcel.readInt();
         if (magic != BUNDLE_MAGIC) {
             //noinspection ThrowableInstanceNeverThrown
@@ -1366,7 +1368,7 @@
 
         // Advance within this Parcel
         int offset = parcel.dataPosition();
-        parcel.setDataPosition(offset + length);
+        parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
 
         Parcel p = Parcel.obtain();
         p.setDataPosition(0);
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index c4f62ba..9fdbec3 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -23,8 +23,11 @@
 
 interface IPowerManager
 {
-    // WARNING: The first five methods must remain the first five methods because their
-    // transaction numbers must not change unless IPowerManager.cpp is also updated.
+    // WARNING: When methods are inserted or deleted, the transaction IDs in
+    // frameworks/native/include/powermanager/IPowerManager.h must be updated to match the order in this file.
+    //
+    // When a method's argument list is changed, BnPowerManager's corresponding serialization code (if any) in
+    // frameworks/native/services/powermanager/IPowerManager.cpp must be updated.
     void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws,
             String historyTag);
     void acquireWakeLockWithUid(IBinder lock, int flags, String tag, String packageName,
diff --git a/core/java/android/os/ParcelableParcel.java b/core/java/android/os/ParcelableParcel.java
index 11785f1..5bbe6488 100644
--- a/core/java/android/os/ParcelableParcel.java
+++ b/core/java/android/os/ParcelableParcel.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import android.util.MathUtils;
+
 /**
  * Parcelable containing a raw Parcel of data.
  * @hide
@@ -33,9 +35,13 @@
         mParcel = Parcel.obtain();
         mClassLoader = loader;
         int size = src.readInt();
+        if (size < 0) {
+            throw new IllegalArgumentException("Negative size read from parcel");
+        }
+
         int pos = src.dataPosition();
-        mParcel.appendFrom(src, src.dataPosition(), size);
-        src.setDataPosition(pos + size);
+        src.setDataPosition(MathUtils.addOrThrow(pos, size));
+        mParcel.appendFrom(src, pos, size);
     }
 
     public Parcel getParcel() {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 37467ca..d178d20 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
@@ -1337,12 +1338,15 @@
     }
 
     /**
-     * Returns true if the user switcher should be shown, this will be if there
-     * are multiple users that aren't managed profiles.
+     * Returns true if the user switcher should be shown, this will be if device supports multi-user
+     * and there are at least 2 users available that are not managed profiles.
      * @hide
      * @return true if user switcher should be shown.
      */
     public boolean isUserSwitcherEnabled() {
+        if (!supportsMultipleUsers()) {
+            return false;
+        }
         List<UserInfo> users = getUsers(true);
         if (users == null) {
            return false;
@@ -1353,8 +1357,8 @@
                 ++switchableUserCount;
             }
         }
-        final boolean guestEnabled = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.GUEST_USER_ENABLED, 0) == 1;
+        final boolean guestEnabled = !mContext.getSystemService(DevicePolicyManager.class)
+                .getGuestUserDisabled(null);
         return switchableUserCount > 1 || guestEnabled;
     }
 
diff --git a/core/java/android/preference/SeekBarPreference.java b/core/java/android/preference/SeekBarPreference.java
index 67f6409..5414f00 100644
--- a/core/java/android/preference/SeekBarPreference.java
+++ b/core/java/android/preference/SeekBarPreference.java
@@ -96,18 +96,15 @@
 
     @Override
     public boolean onKey(View v, int keyCode, KeyEvent event) {
-        if (event.getAction() != KeyEvent.ACTION_UP) {
-            if (keyCode == KeyEvent.KEYCODE_PLUS
-                    || keyCode == KeyEvent.KEYCODE_EQUALS) {
-                setProgress(getProgress() + 1);
-                return true;
-            }
-            if (keyCode == KeyEvent.KEYCODE_MINUS) {
-                setProgress(getProgress() - 1);
-                return true;
-            }
+        if (event.getAction() != KeyEvent.ACTION_DOWN) {
+            return false;
         }
-        return false;
+
+        SeekBar seekBar = (SeekBar) v.findViewById(com.android.internal.R.id.seekbar);
+        if (seekBar == null) {
+            return false;
+        }
+        return seekBar.onKeyDown(keyCode, event);
     }
 
     public void setMax(int max) {
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 4f880b1..342f8c7 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -153,6 +153,18 @@
         /**
          * The type of the call (incoming, outgoing or missed).
          * <P>Type: INTEGER (int)</P>
+         *
+         * <p>
+         * Allowed values:
+         * <ul>
+         * <li>{@link #INCOMING_TYPE}</li>
+         * <li>{@link #OUTGOING_TYPE}</li>
+         * <li>{@link #MISSED_TYPE}</li>
+         * <li>{@link #VOICEMAIL_TYPE}</li>
+         * <li>{@link #REJECTED_TYPE}</li>
+         * <li>{@link #BLOCKED_TYPE}</li>
+         * </ul>
+         * </p>
          */
         public static final String TYPE = "type";
 
@@ -164,6 +176,10 @@
         public static final int MISSED_TYPE = 3;
         /** Call log type for voicemails. */
         public static final int VOICEMAIL_TYPE = 4;
+        /** Call log type for calls rejected by direct user action. */
+        public static final int REJECTED_TYPE = 5;
+        /** Call log type for calls blocked automatically. */
+        public static final int BLOCKED_TYPE = 6;
 
         /**
          * Bit-mask describing features of the call (e.g. video).
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 1a83cd5..241e6db 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -19,6 +19,7 @@
 import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.system.OsConstants.SEEK_SET;
 
+import android.annotation.Nullable;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -136,6 +137,9 @@
      */
     private static final int THUMBNAIL_BUFFER_SIZE = (int) (128 * KB_IN_BYTES);
 
+    /** {@hide} */
+    public static final String PACKAGE_DOCUMENTS_UI = "com.android.documentsui";
+
     /**
      * Constants related to a document, including {@link Cursor} column names
      * and flags.
@@ -761,19 +765,33 @@
      * @see #buildDocumentUri(String, String)
      * @see #buildDocumentUriUsingTree(Uri, String)
      */
-    public static boolean isDocumentUri(Context context, Uri uri) {
-        final List<String> paths = uri.getPathSegments();
-        if (paths.size() == 2 && PATH_DOCUMENT.equals(paths.get(0))) {
-            return isDocumentsProvider(context, uri.getAuthority());
-        }
-        if (paths.size() == 4 && PATH_TREE.equals(paths.get(0))
-                && PATH_DOCUMENT.equals(paths.get(2))) {
-            return isDocumentsProvider(context, uri.getAuthority());
+    public static boolean isDocumentUri(Context context, @Nullable Uri uri) {
+        if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) {
+            final List<String> paths = uri.getPathSegments();
+            if (paths.size() == 2) {
+                return PATH_DOCUMENT.equals(paths.get(0));
+            } else if (paths.size() == 4) {
+                return PATH_TREE.equals(paths.get(0)) && PATH_DOCUMENT.equals(paths.get(2));
+            }
         }
         return false;
     }
 
     /** {@hide} */
+    public static boolean isRootUri(Context context, @Nullable Uri uri) {
+        if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) {
+            final List<String> paths = uri.getPathSegments();
+            return (paths.size() == 2 && PATH_ROOT.equals(paths.get(0)));
+        }
+        return false;
+    }
+
+    /** {@hide} */
+    public static boolean isContentUri(@Nullable Uri uri) {
+        return uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme());
+    }
+
+    /** {@hide} */
     public static boolean isTreeUri(Uri uri) {
         final List<String> paths = uri.getPathSegments();
         return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0)));
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 225f0cf..d601831 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7689,14 +7689,6 @@
         public static final String DEVICE_NAME = "device_name";
 
         /**
-         * Whether it should be possible to create a guest user on the device.
-         * <p>
-         * Type: int (0 for disabled, 1 for enabled)
-         * @hide
-         */
-        public static final String GUEST_USER_ENABLED = "guest_user_enabled";
-
-        /**
          * Whether the NetworkScoringService has been first initialized.
          * <p>
          * Type: int (0 for false, 1 for true)
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 6310570..4de903e 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -755,6 +755,10 @@
         return rt;
     }
 
+    public static ComponentName getScheduleConditionProvider() {
+        return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider");
+    }
+
     public static class ScheduleInfo {
         public int[] days;
         public int startHour;
@@ -827,6 +831,10 @@
         return rt;
     }
 
+    public static ComponentName getEventConditionProvider() {
+        return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider");
+    }
+
     public static class EventInfo {
         public static final int REPLY_ANY_EXCEPT_NO = 0;
         public static final int REPLY_YES_OR_MAYBE = 1;
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 1665c75..f2b6041 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -16,15 +16,17 @@
 
 package android.text;
 
-import com.android.internal.annotations.GuardedBy;
-
 import android.annotation.Nullable;
 import android.util.Log;
 
-import libcore.io.IoUtils;
+import com.android.internal.annotations.GuardedBy;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
 import java.util.HashMap;
 import java.util.Locale;
 
@@ -45,12 +47,18 @@
     @GuardedBy("sLock")
     final static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>();
 
-    final static Hyphenator sEmptyHyphenator = new Hyphenator(StaticLayout.nLoadHyphenator(""));
+    final static Hyphenator sEmptyHyphenator =
+            new Hyphenator(StaticLayout.nLoadHyphenator(null, 0), null);
 
     final private long mNativePtr;
 
-    private Hyphenator(long nativePtr) {
+    // We retain a reference to the buffer to keep the memory mapping valid
+    @SuppressWarnings("unused")
+    final private ByteBuffer mBuffer;
+
+    private Hyphenator(long nativePtr, ByteBuffer b) {
         mNativePtr = nativePtr;
+        mBuffer = b;
     }
 
     public long getNativePtr() {
@@ -94,12 +102,18 @@
     }
 
     private static Hyphenator loadHyphenator(String languageTag) {
-        String patternFilename = "hyph-"+languageTag.toLowerCase(Locale.US)+".pat.txt";
+        String patternFilename = "hyph-" + languageTag.toLowerCase(Locale.US) + ".hyb";
         File patternFile = new File(getSystemHyphenatorLocation(), patternFilename);
         try {
-            String patternData = IoUtils.readFileAsString(patternFile.getAbsolutePath());
-            long nativePtr = StaticLayout.nLoadHyphenator(patternData);
-            return new Hyphenator(nativePtr);
+            RandomAccessFile f = new RandomAccessFile(patternFile, "r");
+            try {
+                FileChannel fc = f.getChannel();
+                MappedByteBuffer buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+                long nativePtr = StaticLayout.nLoadHyphenator(buf, 0);
+                return new Hyphenator(nativePtr, buf);
+            } finally {
+                f.close();
+            }
         } catch (IOException e) {
             Log.e(TAG, "error loading hyphenation " + patternFile, e);
             return null;
@@ -152,7 +166,7 @@
         sMap.put(null, null);
 
         // TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data
-        String[] availableLanguages = {"en-US", "eu", "hu", "hy", "nb", "nn", "sa", "und-Ethi"};
+        String[] availableLanguages = {"en-US", "eu", "hu", "hy", "nb", "nn", "und-Ethi"};
         for (int i = 0; i < availableLanguages.length; i++) {
             String languageTag = availableLanguages[i];
             Hyphenator h = loadHyphenator(languageTag);
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 79c4a55..6ece091 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -29,6 +29,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
 
+import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.Locale;
 
@@ -1244,7 +1245,7 @@
     private static native void nFreeBuilder(long nativePtr);
     private static native void nFinishBuilder(long nativePtr);
 
-    /* package */ static native long nLoadHyphenator(String patternData);
+    /* package */ static native long nLoadHyphenator(ByteBuffer buf, int offset);
 
     private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
 
diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
index 8b57d3d..acca3ed 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -185,4 +185,24 @@
     public static void randomSeed(long seed) {
         sRandom.setSeed(seed);
     }
+
+    /**
+     * Returns the sum of the two parameters, or throws an exception if the resulting sum would
+     * cause an overflow or underflow.
+     * @throws IllegalArgumentException when overflow or underflow would occur.
+     */
+    public static int addOrThrow(int a, int b) throws IllegalArgumentException {
+        if (b == 0) {
+            return a;
+        }
+
+        if (b > 0 && a <= (Integer.MAX_VALUE - b)) {
+            return a + b;
+        }
+
+        if (b < 0 && a >= (Integer.MIN_VALUE - b)) {
+            return a + b;
+        }
+        throw new IllegalArgumentException("Addition overflow: " + a + " + " + b);
+    }
 }
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 37312d0..e200bef 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -72,6 +72,9 @@
     private static final String TAG = LayoutInflater.class.getSimpleName();
     private static final boolean DEBUG = false;
 
+    /** Empty stack trace used to avoid log spam in re-throw exceptions. */
+    private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
+
     /**
      * This field should be made private, so it is hidden from the SDK.
      * {@hide}
@@ -532,15 +535,14 @@
                 }
 
             } catch (XmlPullParserException e) {
-                InflateException ex = new InflateException(e.getMessage());
-                ex.initCause(e);
-                throw ex;
+                final InflateException ie = new InflateException(e.getMessage(), e);
+                ie.setStackTrace(EMPTY_STACK_TRACE);
+                throw ie;
             } catch (Exception e) {
-                InflateException ex = new InflateException(
-                        parser.getPositionDescription()
-                                + ": " + e.getMessage());
-                ex.initCause(e);
-                throw ex;
+                final InflateException ie = new InflateException(parser.getPositionDescription()
+                        + ": " + e.getMessage(), e);
+                ie.setStackTrace(EMPTY_STACK_TRACE);
+                throw ie;
             } finally {
                 // Don't retain static reference on context.
                 mConstructorArgs[0] = lastContext;
@@ -625,27 +627,25 @@
             return view;
 
         } catch (NoSuchMethodException e) {
-            InflateException ie = new InflateException(attrs.getPositionDescription()
-                    + ": Error inflating class "
-                    + (prefix != null ? (prefix + name) : name));
-            ie.initCause(e);
+            final InflateException ie = new InflateException(attrs.getPositionDescription()
+                    + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
+            ie.setStackTrace(EMPTY_STACK_TRACE);
             throw ie;
 
         } catch (ClassCastException e) {
             // If loaded class is not a View subclass
-            InflateException ie = new InflateException(attrs.getPositionDescription()
-                    + ": Class is not a View "
-                    + (prefix != null ? (prefix + name) : name));
-            ie.initCause(e);
+            final InflateException ie = new InflateException(attrs.getPositionDescription()
+                    + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);
+            ie.setStackTrace(EMPTY_STACK_TRACE);
             throw ie;
         } catch (ClassNotFoundException e) {
             // If loadClass fails, we should propagate the exception.
             throw e;
         } catch (Exception e) {
-            InflateException ie = new InflateException(attrs.getPositionDescription()
-                    + ": Error inflating class "
-                    + (clazz == null ? "<unknown>" : clazz.getName()));
-            ie.initCause(e);
+            final InflateException ie = new InflateException(
+                    attrs.getPositionDescription() + ": Error inflating class "
+                            + (clazz == null ? "<unknown>" : clazz.getName()), e);
+            ie.setStackTrace(EMPTY_STACK_TRACE);
             throw ie;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
@@ -657,8 +657,7 @@
      */
     private void failNotAllowed(String name, String prefix, AttributeSet attrs) {
         throw new InflateException(attrs.getPositionDescription()
-                + ": Class not allowed to be inflated "
-                + (prefix != null ? (prefix + name) : name));
+                + ": Class not allowed to be inflated "+ (prefix != null ? (prefix + name) : name));
     }
 
     /**
@@ -774,14 +773,14 @@
 
         } catch (ClassNotFoundException e) {
             final InflateException ie = new InflateException(attrs.getPositionDescription()
-                    + ": Error inflating class " + name);
-            ie.initCause(e);
+                    + ": Error inflating class " + name, e);
+            ie.setStackTrace(EMPTY_STACK_TRACE);
             throw ie;
 
         } catch (Exception e) {
             final InflateException ie = new InflateException(attrs.getPositionDescription()
-                    + ": Error inflating class " + name);
-            ie.initCause(e);
+                    + ": Error inflating class " + name, e);
+            ie.setStackTrace(EMPTY_STACK_TRACE);
             throw ie;
         }
     }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index dcef142..304e9c0 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -349,7 +349,7 @@
      * @param right The right side of the protected bounds.
      * @param bottom The bottom side of the protected bounds.
      */
-    public void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
+    public void setContentDrawBounds(int left, int top, int right, int bottom) {
         mStagedContentBounds.set(left, top, right, bottom);
     }
 
@@ -370,9 +370,9 @@
         // renderer.
         if (!mCurrentContentBounds.equals(mStagedContentBounds)) {
             mCurrentContentBounds.set(mStagedContentBounds);
-            nSetContentOverdrawProtectionBounds(mNativeProxy, mCurrentContentBounds.left,
-                mCurrentContentBounds.top, mCurrentContentBounds.right,
-            mCurrentContentBounds.bottom);
+            nSetContentDrawBounds(mNativeProxy, mCurrentContentBounds.left,
+                    mCurrentContentBounds.top, mCurrentContentBounds.right,
+                    mCurrentContentBounds.bottom);
         }
 
         attachInfo.mIgnoreDirtyState = false;
@@ -600,6 +600,6 @@
              boolean placeFront);
     private static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode);
     private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode);
-    private static native void nSetContentOverdrawProtectionBounds(long nativeProxy, int left,
+    private static native void nSetContentDrawBounds(long nativeProxy, int left,
              int top, int right, int bottom);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 263ec7d..2c7a436 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6649,12 +6649,24 @@
     }
 
     /**
-     * Sets a delegate for implementing accessibility support via composition as
-     * opposed to inheritance. The delegate's primary use is for implementing
-     * backwards compatible widgets. For more details see {@link AccessibilityDelegate}.
+     * Sets a delegate for implementing accessibility support via composition
+     * (as opposed to inheritance). For more details, see
+     * {@link AccessibilityDelegate}.
+     * <p>
+     * <strong>Note:</strong> On platform versions prior to
+     * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
+     * views in the {@code android.widget.*} package are called <i>before</i>
+     * host methods. This prevents certain properties such as class name from
+     * being modified by overriding
+     * {@link AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfo)},
+     * as any changes will be overwritten by the host class.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
+     * methods are called <i>after</i> host methods, which all properties to be
+     * modified without being overwritten by the host class.
      *
-     * @param delegate The delegate instance.
-     *
+     * @param delegate the object to which accessibility method calls should be
+     *                 delegated
      * @see AccessibilityDelegate
      */
     public void setAccessibilityDelegate(@Nullable AccessibilityDelegate delegate) {
@@ -22277,6 +22289,18 @@
      * corresponding delegate method without altering the behavior of the rest
      * accessibility related methods of the host view.
      * </p>
+     * <p>
+     * <strong>Note:</strong> On platform versions prior to
+     * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
+     * views in the {@code android.widget.*} package are called <i>before</i>
+     * host methods. This prevents certain properties such as class name from
+     * being modified by overriding
+     * {@link AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfo)},
+     * as any changes will be overwritten by the host class.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
+     * methods are called <i>after</i> host methods, which all properties to be
+     * modified without being overwritten by the host class.
      */
     public static class AccessibilityDelegate {
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f6c60ed..7cf23e7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -73,6 +73,7 @@
 import android.view.animation.Interpolator;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.view.WindowCallbacks;
 import android.widget.Scroller;
 
 import com.android.internal.R;
@@ -115,6 +116,12 @@
     private static final boolean DEBUG_INPUT_STAGES = false || LOCAL_LOGV;
 
     /**
+     * Set to false if we do not want to use the multi threaded renderer. Note that by disabling
+     * this, WindowCallbacks will not fire.
+     */
+    private static final boolean USE_MT_RENDERER = true;
+
+    /**
      * Set this system property to true to force the view hierarchy to render
      * at 60 Hz. This can be used to measure the potential framerate.
      */
@@ -132,11 +139,11 @@
 
     static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
 
-    static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();
+    static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
     static boolean sFirstDrawComplete = false;
+    static final ArrayList<WindowCallbacks> sWindowCallbacks = new ArrayList();
 
-    static final ArrayList<ComponentCallbacks> sConfigCallbacks
-            = new ArrayList<ComponentCallbacks>();
+    static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList();
 
     final Context mContext;
     final IWindowSession mWindowSession;
@@ -417,6 +424,22 @@
         }
     }
 
+    public static void addWindowCallbacks(WindowCallbacks callback) {
+        if (USE_MT_RENDERER) {
+            synchronized (sWindowCallbacks) {
+                sWindowCallbacks.add(callback);
+            }
+        }
+    }
+
+    public static void removeWindowCallbacks(WindowCallbacks callback) {
+        if (USE_MT_RENDERER) {
+            synchronized (sWindowCallbacks) {
+                sWindowCallbacks.remove(callback);
+            }
+        }
+    }
+
     // FIXME for perf testing only
     private boolean mProfile = false;
 
@@ -1378,6 +1401,7 @@
             mAttachInfo.mWindowVisibility = viewVisibility;
             host.dispatchWindowVisibilityChanged(viewVisibility);
             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
+                endDragResizing();
                 destroyHardwareResources();
             }
             if (viewVisibility == View.GONE) {
@@ -1701,14 +1725,20 @@
                 final boolean dragResizing = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING) != 0;
                 if (mDragResizing != dragResizing) {
-                    mDragResizing = dragResizing;
-                    mFullRedrawNeeded = true;
+                    if (dragResizing) {
+                        startDragResizing(frame);
+                    } else {
+                        // We shouldn't come here, but if we come we should end the resize.
+                        endDragResizing();
+                    }
                 }
-                if (dragResizing) {
-                    mCanvasOffsetX = mWinFrame.left;
-                    mCanvasOffsetY = mWinFrame.top;
-                } else {
-                    mCanvasOffsetX = mCanvasOffsetY = 0;
+                if (!USE_MT_RENDERER) {
+                    if (dragResizing) {
+                        mCanvasOffsetX = mWinFrame.left;
+                        mCanvasOffsetY = mWinFrame.top;
+                    } else {
+                        mCanvasOffsetX = mCanvasOffsetY = 0;
+                    }
                 }
             } catch (RemoteException e) {
             }
@@ -6635,6 +6665,15 @@
                 Configuration newConfig) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
+                // Tell all listeners that we are resizing the window so that the chrome can get
+                // updated as fast as possible on a separate thread,
+                if (mViewAncestor.get().mDragResizing) {
+                    synchronized (sWindowCallbacks) {
+                        for (int i = sWindowCallbacks.size() - 1; i >= 0; i--) {
+                            sWindowCallbacks.get(i).onWindowSizeIsChanging(frame);
+                        }
+                    }
+                }
                 viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
                         visibleInsets, stableInsets, outsets, reportDraw, newConfig);
             }
@@ -6804,6 +6843,36 @@
     }
 
     /**
+     * Start a drag resizing which will inform all listeners that a window resize is taking place.
+     */
+    private void startDragResizing(Rect initialBounds) {
+        if (!mDragResizing) {
+            mDragResizing = true;
+            synchronized (sWindowCallbacks) {
+                for (int i = sWindowCallbacks.size() - 1; i >= 0; i--) {
+                    sWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds);
+                }
+            }
+            mFullRedrawNeeded = true;
+        }
+    }
+
+    /**
+     * End a drag resize which will inform all listeners that a window resize has ended.
+     */
+    private void endDragResizing() {
+        if (mDragResizing) {
+            mDragResizing = false;
+            synchronized (sWindowCallbacks) {
+                for (int i = sWindowCallbacks.size() - 1; i >= 0; i--) {
+                    sWindowCallbacks.get(i).onWindowDragResizeEnd();
+                }
+            }
+            mFullRedrawNeeded = true;
+        }
+    }
+
+    /**
      * Class for managing the accessibility interaction connection
      * based on the global accessibility state.
      */
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0e7089f..5f4e7af 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2016,4 +2016,8 @@
     public boolean hasNonClientDecorView() {
         return false;
     }
+
+    /** @hide */
+    public void setTheme(int resId) {
+    }
 }
diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java
new file mode 100644
index 0000000..cb6e983
--- /dev/null
+++ b/core/java/android/view/WindowCallbacks.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Rect;
+
+/**
+ * These callbacks are used to communicate window configuration changes while the user is performing
+ * window changes.
+ * @hide
+ */
+public interface WindowCallbacks {
+    /**
+     * Called by the system when the window got changed by the user, before the layouter got called.
+     * It can be used to perform a "quick and dirty" resize which should never take more then 4ms to
+     * complete.
+     *
+     * <p>At the time the layouting has not happened yet.
+     *
+     * @param newBounds The new window frame bounds.
+     */
+    void onWindowSizeIsChanging(Rect newBounds);
+
+    /**
+     * Called when a drag resize starts.
+     * @param initialBounds The initial bounds where the window will be.
+     */
+    void onWindowDragResizeStart(Rect initialBounds);
+
+    /**
+     * Called when a drag resize ends.
+     */
+    void onWindowDragResizeEnd();
+}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 72971e8..81ec330 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -88,6 +88,9 @@
     /** @hide */
     public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12;
 
+    /** @hide */
+    public static final int AUTOCLICK_DELAY_DEFAULT = 600;
+
     static final Object sInstanceSync = new Object();
 
     private static AccessibilityManager sInstance;
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 68855ff..10aefe4 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -666,7 +666,7 @@
         progress += scale * max;
 
         setHotspot(x, (int) event.getY());
-        setProgress((int) progress, true);
+        setProgressInternal((int) progress, true, false);
     }
 
     /**
@@ -706,9 +706,12 @@
             int increment = mKeyProgressIncrement;
             switch (keyCode) {
                 case KeyEvent.KEYCODE_DPAD_LEFT:
+                case KeyEvent.KEYCODE_MINUS:
                     increment = -increment;
                     // fallthrough
                 case KeyEvent.KEYCODE_DPAD_RIGHT:
+                case KeyEvent.KEYCODE_PLUS:
+                case KeyEvent.KEYCODE_EQUALS:
                     increment = isLayoutRtl() ? -increment : increment;
 
                     if (setProgressInternal(getProgress() + increment, true, true)) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index fddc40f..f53aa38 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2645,6 +2645,8 @@
         private SuggestionAdapter mSuggestionsAdapter;
         private final Comparator<SuggestionSpan> mSuggestionSpanComparator;
         private final HashMap<SuggestionSpan, Integer> mSpansLengths;
+        private final TextAppearanceSpan mHighlightSpan = new TextAppearanceSpan(
+                mTextView.getContext(), android.R.style.TextAppearance_SuggestionHighlight);
 
         private class CustomPopupWindow extends PopupWindow {
             public CustomPopupWindow(Context context, int defStyleAttr) {
@@ -2710,8 +2712,6 @@
             SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents
             int suggestionIndex; // the index of this suggestion inside suggestionSpan
             SpannableStringBuilder text = new SpannableStringBuilder();
-            TextAppearanceSpan highlightSpan = new TextAppearanceSpan(mTextView.getContext(),
-                    android.R.style.TextAppearance_SuggestionHighlight);
         }
 
         private class SuggestionAdapter extends BaseAdapter {
@@ -2948,7 +2948,7 @@
                     suggestionInfo.suggestionIndex = ADD_TO_DICTIONARY;
                     suggestionInfo.text.replace(0, suggestionInfo.text.length(), mTextView.
                             getContext().getString(com.android.internal.R.string.addToDictionary));
-                    suggestionInfo.text.setSpan(suggestionInfo.highlightSpan, 0, 0,
+                    suggestionInfo.text.setSpan(mHighlightSpan, 0, 0,
                             Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 
                     mNumberOfSuggestions++;
@@ -2961,8 +2961,7 @@
             suggestionInfo.suggestionIndex = DELETE_TEXT;
             suggestionInfo.text.replace(0, suggestionInfo.text.length(),
                     mTextView.getContext().getString(com.android.internal.R.string.deleteText));
-            suggestionInfo.text.setSpan(suggestionInfo.highlightSpan, 0, 0,
-                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            suggestionInfo.text.setSpan(mHighlightSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
             mNumberOfSuggestions++;
 
             if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
@@ -2993,8 +2992,8 @@
             suggestionInfo.suggestionEnd = suggestionInfo.suggestionStart
                     + suggestionInfo.text.length();
 
-            suggestionInfo.text.setSpan(suggestionInfo.highlightSpan, 0,
-                    suggestionInfo.text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            suggestionInfo.text.setSpan(mHighlightSpan, 0, suggestionInfo.text.length(),
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 
             // Add the text before and after the span.
             final String textAsString = text.toString();
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 864a0fe..7b9de79 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.R;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -1504,7 +1505,7 @@
      * @return The maximum available height for the popup to be completely
      *         shown.
      */
-    public int getMaxAvailableHeight(View anchor) {
+    public int getMaxAvailableHeight(@NonNull View anchor) {
         return getMaxAvailableHeight(anchor, 0);
     }
 
@@ -1519,7 +1520,7 @@
      * @return The maximum available height for the popup to be completely
      *         shown.
      */
-    public int getMaxAvailableHeight(View anchor, int yOffset) {
+    public int getMaxAvailableHeight(@NonNull View anchor, int yOffset) {
         return getMaxAvailableHeight(anchor, yOffset, false);
     }
 
@@ -1537,20 +1538,21 @@
      *        bottom decorations
      * @return The maximum available height for the popup to be completely
      *         shown.
-     *
-     * @hide Pending API council approval.
      */
-    public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
+    public int getMaxAvailableHeight(
+            @NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) {
         final Rect displayFrame = new Rect();
         anchor.getWindowVisibleDisplayFrame(displayFrame);
 
         final int[] anchorPos = mDrawingLocation;
         anchor.getLocationOnScreen(anchorPos);
 
-        int bottomEdge = displayFrame.bottom;
+        final int bottomEdge;
         if (ignoreBottomDecorations) {
-            Resources res = anchor.getContext().getResources();
+            final Resources res = anchor.getContext().getResources();
             bottomEdge = res.getDisplayMetrics().heightPixels;
+        } else {
+            bottomEdge = displayFrame.bottom;
         }
 
         final int distanceToBottom;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2172b5c..f0e216f 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -40,6 +40,7 @@
 import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.DocumentsContract;
 import android.service.chooser.ChooserTarget;
 import android.service.chooser.ChooserTargetService;
 import android.service.chooser.IChooserTargetResult;
@@ -269,7 +270,20 @@
     }
 
     @Override
-    boolean shouldAutoLaunchSingleChoice() {
+    boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
+        final Intent intent = target.getResolvedIntent();
+        final ResolveInfo resolve = target.getResolveInfo();
+
+        // When GET_CONTENT is handled by the DocumentsUI system component,
+        // we're okay automatically launching it, since it offers it's own
+        // intent disambiguation UI.
+        if (intent != null && Intent.ACTION_GET_CONTENT.equals(intent.getAction())
+                && resolve != null && resolve.priority > 0
+                && resolve.activityInfo != null && DocumentsContract.PACKAGE_DOCUMENTS_UI
+                        .equals(resolve.activityInfo.packageName)) {
+            return true;
+        }
+
         return false;
     }
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index ef9d1ce..1710489 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -796,7 +796,7 @@
         return false;
     }
 
-    boolean shouldAutoLaunchSingleChoice() {
+    boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
         return true;
     }
 
@@ -837,18 +837,21 @@
         mAlwaysUseOption = alwaysUseOption;
 
         int count = mAdapter.getUnfilteredCount();
-        if ((!shouldAutoLaunchSingleChoice() && count > 0)
-                || count > 1
-                || (count == 1 && mAdapter.getOtherProfile() != null)) {
+        if (count == 1 && mAdapter.getOtherProfile() == null) {
+            // Only one target, so we're a candidate to auto-launch!
+            final TargetInfo target = mAdapter.targetInfoForPosition(0, false);
+            if (shouldAutoLaunchSingleChoice(target)) {
+                safelyStartActivity(target);
+                mPackageMonitor.unregister();
+                mRegistered = false;
+                finish();
+                return true;
+            }
+        }
+        if (count > 0) {
             setContentView(layoutId);
             mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
             onPrepareAdapterView(mAdapterView, mAdapter, alwaysUseOption);
-        } else if (count == 1) {
-            safelyStartActivity(mAdapter.targetInfoForPosition(0, false));
-            mPackageMonitor.unregister();
-            mRegistered = false;
-            finish();
-            return true;
         } else {
             setContentView(R.layout.resolver_list);
 
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index b6240e4..c25db65 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -46,6 +46,9 @@
     public static final int ACTION_FINGERPRINT_RENAME = 254;
     public static final int ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE = 255;
     public static final int ACTION_WIGGLE_CAMERA_GESTURE = 256;
+    public static final int QS_LOCK_TILE = 257;
+    public static final int QS_USER_TILE = 258;
+    public static final int QS_BATTERY_TILE = 259;
 
     public static void visible(Context context, int category) throws IllegalArgumentException {
         if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8cf2dabdb..64b7768 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5704,6 +5704,8 @@
                                 cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in);
                             }
                         }
+                    } else {
+                        mCpuClusterSpeed[cluster] = null;
                     }
                 }
             } else {
@@ -9382,13 +9384,14 @@
 
                 u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
                 for (int cluster = 0; cluster < numClusters; cluster++) {
-                    int NSB = in.readInt();
-                    if (mPowerProfile != null &&
-                            mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) {
-                        throw new ParcelFormatException("File corrupt: too many speed bins " + NSB);
-                    }
-
                     if (in.readInt() != 0) {
+                        final int NSB = in.readInt();
+                        if (mPowerProfile != null &&
+                                mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) {
+                            throw new ParcelFormatException("File corrupt: too many speed bins " +
+                                    NSB);
+                        }
+
                         u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[NSB];
                         for (int speed = 0; speed < NSB; speed++) {
                             if (in.readInt() != 0) {
@@ -9397,6 +9400,8 @@
                                 u.mCpuClusterSpeed[cluster][speed].readSummaryFromParcelLocked(in);
                             }
                         }
+                    } else {
+                        u.mCpuClusterSpeed[cluster] = null;
                     }
                 }
             } else {
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
new file mode 100644
index 0000000..4f17c39
--- /dev/null
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+
+/**
+ * Context for decor views which can be seeded with pure application context and not depend on the
+ * activity, but still provide some of the facilities that Activity has, e.g. themes.
+ *
+ * @hide
+ */
+class DecorContext extends ContextThemeWrapper {
+    private PhoneWindow mPhoneWindow;
+    private WindowManager mWindowManager;
+
+    public DecorContext(Context context) {
+        super(context, null);
+    }
+
+    void setPhoneWindow(PhoneWindow phoneWindow) {
+        mPhoneWindow = phoneWindow;
+        mWindowManager = null;
+    }
+
+    @Override
+    public Object getSystemService(String name) {
+        if (Context.WINDOW_SERVICE.equals(name)) {
+            if (mWindowManager == null) {
+                WindowManagerImpl wm =
+                        (WindowManagerImpl) super.getSystemService(Context.WINDOW_SERVICE);
+                mWindowManager = wm.createLocalWindowManager(mPhoneWindow);
+            }
+            return mWindowManager;
+        }
+        return super.getSystemService(name);
+    }
+}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 2e4d9b5..c9b8119 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -32,6 +32,8 @@
 import android.animation.ObjectAnimator;
 import android.app.ActivityManagerNative;
 import android.app.SearchManager;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
 import android.os.Build;
 import android.os.UserHandle;
 
@@ -317,6 +319,7 @@
     private Rect mOutsets = new Rect();
 
     private boolean mIsStartingWindow;
+    private int mTheme = -1;
 
     static class WindowManagerHolder {
         static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
@@ -337,6 +340,10 @@
             mElevation = preservedWindow.getElevation();
             mLoadEleveation = false;
             mForceDecorInstall = true;
+            // If we're preserving window, carry over the app token from the preserved
+            // window, as we'll be skipping the addView in handleResumeActivity(), and
+            // the token will not be updated as for a new window.
+            getAttributes().token = preservedWindow.getAttributes().token;
         }
     }
 
@@ -2298,7 +2305,7 @@
         }
     }
 
-    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
+    private static final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
 
         /* package */int mDefaultOpacity = PixelFormat.OPAQUE;
 
@@ -2368,6 +2375,8 @@
 
         private int mRootScrollY = 0;
 
+        private PhoneWindow mWindow;
+
         public DecorView(Context context, int featureId) {
             super(context);
             mFeatureId = featureId;
@@ -2389,7 +2398,7 @@
         @Override
         public void onDraw(Canvas c) {
             super.onDraw(c);
-            mBackgroundFallback.draw(mContentRoot, c, mContentParent);
+            mBackgroundFallback.draw(mWindow.mContentRoot, c, mWindow.mContentParent);
         }
 
         @Override
@@ -2401,7 +2410,7 @@
             if (isDown && (event.getRepeatCount() == 0)) {
                 // First handle chording of panel key: if a panel key is held
                 // but not released, try to execute a shortcut in it.
-                if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
+                if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
                     boolean handled = dispatchKeyShortcutEvent(event);
                     if (handled) {
                         return true;
@@ -2410,15 +2419,15 @@
 
                 // If a panel is open, perform a shortcut on it without the
                 // chorded panel key
-                if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
-                    if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
+                if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
+                    if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
                         return true;
                     }
                 }
             }
 
-            if (!isDestroyed()) {
-                final Callback cb = getCallback();
+            if (!mWindow.isDestroyed()) {
+                final Callback cb = mWindow.getCallback();
                 final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
                         : super.dispatchKeyEvent(event);
                 if (handled) {
@@ -2426,28 +2435,28 @@
                 }
             }
 
-            return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
-                    : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
+            return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
+                    : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
         }
 
         @Override
         public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
             // If the panel is already prepared, then perform the shortcut using it.
             boolean handled;
-            if (mPreparedPanel != null) {
-                handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev,
+            if (mWindow.mPreparedPanel != null) {
+                handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
                         Menu.FLAG_PERFORM_NO_CLOSE);
                 if (handled) {
-                    if (mPreparedPanel != null) {
-                        mPreparedPanel.isHandled = true;
+                    if (mWindow.mPreparedPanel != null) {
+                        mWindow.mPreparedPanel.isHandled = true;
                     }
                     return true;
                 }
             }
 
             // Shortcut not handled by the panel.  Dispatch to the view hierarchy.
-            final Callback cb = getCallback();
-            handled = cb != null && !isDestroyed() && mFeatureId < 0
+            final Callback cb = mWindow.getCallback();
+            handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                     ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
             if (handled) {
                 return true;
@@ -2457,10 +2466,10 @@
             // combination such as Control+C.  Temporarily prepare the panel then mark it
             // unprepared again when finished to ensure that the panel will again be prepared
             // the next time it is shown for real.
-            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
-            if (st != null && mPreparedPanel == null) {
-                preparePanel(st, ev);
-                handled = performPanelShortcut(st, ev.getKeyCode(), ev,
+            PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
+            if (st != null && mWindow.mPreparedPanel == null) {
+                mWindow.preparePanel(st, ev);
+                handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
                         Menu.FLAG_PERFORM_NO_CLOSE);
                 st.isPrepared = false;
                 if (handled) {
@@ -2472,23 +2481,23 @@
 
         @Override
         public boolean dispatchTouchEvent(MotionEvent ev) {
-            final Callback cb = getCallback();
-            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
-                    : super.dispatchTouchEvent(ev);
+            final Callback cb = mWindow.getCallback();
+            return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+                    ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
         }
 
         @Override
         public boolean dispatchTrackballEvent(MotionEvent ev) {
-            final Callback cb = getCallback();
-            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTrackballEvent(ev)
-                    : super.dispatchTrackballEvent(ev);
+            final Callback cb = mWindow.getCallback();
+            return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+                    ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
         }
 
         @Override
         public boolean dispatchGenericMotionEvent(MotionEvent ev) {
-            final Callback cb = getCallback();
-            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev)
-                    : super.dispatchGenericMotionEvent(ev);
+            final Callback cb = mWindow.getCallback();
+            return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+                    ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
         }
 
         public boolean superDispatchKeyEvent(KeyEvent event) {
@@ -2540,7 +2549,7 @@
         @Override
         public boolean onInterceptTouchEvent(MotionEvent event) {
             int action = event.getAction();
-            if (mHasNonClientDecor && mNonClientDecorView.mVisible) {
+            if (mHasNonClientDecor && mWindow.mNonClientDecorView.mVisible) {
                 // Don't dispatch ACTION_DOWN to the non client decor if the window is
                 // resizable and the event was (starting) outside the window.
                 // Window resizing events should be handled by WindowManager.
@@ -2563,7 +2572,7 @@
                     int x = (int)event.getX();
                     int y = (int)event.getY();
                     if (isOutOfBounds(x, y)) {
-                        closePanel(mFeatureId);
+                        mWindow.closePanel(mFeatureId);
                         return true;
                     }
                 }
@@ -2589,7 +2598,7 @@
                 if (action == MotionEvent.ACTION_MOVE) {
                     if (y > (mDownY+30)) {
                         Log.i(TAG, "Closing!");
-                        closePanel(mFeatureId);
+                        mWindow.closePanel(mFeatureId);
                         mWatchingForMenu = false;
                         return true;
                     }
@@ -2605,8 +2614,8 @@
 
             if (action == MotionEvent.ACTION_DOWN) {
                 int y = (int)event.getY();
-                if (y >= (getHeight()-5) && !hasChildren()) {
-                    Log.i(TAG, "Watchiing!");
+                if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
+                    Log.i(TAG, "Watching!");
                     mWatchingForMenu = true;
                 }
                 return false;
@@ -2620,7 +2629,7 @@
             if (action == MotionEvent.ACTION_MOVE) {
                 if (y < (getHeight()-30)) {
                     Log.i(TAG, "Opening!");
-                    openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent(
+                    mWindow.openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent(
                             KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
                     mWatchingForMenu = false;
                     return true;
@@ -2654,8 +2663,8 @@
 
         @Override
         public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
-            final Callback cb = getCallback();
-            if (cb != null && !isDestroyed()) {
+            final Callback cb = mWindow.getCallback();
+            if (cb != null && !mWindow.isDestroyed()) {
                 if (cb.dispatchPopulateAccessibilityEvent(event)) {
                     return true;
                 }
@@ -2692,7 +2701,7 @@
 
                 if (SWEEP_OPEN_MENU) {
                     if (mMenuBackground == null && mFeatureId < 0
-                            && getAttributes().height
+                            && mWindow.getAttributes().height
                             == WindowManager.LayoutParams.MATCH_PARENT) {
                         mMenuBackground = getContext().getDrawable(
                                 R.drawable.menu_background);
@@ -2717,7 +2726,8 @@
 
             boolean fixedWidth = false;
             if (widthMode == AT_MOST) {
-                final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
+                final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor
+                        : mWindow.mFixedWidthMajor;
                 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
                     final int w;
                     if (tvw.type == TypedValue.TYPE_DIMENSION) {
@@ -2738,7 +2748,8 @@
             }
 
             if (heightMode == AT_MOST) {
-                final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
+                final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
+                        : mWindow.mFixedHeightMinor;
                 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
                     final int h;
                     if (tvh.type == TypedValue.TYPE_DIMENSION) {
@@ -2756,21 +2767,21 @@
                 }
             }
 
-            getOutsets(mOutsets);
-            if (mOutsets.top > 0 || mOutsets.bottom > 0) {
+            getOutsets(mWindow.mOutsets);
+            if (mWindow.mOutsets.top > 0 || mWindow.mOutsets.bottom > 0) {
                 int mode = MeasureSpec.getMode(heightMeasureSpec);
                 if (mode != MeasureSpec.UNSPECIFIED) {
                     int height = MeasureSpec.getSize(heightMeasureSpec);
                     heightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            height + mOutsets.top + mOutsets.bottom, mode);
+                            height + mWindow.mOutsets.top + mWindow.mOutsets.bottom, mode);
                 }
             }
-            if (mOutsets.left > 0 || mOutsets.right > 0) {
+            if (mWindow.mOutsets.left > 0 || mWindow.mOutsets.right > 0) {
                 int mode = MeasureSpec.getMode(widthMeasureSpec);
                 if (mode != MeasureSpec.UNSPECIFIED) {
                     int width = MeasureSpec.getSize(widthMeasureSpec);
                     widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            width + mOutsets.left + mOutsets.right, mode);
+                            width + mWindow.mOutsets.left + mWindow.mOutsets.right, mode);
                 }
             }
 
@@ -2782,7 +2793,7 @@
             widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
 
             if (!fixedWidth && widthMode == AT_MOST) {
-                final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
+                final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
                 if (tv.type != TypedValue.TYPE_NULL) {
                     final int min;
                     if (tv.type == TypedValue.TYPE_DIMENSION) {
@@ -2810,12 +2821,12 @@
         @Override
         protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
             super.onLayout(changed, left, top, right, bottom);
-            getOutsets(mOutsets);
-            if (mOutsets.left > 0) {
-                offsetLeftAndRight(-mOutsets.left);
+            getOutsets(mWindow.mOutsets);
+            if (mWindow.mOutsets.left > 0) {
+                offsetLeftAndRight(-mWindow.mOutsets.left);
             }
-            if (mOutsets.top > 0) {
-                offsetTopAndBottom(-mOutsets.top);
+            if (mWindow.mOutsets.top > 0) {
+                offsetTopAndBottom(-mWindow.mOutsets.top);
             }
         }
 
@@ -2831,46 +2842,46 @@
         @Override
         public boolean showContextMenuForChild(View originalView) {
             // Reuse the context menu builder
-            if (mContextMenu == null) {
-                mContextMenu = new ContextMenuBuilder(getContext());
-                mContextMenu.setCallback(mContextMenuCallback);
+            if (mWindow.mContextMenu == null) {
+                mWindow.mContextMenu = new ContextMenuBuilder(getContext());
+                mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
             } else {
-                mContextMenu.clearAll();
+                mWindow.mContextMenu.clearAll();
             }
 
-            final MenuDialogHelper helper = mContextMenu.show(originalView,
+            final MenuDialogHelper helper = mWindow.mContextMenu.show(originalView,
                     originalView.getWindowToken());
             if (helper != null) {
-                helper.setPresenterCallback(mContextMenuCallback);
-            } else if (mContextMenuHelper != null) {
+                helper.setPresenterCallback(mWindow.mContextMenuCallback);
+            } else if (mWindow.mContextMenuHelper != null) {
                 // No menu to show, but if we have a menu currently showing it just became blank.
                 // Close it.
-                mContextMenuHelper.dismiss();
+                mWindow.mContextMenuHelper.dismiss();
             }
-            mContextMenuHelper = helper;
+            mWindow.mContextMenuHelper = helper;
             return helper != null;
         }
 
         @Override
         public boolean showContextMenuForChild(View originalView, float x, float y) {
             // Reuse the context menu builder
-            if (mContextMenu == null) {
-                mContextMenu = new ContextMenuBuilder(getContext());
-                mContextMenu.setCallback(mContextMenuCallback);
+            if (mWindow.mContextMenu == null) {
+                mWindow.mContextMenu = new ContextMenuBuilder(getContext());
+                mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
             } else {
-                mContextMenu.clearAll();
+                mWindow.mContextMenu.clearAll();
             }
 
-            final MenuPopupHelper helper = mContextMenu.showPopup(
+            final MenuPopupHelper helper = mWindow.mContextMenu.showPopup(
                     getContext(), originalView, x, y);
             if (helper != null) {
-                helper.setCallback(mContextMenuCallback);
-            } else if (mContextMenuPopupHelper != null) {
+                helper.setCallback(mWindow.mContextMenuCallback);
+            } else if (mWindow.mContextMenuPopupHelper != null) {
                 // No menu to show, but if we have a menu currently showing it just became blank.
                 // Close it.
-                mContextMenuPopupHelper.dismiss();
+                mWindow.mContextMenuPopupHelper.dismiss();
             }
-            mContextMenuPopupHelper = helper;
+            mWindow.mContextMenuPopupHelper = helper;
             return helper != null;
         }
 
@@ -2900,14 +2911,15 @@
                 View originatingView, ActionMode.Callback callback, int type) {
             ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
             ActionMode mode = null;
-            if (getCallback() != null && !isDestroyed()) {
+            if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
                 try {
-                    mode = getCallback().onWindowStartingActionMode(wrappedCallback, type);
+                    mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
                 } catch (AbstractMethodError ame) {
                     // Older apps might not implement the typed version of this method.
                     if (type == ActionMode.TYPE_PRIMARY) {
                         try {
-                            mode = getCallback().onWindowStartingActionMode(wrappedCallback);
+                            mode = mWindow.getCallback().onWindowStartingActionMode(
+                                    wrappedCallback);
                         } catch (AbstractMethodError ame2) {
                             // Older apps might not implement this callback method at all.
                         }
@@ -2932,9 +2944,9 @@
                     mode = null;
                 }
             }
-            if (mode != null && getCallback() != null && !isDestroyed()) {
+            if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
                 try {
-                    getCallback().onActionModeStarted(mode);
+                    mWindow.getCallback().onActionModeStarted(mode);
                 } catch (AbstractMethodError ame) {
                     // Older apps might not implement this callback method.
                 }
@@ -3023,10 +3035,10 @@
         }
 
         private WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
-            WindowManager.LayoutParams attrs = getAttributes();
+            WindowManager.LayoutParams attrs = mWindow.getAttributes();
             int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
 
-            if (!mIsFloating && ActivityManager.isHighEndGfx()) {
+            if (!mWindow.mIsFloating && ActivityManager.isHighEndGfx()) {
                 boolean disallowAnimate = !isLaidOut();
                 disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
                         & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
@@ -3058,14 +3070,14 @@
 
                 boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0;
                 int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset;
-                updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mNavigationBarColor,
-                        navBarSize, navBarToRightEdge, 0 /* rightInset */,
-                        animate && !disallowAnimate);
+                updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
+                        mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge,
+                        0 /* rightInset */, animate && !disallowAnimate);
 
                 boolean statusBarNeedsRightInset = navBarToRightEdge
                         && mNavigationColorViewState.present;
                 int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
-                updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor,
+                updateColorViewInt(mStatusColorViewState, sysUiVisibility, mWindow.mStatusBarColor,
                         mLastTopInset, false /* matchVertical */, statusBarRightInset,
                         animate && !disallowAnimate);
             }
@@ -3082,13 +3094,13 @@
             int consumedRight = consumingNavBar ? mLastRightInset : 0;
             int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
 
-            if (mContentRoot != null
-                    && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
-                MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
+            if (mWindow.mContentRoot != null
+                    && mWindow.mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
+                MarginLayoutParams lp = (MarginLayoutParams) mWindow.mContentRoot.getLayoutParams();
                 if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) {
                     lp.rightMargin = consumedRight;
                     lp.bottomMargin = consumedBottom;
-                    mContentRoot.setLayoutParams(lp);
+                    mWindow.mContentRoot.setLayoutParams(lp);
 
                     if (insets == null) {
                         // The insets have changed, but we're not currently in the process
@@ -3126,11 +3138,11 @@
         private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
                 int size, boolean verticalBar, int rightMargin, boolean animate) {
             state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0
-                    && (getAttributes().flags & state.hideWindowFlag) == 0
-                    && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+                    && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
+                    && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
             boolean show = state.present
                     && (color & Color.BLACK) != 0
-                    && (getAttributes().flags & state.translucentFlag) == 0;
+                    && (mWindow.getAttributes().flags & state.translucentFlag) == 0;
 
             boolean visibilityChanged = false;
             View view = state.view;
@@ -3222,14 +3234,14 @@
                             mPrimaryActionModeView.getLayoutParams();
                     boolean mlpChanged = false;
                     if (mPrimaryActionModeView.isShown()) {
-                        if (mTempRect == null) {
-                            mTempRect = new Rect();
+                        if (mWindow.mTempRect == null) {
+                            mWindow.mTempRect = new Rect();
                         }
-                        final Rect rect = mTempRect;
+                        final Rect rect = mWindow.mTempRect;
 
                         // If the parent doesn't consume the insets, manually
                         // apply the default system window insets.
-                        mContentParent.computeSystemWindowInsets(insets, rect);
+                        mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
                         final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0;
                         if (mlp.topMargin != newMargin) {
                             mlpChanged = true;
@@ -3260,7 +3272,7 @@
                         // mode is overlaid on the app content (e.g. it's
                         // sitting in a FrameLayout, see
                         // screen_simple_overlay_action_mode.xml).
-                        final boolean nonOverlay = (getLocalFeatures()
+                        final boolean nonOverlay = (mWindow.getLocalFeatures()
                                 & (1 << FEATURE_ACTION_MODE_OVERLAY)) == 0;
                         insets = insets.consumeSystemWindowInsets(
                                 false, nonOverlay && showStatusGuard /* top */, false, false);
@@ -3284,14 +3296,14 @@
 
         private void updateNavigationGuard(WindowInsets insets) {
             // IMEs lay out below the nav bar, but the content view must not (for back compat)
-            if (getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
+            if (mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
                 // prevent the content view from including the nav bar height
-                if (mContentParent != null) {
-                    if (mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
+                if (mWindow.mContentParent != null) {
+                    if (mWindow.mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
                         MarginLayoutParams mlp =
-                                (MarginLayoutParams) mContentParent.getLayoutParams();
+                                (MarginLayoutParams) mWindow.mContentParent.getLayoutParams();
                         mlp.bottomMargin = insets.getSystemWindowInsetBottom();
-                        mContentParent.setLayoutParams(mlp);
+                        mWindow.mContentParent.setLayoutParams(mlp);
                     }
                 }
                 // position the navigation guard view, creating it if necessary
@@ -3373,7 +3385,7 @@
 
             mDefaultOpacity = opacity;
             if (mFeatureId < 0) {
-                setDefaultWindowFormat(opacity);
+                mWindow.setDefaultWindowFormat(opacity);
             }
         }
 
@@ -3383,12 +3395,13 @@
 
             // If the user is chording a menu shortcut, release the chord since
             // this window lost focus
-            if (hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus && mPanelChordingKey != 0) {
-                closePanel(FEATURE_OPTIONS_PANEL);
+            if (mWindow.hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus
+                    && mWindow.mPanelChordingKey != 0) {
+                mWindow.closePanel(FEATURE_OPTIONS_PANEL);
             }
 
-            final Callback cb = getCallback();
-            if (cb != null && !isDestroyed() && mFeatureId < 0) {
+            final Callback cb = mWindow.getCallback();
+            if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
                 cb.onWindowFocusChanged(hasWindowFocus);
             }
 
@@ -3404,8 +3417,8 @@
         protected void onAttachedToWindow() {
             super.onAttachedToWindow();
 
-            final Callback cb = getCallback();
-            if (cb != null && !isDestroyed() && mFeatureId < 0) {
+            final Callback cb = mWindow.getCallback();
+            if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
                 cb.onAttachedToWindow();
             }
 
@@ -3417,7 +3430,7 @@
                  * menu was open. When the activity is recreated, the menu
                  * should be shown again.
                  */
-                openPanelsAfterRestore();
+                mWindow.openPanelsAfterRestore();
             }
         }
 
@@ -3425,13 +3438,13 @@
         protected void onDetachedFromWindow() {
             super.onDetachedFromWindow();
 
-            final Callback cb = getCallback();
+            final Callback cb = mWindow.getCallback();
             if (cb != null && mFeatureId < 0) {
                 cb.onDetachedFromWindow();
             }
 
-            if (mDecorContentParent != null) {
-                mDecorContentParent.dismissPopups();
+            if (mWindow.mDecorContentParent != null) {
+                mWindow.mDecorContentParent.dismissPopups();
             }
 
             if (mPrimaryActionModePopup != null) {
@@ -3446,7 +3459,7 @@
                 mFloatingToolbar = null;
             }
 
-            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+            PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
             if (st != null && st.menu != null && mFeatureId < 0) {
                 st.menu.close();
             }
@@ -3455,29 +3468,29 @@
         @Override
         public void onCloseSystemDialogs(String reason) {
             if (mFeatureId >= 0) {
-                closeAllPanels();
+                mWindow.closeAllPanels();
             }
         }
 
         public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
-            return mFeatureId < 0 ? mTakeSurfaceCallback : null;
+            return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
         }
 
         public InputQueue.Callback willYouTakeTheInputQueue() {
-            return mFeatureId < 0 ? mTakeInputQueueCallback : null;
+            return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
         }
 
         public void setSurfaceType(int type) {
-            PhoneWindow.this.setType(type);
+            mWindow.setType(type);
         }
 
         public void setSurfaceFormat(int format) {
-            PhoneWindow.this.setFormat(format);
+            mWindow.setFormat(format);
         }
 
         public void setSurfaceKeepScreenOn(boolean keepOn) {
-            if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-            else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+            if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+            else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         }
 
         @Override
@@ -3509,7 +3522,7 @@
             endOnGoingFadeAnimation();
             cleanupPrimaryActionMode();
             if (mPrimaryActionModeView == null) {
-                if (isFloating()) {
+                if (mWindow.isFloating()) {
                     // Use the action bar theme.
                     final TypedValue outValue = new TypedValue();
                     final Theme baseTheme = mContext.getTheme();
@@ -3656,7 +3669,7 @@
 
         private void setHandledFloatingActionMode(ActionMode mode) {
             mFloatingActionMode = mode;
-            mFloatingToolbar = new FloatingToolbar(mContext, PhoneWindow.this);
+            mFloatingToolbar = new FloatingToolbar(mContext, mWindow);
             ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar);
             mFloatingActionMode.invalidate();  // Will show the floating toolbar if necessary.
             mFloatingActionModeOriginatingView.getViewTreeObserver()
@@ -3695,6 +3708,15 @@
             return windowHasNonClientDecor() && getElevation() > 0;
         }
 
+        void setWindow(PhoneWindow phoneWindow) {
+            mWindow = phoneWindow;
+            Context context = getContext();
+            if (context instanceof DecorContext) {
+                DecorContext decorContex = (DecorContext) context;
+                decorContex.setPhoneWindow(mWindow);
+            }
+        }
+
         /**
          * Clears out internal references when the action mode is destroyed.
          */
@@ -3783,9 +3805,9 @@
                     cleanupFloatingActionModeViews();
                     mFloatingActionMode = null;
                 }
-                if (getCallback() != null && !isDestroyed()) {
+                if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
                     try {
-                        getCallback().onActionModeFinished(mode);
+                        mWindow.getCallback().onActionModeFinished(mode);
                     } catch (AbstractMethodError ame) {
                         // Older apps might not implement this callback method.
                     }
@@ -3805,7 +3827,20 @@
     }
 
     protected DecorView generateDecor(int featureId) {
-        return new DecorView(getContext(), featureId);
+        // System process doesn't have application context and in that case we need to directly use
+        // the context we have. Otherwise we want the application context, so we don't cling to the
+        // activity.
+        Context applicationContext = getContext().getApplicationContext();
+        Context context;
+        if (applicationContext == null) {
+            context = getContext();
+        } else {
+            context = new DecorContext(applicationContext);
+            if (mTheme != -1) {
+                context.setTheme(mTheme);
+            }
+        }
+        return new DecorView(context, featureId);
     }
 
     protected void setFeatureFromAttrs(int featureId, TypedArray attrs,
@@ -4174,13 +4209,15 @@
             // Dependent on the brightness of the used title we either use the
             // dark or the light button frame.
             if (nonClientDecorView == null) {
+                Context context = mDecor.getContext();
                 TypedValue value = new TypedValue();
-                getContext().getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
+                context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
+                LayoutInflater inflater = mLayoutInflater.from(context);
                 if (Color.luminance(value.data) < 0.5) {
-                    nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
+                    nonClientDecorView = (NonClientDecorView) inflater.inflate(
                             R.layout.non_client_decor_dark, null);
                 } else {
-                    nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
+                    nonClientDecorView = (NonClientDecorView) inflater.inflate(
                             R.layout.non_client_decor_light, null);
                 }
             }
@@ -4202,11 +4239,14 @@
         mForceDecorInstall = false;
         if (mDecor == null) {
             mDecor = generateDecor(-1);
+            mDecor.setWindow(this);
             mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
             mDecor.setIsRootNamespace(true);
             if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                 mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
             }
+        } else {
+            mDecor.setWindow(this);
         }
         if (mContentParent == null) {
             mContentParent = generateLayout(mDecor);
@@ -5381,4 +5421,15 @@
     public boolean hasNonClientDecorView() {
         return mNonClientDecorView != null;
     }
+
+    @Override
+    public void setTheme(int resid) {
+        mTheme = resid;
+        if (mDecor != null) {
+            Context context = mDecor.getContext();
+            if (context instanceof DecorContext) {
+                context.setTheme(resid);
+            }
+        }
+    }
 }
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 2a25db6..7bab446 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -73,6 +73,8 @@
     // This class is responsible for the public API of the floating toolbar.
     // It delegates rendering operations to the FloatingToolbarPopup.
 
+    public static final String FLOATING_TOOLBAR_TAG = "floating_toolbar";
+
     private static final MenuItem.OnMenuItemClickListener NO_OP_MENUITEM_CLICK_LISTENER =
             new MenuItem.OnMenuItemClickListener() {
                 @Override
@@ -1460,8 +1462,10 @@
     }
 
     private static ViewGroup createContentContainer(Context context) {
-        return (ViewGroup) LayoutInflater.from(context)
+        ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context)
                 .inflate(R.layout.floating_popup_container, null);
+        contentContainer.setTag(FLOATING_TOOLBAR_TAG);
+        return contentContainer;
     }
 
     private static PopupWindow createPopupWindow(View content) {
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 6ab306c..6960a3b 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -17,15 +17,23 @@
 package com.android.internal.widget;
 
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Rect;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.util.AttributeSet;
+import android.view.Choreographer;
+import android.view.DisplayListCanvas;
 import android.view.MotionEvent;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
 import android.view.View;
+import android.view.ViewRootImpl;
 import android.widget.LinearLayout;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.Window;
+import android.view.WindowCallbacks;
 import android.util.Log;
 import android.util.TypedValue;
 
@@ -57,7 +65,8 @@
  * </ul>
  * This will be mitigated once b/22527834 will be addressed.
  */
-public class NonClientDecorView extends LinearLayout implements View.OnClickListener {
+public class NonClientDecorView extends LinearLayout
+        implements View.OnClickListener, View.OnTouchListener, WindowCallbacks {
     private final static String TAG = "NonClientDecorView";
     // The height of a window which has focus in DIP.
     private final int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
@@ -66,6 +75,8 @@
     private PhoneWindow mOwner = null;
     private boolean mWindowHasShadow = false;
     private boolean mShowDecor = false;
+    // True when this object is listening for window size changes.
+    private boolean mAttachedCallbacksToRootViewImpl = false;
 
     // True if the window is being dragged.
     private boolean mDragging = false;
@@ -84,6 +95,9 @@
     // to max until the first layout command has been executed.
     private boolean mAllowUpdateElevation = false;
 
+    // The resize frame renderer.
+    private ResizeFrameThread mFrameRendererThread = null;
+
     public NonClientDecorView(Context context) {
         super(context);
     }
@@ -107,12 +121,24 @@
         // By changing the outline provider to BOUNDS, the window can remove its
         // background without removing the shadow.
         mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
+
+        if (!mAttachedCallbacksToRootViewImpl) {
+            // If there is no window callback installed there was no window set before. Set it now.
+            // Note that our ViewRootImpl object will not change.
+            getViewRootImpl().addWindowCallbacks(this);
+            mAttachedCallbacksToRootViewImpl = true;
+        } else if (mFrameRendererThread != null) {
+            // We are resizing and this call happened due to a configuration change. Tell the
+            // renderer about it.
+            mFrameRendererThread.onConfigurationChange();
+        }
+
         findViewById(R.id.maximize_window).setOnClickListener(this);
         findViewById(R.id.close_window).setOnClickListener(this);
     }
 
     @Override
-    public boolean onTouchEvent(MotionEvent e) {
+    public boolean onTouch(View v, MotionEvent e) {
         // Note: There are no mixed events. When a new device gets used (e.g. 1. Mouse, 2. touch)
         // the old input device events get cancelled first. So no need to remember the kind of
         // input device we are listening to.
@@ -224,6 +250,7 @@
         boolean invisible = isFillingScreen() || !mShowDecor;
         View caption = getChildAt(0);
         caption.setVisibility(invisible ? GONE : VISIBLE);
+        caption.setOnTouchListener(this);
         mVisible = !invisible;
     }
 
@@ -249,7 +276,9 @@
      **/
     private void updateElevation() {
         float elevation = 0;
-        if (mWindowHasShadow) {
+        // Do not use a shadow when we are in resizing mode (mRenderer not null) since the shadow
+        // is bound to the content size and not the target size.
+        if (mWindowHasShadow && mFrameRendererThread == null) {
             boolean fill = isFillingScreen();
             elevation = fill ? 0 :
                     (mWindowHasFocus ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP :
@@ -291,4 +320,270 @@
             }
         }
     }
+
+    @Override
+    public void onWindowDragResizeStart(Rect initialBounds) {
+        if (mOwner.isDestroyed()) {
+            // If the owner's window is gone, we should not be able to come here anymore.
+            releaseResources();
+            return;
+        }
+        if (mFrameRendererThread != null) {
+            return;
+        }
+        final ThreadedRenderer renderer =
+                (ThreadedRenderer) mOwner.getDecorView().getHardwareRenderer();
+        if (renderer != null) {
+            mFrameRendererThread = new ResizeFrameThread(renderer, initialBounds);
+            // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
+            // If we want to get the shadow shown while resizing, we would need to elevate a new
+            // element which owns the caption and has the elevation.
+            updateElevation();
+        }
+    }
+
+    @Override
+    public void onWindowDragResizeEnd() {
+        releaseThreadedRenderer();
+    }
+
+    @Override
+    public void onWindowSizeIsChanging(Rect newBounds) {
+        if (mFrameRendererThread != null) {
+            mFrameRendererThread.setTargetRect(newBounds);
+        }
+    }
+
+    /**
+     * Release the renderer thread which is usually done when the user stops resizing.
+     */
+    private void releaseThreadedRenderer() {
+        if (mFrameRendererThread != null) {
+            mFrameRendererThread.releaseRenderer();
+            mFrameRendererThread = null;
+            // Bring the shadow back.
+            updateElevation();
+        }
+    }
+
+    /**
+     * Called when the parent window is destroyed to release all resources. Note that this will also
+     * destroy the renderer thread.
+     */
+    private void releaseResources() {
+        releaseThreadedRenderer();
+        if (mAttachedCallbacksToRootViewImpl) {
+            ViewRootImpl.removeWindowCallbacks(this);
+            mAttachedCallbacksToRootViewImpl = false;
+        }
+    }
+
+    /**
+     * The thread which draws the chrome while we are resizing.
+     * It starts with the creation and it ends once someone calls destroy().
+     * Any size changes can be passed by a call to setTargetRect will passed to the thread and
+     * executed via the Choreographer.
+     */
+    private class ResizeFrameThread extends Thread implements Choreographer.FrameCallback {
+        // This is containing the last requested size by a resize command. Note that this size might
+        // or might not have been applied to the output already.
+        private final Rect mTargetRect = new Rect();
+
+        // The render nodes for the multi threaded renderer.
+        private ThreadedRenderer mRenderer;
+        private RenderNode mFrameNode;
+        private RenderNode mBackdropNode;
+
+        private final Rect mOldTargetRect = new Rect();
+        private final Rect mNewTargetRect = new Rect();
+        private Choreographer mChoreographer;
+
+        // Cached size values from the last render for the case that the view hierarchy is gone
+        // during a configuration change.
+        private int mLastContentWidth;
+        private int mLastContentHeight;
+        private int mLastCaptionHeight;
+        private int mLastXOffset;
+        private int mLastYOffset;
+
+        ResizeFrameThread(ThreadedRenderer renderer, Rect initialBounds) {
+            mRenderer = renderer;
+
+            // Create the render nodes for our frame and backdrop which can be resized independently
+            // from the content.
+            mFrameNode = RenderNode.create("FrameNode", null);
+            mBackdropNode = RenderNode.create("BackdropNode", null);
+
+            mRenderer.addRenderNode(mFrameNode, false);
+            mRenderer.addRenderNode(mBackdropNode, true);
+
+            // Set the initial bounds and draw once so that we do not get a broken frame.
+            mTargetRect.set(initialBounds);
+            changeWindowSize(initialBounds);
+
+            // Kick off our draw thread.
+            start();
+        }
+
+        /**
+         * Call this function asynchronously when the window size has been changed. The change will
+         * be picked up once per frame and the frame will be re-rendered accordingly.
+         * @param newTargetBounds The new target bounds.
+         */
+        public void setTargetRect(Rect newTargetBounds) {
+            synchronized (this) {
+                mTargetRect.set(newTargetBounds);
+                // Notify of a bounds change.
+                pingRenderLocked();
+            }
+        }
+
+        /**
+         * The window got replaced due to a configuration change.
+         */
+        public void onConfigurationChange() {
+            if (mRenderer != null) {
+                // Enforce a window redraw.
+                mOldTargetRect.set(0, 0, 0, 0);
+                pingRenderLocked();
+            }
+        }
+
+        /**
+         * All resources of the renderer will be released. This function can be called from the
+         * the UI thread as well as the renderer thread.
+         */
+        public void releaseRenderer() {
+            synchronized (this) {
+                if (mRenderer != null) {
+                    // Invalidate the current content bounds.
+                    mRenderer.setContentDrawBounds(0, 0, 0, 0);
+
+                    // Remove the render nodes again (see comment above - better to do that only once).
+                    mRenderer.removeRenderNode(mFrameNode);
+                    mRenderer.removeRenderNode(mBackdropNode);
+
+                    mRenderer = null;
+
+                    // Exit the renderer loop.
+                    pingRenderLocked();
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+            try {
+                Looper.prepare();
+                mChoreographer = Choreographer.getInstance();
+                Looper.loop();
+            } finally {
+                releaseRenderer();
+            }
+            synchronized (this) {
+                // Make sure no more messages are being sent.
+                mChoreographer = null;
+            }
+        }
+
+        /**
+         * The implementation of the FrameCallback.
+         * @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
+         * in the {@link System#nanoTime()} timebase.  Divide this value by {@code 1000000}
+         */
+        @Override
+        public void doFrame(long frameTimeNanos) {
+            if (mRenderer == null) {
+                // Tell the looper to stop. We are done.
+                Looper.myLooper().quit();
+                return;
+            }
+            // Prevent someone from changing this while we are copying.
+            synchronized (this) {
+                mNewTargetRect.set(mTargetRect);
+            }
+            if (!mNewTargetRect.equals(mOldTargetRect)) {
+                mOldTargetRect.set(mNewTargetRect);
+                changeWindowSize(mNewTargetRect);
+            }
+        }
+
+        /**
+         * Resizing the frame to fit the new window size.
+         * @param newBounds The window bounds which needs to be drawn.
+         */
+        private void changeWindowSize(Rect newBounds) {
+            long startTime = System.currentTimeMillis();
+
+            // While a configuration change is taking place the view hierarchy might become
+            // inaccessible. For that case we remember the previous metrics to avoid flashes.
+            View caption = getChildAt(0);
+            View content = getChildAt(1);
+            if (content != null && caption != null) {
+                mLastContentWidth = content.getWidth();
+                mLastContentHeight = content.getHeight();
+                mLastCaptionHeight = caption.getHeight();
+
+                // Get the draw position within our surface.
+                int[] surfaceOrigin = new int[2];
+                surfaceOrigin[0] = 0;
+                surfaceOrigin[1] = 0;
+
+                // Get the shadow offsets.
+                getLocationInSurface(surfaceOrigin);
+                mLastXOffset = surfaceOrigin[0];
+                mLastYOffset = surfaceOrigin[1];
+            }
+
+            // Since the surface is spanning the entire screen, we have to add the start offset of
+            // the bounds to get to the surface location.
+            final int left = mLastXOffset + newBounds.left;
+            final int top = mLastYOffset + newBounds.top;
+            final int width = newBounds.width();
+            final int height = newBounds.height();
+
+            // Produce the draw calls.
+            // TODO(skuhne): Create a separate caption view which draws this. If the shadow should
+            // be resized while the window resizes, this hierarchy needs to have the elevation.
+            // That said - it is probably no good idea to draw the shadow every time since it costs
+            // a considerable time which we should rather spend for resizing the content and it does
+            // barely show while the entire screen is moving.
+            mFrameNode.setLeftTopRightBottom(left, top, left + width, top + mLastCaptionHeight);
+            DisplayListCanvas canvas = mFrameNode.start(width, height);
+            canvas.drawColor(Color.BLACK);
+            mFrameNode.end(canvas);
+
+            mBackdropNode.setLeftTopRightBottom(left, top + mLastCaptionHeight, left + width,
+                    top + height);
+
+            // The backdrop: clear everything with the background. Clipping is done elsewhere.
+            canvas = mBackdropNode.start(width, height - mLastCaptionHeight);
+            // TODO(skuhne): mOwner.getDecorView().mBackgroundFallback.draw(..) - or similar.
+            // Note: This might not work (calculator for example uses a transparent background).
+            canvas.drawColor(0xff808080);
+            mBackdropNode.end(canvas);
+
+            // The current content buffer is drawn here.
+            mRenderer.setContentDrawBounds(
+                    mLastXOffset,
+                    mLastYOffset + mLastCaptionHeight,
+                    mLastXOffset + mLastContentWidth,
+                    mLastYOffset + mLastCaptionHeight + mLastContentHeight);
+
+            // We need to render both rendered nodes explicitly.
+            mRenderer.drawRenderNode(mFrameNode);
+            mRenderer.drawRenderNode(mBackdropNode);
+        }
+
+        /**
+         * Sends a message to the renderer to wake up and perform the next action which can be
+         * either the next rendering or the self destruction if mRenderer is null.
+         * Note: This call must be synchronized.
+         */
+        private void pingRenderLocked() {
+            if (mChoreographer != null) {
+                mChoreographer.postFrameCallback(this);
+            }
+        }
+    }
 }
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 520dc4f..314e4b6 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -44,6 +44,7 @@
 #include "TypefaceImpl.h"
 
 #include <vector>
+#include <memory>
 
 // temporary for debugging
 #include <Caches.h>
@@ -65,10 +66,6 @@
 static jclass   gFontMetricsInt_class;
 static JMetricsID gFontMetricsInt_fieldID;
 
-static jclass   gPaint_class;
-static jfieldID gPaint_nativeInstanceID;
-static jfieldID gPaint_nativeTypefaceID;
-
 static void defaultSettingsForAndroid(Paint* paint) {
     // GlyphID encoding is required because we are using Harfbuzz shaping
     paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
@@ -81,37 +78,17 @@
 
 static thread_local LocaleCacheEntry sSingleEntryLocaleCache;
 
-class PaintGlue {
-public:
+namespace PaintGlue {
     enum MoveOpt {
         AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
     };
 
-    static Paint* getNativePaint(JNIEnv* env, jobject paint) {
-        SkASSERT(env);
-        SkASSERT(paint);
-        SkASSERT(env->IsInstanceOf(paint, gPaint_class));
-        jlong paintHandle = env->GetLongField(paint, gPaint_nativeInstanceID);
-        android::Paint* p = reinterpret_cast<android::Paint*>(paintHandle);
-        SkASSERT(p);
-        return p;
-    }
-
-    static TypefaceImpl* getNativeTypeface(JNIEnv* env, jobject paint) {
-        SkASSERT(env);
-        SkASSERT(paint);
-        SkASSERT(env->IsInstanceOf(paint, gPaint_class));
-        jlong typefaceHandle = env->GetLongField(paint, gPaint_nativeTypefaceID);
-        android::TypefaceImpl* p = reinterpret_cast<android::TypefaceImpl*>(typefaceHandle);
-        return p;
-    }
-
     static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
         Paint* obj = reinterpret_cast<Paint*>(objHandle);
         delete obj;
     }
 
-    static jlong init(JNIEnv* env, jobject clazz) {
+    static jlong init(JNIEnv* env, jobject) {
         static_assert(1 <<  0 == SkPaint::kAntiAlias_Flag,          "paint_flags_mismatch");
         static_assert(1 <<  2 == SkPaint::kDither_Flag,             "paint_flags_mismatch");
         static_assert(1 <<  3 == SkPaint::kUnderlineText_Flag,      "paint_flags_mismatch");
@@ -148,9 +125,8 @@
     // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
     static const uint32_t sFilterBitmapFlag = 0x02;
 
-    static jint getFlags(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        Paint* nativePaint = getNativePaint(env, paint);
+    static jint getFlags(JNIEnv* env, jobject, jlong paintHandle) {
+        Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
         uint32_t result = nativePaint->getFlags();
         result &= ~sFilterBitmapFlag; // Filtering no longer stored in this bit. Mask away.
         if (nativePaint->getFilterQuality() != kNone_SkFilterQuality) {
@@ -159,9 +135,8 @@
         return static_cast<jint>(result);
     }
 
-    static void setFlags(JNIEnv* env, jobject paint, jint flags) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        Paint* nativePaint = getNativePaint(env, paint);
+    static void setFlags(JNIEnv* env, jobject, jlong paintHandle, jint flags) {
+        Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
         // Instead of modifying 0x02, change the filter level.
         nativePaint->setFilterQuality(flags & sFilterBitmapFlag
                 ? kLow_SkFilterQuality
@@ -174,57 +149,47 @@
         nativePaint->setFlags(flags);
     }
 
-    static jint getHinting(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return getNativePaint(env, paint)->getHinting()
+    static jint getHinting(JNIEnv* env, jobject, jlong paintHandle) {
+        return reinterpret_cast<Paint*>(paintHandle)->getHinting()
                 == Paint::kNo_Hinting ? 0 : 1;
     }
 
-    static void setHinting(JNIEnv* env, jobject paint, jint mode) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setHinting(
+    static void setHinting(JNIEnv* env, jobject, jlong paintHandle, jint mode) {
+        reinterpret_cast<Paint*>(paintHandle)->setHinting(
                 mode == 0 ? Paint::kNo_Hinting : Paint::kNormal_Hinting);
     }
 
-    static void setAntiAlias(JNIEnv* env, jobject paint, jboolean aa) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setAntiAlias(aa);
+    static void setAntiAlias(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) {
+        reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa);
     }
 
-    static void setLinearText(JNIEnv* env, jobject paint, jboolean linearText) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setLinearText(linearText);
+    static void setLinearText(JNIEnv* env, jobject, jlong paintHandle, jboolean linearText) {
+        reinterpret_cast<Paint*>(paintHandle)->setLinearText(linearText);
     }
 
-    static void setSubpixelText(JNIEnv* env, jobject paint, jboolean subpixelText) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setSubpixelText(subpixelText);
+    static void setSubpixelText(JNIEnv* env, jobject, jlong paintHandle, jboolean subpixelText) {
+        reinterpret_cast<Paint*>(paintHandle)->setSubpixelText(subpixelText);
     }
 
-    static void setUnderlineText(JNIEnv* env, jobject paint, jboolean underlineText) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setUnderlineText(underlineText);
+    static void setUnderlineText(JNIEnv* env, jobject, jlong paintHandle, jboolean underlineText) {
+        reinterpret_cast<Paint*>(paintHandle)->setUnderlineText(underlineText);
     }
 
-    static void setStrikeThruText(JNIEnv* env, jobject paint, jboolean strikeThruText) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setStrikeThruText(strikeThruText);
+    static void setStrikeThruText(JNIEnv* env, jobject, jlong paintHandle, jboolean strikeThruText) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrikeThruText(strikeThruText);
     }
 
-    static void setFakeBoldText(JNIEnv* env, jobject paint, jboolean fakeBoldText) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setFakeBoldText(fakeBoldText);
+    static void setFakeBoldText(JNIEnv* env, jobject, jlong paintHandle, jboolean fakeBoldText) {
+        reinterpret_cast<Paint*>(paintHandle)->setFakeBoldText(fakeBoldText);
     }
 
-    static void setFilterBitmap(JNIEnv* env, jobject paint, jboolean filterBitmap) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setFilterQuality(
+    static void setFilterBitmap(JNIEnv* env, jobject, jlong paintHandle, jboolean filterBitmap) {
+        reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
                 filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
     }
 
-    static void setDither(JNIEnv* env, jobject paint, jboolean dither) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setDither(dither);
+    static void setDither(JNIEnv* env, jobject, jlong paintHandle, jboolean dither) {
+        reinterpret_cast<Paint*>(paintHandle)->setDither(dither);
     }
 
     static jint getStyle(JNIEnv* env, jobject clazz,jlong objHandle) {
@@ -238,48 +203,40 @@
         obj->setStyle(style);
     }
 
-    static jint getColor(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jint getColor(JNIEnv* env, jobject, jlong paintHandle) {
         int color;
-        color = getNativePaint(env, paint)->getColor();
+        color = reinterpret_cast<Paint*>(paintHandle)->getColor();
         return static_cast<jint>(color);
     }
 
-    static jint getAlpha(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jint getAlpha(JNIEnv* env, jobject, jlong paintHandle) {
         int alpha;
-        alpha = getNativePaint(env, paint)->getAlpha();
+        alpha = reinterpret_cast<Paint*>(paintHandle)->getAlpha();
         return static_cast<jint>(alpha);
     }
 
-    static void setColor(JNIEnv* env, jobject paint, jint color) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setColor(color);
+    static void setColor(JNIEnv* env, jobject, jlong paintHandle, jint color) {
+        reinterpret_cast<Paint*>(paintHandle)->setColor(color);
     }
 
-    static void setAlpha(JNIEnv* env, jobject paint, jint a) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setAlpha(a);
+    static void setAlpha(JNIEnv* env, jobject, jlong paintHandle, jint a) {
+        reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
     }
 
-    static jfloat getStrokeWidth(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return SkScalarToFloat(getNativePaint(env, paint)->getStrokeWidth());
+    static jfloat getStrokeWidth(JNIEnv* env, jobject, jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth());
     }
 
-    static void setStrokeWidth(JNIEnv* env, jobject paint, jfloat width) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setStrokeWidth(width);
+    static void setStrokeWidth(JNIEnv* env, jobject, jlong paintHandle, jfloat width) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width);
     }
 
-    static jfloat getStrokeMiter(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return SkScalarToFloat(getNativePaint(env, paint)->getStrokeMiter());
+    static jfloat getStrokeMiter(JNIEnv* env, jobject, jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter());
     }
 
-    static void setStrokeMiter(JNIEnv* env, jobject paint, jfloat miter) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setStrokeMiter(miter);
+    static void setStrokeMiter(JNIEnv* env, jobject, jlong paintHandle, jfloat miter) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter);
     }
 
     static jint getStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle) {
@@ -416,46 +373,38 @@
         obj->setTextLocale(sSingleEntryLocaleCache.languageTag);
     }
 
-    static jboolean isElegantTextHeight(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        Paint* obj = getNativePaint(env, paint);
+    static jboolean isElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(paintHandle);
         return obj->getFontVariant() == VARIANT_ELEGANT;
     }
 
-    static void setElegantTextHeight(JNIEnv* env, jobject paint, jboolean aa) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        Paint* obj = getNativePaint(env, paint);
+    static void setElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) {
+        Paint* obj = reinterpret_cast<Paint*>(paintHandle);
         obj->setFontVariant(aa ? VARIANT_ELEGANT : VARIANT_DEFAULT);
     }
 
-    static jfloat getTextSize(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return SkScalarToFloat(getNativePaint(env, paint)->getTextSize());
+    static jfloat getTextSize(JNIEnv* env, jobject, jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSize());
     }
 
-    static void setTextSize(JNIEnv* env, jobject paint, jfloat textSize) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setTextSize(textSize);
+    static void setTextSize(JNIEnv* env, jobject, jlong paintHandle, jfloat textSize) {
+        reinterpret_cast<Paint*>(paintHandle)->setTextSize(textSize);
     }
 
-    static jfloat getTextScaleX(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return SkScalarToFloat(getNativePaint(env, paint)->getTextScaleX());
+    static jfloat getTextScaleX(JNIEnv* env, jobject, jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextScaleX());
     }
 
-    static void setTextScaleX(JNIEnv* env, jobject paint, jfloat scaleX) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setTextScaleX(scaleX);
+    static void setTextScaleX(JNIEnv* env, jobject, jlong paintHandle, jfloat scaleX) {
+        reinterpret_cast<Paint*>(paintHandle)->setTextScaleX(scaleX);
     }
 
-    static jfloat getTextSkewX(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return SkScalarToFloat(getNativePaint(env, paint)->getTextSkewX());
+    static jfloat getTextSkewX(JNIEnv* env, jobject, jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSkewX());
     }
 
-    static void setTextSkewX(JNIEnv* env, jobject paint, jfloat skewX) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setTextSkewX(skewX);
+    static void setTextSkewX(JNIEnv* env, jobject, jlong paintHandle, jfloat skewX) {
+        reinterpret_cast<Paint*>(paintHandle)->setTextSkewX(skewX);
     }
 
     static jfloat getLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle) {
@@ -488,14 +437,15 @@
         paint->setHyphenEdit((uint32_t)hyphen);
     }
 
-    static SkScalar getMetricsInternal(JNIEnv* env, jobject jpaint, Paint::FontMetrics *metrics) {
+    static SkScalar getMetricsInternal(jlong paintHandle, jlong typefaceHandle,
+            Paint::FontMetrics *metrics) {
         const int kElegantTop = 2500;
         const int kElegantBottom = -1000;
         const int kElegantAscent = 1900;
         const int kElegantDescent = -500;
         const int kElegantLeading = 0;
-        Paint* paint = getNativePaint(env, jpaint);
-        TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
         typeface = TypefaceImpl_resolveDefault(typeface);
         FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
         float saveSkewX = paint->getTextSkewX();
@@ -519,24 +469,22 @@
         return spacing;
     }
 
-    static jfloat ascent(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jfloat ascent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) {
         Paint::FontMetrics metrics;
-        getMetricsInternal(env, paint, &metrics);
+        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
         return SkScalarToFloat(metrics.fAscent);
     }
 
-    static jfloat descent(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jfloat descent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) {
         Paint::FontMetrics metrics;
-        getMetricsInternal(env, paint, &metrics);
+        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
         return SkScalarToFloat(metrics.fDescent);
     }
 
-    static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle,
+            jlong typefaceHandle, jobject metricsObj) {
         Paint::FontMetrics metrics;
-        SkScalar spacing = getMetricsInternal(env, paint, &metrics);
+        SkScalar spacing = getMetricsInternal(paintHandle, typefaceHandle, &metrics);
 
         if (metricsObj) {
             SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
@@ -549,11 +497,11 @@
         return SkScalarToFloat(spacing);
     }
 
-    static jint getFontMetricsInt(JNIEnv* env, jobject paint, jobject metricsObj) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle,
+            jlong typefaceHandle, jobject metricsObj) {
         Paint::FontMetrics metrics;
 
-        getMetricsInternal(env, paint, &metrics);
+        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
         int ascent = SkScalarRoundToInt(metrics.fAscent);
         int descent = SkScalarRoundToInt(metrics.fDescent);
         int leading = SkScalarRoundToInt(metrics.fLeading);
@@ -569,138 +517,9 @@
         return descent - ascent + leading;
     }
 
-    static jfloat measureText_CIII(JNIEnv* env, jobject jpaint, jcharArray text, jint index, jint count,
-            jint bidiFlags) {
-        NPE_CHECK_RETURN_ZERO(env, jpaint);
-        NPE_CHECK_RETURN_ZERO(env, text);
-
-        size_t textLength = env->GetArrayLength(text);
-        if ((index | count) < 0 || (size_t)(index + count) > textLength) {
-            doThrowAIOOBE(env);
-            return 0;
-        }
-        if (count == 0) {
-            return 0;
-        }
-
-        Paint* paint = getNativePaint(env, jpaint);
-        const jchar* textArray = env->GetCharArrayElements(text, NULL);
-        jfloat result = 0;
-
-        Layout layout;
-        TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
-        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray + index, 0, count,
-                count);
-        result = layout.getAdvance();
-        env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
-        return result;
-    }
-
-    static jfloat measureText_StringIII(JNIEnv* env, jobject jpaint, jstring text, jint start, jint end,
-            jint bidiFlags) {
-        NPE_CHECK_RETURN_ZERO(env, jpaint);
-        NPE_CHECK_RETURN_ZERO(env, text);
-
-        size_t textLength = env->GetStringLength(text);
-        int count = end - start;
-        if ((start | count) < 0 || (size_t)end > textLength) {
-            doThrowAIOOBE(env);
-            return 0;
-        }
-        if (count == 0) {
-            return 0;
-        }
-
-        const jchar* textArray = env->GetStringChars(text, NULL);
-        Paint* paint = getNativePaint(env, jpaint);
-        jfloat width = 0;
-
-        Layout layout;
-        TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
-        // Only the substring is used for measurement, so no additional context is passed in. This
-        // behavior is consistent between char[] and String specializations.
-        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray + start, 0, count, count);
-        width = layout.getAdvance();
-
-        env->ReleaseStringChars(text, textArray);
-        return width;
-    }
-
-    static jfloat measureText_StringI(JNIEnv* env, jobject jpaint, jstring text, jint bidiFlags) {
-        NPE_CHECK_RETURN_ZERO(env, jpaint);
-        NPE_CHECK_RETURN_ZERO(env, text);
-
-        size_t textLength = env->GetStringLength(text);
-        if (textLength == 0) {
-            return 0;
-        }
-
-        const jchar* textArray = env->GetStringChars(text, NULL);
-        Paint* paint = getNativePaint(env, jpaint);
-        jfloat width = 0;
-
-        Layout layout;
-        TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
-        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray, 0, textLength, textLength);
-        width = layout.getAdvance();
-
-        env->ReleaseStringChars(text, textArray);
-        return width;
-    }
-
-    static int dotextwidths(JNIEnv* env, Paint* paint, TypefaceImpl* typeface, const jchar text[], int count,
-            jfloatArray widths, jint bidiFlags) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        NPE_CHECK_RETURN_ZERO(env, text);
-
-        if (count < 0 || !widths) {
-            doThrowAIOOBE(env);
-            return 0;
-        }
-        if (count == 0) {
-            return 0;
-        }
-        size_t widthsLength = env->GetArrayLength(widths);
-        if ((size_t)count > widthsLength) {
-            doThrowAIOOBE(env);
-            return 0;
-        }
-
-        AutoJavaFloatArray autoWidths(env, widths, count);
-        jfloat* widthsArray = autoWidths.ptr();
-
-        Layout layout;
-        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, text, 0, count, count);
-        layout.getAdvances(widthsArray);
-
-        return count;
-    }
-
-    static jint getTextWidths___CIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jcharArray text,
-            jint index, jint count, jint bidiFlags, jfloatArray widths) {
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-        const jchar* textArray = env->GetCharArrayElements(text, NULL);
-        count = dotextwidths(env, paint, typeface, textArray + index, count, widths, bidiFlags);
-        env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
-                                      JNI_ABORT);
-        return count;
-    }
-
-    static jint getTextWidths__StringIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring text,
-            jint start, jint end, jint bidiFlags, jfloatArray widths) {
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
-        const jchar* textArray = env->GetStringChars(text, NULL);
-        int count = dotextwidths(env, paint, typeface, textArray + start, end - start, widths, bidiFlags);
-        env->ReleaseStringChars(text, textArray);
-        return count;
-    }
-
-    static jfloat doTextRunAdvances(JNIEnv *env, Paint *paint, TypefaceImpl* typeface, const jchar *text,
-                                    jint start, jint count, jint contextCount, jboolean isRtl,
-                                    jfloatArray advances, jint advancesIndex) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jfloat doTextAdvances(JNIEnv *env, Paint *paint, TypefaceImpl* typeface,
+            const jchar *text, jint start, jint count, jint contextCount, jint bidiFlags,
+            jfloatArray advances, jint advancesIndex) {
         NPE_CHECK_RETURN_ZERO(env, text);
 
         if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) {
@@ -712,50 +531,45 @@
         }
         if (advances) {
             size_t advancesLength = env->GetArrayLength(advances);
-            if ((size_t)count > advancesLength) {
+            if ((size_t)(count  + advancesIndex) > advancesLength) {
                 doThrowAIOOBE(env);
                 return 0;
             }
         }
-        jfloat* advancesArray = new jfloat[count];
-        jfloat totalAdvance = 0;
-
-        int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
 
         Layout layout;
-        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, text, start, count, contextCount);
-        layout.getAdvances(advancesArray);
-        totalAdvance = layout.getAdvance();
-
+        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, text, start, count,
+                contextCount);
         if (advances != NULL) {
-            env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray);
+            std::unique_ptr<jfloat> advancesArray(new jfloat[count]);
+            layout.getAdvances(advancesArray.get());
+            env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
         }
-        delete [] advancesArray;
-        return totalAdvance;
+        return layout.getAdvance();
     }
 
-    static jfloat getTextRunAdvances___CIIIIZ_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
+    static jfloat getTextAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
             jlong typefaceHandle,
             jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
-            jboolean isRtl, jfloatArray advances, jint advancesIndex) {
+            jint bidiFlags, jfloatArray advances, jint advancesIndex) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
         jchar* textArray = env->GetCharArrayElements(text, NULL);
-        jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextIndex,
-                index - contextIndex, count, contextCount, isRtl, advances, advancesIndex);
+        jfloat result = doTextAdvances(env, paint, typeface, textArray + contextIndex,
+                index - contextIndex, count, contextCount, bidiFlags, advances, advancesIndex);
         env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
         return result;
     }
 
-    static jfloat getTextRunAdvances__StringIIIIZ_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
+    static jfloat getTextAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
             jlong typefaceHandle,
-            jstring text, jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl,
+            jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint bidiFlags,
             jfloatArray advances, jint advancesIndex) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
         const jchar* textArray = env->GetStringChars(text, NULL);
-        jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextStart,
-                start - contextStart, end - start, contextEnd - contextStart, isRtl,
+        jfloat result = doTextAdvances(env, paint, typeface, textArray + contextStart,
+                start - contextStart, end - start, contextEnd - contextStart, bidiFlags,
                 advances, advancesIndex);
         env->ReleaseStringChars(text, textArray);
         return result;
@@ -973,7 +787,7 @@
 
     static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle,
                                 jstring text, jint start, jint end, jint bidiFlags, jobject bounds) {
-        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);;
+        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
         const jchar* textArray = env->GetStringChars(text, NULL);
         doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
@@ -1093,102 +907,97 @@
         return result;
     }
 
-};
+}; // namespace PaintGlue
 
 static const JNINativeMethod methods[] = {
-    {"finalizer", "(J)V", (void*) PaintGlue::finalizer},
-    {"native_init","()J", (void*) PaintGlue::init},
-    {"native_initWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
+    {"nFinalizer", "(J)V", (void*) PaintGlue::finalizer},
+    {"nInit","()J", (void*) PaintGlue::init},
+    {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
 
-    {"native_reset","!(J)V", (void*) PaintGlue::reset},
-    {"native_set","!(JJ)V", (void*) PaintGlue::assign},
-    {"getFlags","!()I", (void*) PaintGlue::getFlags},
-    {"setFlags","!(I)V", (void*) PaintGlue::setFlags},
-    {"getHinting","!()I", (void*) PaintGlue::getHinting},
-    {"setHinting","!(I)V", (void*) PaintGlue::setHinting},
-    {"setAntiAlias","!(Z)V", (void*) PaintGlue::setAntiAlias},
-    {"setSubpixelText","!(Z)V", (void*) PaintGlue::setSubpixelText},
-    {"setLinearText","!(Z)V", (void*) PaintGlue::setLinearText},
-    {"setUnderlineText","!(Z)V", (void*) PaintGlue::setUnderlineText},
-    {"setStrikeThruText","!(Z)V", (void*) PaintGlue::setStrikeThruText},
-    {"setFakeBoldText","!(Z)V", (void*) PaintGlue::setFakeBoldText},
-    {"setFilterBitmap","!(Z)V", (void*) PaintGlue::setFilterBitmap},
-    {"setDither","!(Z)V", (void*) PaintGlue::setDither},
-    {"native_getStyle","!(J)I", (void*) PaintGlue::getStyle},
-    {"native_setStyle","!(JI)V", (void*) PaintGlue::setStyle},
-    {"getColor","!()I", (void*) PaintGlue::getColor},
-    {"setColor","!(I)V", (void*) PaintGlue::setColor},
-    {"getAlpha","!()I", (void*) PaintGlue::getAlpha},
-    {"setAlpha","!(I)V", (void*) PaintGlue::setAlpha},
-    {"getStrokeWidth","!()F", (void*) PaintGlue::getStrokeWidth},
-    {"setStrokeWidth","!(F)V", (void*) PaintGlue::setStrokeWidth},
-    {"getStrokeMiter","!()F", (void*) PaintGlue::getStrokeMiter},
-    {"setStrokeMiter","!(F)V", (void*) PaintGlue::setStrokeMiter},
-    {"native_getStrokeCap","!(J)I", (void*) PaintGlue::getStrokeCap},
-    {"native_setStrokeCap","!(JI)V", (void*) PaintGlue::setStrokeCap},
-    {"native_getStrokeJoin","!(J)I", (void*) PaintGlue::getStrokeJoin},
-    {"native_setStrokeJoin","!(JI)V", (void*) PaintGlue::setStrokeJoin},
-    {"native_getFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath},
-    {"native_setShader","!(JJ)J", (void*) PaintGlue::setShader},
-    {"native_setColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter},
-    {"native_setXfermode","!(JJ)J", (void*) PaintGlue::setXfermode},
-    {"native_setPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect},
-    {"native_setMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter},
-    {"native_setTypeface","!(JJ)J", (void*) PaintGlue::setTypeface},
-    {"native_setRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
-    {"native_getTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
-    {"native_setTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
-    {"native_setTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale},
-    {"isElegantTextHeight","!()Z", (void*) PaintGlue::isElegantTextHeight},
-    {"setElegantTextHeight","!(Z)V", (void*) PaintGlue::setElegantTextHeight},
-    {"getTextSize","!()F", (void*) PaintGlue::getTextSize},
-    {"setTextSize","!(F)V", (void*) PaintGlue::setTextSize},
-    {"getTextScaleX","!()F", (void*) PaintGlue::getTextScaleX},
-    {"setTextScaleX","!(F)V", (void*) PaintGlue::setTextScaleX},
-    {"getTextSkewX","!()F", (void*) PaintGlue::getTextSkewX},
-    {"setTextSkewX","!(F)V", (void*) PaintGlue::setTextSkewX},
-    {"native_getLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing},
-    {"native_setLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing},
-    {"native_setFontFeatureSettings","(JLjava/lang/String;)V",
+    {"nReset","!(J)V", (void*) PaintGlue::reset},
+    {"nSet","!(JJ)V", (void*) PaintGlue::assign},
+    {"nGetFlags","!(J)I", (void*) PaintGlue::getFlags},
+    {"nSetFlags","!(JI)V", (void*) PaintGlue::setFlags},
+    {"nGetHinting","!(J)I", (void*) PaintGlue::getHinting},
+    {"nSetHinting","!(JI)V", (void*) PaintGlue::setHinting},
+    {"nSetAntiAlias","!(JZ)V", (void*) PaintGlue::setAntiAlias},
+    {"nSetSubpixelText","!(JZ)V", (void*) PaintGlue::setSubpixelText},
+    {"nSetLinearText","!(JZ)V", (void*) PaintGlue::setLinearText},
+    {"nSetUnderlineText","!(JZ)V", (void*) PaintGlue::setUnderlineText},
+    {"nSetStrikeThruText","!(JZ)V", (void*) PaintGlue::setStrikeThruText},
+    {"nSetFakeBoldText","!(JZ)V", (void*) PaintGlue::setFakeBoldText},
+    {"nSetFilterBitmap","!(JZ)V", (void*) PaintGlue::setFilterBitmap},
+    {"nSetDither","!(JZ)V", (void*) PaintGlue::setDither},
+    {"nGetStyle","!(J)I", (void*) PaintGlue::getStyle},
+    {"nSetStyle","!(JI)V", (void*) PaintGlue::setStyle},
+    {"nGetColor","!(J)I", (void*) PaintGlue::getColor},
+    {"nSetColor","!(JI)V", (void*) PaintGlue::setColor},
+    {"nGetAlpha","!(J)I", (void*) PaintGlue::getAlpha},
+    {"nSetAlpha","!(JI)V", (void*) PaintGlue::setAlpha},
+    {"nGetStrokeWidth","!(J)F", (void*) PaintGlue::getStrokeWidth},
+    {"nSetStrokeWidth","!(JF)V", (void*) PaintGlue::setStrokeWidth},
+    {"nGetStrokeMiter","!(J)F", (void*) PaintGlue::getStrokeMiter},
+    {"nSetStrokeMiter","!(JF)V", (void*) PaintGlue::setStrokeMiter},
+    {"nGetStrokeCap","!(J)I", (void*) PaintGlue::getStrokeCap},
+    {"nSetStrokeCap","!(JI)V", (void*) PaintGlue::setStrokeCap},
+    {"nGetStrokeJoin","!(J)I", (void*) PaintGlue::getStrokeJoin},
+    {"nSetStrokeJoin","!(JI)V", (void*) PaintGlue::setStrokeJoin},
+    {"nGetFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath},
+    {"nSetShader","!(JJ)J", (void*) PaintGlue::setShader},
+    {"nSetColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter},
+    {"nSetXfermode","!(JJ)J", (void*) PaintGlue::setXfermode},
+    {"nSetPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect},
+    {"nSetMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter},
+    {"nSetTypeface","!(JJ)J", (void*) PaintGlue::setTypeface},
+    {"nSetRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
+    {"nGetTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
+    {"nSetTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
+    {"nSetTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale},
+    {"nIsElegantTextHeight","!(J)Z", (void*) PaintGlue::isElegantTextHeight},
+    {"nSetElegantTextHeight","!(JZ)V", (void*) PaintGlue::setElegantTextHeight},
+    {"nGetTextSize","!(J)F", (void*) PaintGlue::getTextSize},
+    {"nSetTextSize","!(JF)V", (void*) PaintGlue::setTextSize},
+    {"nGetTextScaleX","!(J)F", (void*) PaintGlue::getTextScaleX},
+    {"nSetTextScaleX","!(JF)V", (void*) PaintGlue::setTextScaleX},
+    {"nGetTextSkewX","!(J)F", (void*) PaintGlue::getTextSkewX},
+    {"nSetTextSkewX","!(JF)V", (void*) PaintGlue::setTextSkewX},
+    {"nGetLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing},
+    {"nSetLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing},
+    {"nSetFontFeatureSettings","(JLjava/lang/String;)V",
             (void*) PaintGlue::setFontFeatureSettings},
-    {"native_getHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit},
-    {"native_setHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit},
-    {"ascent","!()F", (void*) PaintGlue::ascent},
-    {"descent","!()F", (void*) PaintGlue::descent},
+    {"nGetHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit},
+    {"nSetHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit},
+    {"nAscent","!(JJ)F", (void*) PaintGlue::ascent},
+    {"nDescent","!(JJ)F", (void*) PaintGlue::descent},
 
-    {"getFontMetrics", "!(Landroid/graphics/Paint$FontMetrics;)F",
+    {"nGetFontMetrics", "!(JJLandroid/graphics/Paint$FontMetrics;)F",
             (void*)PaintGlue::getFontMetrics},
-    {"getFontMetricsInt", "!(Landroid/graphics/Paint$FontMetricsInt;)I",
+    {"nGetFontMetricsInt", "!(JJLandroid/graphics/Paint$FontMetricsInt;)I",
             (void*)PaintGlue::getFontMetricsInt},
-    {"native_measureText","([CIII)F", (void*) PaintGlue::measureText_CIII},
-    {"native_measureText","(Ljava/lang/String;I)F", (void*) PaintGlue::measureText_StringI},
-    {"native_measureText","(Ljava/lang/String;III)F", (void*) PaintGlue::measureText_StringIII},
-    {"native_breakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC},
-    {"native_breakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
-    {"native_getTextWidths","(JJ[CIII[F)I", (void*) PaintGlue::getTextWidths___CIII_F},
-    {"native_getTextWidths","(JJLjava/lang/String;III[F)I",
-            (void*) PaintGlue::getTextWidths__StringIII_F},
-    {"native_getTextRunAdvances","(JJ[CIIIIZ[FI)F",
-            (void*) PaintGlue::getTextRunAdvances___CIIIIZ_FI},
-    {"native_getTextRunAdvances","(JJLjava/lang/String;IIIIZ[FI)F",
-            (void*) PaintGlue::getTextRunAdvances__StringIIIIZ_FI},
 
-    {"native_getTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
-    {"native_getTextRunCursor", "(JLjava/lang/String;IIIII)I",
+    {"nBreakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC},
+    {"nBreakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
+    {"nGetTextAdvances","(JJ[CIIIII[FI)F",
+            (void*) PaintGlue::getTextAdvances___CIIIII_FI},
+    {"nGetTextAdvances","(JJLjava/lang/String;IIIII[FI)F",
+            (void*) PaintGlue::getTextAdvances__StringIIIII_FI},
+
+    {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
+    {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
             (void*) PaintGlue::getTextRunCursor__String},
-    {"native_getTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
-    {"native_getTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
-    {"nativeGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V",
+    {"nGetTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
+    {"nGetTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
+    {"nGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V",
             (void*) PaintGlue::getStringBounds },
-    {"nativeGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V",
+    {"nGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V",
             (void*) PaintGlue::getCharArrayBounds },
-    {"native_hasGlyph", "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
-    {"native_getRunAdvance", "(JJ[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
-    {"native_getOffsetForAdvance", "(JJ[CIIIIZF)I",
+    {"nHasGlyph", "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
+    {"nGetRunAdvance", "(JJ[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
+    {"nGetOffsetForAdvance", "(JJ[CIIIIZF)I",
             (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
 
-    {"native_setShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer},
-    {"native_hasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer}
+    {"nSetShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer},
+    {"nHasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer}
 };
 
 int register_android_graphics_Paint(JNIEnv* env) {
@@ -1210,10 +1019,6 @@
     gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
     gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
 
-    gPaint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Paint"));
-    gPaint_nativeInstanceID = GetFieldIDOrDie(env, gPaint_class, "mNativePaint", "J");
-    gPaint_nativeTypefaceID = GetFieldIDOrDie(env, gPaint_class, "mNativeTypeface", "J");
-
     return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
 }
 
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index a151e00..83f76ea 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -117,9 +117,17 @@
     b->finish();
 }
 
-static jlong nLoadHyphenator(JNIEnv* env, jclass, jstring patternData) {
-    ScopedStringChars str(env, patternData);
-    Hyphenator* hyphenator = Hyphenator::load(str.get(), str.size());
+static jlong nLoadHyphenator(JNIEnv* env, jclass, jobject buffer, jint offset) {
+    const uint8_t* bytebuf = nullptr;
+    if (buffer != nullptr) {
+        void* rawbuf = env->GetDirectBufferAddress(buffer);
+        if (rawbuf != nullptr) {
+            bytebuf = reinterpret_cast<const uint8_t*>(rawbuf) + offset;
+        } else {
+            ALOGE("failed to get direct buffer address");
+        }
+    }
+    Hyphenator* hyphenator = Hyphenator::loadBinary(bytebuf);
     return reinterpret_cast<jlong>(hyphenator);
 }
 
@@ -177,7 +185,7 @@
     {"nNewBuilder", "()J", (void*) nNewBuilder},
     {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
     {"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
-    {"nLoadHyphenator", "(Ljava/lang/String;)J", (void*) nLoadHyphenator},
+    {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;I)J", (void*) nLoadHyphenator},
     {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
     {"nSetupParagraph", "(J[CIFIF[IIII)V", (void*) nSetupParagraph},
     {"nSetIndents", "(J[I)V", (void*) nSetIndents},
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index c79f833..17eb876 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -461,10 +461,10 @@
     proxy->drawRenderNode(renderNode);
 }
 
-static void android_view_ThreadedRenderer_setContentOverdrawProtectionBounds(JNIEnv* env,
+static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env,
         jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->setContentOverdrawProtectionBounds(left, top, right, bottom);
+    proxy->setContentDrawBounds(left, top, right, bottom);
 }
 
 // ----------------------------------------------------------------------------
@@ -522,8 +522,7 @@
     { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
     { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
     { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
-    { "nSetContentOverdrawProtectionBounds", "(JIIII)V",
-                (void*)android_view_ThreadedRenderer_setContentOverdrawProtectionBounds},
+    { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds},
 };
 
 int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/res/res/color/btn_colored_text_material.xml b/core/res/res/color/btn_colored_text_material.xml
index 23d05a9..c80fea6 100644
--- a/core/res/res/color/btn_colored_text_material.xml
+++ b/core/res/res/color/btn_colored_text_material.xml
@@ -18,6 +18,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false"
           android:alpha="?attr/disabledAlpha"
-          android:color="?attr/textColorSecondary" />
-    <item android:color="?attr/textColorSecondaryInverse" />
+          android:color="?attr/textColorPrimary" />
+    <item android:color="?attr/textColorPrimaryInverse" />
 </selector>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index a08f040..2f1627b 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -227,23 +227,23 @@
     <string name="user_owner_label" msgid="2804351898001038951">"Persoonlik"</string>
     <string name="managed_profile_label" msgid="6260850669674791528">"Werk"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakte"</string>
-    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"gaan by jou kontakte in"</string>
+    <string name="permgroupdesc_contacts" msgid="6951499528303668046">"in te gaan by jou kontakte"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Ligging"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"verkry toegang tot hierdie toestel se ligging"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"toegang te verkry tot hierdie toestel se ligging"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
-    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"gaan by jou kalender in"</string>
+    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"by jou kalender in te gaan"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
-    <string name="permgroupdesc_sms" msgid="4656988620100940350">"stuur en bekyk SMS-boodskappe"</string>
+    <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS-boodskappe te stuur en te bekyk"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Stoor"</string>
-    <string name="permgroupdesc_storage" msgid="637758554581589203">"verkry toegang tot foto\'s, media en lêers op jou toestel"</string>
+    <string name="permgroupdesc_storage" msgid="637758554581589203">"toegang te verkry tot foto\'s, media en lêers op jou toestel"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofoon"</string>
-    <string name="permgroupdesc_microphone" msgid="4988812113943554584">"neem oudio op"</string>
+    <string name="permgroupdesc_microphone" msgid="4988812113943554584">"oudio op te neem"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
-    <string name="permgroupdesc_camera" msgid="3250611594678347720">"neem foto\'s en neem video op"</string>
+    <string name="permgroupdesc_camera" msgid="3250611594678347720">"foto\'s en video te neem"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Foon"</string>
-    <string name="permgroupdesc_phone" msgid="6234224354060641055">"maak en bestuur foonoproepe"</string>
+    <string name="permgroupdesc_phone" msgid="6234224354060641055">"foonoproepe te maak en te bestuur"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Liggaamsensors"</string>
-    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"kry toegang tot sensordata oor jou lewenstekens"</string>
+    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"toegang te verkry tot sensordata oor jou lewenstekens"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Haal venster-inhoud op"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Ondersoek die inhoud van \'n venster waarmee jy interaksie het."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Skakel Verken deur raak aan"</string>
@@ -272,7 +272,7 @@
     <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Laat die program toe om seluitsending-boodskappe te lees wat deur jou toestel ontvang word. Seluitsending-waarskuwings word in sommige plekke afgelewer om jou van noodsituasies te waarsku. Kwaadwillige programme mag inmeng met die prestasie of die werking van jou toestel wanneer \'n noodgeval se seluitsending ontvang word."</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lees ingetekende nuus"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Laat die program toe om details oor die tans gesinkroniseerde strome te kry."</string>
-    <string name="permlab_sendSms" msgid="7544599214260982981">"stuur en bekyk SMS-boodskappe"</string>
+    <string name="permlab_sendSms" msgid="7544599214260982981">"SMS-boodskappe te stuur en te bekyk"</string>
     <string name="permdesc_sendSms" msgid="7094729298204937667">"Laat die program toe om SMS-boodskappe te stuur. Dit kan tot onverwagse heffings lei. Kwaadwillige programme kan jou geld kos deur boodskappe sonder jou bevestiging te stuur."</string>
     <string name="permlab_readSms" msgid="8745086572213270480">"lees jou teksboodskappe (SMS of MMS)"</string>
     <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Laat die program toe om SMS-boodskappe wat op jou tablet of SIM-kaart gestoor is, te lees. Dit laat die program toe om alle SMS-boodskappe te lees, ongeag van die inhoud of vertroulikheid daarvan."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index af80ed3..0621bbe 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -915,7 +915,7 @@
     <string name="anr_application_process" msgid="8941757607340481057">"Приложение \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" не отвечает. Закрыть его?"</string>
     <string name="anr_process" msgid="6513209874880517125">"Приложение \"<xliff:g id="PROCESS">%1$s</xliff:g>\" не отвечает.\n\nЗакрыть его?"</string>
     <string name="force_close" msgid="8346072094521265605">"ОК"</string>
-    <string name="report" msgid="4060218260984795706">"Создать отчет"</string>
+    <string name="report" msgid="4060218260984795706">"Отправить отчет"</string>
     <string name="wait" msgid="7147118217226317732">"Подождать"</string>
     <string name="webpage_unresponsive" msgid="3272758351138122503">"Страница не отвечает.\n\nЗакрыть ее?"</string>
     <string name="launch_warning_title" msgid="1547997780506713581">"Приложение перенаправлено"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 488470f..29fd6a1 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1235,7 +1235,7 @@
     <string name="activity_resolver_use_once" msgid="2404644797149173758">"Bara en gång"</string>
     <string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"%1$s har inte stöd för arbetsprofil"</string>
     <string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Surfplatta"</string>
-    <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"Tv"</string>
+    <string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
     <string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Mobil"</string>
     <string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Hörlurar"</string>
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dockningsstationens högtalare"</string>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 38a1693..58640eb 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -307,9 +307,10 @@
     <style name="TextAppearance.Material.Widget.PopupMenu"/>
     <style name="TextAppearance.Material.Widget.PopupMenu.Large" parent="TextAppearance.Material.Menu" />
     <style name="TextAppearance.Material.Widget.PopupMenu.Small" parent="TextAppearance.Material.Menu" />
-    <style name="TextAppearance.Material.Widget.PopupMenu.Header" parent="TextAppearance.Material.Subhead">
+    <style name="TextAppearance.Material.Widget.PopupMenu.Header">
         <item name="fontFamily">@string/font_family_title_material</item>
         <item name="textSize">@dimen/text_size_menu_header_material</item>
+        <item name="textColor">?attr/textColorSecondary</item>
     </style>
 
     <style name="TextAppearance.Material.Widget.DropDownHint" parent="TextAppearance.Material.Menu" />
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index ce9ae02..4db1d9a 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -18,8 +18,12 @@
 
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
+import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.longPressAndDragOnText;
+import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
+import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
 import static android.widget.espresso.TextViewAssertions.hasSelection;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.action.ViewActions.pressKey;
@@ -59,6 +63,7 @@
         getActivity();
 
         final String helloWorld = "Hello world!";
+        onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
         onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("world")));
 
@@ -68,10 +73,39 @@
     }
 
     @SmallTest
+    public void testLongPressToSelect() throws Exception {
+        getActivity();
+
+        final String helloWorld = "Hello Kirk!";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+        onView(withId(R.id.textview)).perform(
+                longPressOnTextAtIndex(helloWorld.indexOf("Kirk")));
+
+        onView(withId(R.id.textview)).check(hasSelection("Kirk"));
+    }
+
+    @SmallTest
+    public void testLongPressEmptySpace() throws Exception {
+        getActivity();
+
+        final String helloWorld = "Hello big round sun!";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+        // Move cursor somewhere else
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("big")));
+        // Long-press at end of line.
+        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(helloWorld.length()));
+
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(helloWorld.length()));
+    }
+
+    @SmallTest
     public void testLongPressAndDragToSelect() throws Exception {
         getActivity();
 
         final String helloWorld = "Hello little handsome boy!";
+        onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
         onView(withId(R.id.textview)).perform(
                 longPressAndDragOnText(helloWorld.indexOf("little"), helloWorld.indexOf(" boy!")));
@@ -80,14 +114,60 @@
     }
 
     @SmallTest
+    public void testDoubleTapToSelect() throws Exception {
+        getActivity();
+
+        final String helloWorld = "Hello SuetYi!";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+        onView(withId(R.id.textview)).perform(
+                doubleClickOnTextAtIndex(helloWorld.indexOf("SuetYi")));
+
+        onView(withId(R.id.textview)).check(hasSelection("SuetYi"));
+    }
+
+    @SmallTest
     public void testDoubleTapAndDragToSelect() throws Exception {
         getActivity();
 
         final String helloWorld = "Hello young beautiful girl!";
+        onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
         onView(withId(R.id.textview)).perform(
                 doubleTapAndDragOnText(helloWorld.indexOf("young"), helloWorld.indexOf(" girl!")));
 
         onView(withId(R.id.textview)).check(hasSelection("young beautiful"));
     }
+
+    @SmallTest
+    public void testSelectBackwordsByTouch() throws Exception {
+        getActivity();
+
+        final String helloWorld = "Hello king of the Jungle!";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+        onView(withId(R.id.textview)).perform(
+                doubleTapAndDragOnText(helloWorld.indexOf(" Jungle!"), helloWorld.indexOf("king")));
+
+        onView(withId(R.id.textview)).check(hasSelection("king of the"));
+    }
+
+    @SmallTest
+    public void testToolbarAppearsAfterSelection() throws Exception {
+        getActivity();
+
+        // It'll be nice to check that the toolbar is not visible (or does not exist) here
+        // I can't currently find a way to do this. I'll get to it later.
+
+        final String text = "Toolbar appears after selection.";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+        onView(withId(R.id.textview)).perform(
+                longPressOnTextAtIndex(text.indexOf("appears")));
+
+        // It takes the toolbar less than 100ms to start to animate into screen.
+        // Ideally, we'll wait using the UiController, but I guess this works for now.
+        Thread.sleep(100);
+        assertFloatingToolbarIsDisplayed(getActivity());
+    }
 }
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
new file mode 100644
index 0000000..fc01d84
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withTagValue;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+
+import android.app.Activity;
+import com.android.internal.widget.FloatingToolbar;
+
+/**
+ * Espresso utility methods for the floating toolbar.
+ */
+public class FloatingToolbarEspressoUtils {
+
+
+    private FloatingToolbarEspressoUtils() {}
+
+    /**
+     * Asserts that the floating toolbar is displayed on screen.
+     *
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertFloatingToolbarIsDisplayed(Activity activity) {
+        onView(withTagValue(is((Object) FloatingToolbar.FLOATING_TOOLBAR_TAG)))
+                .inRoot(withDecorView(not(is(activity.getWindow().getDecorView()))))
+                .check(matches(isDisplayed()));
+    }
+
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index 7e4735b..835b1b9 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -52,6 +52,36 @@
     }
 
     /**
+     * Returns an action that double-clicks on text at an index on the TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param index The index of the TextView's text to double-click on.
+     */
+    public static ViewAction doubleClickOnTextAtIndex(int index) {
+        return actionWithAssertions(
+                new GeneralClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.FINGER));
+    }
+
+    /**
+     * Returns an action that long presses on text at an index on the TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param index The index of the TextView's text to long press on.
+     */
+    public static ViewAction longPressOnTextAtIndex(int index) {
+        return actionWithAssertions(
+                new GeneralClickAction(Tap.LONG, new TextCoordinates(index), Press.FINGER));
+    }
+
+    /**
      * Returns an action that long presses then drags on text from startIndex to endIndex on the
      * TextView.<br>
      * <br>
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
index dce3182..37c7425 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
@@ -47,7 +47,7 @@
      * @param selection  The expected selection.
      */
     public static ViewAssertion hasSelection(String selection) {
-        return new TextSelectionAssertion(is(selection));
+        return hasSelection(is(selection));
     }
 
     /**
@@ -66,6 +66,53 @@
     }
 
     /**
+     * Returns a {@link ViewAssertion} that asserts that the text view insertion pointer is at
+     * a specified index.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a text view displayed on screen
+     * <ul>
+     *
+     * @param index  The expected index.
+     */
+    public static ViewAssertion hasInsertionPointerAtIndex(int index) {
+        return hasInsertionPointerAtIndex(is(index));
+    }
+
+    /**
+     * Returns a {@link ViewAssertion} that asserts that the text view insertion pointer is at
+     * a specified index.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a text view displayed on screen
+     * <ul>
+     *
+     * @param index  A matcher representing the expected index.
+     */
+    public static ViewAssertion hasInsertionPointerAtIndex(final Matcher<Integer> index) {
+        return new ViewAssertion() {
+            @Override
+            public void check(View view, NoMatchingViewException exception) {
+                if (view instanceof TextView) {
+                    TextView textView = (TextView) view;
+                    int selectionStart = textView.getSelectionStart();
+                    int selectionEnd = textView.getSelectionEnd();
+                    try {
+                        assertThat(selectionStart, index);
+                        assertThat(selectionEnd, index);
+                    } catch (IndexOutOfBoundsException e) {
+                        throw new AssertionFailedError(e.getMessage());
+                    }
+                } else {
+                    throw new AssertionFailedError("TextView not found");
+                }
+            }
+        };
+    }
+
+    /**
      * A {@link ViewAssertion} to check the selected text in a {@link TextView}.
      */
     private static final class TextSelectionAssertion implements ViewAssertion {
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 6582b7e..11b4a9e 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -430,7 +430,7 @@
      * @param flags initial flag bits, as if they were passed via setFlags().
      */
     public Paint(int flags) {
-        mNativePaint = native_init();
+        mNativePaint = nInit();
         setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
         // TODO: Turning off hinting has undesirable side effects, we need to
         //       revisit hinting once we add support for subpixel positioning
@@ -448,13 +448,14 @@
      *              new paint.
      */
     public Paint(Paint paint) {
-        mNativePaint = native_initWithPaint(paint.getNativeInstance());
+        mNativePaint = nInitWithPaint(paint.getNativeInstance());
         setClassVariablesFrom(paint);
     }
 
     /** Restores the paint to its default settings. */
     public void reset() {
-        native_reset(mNativePaint);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nReset(mNativePaint);
         setFlags(HIDDEN_DEFAULT_PAINT_FLAGS);
 
         // TODO: Turning off hinting has undesirable side effects, we need to
@@ -488,9 +489,11 @@
      * methods on this.
      */
     public void set(Paint src) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        if (src.mNativePaint == 0) throw new NullPointerException("Source is already finalized!");
         if (this != src) {
             // copy over the native settings
-            native_set(mNativePaint, src.mNativePaint);
+            nSet(mNativePaint, src.mNativePaint);
             setClassVariablesFrom(src);
         }
     }
@@ -538,10 +541,11 @@
      * @hide
      */
     public long getNativeInstance() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance();
         if (newNativeShader != mNativeShader) {
             mNativeShader = newNativeShader;
-            native_setShader(mNativePaint, mNativeShader);
+            nSetShader(mNativePaint, mNativeShader);
         }
         return mNativePaint;
     }
@@ -574,26 +578,46 @@
      *
      * @return the paint's flags (see enums ending in _Flag for bit masks)
      */
-    public native int getFlags();
+    public int getFlags() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetFlags(mNativePaint);
+    }
+
+    private native int nGetFlags(long paintPtr);
 
     /**
      * Set the paint's flags. Use the Flag enum to specific flag values.
      *
      * @param flags The new flag bits for the paint
      */
-    public native void setFlags(int flags);
+    public void setFlags(int flags) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetFlags(mNativePaint, flags);
+    }
+
+    private native void nSetFlags(long paintPtr, int flags);
 
     /**
      * Return the paint's hinting mode.  Returns either
      * {@link #HINTING_OFF} or {@link #HINTING_ON}.
      */
-    public native int getHinting();
+    public int getHinting() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetHinting(mNativePaint);
+    }
+
+    private native int nGetHinting(long paintPtr);
 
     /**
      * Set the paint's hinting mode.  May be either
      * {@link #HINTING_OFF} or {@link #HINTING_ON}.
      */
-    public native void setHinting(int mode);
+    public void setHinting(int mode) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetHinting(mNativePaint, mode);
+    }
+
+    private native void nSetHinting(long paintPtr, int mode);
 
     /**
      * Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set
@@ -615,7 +639,12 @@
      *
      * @param aa true to set the antialias bit in the flags, false to clear it
      */
-    public native void setAntiAlias(boolean aa);
+    public void setAntiAlias(boolean aa) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetAntiAlias(mNativePaint, aa);
+    }
+
+    private native void nSetAntiAlias(long paintPtr, boolean aa);
 
     /**
      * Helper for getFlags(), returning true if DITHER_FLAG bit is set
@@ -641,7 +670,12 @@
      *
      * @param dither true to set the dithering bit in flags, false to clear it
      */
-    public native void setDither(boolean dither);
+    public void setDither(boolean dither) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetDither(mNativePaint, dither);
+    }
+
+    private native void nSetDither(long paintPtr, boolean dither);
 
     /**
      * Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set
@@ -658,7 +692,12 @@
      * @param linearText true to set the linearText bit in the paint's flags,
      *                   false to clear it.
      */
-    public native void setLinearText(boolean linearText);
+    public void setLinearText(boolean linearText) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetLinearText(mNativePaint, linearText);
+    }
+
+    private native void nSetLinearText(long paintPtr, boolean linearText);
 
     /**
      * Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set
@@ -675,7 +714,12 @@
      * @param subpixelText true to set the subpixelText bit in the paint's
      *                     flags, false to clear it.
      */
-    public native void setSubpixelText(boolean subpixelText);
+    public void setSubpixelText(boolean subpixelText) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetSubpixelText(mNativePaint, subpixelText);
+    }
+
+    private native void nSetSubpixelText(long paintPtr, boolean subpixelText);
 
     /**
      * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
@@ -692,7 +736,12 @@
      * @param underlineText true to set the underlineText bit in the paint's
      *                      flags, false to clear it.
      */
-    public native void setUnderlineText(boolean underlineText);
+    public void setUnderlineText(boolean underlineText) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetUnderlineText(mNativePaint, underlineText);
+    }
+
+    private native void nSetUnderlineText(long paintPtr, boolean underlineText);
 
     /**
      * Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
@@ -709,7 +758,12 @@
      * @param strikeThruText true to set the strikeThruText bit in the paint's
      *                       flags, false to clear it.
      */
-    public native void setStrikeThruText(boolean strikeThruText);
+    public void setStrikeThruText(boolean strikeThruText) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStrikeThruText(mNativePaint, strikeThruText);
+    }
+
+    private native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
 
     /**
      * Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set
@@ -726,7 +780,12 @@
      * @param fakeBoldText true to set the fakeBoldText bit in the paint's
      *                     flags, false to clear it.
      */
-    public native void setFakeBoldText(boolean fakeBoldText);
+    public void setFakeBoldText(boolean fakeBoldText) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetFakeBoldText(mNativePaint, fakeBoldText);
+    }
+
+    private native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
 
     /**
      * Whether or not the bitmap filter is activated.
@@ -749,7 +808,12 @@
      * @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's
      *               flags, false to clear it.
      */
-    public native void setFilterBitmap(boolean filter);
+    public void setFilterBitmap(boolean filter) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetFilterBitmap(mNativePaint, filter);
+    }
+
+    private native void nSetFilterBitmap(long paintPtr, boolean filter);
 
     /**
      * Return the paint's style, used for controlling how primitives'
@@ -759,7 +823,8 @@
      * @return the paint's style setting (Fill, Stroke, StrokeAndFill)
      */
     public Style getStyle() {
-        return sStyleArray[native_getStyle(mNativePaint)];
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return sStyleArray[nGetStyle(mNativePaint)];
     }
 
     /**
@@ -770,7 +835,8 @@
      * @param style The new style to set in the paint
      */
     public void setStyle(Style style) {
-        native_setStyle(mNativePaint, style.nativeInt);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStyle(mNativePaint, style.nativeInt);
     }
 
     /**
@@ -782,7 +848,12 @@
      * @return the paint's color (and alpha).
      */
     @ColorInt
-    public native int getColor();
+    public int getColor() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetColor(mNativePaint);
+    }
+
+    private native int nGetColor(long paintPtr);
 
     /**
      * Set the paint's color. Note that the color is an int containing alpha
@@ -792,7 +863,12 @@
      *
      * @param color The new color (including alpha) to set in the paint.
      */
-    public native void setColor(@ColorInt int color);
+    public void setColor(@ColorInt int color) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetColor(mNativePaint, color);
+    }
+
+    private native void nSetColor(long paintPtr, @ColorInt int color);
 
     /**
      * Helper to getColor() that just returns the color's alpha value. This is
@@ -801,7 +877,12 @@
      *
      * @return the alpha component of the paint's color.
      */
-    public native int getAlpha();
+    public int getAlpha() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetAlpha(mNativePaint);
+    }
+
+    private native int nGetAlpha(long paintPtr);
 
     /**
      * Helper to setColor(), that only assigns the color's alpha value,
@@ -810,7 +891,12 @@
      *
      * @param a set the alpha component [0..255] of the paint's color.
      */
-    public native void setAlpha(int a);
+    public void setAlpha(int a) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetAlpha(mNativePaint, a);
+    }
+
+    private native void nSetAlpha(long paintPtr, int a);
 
     /**
      * Helper to setColor(), that takes a,r,g,b and constructs the color int
@@ -833,7 +919,12 @@
      * @return the paint's stroke width, used whenever the paint's style is
      *         Stroke or StrokeAndFill.
      */
-    public native float getStrokeWidth();
+    public float getStrokeWidth() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetStrokeWidth(mNativePaint);
+    }
+
+    private native float nGetStrokeWidth(long paintPtr);
 
     /**
      * Set the width for stroking.
@@ -843,7 +934,12 @@
      * @param width set the paint's stroke width, used whenever the paint's
      *              style is Stroke or StrokeAndFill.
      */
-    public native void setStrokeWidth(float width);
+    public void setStrokeWidth(float width) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStrokeWidth(mNativePaint, width);
+    }
+
+    private native void nSetStrokeWidth(long paintPtr, float width);
 
     /**
      * Return the paint's stroke miter value. Used to control the behavior
@@ -852,7 +948,12 @@
      * @return the paint's miter limit, used whenever the paint's style is
      *         Stroke or StrokeAndFill.
      */
-    public native float getStrokeMiter();
+    public float getStrokeMiter() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetStrokeMiter(mNativePaint);
+    }
+
+    private native float nGetStrokeMiter(long paintPtr);
 
     /**
      * Set the paint's stroke miter value. This is used to control the behavior
@@ -861,7 +962,12 @@
      * @param miter set the miter limit on the paint, used whenever the paint's
      *              style is Stroke or StrokeAndFill.
      */
-    public native void setStrokeMiter(float miter);
+    public void setStrokeMiter(float miter) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStrokeMiter(mNativePaint, miter);
+    }
+
+    private native void nSetStrokeMiter(long paintPtr, float miter);
 
     /**
      * Return the paint's Cap, controlling how the start and end of stroked
@@ -871,7 +977,8 @@
      *         style is Stroke or StrokeAndFill.
      */
     public Cap getStrokeCap() {
-        return sCapArray[native_getStrokeCap(mNativePaint)];
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return sCapArray[nGetStrokeCap(mNativePaint)];
     }
 
     /**
@@ -881,7 +988,8 @@
      *            style is Stroke or StrokeAndFill.
      */
     public void setStrokeCap(Cap cap) {
-        native_setStrokeCap(mNativePaint, cap.nativeInt);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStrokeCap(mNativePaint, cap.nativeInt);
     }
 
     /**
@@ -890,7 +998,8 @@
      * @return the paint's Join.
      */
     public Join getStrokeJoin() {
-        return sJoinArray[native_getStrokeJoin(mNativePaint)];
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return sJoinArray[nGetStrokeJoin(mNativePaint)];
     }
 
     /**
@@ -900,7 +1009,8 @@
      *             Stroke or StrokeAndFill.
      */
     public void setStrokeJoin(Join join) {
-        native_setStrokeJoin(mNativePaint, join.nativeInt);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStrokeJoin(mNativePaint, join.nativeInt);
     }
 
     /**
@@ -915,7 +1025,8 @@
      *                 drawn with a hairline (width == 0)
      */
     public boolean getFillPath(Path src, Path dst) {
-        return native_getFillPath(mNativePaint, src.ni(), dst.ni());
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetFillPath(mNativePaint, src.ni(), dst.ni());
     }
 
     /**
@@ -958,10 +1069,11 @@
      * @return       filter
      */
     public ColorFilter setColorFilter(ColorFilter filter) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long filterNative = 0;
         if (filter != null)
             filterNative = filter.native_instance;
-        native_setColorFilter(mNativePaint, filterNative);
+        nSetColorFilter(mNativePaint, filterNative);
         mColorFilter = filter;
         return filter;
     }
@@ -985,10 +1097,11 @@
      * @return         xfermode
      */
     public Xfermode setXfermode(Xfermode xfermode) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long xfermodeNative = 0;
         if (xfermode != null)
             xfermodeNative = xfermode.native_instance;
-        native_setXfermode(mNativePaint, xfermodeNative);
+        nSetXfermode(mNativePaint, xfermodeNative);
         mXfermode = xfermode;
         return xfermode;
     }
@@ -1012,11 +1125,12 @@
      * @return       effect
      */
     public PathEffect setPathEffect(PathEffect effect) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long effectNative = 0;
         if (effect != null) {
             effectNative = effect.native_instance;
         }
-        native_setPathEffect(mNativePaint, effectNative);
+        nSetPathEffect(mNativePaint, effectNative);
         mPathEffect = effect;
         return effect;
     }
@@ -1041,11 +1155,12 @@
      * @return           maskfilter
      */
     public MaskFilter setMaskFilter(MaskFilter maskfilter) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long maskfilterNative = 0;
         if (maskfilter != null) {
             maskfilterNative = maskfilter.native_instance;
         }
-        native_setMaskFilter(mNativePaint, maskfilterNative);
+        nSetMaskFilter(mNativePaint, maskfilterNative);
         mMaskFilter = maskfilter;
         return maskfilter;
     }
@@ -1072,11 +1187,12 @@
      * @return         typeface
      */
     public Typeface setTypeface(Typeface typeface) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long typefaceNative = 0;
         if (typeface != null) {
             typefaceNative = typeface.native_instance;
         }
-        native_setTypeface(mNativePaint, typefaceNative);
+        nSetTypeface(mNativePaint, typefaceNative);
         mTypeface = typeface;
         mNativeTypeface = typefaceNative;
         return typeface;
@@ -1110,11 +1226,12 @@
      */
     @Deprecated
     public Rasterizer setRasterizer(Rasterizer rasterizer) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long rasterizerNative = 0;
         if (rasterizer != null) {
             rasterizerNative = rasterizer.native_instance;
         }
-        native_setRasterizer(mNativePaint, rasterizerNative);
+        nSetRasterizer(mNativePaint, rasterizerNative);
         mRasterizer = rasterizer;
         return rasterizer;
     }
@@ -1132,7 +1249,8 @@
      * opaque, or the alpha from the shadow color if not.
      */
     public void setShadowLayer(float radius, float dx, float dy, int shadowColor) {
-      native_setShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+      nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
     }
 
     /**
@@ -1149,7 +1267,8 @@
      * @hide
      */
     public boolean hasShadowLayer() {
-      return native_hasShadowLayer(mNativePaint);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nHasShadowLayer(mNativePaint);
     }
 
     /**
@@ -1161,7 +1280,8 @@
      * @return the paint's Align value for drawing text.
      */
     public Align getTextAlign() {
-        return sAlignArray[native_getTextAlign(mNativePaint)];
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return sAlignArray[nGetTextAlign(mNativePaint)];
     }
 
     /**
@@ -1173,7 +1293,8 @@
      * @param align set the paint's Align value for drawing text.
      */
     public void setTextAlign(Align align) {
-        native_setTextAlign(mNativePaint, align.nativeInt);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetTextAlign(mNativePaint, align.nativeInt);
     }
 
     /**
@@ -1206,6 +1327,7 @@
      * @param locale the paint's locale value for drawing text, must not be null.
      */
     public void setTextLocale(@NonNull Locale locale) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (locale == null) {
             throw new IllegalArgumentException("locale cannot be null");
         }
@@ -1213,7 +1335,7 @@
             return;
         }
         mLocales = new LocaleList(locale);
-        native_setTextLocale(mNativePaint, locale.toString());
+        nSetTextLocale(mNativePaint, locale.toString());
     }
 
     /**
@@ -1244,13 +1366,14 @@
      * @param locales the paint's locale list for drawing text, must not be null or empty.
      */
     public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (locales == null || locales.isEmpty()) {
             throw new IllegalArgumentException("locales cannot be null or empty");
         }
         if (locales.equals(mLocales)) return;
         mLocales = locales;
         // TODO: Pass the whole LocaleList to native code
-        native_setTextLocale(mNativePaint, locales.getPrimary().toString());
+        nSetTextLocale(mNativePaint, locales.getPrimary().toString());
     }
 
     /**
@@ -1258,7 +1381,12 @@
      *
      * @return true if elegant metrics are enabled for text drawing.
      */
-    public native boolean isElegantTextHeight();
+    public boolean isElegantTextHeight() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nIsElegantTextHeight(mNativePaint);
+    }
+
+    private native boolean nIsElegantTextHeight(long paintPtr);
 
     /**
      * Set the paint's elegant height metrics flag. This setting selects font
@@ -1267,21 +1395,36 @@
      *
      * @param elegant set the paint's elegant metrics flag for drawing text.
      */
-    public native void setElegantTextHeight(boolean elegant);
+    public void setElegantTextHeight(boolean elegant) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetElegantTextHeight(mNativePaint, elegant);
+    }
+
+    private native void nSetElegantTextHeight(long paintPtr, boolean elegant);
 
     /**
      * Return the paint's text size.
      *
      * @return the paint's text size.
      */
-    public native float getTextSize();
+    public float getTextSize() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetTextSize(mNativePaint);
+    }
+
+    private native float nGetTextSize(long paintPtr);
 
     /**
      * Set the paint's text size. This value must be > 0
      *
      * @param textSize set the paint's text size.
      */
-    public native void setTextSize(float textSize);
+    public void setTextSize(float textSize) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetTextSize(mNativePaint, textSize);
+    }
+
+    private native void nSetTextSize(long paintPtr, float textSize);
 
     /**
      * Return the paint's horizontal scale factor for text. The default value
@@ -1289,7 +1432,12 @@
      *
      * @return the paint's scale factor in X for drawing/measuring text
      */
-    public native float getTextScaleX();
+    public float getTextScaleX() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetTextScaleX(mNativePaint);
+    }
+
+    private native float nGetTextScaleX(long paintPtr);
 
     /**
      * Set the paint's horizontal scale factor for text. The default value
@@ -1298,7 +1446,12 @@
      *
      * @param scaleX set the paint's scale in X for drawing/measuring text.
      */
-    public native void setTextScaleX(float scaleX);
+    public void setTextScaleX(float scaleX) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetTextScaleX(mNativePaint, scaleX);
+    }
+
+    private native void nSetTextScaleX(long paintPtr, float scaleX);
 
     /**
      * Return the paint's horizontal skew factor for text. The default value
@@ -1306,7 +1459,12 @@
      *
      * @return         the paint's skew factor in X for drawing text.
      */
-    public native float getTextSkewX();
+    public float getTextSkewX() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetTextSkewX(mNativePaint);
+    }
+
+    private native float nGetTextSkewX(long paintPtr);
 
     /**
      * Set the paint's horizontal skew factor for text. The default value
@@ -1314,7 +1472,12 @@
      *
      * @param skewX set the paint's skew factor in X for drawing text.
      */
-    public native void setTextSkewX(float skewX);
+    public void setTextSkewX(float skewX) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetTextSkewX(mNativePaint, skewX);
+    }
+
+    private native void nSetTextSkewX(long paintPtr, float skewX);
 
     /**
      * Return the paint's letter-spacing for text. The default value
@@ -1323,7 +1486,8 @@
      * @return         the paint's letter-spacing for drawing text.
      */
     public float getLetterSpacing() {
-        return native_getLetterSpacing(mNativePaint);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetLetterSpacing(mNativePaint);
     }
 
     /**
@@ -1334,7 +1498,8 @@
      * @param letterSpacing set the paint's letter-spacing for drawing text.
      */
     public void setLetterSpacing(float letterSpacing) {
-        native_setLetterSpacing(mNativePaint, letterSpacing);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetLetterSpacing(mNativePaint, letterSpacing);
     }
 
     /**
@@ -1355,6 +1520,7 @@
      * @param settings the font feature settings string to use, may be null.
      */
     public void setFontFeatureSettings(String settings) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (settings != null && settings.equals("")) {
             settings = null;
         }
@@ -1363,7 +1529,7 @@
             return;
         }
         mFontFeatureSettings = settings;
-        native_setFontFeatureSettings(mNativePaint, settings);
+        nSetFontFeatureSettings(mNativePaint, settings);
     }
 
     /**
@@ -1374,7 +1540,8 @@
      * @hide
      */
     public int getHyphenEdit() {
-        return native_getHyphenEdit(mNativePaint);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetHyphenEdit(mNativePaint);
     }
 
     /**
@@ -1386,7 +1553,8 @@
      * @hide
      */
     public void setHyphenEdit(int hyphen) {
-        native_setHyphenEdit(mNativePaint, hyphen);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetHyphenEdit(mNativePaint, hyphen);
     }
 
     /**
@@ -1396,7 +1564,12 @@
      * @return the distance above (negative) the baseline (ascent) based on the
      *         current typeface and text size.
      */
-    public native float ascent();
+    public float ascent() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nAscent(mNativePaint, mNativeTypeface);
+    }
+
+    private native float nAscent(long paintPtr, long typefacePtr);
 
     /**
      * Return the distance below (positive) the baseline (descent) based on the
@@ -1405,7 +1578,12 @@
      * @return the distance below (positive) the baseline (descent) based on
      *         the current typeface and text size.
      */
-    public native float descent();
+    public float descent() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nDescent(mNativePaint, mNativeTypeface);
+    }
+
+    private native float nDescent(long paintPtr, long typefacePtr);
 
     /**
      * Class that describes the various metrics for a font at a given text size.
@@ -1447,7 +1625,13 @@
      *                the appropriate values given the paint's text attributes.
      * @return the font's recommended interline spacing.
      */
-    public native float getFontMetrics(FontMetrics metrics);
+    public float getFontMetrics(FontMetrics metrics) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics);
+    }
+
+    private native float nGetFontMetrics(long paintPtr,
+            long typefacePtr, FontMetrics metrics);
 
     /**
      * Allocates a new FontMetrics object, and then calls getFontMetrics(fm)
@@ -1487,7 +1671,13 @@
      *
      * @return the font's interline spacing.
      */
-    public native int getFontMetricsInt(FontMetricsInt fmi);
+    public int getFontMetricsInt(FontMetricsInt fmi) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi);
+    }
+
+    private native int nGetFontMetricsInt(long paintPtr,
+            long typefacePtr, FontMetricsInt fmi);
 
     public FontMetricsInt getFontMetricsInt() {
         FontMetricsInt fm = new FontMetricsInt();
@@ -1515,6 +1705,7 @@
      * @return      The width of the text
      */
     public float measureText(char[] text, int index, int count) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1526,18 +1717,18 @@
             return 0f;
         }
         if (!mHasCompatScaling) {
-            return (float) Math.ceil(native_measureText(text, index, count, mBidiFlags));
+            return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
+                    index, count, index, count, mBidiFlags, null, 0));
         }
 
         final float oldSize = getTextSize();
-        setTextSize(oldSize*mCompatScaling);
-        float w = native_measureText(text, index, count, mBidiFlags);
+        setTextSize(oldSize * mCompatScaling);
+        float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index,
+                count, mBidiFlags, null, 0);
         setTextSize(oldSize);
         return (float) Math.ceil(w*mInvCompatScaling);
     }
 
-    private native float native_measureText(char[] text, int index, int count, int bidiFlags);
-
     /**
      * Return the width of the text.
      *
@@ -1547,6 +1738,7 @@
      * @return      The width of the text
      */
     public float measureText(String text, int start, int end) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1558,18 +1750,17 @@
             return 0f;
         }
         if (!mHasCompatScaling) {
-            return (float) Math.ceil(native_measureText(text, start, end, mBidiFlags));
+            return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
+                    start, end, start, end, mBidiFlags, null, 0));
         }
-
         final float oldSize = getTextSize();
-        setTextSize(oldSize*mCompatScaling);
-        float w = native_measureText(text, start, end, mBidiFlags);
+        setTextSize(oldSize * mCompatScaling);
+        float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start,
+                end, mBidiFlags, null, 0);
         setTextSize(oldSize);
-        return (float) Math.ceil(w*mInvCompatScaling);
+        return (float) Math.ceil(w * mInvCompatScaling);
     }
 
-    private native float native_measureText(String text, int start, int end, int bidiFlags);
-
     /**
      * Return the width of the text.
      *
@@ -1577,26 +1768,13 @@
      * @return      The width of the text
      */
     public float measureText(String text) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
-
-        if (text.length() == 0) {
-            return 0f;
-        }
-
-        if (!mHasCompatScaling) {
-            return (float) Math.ceil(native_measureText(text, mBidiFlags));
-        }
-        final float oldSize = getTextSize();
-        setTextSize(oldSize*mCompatScaling);
-        float w = native_measureText(text, mBidiFlags);
-        setTextSize(oldSize);
-        return (float) Math.ceil(w*mInvCompatScaling);
+        return measureText(text, 0, text.length());
     }
 
-    private native float native_measureText(String text, int bidiFlags);
-
     /**
      * Return the width of the text.
      *
@@ -1651,6 +1829,7 @@
      */
     public int breakText(char[] text, int index, int count,
                                 float maxWidth, float[] measuredWidth) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1662,20 +1841,20 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            return native_breakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth,
+            return nBreakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth,
                     mBidiFlags, measuredWidth);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
-        int res = native_breakText(mNativePaint, mNativeTypeface, text, index, count,
+        int res = nBreakText(mNativePaint, mNativeTypeface, text, index, count,
                 maxWidth * mCompatScaling, mBidiFlags, measuredWidth);
         setTextSize(oldSize);
         if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
         return res;
     }
 
-    private static native int native_breakText(long native_object, long native_typeface,
+    private static native int nBreakText(long nObject, long nTypeface,
                                                char[] text, int index, int count,
                                                float maxWidth, int bidiFlags, float[] measuredWidth);
 
@@ -1698,6 +1877,7 @@
     public int breakText(CharSequence text, int start, int end,
                          boolean measureForwards,
                          float maxWidth, float[] measuredWidth) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1746,6 +1926,7 @@
      */
     public int breakText(String text, boolean measureForwards,
                                 float maxWidth, float[] measuredWidth) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1754,20 +1935,20 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            return native_breakText(mNativePaint, mNativeTypeface, text, measureForwards,
+            return nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
                     maxWidth, mBidiFlags, measuredWidth);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize*mCompatScaling);
-        int res = native_breakText(mNativePaint, mNativeTypeface, text, measureForwards,
+        int res = nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
                 maxWidth*mCompatScaling, mBidiFlags, measuredWidth);
         setTextSize(oldSize);
         if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
         return res;
     }
 
-    private static native int native_breakText(long native_object, long native_typeface,
+    private static native int nBreakText(long nObject, long nTypeface,
                                         String text, boolean measureForwards,
                                         float maxWidth, int bidiFlags, float[] measuredWidth);
 
@@ -1783,6 +1964,7 @@
      */
     public int getTextWidths(char[] text, int index, int count,
                              float[] widths) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1795,17 +1977,20 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            return native_getTextWidths(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags, widths);
+            nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
+                    mBidiFlags, widths, 0);
+            return count;
         }
 
         final float oldSize = getTextSize();
-        setTextSize(oldSize*mCompatScaling);
-        int res = native_getTextWidths(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags, widths);
+        setTextSize(oldSize * mCompatScaling);
+        nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
+                mBidiFlags, widths, 0);
         setTextSize(oldSize);
-        for (int i=0; i<res; i++) {
+        for (int i = 0; i < count; i++) {
             widths[i] *= mInvCompatScaling;
         }
-        return res;
+        return count;
     }
 
     /**
@@ -1860,9 +2045,10 @@
      * @param end    The end of the text slice to measure
      * @param widths array to receive the advance widths of the characters.
      *               Must be at least a large as the text.
-     * @return       the number of unichars in the specified text.
+     * @return       the number of code units in the specified text.
      */
     public int getTextWidths(String text, int start, int end, float[] widths) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1877,17 +2063,20 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            return native_getTextWidths(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, widths);
+            nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
+                    mBidiFlags, widths, 0);
+            return end - start;
         }
 
         final float oldSize = getTextSize();
-        setTextSize(oldSize*mCompatScaling);
-        int res = native_getTextWidths(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, widths);
+        setTextSize(oldSize * mCompatScaling);
+        nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
+                mBidiFlags, widths, 0);
         setTextSize(oldSize);
-        for (int i=0; i<res; i++) {
+        for (int i = 0; i < end - start; i++) {
             widths[i] *= mInvCompatScaling;
         }
-        return res;
+        return end - start;
     }
 
     /**
@@ -1896,7 +2085,7 @@
      * @param text   The text to measure
      * @param widths array to receive the advance widths of the characters.
      *               Must be at least a large as the text.
-     * @return       the number of unichars in the specified text.
+     * @return       the number of code units in the specified text.
      */
     public int getTextWidths(String text, float[] widths) {
         return getTextWidths(text, 0, text.length(), widths);
@@ -1913,6 +2102,7 @@
             int contextIndex, int contextCount, boolean isRtl, float[] advances,
             int advancesIndex) {
 
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (chars == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1929,14 +2119,16 @@
             return 0f;
         }
         if (!mHasCompatScaling) {
-            return native_getTextRunAdvances(mNativePaint, mNativeTypeface, chars, index, count,
-                    contextIndex, contextCount, isRtl, advances, advancesIndex);
+            return nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
+                    contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
+                    advancesIndex);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
-        float res = native_getTextRunAdvances(mNativePaint, mNativeTypeface, chars, index, count,
-                contextIndex, contextCount, isRtl, advances, advancesIndex);
+        float res = nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
+                contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
+                advancesIndex);
         setTextSize(oldSize);
 
         if (advances != null) {
@@ -1957,7 +2149,7 @@
     public float getTextRunAdvances(CharSequence text, int start, int end,
             int contextStart, int contextEnd, boolean isRtl, float[] advances,
             int advancesIndex) {
-
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2039,7 +2231,7 @@
      */
     public float getTextRunAdvances(String text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, float[] advances, int advancesIndex) {
-
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2056,14 +2248,16 @@
         }
 
         if (!mHasCompatScaling) {
-            return native_getTextRunAdvances(mNativePaint, mNativeTypeface, text, start, end,
-                    contextStart, contextEnd, isRtl, advances, advancesIndex);
+            return nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end,
+                    contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
+                    advancesIndex);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
-        float totalAdvance = native_getTextRunAdvances(mNativePaint, mNativeTypeface, text, start, end,
-                contextStart, contextEnd, isRtl, advances, advancesIndex);
+        float totalAdvance = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start,
+                end, contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
+                advancesIndex);
         setTextSize(oldSize);
 
         if (advances != null) {
@@ -2102,6 +2296,7 @@
      */
     public int getTextRunCursor(char[] text, int contextStart, int contextLength,
             int dir, int offset, int cursorOpt) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         int contextEnd = contextStart + contextLength;
         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
                 | (offset - contextStart) | (contextEnd - offset)
@@ -2110,7 +2305,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return native_getTextRunCursor(mNativePaint, text,
+        return nGetTextRunCursor(mNativePaint, text,
                 contextStart, contextLength, dir, offset, cursorOpt);
     }
 
@@ -2189,6 +2384,7 @@
      */
     public int getTextRunCursor(String text, int contextStart, int contextEnd,
             int dir, int offset, int cursorOpt) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
                 | (offset - contextStart) | (contextEnd - offset)
                 | (text.length() - contextEnd) | cursorOpt) < 0)
@@ -2196,7 +2392,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return native_getTextRunCursor(mNativePaint, text,
+        return nGetTextRunCursor(mNativePaint, text,
                 contextStart, contextEnd, dir, offset, cursorOpt);
     }
 
@@ -2215,10 +2411,11 @@
      */
     public void getTextPath(char[] text, int index, int count,
                             float x, float y, Path path) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((index | count) < 0 || index + count > text.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
+        nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
                 path.ni());
     }
 
@@ -2237,10 +2434,11 @@
      */
     public void getTextPath(String text, int start, int end,
                             float x, float y, Path path) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
+        nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
                 path.ni());
     }
 
@@ -2255,13 +2453,14 @@
      *               allocated by the caller.
      */
     public void getTextBounds(String text, int start, int end, Rect bounds) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
         if (bounds == null) {
             throw new NullPointerException("need bounds Rect");
         }
-        nativeGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds);
+        nGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds);
     }
 
     /**
@@ -2275,13 +2474,14 @@
      *               allocated by the caller.
      */
     public void getTextBounds(char[] text, int index, int count, Rect bounds) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((index | count) < 0 || index + count > text.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
         if (bounds == null) {
             throw new NullPointerException("need bounds Rect");
         }
-        nativeGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags,
+        nGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags,
             bounds);
     }
 
@@ -2302,7 +2502,8 @@
      * @return true if the typeface has a glyph for the string
      */
     public boolean hasGlyph(String string) {
-        return native_hasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nHasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
     }
 
     /**
@@ -2343,6 +2544,7 @@
      */
     public float getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd,
             boolean isRtl, int offset) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2355,7 +2557,7 @@
             return 0.0f;
         }
         // TODO: take mCompatScaling into account (or eliminate compat scaling)?
-        return native_getRunAdvance(mNativePaint, mNativeTypeface, text, start, end,
+        return nGetRunAdvance(mNativePaint, mNativeTypeface, text, start, end,
                 contextStart, contextEnd, isRtl, offset);
     }
 
@@ -2373,6 +2575,7 @@
      */
     public float getRunAdvance(CharSequence text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, int offset) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2423,6 +2626,7 @@
      */
     public int getOffsetForAdvance(char[] text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, float advance) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2432,7 +2636,7 @@
             throw new IndexOutOfBoundsException();
         }
         // TODO: take mCompatScaling into account (or eliminate compat scaling)?
-        return native_getOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end,
+        return nGetOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end,
                 contextStart, contextEnd, isRtl, advance);
     }
 
@@ -2450,6 +2654,7 @@
      */
     public int getOffsetForAdvance(CharSequence text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, float advance) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2470,95 +2675,88 @@
     @Override
     protected void finalize() throws Throwable {
         try {
-            finalizer(mNativePaint);
-            mNativePaint = 0;
+            if (mNativePaint != 0) {
+                nFinalizer(mNativePaint);
+                mNativePaint = 0;
+            }
         } finally {
             super.finalize();
         }
     }
 
-    private static native long native_init();
-    private static native long native_initWithPaint(long paint);
-    private static native void native_reset(long native_object);
-    private static native void native_set(long native_dst, long native_src);
-    private static native int native_getStyle(long native_object);
-    private static native void native_setStyle(long native_object, int style);
-    private static native int native_getStrokeCap(long native_object);
-    private static native void native_setStrokeCap(long native_object, int cap);
-    private static native int native_getStrokeJoin(long native_object);
-    private static native void native_setStrokeJoin(long native_object,
+    private static native long nInit();
+    private static native long nInitWithPaint(long paint);
+    private static native void nReset(long paintPtr);
+    private static native void nSet(long paintPtrDest, long paintPtrSrc);
+    private static native int nGetStyle(long paintPtr);
+    private static native void nSetStyle(long paintPtr, int style);
+    private static native int nGetStrokeCap(long paintPtr);
+    private static native void nSetStrokeCap(long paintPtr, int cap);
+    private static native int nGetStrokeJoin(long paintPtr);
+    private static native void nSetStrokeJoin(long paintPtr,
                                                     int join);
-    private static native boolean native_getFillPath(long native_object,
+    private static native boolean nGetFillPath(long paintPtr,
                                                      long src, long dst);
-    private static native long native_setShader(long native_object, long shader);
-    private static native long native_setColorFilter(long native_object,
+    private static native long nSetShader(long paintPtr, long shader);
+    private static native long nSetColorFilter(long paintPtr,
                                                     long filter);
-    private static native long native_setXfermode(long native_object,
+    private static native long nSetXfermode(long paintPtr,
                                                   long xfermode);
-    private static native long native_setPathEffect(long native_object,
+    private static native long nSetPathEffect(long paintPtr,
                                                     long effect);
-    private static native long native_setMaskFilter(long native_object,
+    private static native long nSetMaskFilter(long paintPtr,
                                                     long maskfilter);
-    private static native long native_setTypeface(long native_object,
+    private static native long nSetTypeface(long paintPtr,
                                                   long typeface);
-    private static native long native_setRasterizer(long native_object,
+    private static native long nSetRasterizer(long paintPtr,
                                                    long rasterizer);
 
-    private static native int native_getTextAlign(long native_object);
-    private static native void native_setTextAlign(long native_object,
+    private static native int nGetTextAlign(long paintPtr);
+    private static native void nSetTextAlign(long paintPtr,
                                                    int align);
 
-    private static native void native_setTextLocale(long native_object,
+    private static native void nSetTextLocale(long paintPtr,
                                                     String locale);
 
-    private static native int native_getTextWidths(long native_object, long native_typeface,
-                            char[] text, int index, int count, int bidiFlags, float[] widths);
-    private static native int native_getTextWidths(long native_object, long native_typeface,
-                            String text, int start, int end, int bidiFlags, float[] widths);
-
-    private static native int native_getTextGlyphs(long native_object,
-            String text, int start, int end, int contextStart, int contextEnd,
-            int flags, char[] glyphs);
-
-    private static native float native_getTextRunAdvances(long native_object, long native_typeface,
+    private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
             char[] text, int index, int count, int contextIndex, int contextCount,
-            boolean isRtl, float[] advances, int advancesIndex);
-    private static native float native_getTextRunAdvances(long native_object, long native_typeface,
+            int bidiFlags, float[] advances, int advancesIndex);
+    private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
             String text, int start, int end, int contextStart, int contextEnd,
-            boolean isRtl, float[] advances, int advancesIndex);
+            int bidiFlags, float[] advances, int advancesIndex);
 
-    private native int native_getTextRunCursor(long native_object, char[] text,
+    private native int nGetTextRunCursor(long paintPtr, char[] text,
             int contextStart, int contextLength, int dir, int offset, int cursorOpt);
-    private native int native_getTextRunCursor(long native_object, String text,
+    private native int nGetTextRunCursor(long paintPtr, String text,
             int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
 
-    private static native void native_getTextPath(long native_object, long native_typeface,
+    private static native void nGetTextPath(long paintPtr, long typefacePtr,
             int bidiFlags, char[] text, int index, int count, float x, float y, long path);
-    private static native void native_getTextPath(long native_object, long native_typeface,
+    private static native void nGetTextPath(long paintPtr, long typefacePtr,
             int bidiFlags, String text, int start, int end, float x, float y, long path);
-    private static native void nativeGetStringBounds(long nativePaint, long native_typeface,
+    private static native void nGetStringBounds(long nativePaint, long typefacePtr,
                                 String text, int start, int end, int bidiFlags, Rect bounds);
-    private static native void nativeGetCharArrayBounds(long nativePaint, long native_typeface,
+    private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr,
                                 char[] text, int index, int count, int bidiFlags, Rect bounds);
-    private static native void finalizer(long nativePaint);
+    private static native void nFinalizer(long nativePaint);
 
-    private static native void native_setShadowLayer(long native_object,
+    private static native void nSetShadowLayer(long paintPtr,
             float radius, float dx, float dy, int color);
-    private static native boolean native_hasShadowLayer(long native_object);
+    private static native boolean nHasShadowLayer(long paintPtr);
 
-    private static native float native_getLetterSpacing(long native_object);
-    private static native void native_setLetterSpacing(long native_object,
+    private static native float nGetLetterSpacing(long paintPtr);
+    private static native void nSetLetterSpacing(long paintPtr,
                                                        float letterSpacing);
-    private static native void native_setFontFeatureSettings(long native_object,
+    private static native void nSetFontFeatureSettings(long paintPtr,
                                                              String settings);
-    private static native int native_getHyphenEdit(long native_object);
-    private static native void native_setHyphenEdit(long native_object, int hyphen);
-    private static native boolean native_hasGlyph(long native_object, long native_typeface,
+    private static native int nGetHyphenEdit(long paintPtr);
+    private static native void nSetHyphenEdit(long paintPtr, int hyphen);
+    private static native boolean nHasGlyph(long paintPtr, long typefacePtr,
             int bidiFlags, String string);
-    private static native float native_getRunAdvance(long native_object, long native_typeface,
+    private static native float nGetRunAdvance(long paintPtr, long typefacePtr,
             char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl,
             int offset);
-    private static native int native_getOffsetForAdvance(long native_object,
-            long native_typeface, char[] text, int start, int end, int contextStart, int contextEnd,
+    private static native int nGetOffsetForAdvance(long paintPtr,
+            long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd,
             boolean isRtl, float advance);
 }
diff --git a/graphics/tests/graphicstests/src/android/graphics/PaintTest.java b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java
new file mode 100644
index 0000000..6763dd1
--- /dev/null
+++ b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.test.AndroidTestCase;
+
+public class PaintTest extends AndroidTestCase {
+    public void testGetTextRunAdvances() {
+        {
+            // LTR
+            String text = "abcdef";
+            assertGetTextRunAdvances(text, 0, text.length(), 0, text.length(), false, true);
+            assertGetTextRunAdvances(text, 1, text.length() - 1, 0, text.length(), false, false);
+        }
+        {
+            // RTL
+            final String text =
+                    "\u0645\u0627\u0020\u0647\u064A\u0020\u0627\u0644\u0634" +
+                    "\u0641\u0631\u0629\u0020\u0627\u0644\u0645\u0648\u062D" +
+                    "\u062F\u0629\u0020\u064A\u0648\u0646\u064A\u0643\u0648" +
+                    "\u062F\u061F";
+            assertGetTextRunAdvances(text, 0, text.length(), 0, text.length(), true, true);
+            assertGetTextRunAdvances(text, 1, text.length() - 1, 0, text.length(), true, false);
+        }
+    }
+
+    private void assertGetTextRunAdvances(String str, int start, int end,
+            int contextStart, int contextEnd, boolean isRtl, boolean compareWithOtherMethods) {
+        Paint p = new Paint();
+
+        final int count = end - start;
+        final float[][] advanceArrays = new float[4][count];
+
+        final float advance = p.getTextRunAdvances(str, start, end, contextStart, contextEnd,
+                isRtl, advanceArrays[0], 0);
+
+        char chars[] = str.toCharArray();
+        final float advance_c = p.getTextRunAdvances(chars, start, count, contextStart,
+                contextEnd - contextStart, isRtl, advanceArrays[1], 0);
+        assertEquals(advance, advance_c, 1.0f);
+
+        for (int c = 1; c < count; ++c) {
+            final float firstPartAdvance = p.getTextRunAdvances(str, start, start + c,
+                    contextStart, contextEnd, isRtl, advanceArrays[2], 0);
+            final float secondPartAdvance = p.getTextRunAdvances(str, start + c, end,
+                    contextStart, contextEnd, isRtl, advanceArrays[2], c);
+            assertEquals(advance, firstPartAdvance + secondPartAdvance, 1.0f);
+
+
+            final float firstPartAdvance_c = p.getTextRunAdvances(chars, start, c,
+                    contextStart, contextEnd - contextStart, isRtl, advanceArrays[3], 0);
+            final float secondPartAdvance_c = p.getTextRunAdvances(chars, start + c,
+                    count - c, contextStart, contextEnd - contextStart, isRtl,
+                    advanceArrays[3], c);
+            assertEquals(advance, firstPartAdvance_c + secondPartAdvance_c, 1.0f);
+            assertEquals(firstPartAdvance, firstPartAdvance_c, 1.0f);
+            assertEquals(secondPartAdvance, secondPartAdvance_c, 1.0f);
+
+            for (int i = 1; i < advanceArrays.length; i++) {
+                for (int j = 0; j < count; j++) {
+                    assertEquals(advanceArrays[0][j], advanceArrays[i][j], 1.0f);
+                }
+            }
+
+            // Compare results with measureText, getRunAdvance, and getTextWidths.
+            if (compareWithOtherMethods && start == contextStart && end == contextEnd) {
+                assertEquals(advance, p.measureText(str, start, end), 1.0f);
+                assertEquals(advance, p.getRunAdvance(
+                        str, start, end, contextStart, contextEnd, isRtl, end), 1.0f);
+
+                final float[] widths = new float[count];
+                p.getTextWidths(str, start, end, widths);
+                for (int i = 0; i < count; i++) {
+                    assertEquals(advanceArrays[0][i], widths[i], 1.0f);
+                }
+            }
+        }
+    }
+
+    public void testGetTextRunAdvances_invalid() {
+        Paint p = new Paint();
+        String text = "test";
+
+        try {
+            p.getTextRunAdvances((String)null, 0, 0, 0, 0, false, null, 0);
+            fail("Should throw an IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            p.getTextRunAdvances((CharSequence)null, 0, 0, 0, 0, false, null, 0);
+            fail("Should throw an IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            p.getTextRunAdvances((char[])null, 0, 0, 0, 0, false, null, 0);
+            fail("Should throw an IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false,
+                    new float[text.length() - 1], 0);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false,
+                    new float[text.length()], 1);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        // 0 > contextStart
+        try {
+            p.getTextRunAdvances(text, 0, text.length(), -1, text.length(), false, null, 0);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        // contextStart > start
+        try {
+            p.getTextRunAdvances(text, 0, text.length(), 1, text.length(), false, null, 0);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        // start > end
+        try {
+            p.getTextRunAdvances(text, 1, 0, 0, text.length(), false, null, 0);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        // end > contextEnd
+        try {
+            p.getTextRunAdvances(text, 0, text.length(), 0, text.length() - 1, false, null, 0);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        // contextEnd > text.length
+        try {
+            p.getTextRunAdvances(text, 0, text.length(), 0, text.length() + 1, false, null, 0);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    public void testMeasureTextBidi() {
+        Paint p = new Paint();
+        {
+            String bidiText = "abc \u0644\u063A\u0629 def";
+            p.setBidiFlags(Paint.BIDI_LTR);
+            float width = p.measureText(bidiText, 0, 4);
+            p.setBidiFlags(Paint.BIDI_RTL);
+            width += p.measureText(bidiText, 4, 7);
+            p.setBidiFlags(Paint.BIDI_LTR);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+        {
+            String bidiText = "abc \u0644\u063A\u0629 def";
+            p.setBidiFlags(Paint.BIDI_DEFAULT_LTR);
+            float width = p.measureText(bidiText, 0, 4);
+            width += p.measureText(bidiText, 4, 7);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+        {
+            String bidiText = "abc \u0644\u063A\u0629 def";
+            p.setBidiFlags(Paint.BIDI_FORCE_LTR);
+            float width = p.measureText(bidiText, 0, 4);
+            width += p.measureText(bidiText, 4, 7);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+        {
+            String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
+            p.setBidiFlags(Paint.BIDI_RTL);
+            float width = p.measureText(bidiText, 0, 4);
+            p.setBidiFlags(Paint.BIDI_LTR);
+            width += p.measureText(bidiText, 4, 7);
+            p.setBidiFlags(Paint.BIDI_RTL);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+        {
+            String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
+            p.setBidiFlags(Paint.BIDI_DEFAULT_RTL);
+            float width = p.measureText(bidiText, 0, 4);
+            width += p.measureText(bidiText, 4, 7);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+        {
+            String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
+            p.setBidiFlags(Paint.BIDI_FORCE_RTL);
+            float width = p.measureText(bidiText, 0, 4);
+            width += p.measureText(bidiText, 4, 7);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+    }
+}
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index a81ffb9..0c29a9e 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -44,6 +44,12 @@
 #define DEBUG_COLOR_MERGEDBATCH      0x5f7f7fff
 #define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f
 
+static bool avoidOverdraw() {
+    // Don't avoid overdraw when visualizing it, since that makes it harder to
+    // debug where it's coming from, and when the problem occurs.
+    return !Properties::debugOverdraw;
+};
+
 /////////////////////////////////////////////////////////////////////////////////
 // Operation Batches
 /////////////////////////////////////////////////////////////////////////////////
@@ -218,7 +224,10 @@
         // if paints are equal, then modifiers + paint attribs don't need to be compared
         if (op->mPaint == mOps[0].op->mPaint) return true;
 
-        if (op->getPaintAlpha() != mOps[0].op->getPaintAlpha()) return false;
+        if (PaintUtils::getAlphaDirect(op->mPaint)
+                != PaintUtils::getAlphaDirect(mOps[0].op->mPaint)) {
+            return false;
+        }
 
         if (op->mPaint && mOps[0].op->mPaint &&
             op->mPaint->getColorFilter() != mOps[0].op->mPaint->getColorFilter()) {
@@ -495,7 +504,7 @@
             && mSaveStack.empty()
             && !state->mRoundRectClipState;
 
-    if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
+    if (CC_LIKELY(avoidOverdraw()) && mBatches.size() &&
             state->mClipSideFlags != kClipSide_ConservativeFull &&
             deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) {
         // avoid overdraw by resetting drawing state + discarding drawing ops
@@ -533,7 +542,11 @@
 
         if (deferInfo.mergeable) {
             // Try to merge with any existing batch with same mergeId.
-            if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) {
+            std::unordered_map<mergeid_t, DrawBatch*>& mergingBatch
+                    = mMergingBatches[deferInfo.batchId];
+            auto getResult = mergingBatch.find(deferInfo.mergeId);
+            if (getResult != mergingBatch.end()) {
+                targetBatch = getResult->second;
                 if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
                     targetBatch = nullptr;
                 }
@@ -577,7 +590,8 @@
         if (deferInfo.mergeable) {
             targetBatch = new MergingDrawBatch(deferInfo,
                     renderer.getViewportWidth(), renderer.getViewportHeight());
-            mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch);
+            mMergingBatches[deferInfo.batchId].insert(
+                    std::make_pair(deferInfo.mergeId, targetBatch));
         } else {
             targetBatch = new DrawBatch(deferInfo);
             mBatchLookup[deferInfo.batchId] = targetBatch;
@@ -642,7 +656,7 @@
     // save and restore so that reordering doesn't affect final state
     renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
 
-    if (CC_LIKELY(mAvoidOverdraw)) {
+    if (CC_LIKELY(avoidOverdraw())) {
         for (unsigned int i = 1; i < mBatches.size(); i++) {
             if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
                 discardDrawingBatches(i - 1);
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 4f2dca5..7873fbd 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -17,9 +17,10 @@
 #ifndef ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
 #define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
 
+#include <unordered_map>
+
 #include <utils/Errors.h>
 #include <utils/LinearAllocator.h>
-#include <utils/TinyHashMap.h>
 
 #include "Matrix.h"
 #include "OpenGLRenderer.h"
@@ -82,8 +83,8 @@
 class DeferredDisplayList {
     friend struct DeferStateStruct; // used to give access to allocator
 public:
-    DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) :
-            mBounds(bounds), mAvoidOverdraw(avoidOverdraw) {
+    DeferredDisplayList(const Rect& bounds)
+            : mBounds(bounds) {
         clear();
     }
     ~DeferredDisplayList() { clear(); }
@@ -151,7 +152,6 @@
 
     // layer space bounds of rendering
     Rect mBounds;
-    const bool mAvoidOverdraw;
 
     /**
      * At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so
@@ -177,7 +177,7 @@
      * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
      * collide, which avoids the need to resolve mergeid collisions.
      */
-    TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
+    std::unordered_map<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
 
     LinearAllocator mAllocator;
 };
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 38f2363..7038334 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -47,7 +47,8 @@
 }
 
 void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
-    OpenGLRenderer::getAlphaAndModeDirect(paint, &mAlpha, &mMode);
+    mAlpha = PaintUtils::getAlphaDirect(paint);
+    mMode = PaintUtils::getXfermodeDirect(paint);
     SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
     SkRefCnt_SafeAssign(mColorFilter, colorFilter);
 }
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index dc5cb8b..ddfc533 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -172,10 +172,6 @@
     void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
     bool getQuickRejected() { return mQuickRejected; }
 
-    inline int getPaintAlpha() const {
-        return OpenGLRenderer::getAlphaDirect(mPaint);
-    }
-
     virtual bool hasTextShadow() const {
         return false;
     }
@@ -213,7 +209,7 @@
 
         if (state.mAlpha != 1.0f) return false;
 
-        SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
+        SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
         return (mode == SkXfermode::kSrcOver_Mode ||
                 mode == SkXfermode::kSrc_Mode);
 
@@ -249,8 +245,8 @@
 
     virtual bool getLocalBounds(Rect& localBounds) override {
         localBounds.set(mLocalBounds);
-        OpenGLRenderer::TextShadow textShadow;
-        if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) {
+        PaintUtils::TextShadow textShadow;
+        if (PaintUtils::getTextShadow(mPaint, &textShadow)) {
             Rect shadow(mLocalBounds);
             shadow.translate(textShadow.dx, textShadow.dx);
             shadow.outset(textShadow.radius);
@@ -372,8 +368,8 @@
 
 private:
     bool isSaveLayerAlpha() const {
-        SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
-        int alpha = OpenGLRenderer::getAlphaDirect(mPaint);
+        SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
+        int alpha = PaintUtils::getAlphaDirect(mPaint);
         return alpha < 255 && mode == SkXfermode::kSrcOver_Mode;
     }
 
@@ -691,7 +687,7 @@
         // TODO: support clipped bitmaps by handling them in SET_TEXTURE
         deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() &&
                 !state.mClipSideFlags &&
-                OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
+                PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
                 (mBitmap->colorType() != kAlpha_8_SkColorType);
     }
 
@@ -895,7 +891,7 @@
         deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch;
         deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap;
         deferInfo.mergeable = state.mMatrix.isPureTranslate() &&
-                OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+                PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
         deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mBitmap->isOpaque();
     }
 
@@ -1241,7 +1237,7 @@
     }
 
     virtual bool hasTextShadow() const override {
-        return OpenGLRenderer::hasTextShadow(mPaint);
+        return PaintUtils::hasTextShadow(mPaint);
     }
 
     virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
@@ -1330,7 +1326,7 @@
 
         deferInfo.mergeable = state.mMatrix.isPureTranslate()
                 && !hasDecorations
-                && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+                && PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
     }
 
     virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index fa20b08..4785ea4 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -135,10 +135,6 @@
     } fill;
 
     struct Transform {
-        // Orthographic projection matrix for current FBO
-        // TODO: move out of Glop, since this is static per FBO
-        Matrix4 ortho;
-
         // modelView transform, accounting for delta between mesh transform and content of the mesh
         // often represents x/y offsets within command, or scaling for mesh unit size
         Matrix4 modelView;
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 69559a7..fa166ae 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -461,11 +461,10 @@
 // Transform
 ////////////////////////////////////////////////////////////////////////////////
 
-void GlopBuilder::setTransform(const Matrix4& ortho, const Matrix4& canvas,
+void GlopBuilder::setTransform(const Matrix4& canvas,
         const int transformFlags) {
     TRIGGER_STAGE(kTransformStage);
 
-    mOutGlop->transform.ortho = ortho;
     mOutGlop->transform.canvas = canvas;
     mOutGlop->transform.transformFlags = transformFlags;
 }
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 549bb21..8d05570 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -71,7 +71,7 @@
     GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
 
     GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) {
-        setTransform(snapshot.getOrthoMatrix(), *snapshot.transform, transformFlags);
+        setTransform(*snapshot.transform, transformFlags);
         return *this;
     }
 
@@ -102,8 +102,7 @@
     void setFill(int color, float alphaScale,
             SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
             const SkShader* shader, const SkColorFilter* colorFilter);
-    void setTransform(const Matrix4& ortho, const Matrix4& canvas,
-            const int transformFlags);
+    void setTransform(const Matrix4& canvas, const int transformFlags);
 
     enum StageFlags {
         kInitialStage = 0,
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 8d85289..f99d92b 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -170,7 +170,8 @@
 }
 
 void Layer::setPaint(const SkPaint* paint) {
-    OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+    alpha = PaintUtils::getAlphaDirect(paint);
+    mode = PaintUtils::getXfermodeDirect(paint);
     setColorFilter((paint) ? paint->getColorFilter() : nullptr);
 }
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index a401ce1..9f24e37 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -540,7 +540,7 @@
         Rect bounds(left, top, right, bottom);
         Rect clip;
         calculateLayerBoundsAndClip(bounds, clip, true);
-        updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint));
+        updateSnapshotIgnoreForLayer(bounds, clip, true, PaintUtils::getAlphaDirect(paint));
 
         if (!mState.currentlyIgnored()) {
             writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
@@ -615,7 +615,7 @@
     Rect clip;
     Rect bounds(left, top, right, bottom);
     calculateLayerBoundsAndClip(bounds, clip, fboLayer);
-    updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint));
+    updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, PaintUtils::getAlphaDirect(paint));
 
     // Bail out if we won't draw in this snapshot
     if (mState.currentlyIgnored()) {
@@ -1405,7 +1405,7 @@
 
         setStencilFromClip();
     }
-    mRenderState.render(glop);
+    mRenderState.render(glop, currentSnapshot()->getOrthoMatrix());
     if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) {
         // TODO: specify more clearly when a draw should dirty the layer.
         // is writing to the stencil the only time we should ignore this?
@@ -1431,10 +1431,7 @@
             return;
         }
 
-        // Don't avoid overdraw when visualizing, since that makes it harder to
-        // debug where it's coming from, and when the problem occurs.
-        bool avoidOverdraw = !Properties::debugOverdraw;
-        DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
+        DeferredDisplayList deferredList(mState.currentClipRect());
         DeferStateStruct deferStruct(deferredList, *this, replayFlags);
         renderNode->defer(deferStruct, 0);
 
@@ -1958,8 +1955,8 @@
         FontRenderer& fontRenderer, int alpha, float x, float y) {
     mCaches.textureState().activateTexture(0);
 
-    TextShadow textShadow;
-    if (!getTextShadow(paint, &textShadow)) {
+    PaintUtils::TextShadow textShadow;
+    if (!PaintUtils::getTextShadow(paint, &textShadow)) {
         LOG_ALWAYS_FATAL("failed to query shadow attributes");
     }
 
@@ -1987,8 +1984,10 @@
     renderGlop(glop);
 }
 
+// TODO: remove this, once mState.currentlyIgnored captures snapshot alpha
 bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
-    float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
+    float alpha = (PaintUtils::hasTextShadow(paint)
+            ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
     return MathUtils::isZero(alpha)
             && PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
 }
@@ -2017,11 +2016,10 @@
     FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
     fontRenderer.setFont(paint, SkMatrix::I());
 
-    int alpha;
-    SkXfermode::Mode mode;
-    getAlphaAndMode(paint, &alpha, &mode);
+    int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+    SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
 
-    if (CC_UNLIKELY(hasTextShadow(paint))) {
+    if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
         drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
                 alpha, 0.0f, 0.0f);
     }
@@ -2162,13 +2160,12 @@
         y = floorf(y + transform.getTranslateY() + 0.5f);
     }
 
-    int alpha;
-    SkXfermode::Mode mode;
-    getAlphaAndMode(paint, &alpha, &mode);
+    int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+    SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
 
     FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
 
-    if (CC_UNLIKELY(hasTextShadow(paint))) {
+    if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
         fontRenderer.setFont(paint, SkMatrix::I());
         drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
                 alpha, oldX, oldY);
@@ -2238,9 +2235,8 @@
     fontRenderer.setFont(paint, SkMatrix::I());
     fontRenderer.setTextureFiltering(true);
 
-    int alpha;
-    SkXfermode::Mode mode;
-    getAlphaAndMode(paint, &alpha, &mode);
+    int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+    SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
     TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
 
     const Rect* clip = &writableSnapshot()->getLocalClip();
@@ -2530,12 +2526,6 @@
     renderGlop(glop);
 }
 
-void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha,
-        SkXfermode::Mode* mode) const {
-    getAlphaAndModeDirect(paint, alpha,  mode);
-    *alpha *= currentSnapshot()->alpha;
-}
-
 float OpenGLRenderer::getLayerAlpha(const Layer* layer) const {
     return (layer->getAlpha() / 255.0f) * currentSnapshot()->alpha;
 }
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 910af57..400c225 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -260,57 +260,6 @@
     void endMark() const;
 
     /**
-     * Gets the alpha and xfermode out of a paint object. If the paint is null
-     * alpha will be 255 and the xfermode will be SRC_OVER. This method does
-     * not multiply the paint's alpha by the current snapshot's alpha, and does
-     * not replace the alpha with the overrideLayerAlpha
-     *
-     * @param paint The paint to extract values from
-     * @param alpha Where to store the resulting alpha
-     * @param mode Where to store the resulting xfermode
-     */
-    static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha,
-            SkXfermode::Mode* mode) {
-        *mode = getXfermodeDirect(paint);
-        *alpha = getAlphaDirect(paint);
-    }
-
-    static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
-        if (!paint) return SkXfermode::kSrcOver_Mode;
-        return PaintUtils::getXfermode(paint->getXfermode());
-    }
-
-    static inline int getAlphaDirect(const SkPaint* paint) {
-        if (!paint) return 255;
-        return paint->getAlpha();
-    }
-
-    struct TextShadow {
-        SkScalar radius;
-        float dx;
-        float dy;
-        SkColor color;
-    };
-
-    static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
-        SkDrawLooper::BlurShadowRec blur;
-        if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
-            if (textShadow) {
-                textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
-                textShadow->dx = blur.fOffset.fX;
-                textShadow->dy = blur.fOffset.fY;
-                textShadow->color = blur.fColor;
-            }
-            return true;
-        }
-        return false;
-    }
-
-    static inline bool hasTextShadow(const SkPaint* paint) {
-        return getTextShadow(paint, nullptr);
-    }
-
-    /**
      * Build the best transform to use to rasterize text given a full
      * transform matrix, and whether filteration is needed.
      *
@@ -493,16 +442,6 @@
     void drawTextureLayer(Layer* layer, const Rect& rect);
 
     /**
-     * Gets the alpha and xfermode out of a paint object. If the paint is null
-     * alpha will be 255 and the xfermode will be SRC_OVER. Accounts for snapshot alpha.
-     *
-     * @param paint The paint to extract values from
-     * @param alpha Where to store the resulting alpha
-     * @param mode Where to store the resulting xfermode
-     */
-    inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const;
-
-    /**
      * Gets the alpha from a layer, accounting for snapshot alpha
      *
      * @param layer The layer from which the alpha is extracted
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index ddc7ecd..bf1b4d0 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -725,7 +725,9 @@
 void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
     if (properties().getAlpha() <= 0.0f
             || properties().getOutline().getAlpha() <= 0.0f
-            || !properties().getOutline().getPath()) {
+            || !properties().getOutline().getPath()
+            || properties().getScaleX() == 0
+            || properties().getScaleY() == 0) {
         // no shadow to draw
         return;
     }
@@ -915,7 +917,10 @@
     const bool useViewProperties = (!mLayer || drawLayer);
     if (useViewProperties) {
         const Outline& outline = properties().getOutline();
-        if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) {
+        if (properties().getAlpha() <= 0
+                || (outline.getShouldClip() && outline.isEmpty())
+                || properties().getScaleX() == 0
+                || properties().getScaleY() == 0) {
             DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "",
                     this, getName());
             return;
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index ad74bff..ce1bd6a 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -52,11 +52,8 @@
 
 bool LayerProperties::setFromPaint(const SkPaint* paint) {
     bool changed = false;
-    SkXfermode::Mode mode;
-    int alpha;
-    OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
-    changed |= setAlpha(static_cast<uint8_t>(alpha));
-    changed |= setXferMode(mode);
+    changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
+    changed |= setXferMode(PaintUtils::getXfermodeDirect(paint));
     changed |= setColorFilter(paint ? paint->getColorFilter() : nullptr);
     return changed;
 }
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 2209365..eb0fa74 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -73,8 +73,8 @@
     }
 
 #if DEBUG_SHADOW
-    ALOGD("light center %f %f %f",
-            adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z);
+    ALOGD("light center %f %f %f %d",
+            adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius);
 #endif
 
     // light position (because it's in local space) needs to compensate for receiver transform
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 9b0a1aa..bdce73c 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -1051,7 +1051,7 @@
  */
 void SpotShadow::dumpPolygon(const Vector3* poly, int polyLength, const char* polyName) {
     for (int i = 0; i < polyLength; i++) {
-        ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y);
+        ALOGD("polygon %s i %d x %f y %f z %f", polyName, i, poly[i].x, poly[i].y, poly[i].z);
     }
 }
 
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index c5126de..dfa70ac 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -208,7 +208,7 @@
 // Render
 ///////////////////////////////////////////////////////////////////////////////
 
-void RenderState::render(const Glop& glop) {
+void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
     const Glop::Mesh& mesh = glop.mesh;
     const Glop::Mesh::Vertices& vertices = mesh.vertices;
     const Glop::Mesh::Indices& indices = mesh.indices;
@@ -223,7 +223,7 @@
         fill.program->setColor(fill.color);
     }
 
-    fill.program->set(glop.transform.ortho,
+    fill.program->set(orthoMatrix,
             glop.transform.modelView,
             glop.transform.meshTransform(),
             glop.transform.transformFlags & TransformFlags::OffsetByFudgeFactor);
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 4fd792c..9ae0845 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -84,7 +84,7 @@
     // more thinking...
     void postDecStrong(VirtualLightRefBase* object);
 
-    void render(const Glop& glop);
+    void render(const Glop& glop, const Matrix4& orthoMatrix);
 
     AssetAtlas& assetAtlas() { return mAssetAtlas; }
     Blend& blend() { return *mBlend; }
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9dc5b45..ddfd621 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -62,7 +62,7 @@
         , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
         , mJankTracker(thread.timeLord().frameIntervalNanos())
         , mProfiler(mFrames)
-        , mContentOverdrawProtectionBounds(0, 0, 0, 0) {
+        , mContentDrawBounds(0, 0, 0, 0) {
     mRenderNodes.emplace_back(rootRenderNode);
     mRenderThread.renderState().registerCanvasContext(this);
     mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
@@ -309,7 +309,7 @@
     Rect outBounds;
     // It there are multiple render nodes, they are as follows:
     // #0 - backdrop
-    // #1 - content (with - and clipped to - bounds mContentOverdrawProtectionBounds)
+    // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
     // #2 - frame
     // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
     // resizing however it might become partially visible. The following render loop will crop the
@@ -317,66 +317,72 @@
     // against the backdrop (since that indicates a shrinking of the window) and then the frame
     // around everything.
     // The bounds of the backdrop against which the content should be clipped.
-    Rect backdropBounds = mContentOverdrawProtectionBounds;
+    Rect backdropBounds = mContentDrawBounds;
+    // Usually the contents bounds should be mContentDrawBounds - however - we will
+    // move it towards the fixed edge to give it a more stable appearance (for the moment).
+    Rect contentBounds;
     // If there is no content bounds we ignore the layering as stated above and start with 2.
-    int layer = mContentOverdrawProtectionBounds.isEmpty() ? 2 : 0;
+    int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() <= 2) ? 2 : 0;
     // Draw all render nodes. Note that
     for (const sp<RenderNode>& node : mRenderNodes) {
         if (layer == 0) { // Backdrop.
-            // Draw the backdrop clipped to the inverse content bounds.
+            // Draw the backdrop clipped to the inverse content bounds, but assume that the content
+            // was moved to the upper left corner.
             const RenderProperties& properties = node->properties();
             Rect targetBounds(properties.getLeft(), properties.getTop(),
                               properties.getRight(), properties.getBottom());
+            // Move the content bounds towards the fixed corner of the backdrop.
+            const int x = targetBounds.left;
+            const int y = targetBounds.top;
+            contentBounds.set(x, y, x + mContentDrawBounds.getWidth(),
+                                    y + mContentDrawBounds.getHeight());
             // Remember the intersection of the target bounds and the intersection bounds against
             // which we have to crop the content.
+            backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
             backdropBounds.intersect(targetBounds);
             // Check if we have to draw something on the left side ...
-            if (targetBounds.left < mContentOverdrawProtectionBounds.left) {
+            if (targetBounds.left < contentBounds.left) {
                 mCanvas->save(SkCanvas::kClip_SaveFlag);
                 if (mCanvas->clipRect(targetBounds.left, targetBounds.top,
-                                      mContentOverdrawProtectionBounds.left, targetBounds.bottom,
+                                      contentBounds.left, targetBounds.bottom,
                                       SkRegion::kIntersect_Op)) {
                     mCanvas->drawRenderNode(node.get(), outBounds);
                 }
                 // Reduce the target area by the area we have just painted.
-                targetBounds.left = std::min(mContentOverdrawProtectionBounds.left,
-                                             targetBounds.right);
+                targetBounds.left = std::min(contentBounds.left, targetBounds.right);
                 mCanvas->restore();
             }
             // ... or on the right side ...
-            if (targetBounds.right > mContentOverdrawProtectionBounds.right &&
+            if (targetBounds.right > contentBounds.right &&
                 !targetBounds.isEmpty()) {
                 mCanvas->save(SkCanvas::kClip_SaveFlag);
-                if (mCanvas->clipRect(mContentOverdrawProtectionBounds.right, targetBounds.top,
+                if (mCanvas->clipRect(contentBounds.right, targetBounds.top,
                                       targetBounds.right, targetBounds.bottom,
                                       SkRegion::kIntersect_Op)) {
                     mCanvas->drawRenderNode(node.get(), outBounds);
                 }
                 // Reduce the target area by the area we have just painted.
-                targetBounds.right = std::max(targetBounds.left,
-                                              mContentOverdrawProtectionBounds.right);
+                targetBounds.right = std::max(targetBounds.left, contentBounds.right);
                 mCanvas->restore();
             }
             // ... or at the top ...
-            if (targetBounds.top < mContentOverdrawProtectionBounds.top &&
+            if (targetBounds.top < contentBounds.top &&
                 !targetBounds.isEmpty()) {
                 mCanvas->save(SkCanvas::kClip_SaveFlag);
                 if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right,
-                                      mContentOverdrawProtectionBounds.top,
+                                      contentBounds.top,
                                       SkRegion::kIntersect_Op)) {
                     mCanvas->drawRenderNode(node.get(), outBounds);
                 }
                 // Reduce the target area by the area we have just painted.
-                targetBounds.top = std::min(mContentOverdrawProtectionBounds.top,
-                                            targetBounds.bottom);
+                targetBounds.top = std::min(contentBounds.top, targetBounds.bottom);
                 mCanvas->restore();
             }
             // ... or at the bottom.
-            if (targetBounds.bottom > mContentOverdrawProtectionBounds.bottom &&
+            if (targetBounds.bottom > contentBounds.bottom &&
                 !targetBounds.isEmpty()) {
                 mCanvas->save(SkCanvas::kClip_SaveFlag);
-                if (mCanvas->clipRect(targetBounds.left,
-                                      mContentOverdrawProtectionBounds.bottom, targetBounds.right,
+                if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right,
                                       targetBounds.bottom, SkRegion::kIntersect_Op)) {
                     mCanvas->drawRenderNode(node.get(), outBounds);
                 }
@@ -384,10 +390,17 @@
             }
         } else if (layer == 1) { // Content
             // It gets cropped against the bounds of the backdrop to stay inside.
-            mCanvas->save(SkCanvas::kClip_SaveFlag);
-            if (mCanvas->clipRect(backdropBounds.left, backdropBounds.top,
-                                  backdropBounds.right, backdropBounds.bottom,
-                                  SkRegion::kIntersect_Op)) {
+            mCanvas->save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+
+            // We shift and clip the content to match its final location in the window.
+            const float left = mContentDrawBounds.left;
+            const float top = mContentDrawBounds.top;
+            const float dx = backdropBounds.left - left;
+            const float dy = backdropBounds.top - top;
+            const float width = backdropBounds.getWidth();
+            const float height = backdropBounds.getHeight();
+            mCanvas->translate(dx, dy);
+            if (mCanvas->clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op)) {
                 mCanvas->drawRenderNode(node.get(), outBounds);
             }
             mCanvas->restore();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 1c3845c..e0cbabd 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -126,8 +126,8 @@
                 mRenderNodes.end());
     }
 
-    void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
-        mContentOverdrawProtectionBounds.set(left, top, right, bottom);
+    void setContentDrawBounds(int left, int top, int right, int bottom) {
+        mContentDrawBounds.set(left, top, right, bottom);
     }
 
 private:
@@ -167,7 +167,7 @@
     std::set<RenderNode*> mPrefetechedLayers;
 
     // Stores the bounds of the main content.
-    Rect mContentOverdrawProtectionBounds;
+    Rect mContentDrawBounds;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index f43a769..26aae90 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -529,15 +529,14 @@
     staticPostAndWait(task);
 }
 
-CREATE_BRIDGE5(setContentOverdrawProtectionBounds, CanvasContext* context, int left, int top,
+CREATE_BRIDGE5(setContentDrawBounds, CanvasContext* context, int left, int top,
         int right, int bottom) {
-    args->context->setContentOverdrawProtectionBounds(args->left, args->top, args->right,
-                                                      args->bottom);
+    args->context->setContentDrawBounds(args->left, args->top, args->right, args->bottom);
     return nullptr;
 }
 
-void RenderProxy::setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
-    SETUP_TASK(setContentOverdrawProtectionBounds);
+void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) {
+    SETUP_TASK(setContentDrawBounds);
     args->context = mContext;
     args->left = left;
     args->top = top;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 046f24a..d1b62f1 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -109,7 +109,7 @@
     ANDROID_API void addRenderNode(RenderNode* node, bool placeFront);
     ANDROID_API void removeRenderNode(RenderNode* node);
     ANDROID_API void drawRenderNode(RenderNode* node);
-    ANDROID_API void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom);
+    ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom);
 
 private:
     RenderThread& mRenderThread;
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index ba02f5f..019e5d3 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -16,12 +16,18 @@
 #ifndef PAINT_UTILS_H
 #define PAINT_UTILS_H
 
+#include <utils/Blur.h>
+
 #include <SkColorFilter.h>
 #include <SkXfermode.h>
 
 namespace android {
 namespace uirenderer {
 
+/**
+ * Utility methods for accessing data within SkPaint, and providing defaults
+ * with optional SkPaint pointers.
+ */
 class PaintUtils {
 public:
 
@@ -73,6 +79,39 @@
         return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
     }
 
+    struct TextShadow {
+        SkScalar radius;
+        float dx;
+        float dy;
+        SkColor color;
+    };
+
+    static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
+        SkDrawLooper::BlurShadowRec blur;
+        if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
+            if (textShadow) {
+                textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
+                textShadow->dx = blur.fOffset.fX;
+                textShadow->dy = blur.fOffset.fY;
+                textShadow->color = blur.fColor;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    static inline bool hasTextShadow(const SkPaint* paint) {
+        return getTextShadow(paint, nullptr);
+    }
+
+    static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
+        return paint ? getXfermode(paint->getXfermode()) : SkXfermode::kSrcOver_Mode;
+    }
+
+    static inline int getAlphaDirect(const SkPaint* paint) {
+        return paint ? paint->getAlpha() : 255;
+    }
+
 }; // class PaintUtils
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/utils/TinyHashMap.h b/libs/hwui/utils/TinyHashMap.h
deleted file mode 100644
index 4ff9a42..0000000
--- a/libs/hwui/utils/TinyHashMap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_TINYHASHMAP_H
-#define ANDROID_HWUI_TINYHASHMAP_H
-
-#include <utils/BasicHashtable.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * A very simple hash map that doesn't allow duplicate keys, overwriting the older entry.
- */
-template <typename TKey, typename TValue>
-class TinyHashMap {
-public:
-    typedef key_value_pair_t<TKey, TValue> TEntry;
-
-    /**
-     * Puts an entry in the hash, removing any existing entry with the same key
-     */
-    void put(TKey key, TValue value) {
-        hash_t hash = android::hash_type(key);
-
-        ssize_t index = mTable.find(-1, hash, key);
-        if (index != -1) {
-            mTable.removeAt(index);
-        }
-
-        TEntry initEntry(key, value);
-        mTable.add(hash, initEntry);
-    }
-
-    /**
-     * Return true if key is in the map, in which case stores the value in the output ref
-     */
-    bool get(TKey key, TValue& outValue) {
-        hash_t hash = android::hash_type(key);
-        ssize_t index = mTable.find(-1, hash, key);
-        if (index == -1) {
-            return false;
-        }
-        outValue = mTable.entryAt(index).value;
-        return true;
-    }
-
-    void clear() { mTable.clear(); }
-
-private:
-    BasicHashtable<TKey, TEntry> mTable;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_TINYHASHMAP_H
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
index 54ad60e..4b8f81e 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.util.Log;
+import android.util.MathUtils;
 
 import java.util.Calendar;
 import java.util.Collections;
@@ -332,7 +333,14 @@
             }
 
             // Skip to the next one.
-            parcel.setDataPosition(start + size);
+            try {
+                parcel.setDataPosition(MathUtils.addOrThrow(start, size));
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "Invalid size: " + e.getMessage());
+                error = true;
+                break;
+            }
+
             bytesLeft -= size;
             ++recCount;
         }
diff --git a/media/java/android/media/SRTRenderer.java b/media/java/android/media/SRTRenderer.java
index ee4edee..a3e2abd 100644
--- a/media/java/android/media/SRTRenderer.java
+++ b/media/java/android/media/SRTRenderer.java
@@ -165,7 +165,6 @@
             return;
         }
 
-        final int _ = 0;
         for (Cue cue : activeCues) {
             TextTrackCue ttc = (TextTrackCue) cue;
 
@@ -184,7 +183,8 @@
             parcel.writeInt(buf.length);
             parcel.writeByteArray(buf);
 
-            Message msg = mEventHandler.obtainMessage(MEDIA_TIMED_TEXT, _, _, parcel);
+            Message msg = mEventHandler.obtainMessage(MEDIA_TIMED_TEXT, 0 /* arg1 */, 0 /* arg2 */,
+                    parcel);
             mEventHandler.sendMessage(msg);
         }
         activeCues.clear();
diff --git a/packages/DocumentsUI/res/layout/drawer_layout.xml b/packages/DocumentsUI/res/layout/drawer_layout.xml
index dec4e92..0dac0d5 100644
--- a/packages/DocumentsUI/res/layout/drawer_layout.xml
+++ b/packages/DocumentsUI/res/layout/drawer_layout.xml
@@ -14,62 +14,71 @@
      limitations under the License.
 -->
 
-<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/drawer_layout"
+<!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and
+     floating action buttons) to operate correctly. -->
+<android.support.design.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:id="@+id/coordinator_layout">
 
-    <LinearLayout
+    <android.support.v4.widget.DrawerLayout
+        android:id="@+id/drawer_layout"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical">
+        android:layout_height="match_parent">
 
-        <com.android.documentsui.DocumentsToolBar
-            android:id="@+id/toolbar"
+        <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="?android:attr/actionBarSize"
-            android:background="?android:attr/colorPrimary"
-            android:elevation="8dp"
-            android:theme="?actionBarTheme"
-            android:popupTheme="?actionBarPopupTheme">
+            android:layout_height="match_parent"
+            android:orientation="vertical">
 
-            <Spinner
-                android:id="@+id/stack"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginStart="4dp"
-                android:overlapAnchor="true" />
+            <com.android.documentsui.DocumentsToolBar
+                android:id="@+id/toolbar"
+                android:layout_width="match_parent"
+                android:layout_height="?android:attr/actionBarSize"
+                android:background="?android:attr/colorPrimary"
+                android:elevation="8dp"
+                android:theme="?actionBarTheme"
+                android:popupTheme="?actionBarPopupTheme">
 
-        </com.android.documentsui.DocumentsToolBar>
+                <Spinner
+                    android:id="@+id/stack"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="4dp"
+                    android:overlapAnchor="true" />
 
-        <include layout="@layout/directory_cluster"/>
+            </com.android.documentsui.DocumentsToolBar>
 
-    </LinearLayout>
+            <include layout="@layout/directory_cluster"/>
 
-    <LinearLayout
-        android:id="@+id/drawer_roots"
-        android:layout_width="256dp"
-        android:layout_height="match_parent"
-        android:layout_gravity="start"
-        android:orientation="vertical"
-        android:elevation="16dp"
-        android:background="@*android:color/white">
+        </LinearLayout>
 
-        <Toolbar
-            android:id="@+id/roots_toolbar"
-            android:layout_width="match_parent"
-            android:layout_height="?android:attr/actionBarSize"
-            android:background="?android:attr/colorPrimary"
-            android:elevation="8dp"
-            android:theme="?actionBarTheme"
-            android:popupTheme="?actionBarPopupTheme" />
+        <LinearLayout
+            android:id="@+id/drawer_roots"
+            android:layout_width="256dp"
+            android:layout_height="match_parent"
+            android:layout_gravity="start"
+            android:orientation="vertical"
+            android:elevation="16dp"
+            android:background="@*android:color/white">
 
-        <FrameLayout
-            android:id="@+id/container_roots"
-            android:layout_width="match_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1" />
+            <Toolbar
+                android:id="@+id/roots_toolbar"
+                android:layout_width="match_parent"
+                android:layout_height="?android:attr/actionBarSize"
+                android:background="?android:attr/colorPrimary"
+                android:elevation="8dp"
+                android:theme="?actionBarTheme"
+                android:popupTheme="?actionBarPopupTheme" />
 
-    </LinearLayout>
+            <FrameLayout
+                android:id="@+id/container_roots"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
 
-</android.support.v4.widget.DrawerLayout>
+        </LinearLayout>
+
+    </android.support.v4.widget.DrawerLayout>
+</android.support.design.widget.CoordinatorLayout>
diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml
index eba9af4..403c667 100644
--- a/packages/DocumentsUI/res/layout/fixed_layout.xml
+++ b/packages/DocumentsUI/res/layout/fixed_layout.xml
@@ -14,49 +14,59 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and
+     floating action buttons) to operate correctly. -->
+<android.support.design.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:id="@+id/coordinator_layout">
 
-    <com.android.documentsui.DocumentsToolBar
-        android:id="@+id/toolbar"
+    <LinearLayout 
         android:layout_width="match_parent"
-        android:layout_height="?android:attr/actionBarSize"
-        android:background="?android:attr/colorPrimary"
-        android:elevation="8dp"
-        android:theme="?actionBarTheme"
-        android:popupTheme="?actionBarPopupTheme">
+        android:layout_height="match_parent"
+        android:orientation="vertical">
 
-        <Spinner
-            android:id="@+id/stack"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="4dp"
-            android:overlapAnchor="true" />
-
-    </com.android.documentsui.DocumentsToolBar>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:orientation="horizontal"
-        android:baselineAligned="false"
-        android:divider="?android:attr/dividerVertical"
-        android:showDividers="middle">
-
-        <FrameLayout
-            android:id="@+id/container_roots"
-            android:layout_width="256dp"
-            android:layout_height="match_parent" />
-
-        <include layout="@layout/directory_cluster"
-            android:layout_width="0dp"
-            android:layout_weight="1"
+        <com.android.documentsui.DocumentsToolBar
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="?android:attr/actionBarSize"
+            android:background="?android:attr/colorPrimary"
             android:elevation="8dp"
-            android:background="@color/material_grey_50" />
+            android:theme="?actionBarTheme"
+            android:popupTheme="?actionBarPopupTheme">
+
+            <Spinner
+                android:id="@+id/stack"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="4dp"
+                android:overlapAnchor="true" />
+
+        </com.android.documentsui.DocumentsToolBar>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:orientation="horizontal"
+            android:baselineAligned="false"
+            android:divider="?android:attr/dividerVertical"
+            android:showDividers="middle">
+
+            <FrameLayout
+                android:id="@+id/container_roots"
+                android:layout_width="256dp"
+                android:layout_height="match_parent" />
+
+            <include layout="@layout/directory_cluster"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:elevation="8dp"
+                android:background="@color/material_grey_50" />
+
+        </LinearLayout>
 
     </LinearLayout>
 
-</LinearLayout>
+</android.support.design.widget.CoordinatorLayout>
diff --git a/packages/DocumentsUI/res/layout/single_pane_layout.xml b/packages/DocumentsUI/res/layout/single_pane_layout.xml
index 20c3232..c5a5745 100644
--- a/packages/DocumentsUI/res/layout/single_pane_layout.xml
+++ b/packages/DocumentsUI/res/layout/single_pane_layout.xml
@@ -14,29 +14,39 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<!-- CoordinatorLayout is necessary for various components (e.g. Snackbars, and
+     floating action buttons) to operate correctly. -->
+<android.support.design.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:id="@+id/coordinator_layout">
 
-    <com.android.documentsui.DocumentsToolBar
-        android:id="@+id/toolbar"
+    <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="?android:attr/actionBarSize"
-        android:background="?android:attr/colorPrimary"
-        android:elevation="8dp"
-        android:theme="?actionBarTheme"
-        android:popupTheme="?actionBarPopupTheme">
+        android:layout_height="match_parent"
+        android:orientation="vertical">
 
-        <Spinner
-            android:id="@+id/stack"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="4dp"
-            android:overlapAnchor="true" />
+        <com.android.documentsui.DocumentsToolBar
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="?android:attr/actionBarSize"
+            android:background="?android:attr/colorPrimary"
+            android:elevation="8dp"
+            android:theme="?actionBarTheme"
+            android:popupTheme="?actionBarPopupTheme">
 
-    </com.android.documentsui.DocumentsToolBar>
+            <Spinner
+                android:id="@+id/stack"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="4dp"
+                android:overlapAnchor="true" />
 
-    <include layout="@layout/directory_cluster"/>
+        </com.android.documentsui.DocumentsToolBar>
 
-</LinearLayout>
+        <include layout="@layout/directory_cluster"/>
+
+    </LinearLayout>
+
+</android.support.design.widget.CoordinatorLayout>
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index 673a254..7e0649b 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -23,13 +23,6 @@
         android:actionViewClass="android.widget.SearchView"
         android:imeOptions="actionSearch" />
     <item
-        android:id="@+id/menu_create_dir"
-        android:title="@string/menu_create_dir"
-        android:icon="@drawable/ic_menu_new_folder"
-        android:alphabeticShortcut="e"
-        android:showAsAction="always"
-        android:visible="false" />
-    <item
         android:id="@+id/menu_sort"
         android:title="@string/menu_sort"
         android:icon="@drawable/ic_menu_sortby"
@@ -63,6 +56,13 @@
         android:showAsAction="never"
         android:visible="false" />
     <item
+        android:id="@+id/menu_create_dir"
+        android:title="@string/menu_create_dir"
+        android:icon="@drawable/ic_menu_new_folder"
+        android:alphabeticShortcut="e"
+        android:showAsAction="always"
+        android:visible="false" />
+    <item
         android:id="@+id/menu_paste_from_clipboard"
         android:title="@string/menu_paste_from_clipboard"
         android:alphabeticShortcut="v"
@@ -70,11 +70,11 @@
         android:visible="false" />
     <!-- Copy action is defined in mode_directory.xml -->
     <item
-        android:id="@+id/menu_advanced"
+        android:id="@+id/menu_file_size"
         android:showAsAction="never"
         android:visible="false" />
     <item
-        android:id="@+id/menu_file_size"
+        android:id="@+id/menu_advanced"
         android:showAsAction="never"
         android:visible="false" />
     <item
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index a4acb60..d21b5ee 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -27,7 +27,7 @@
     <string name="title_save">Save to</string>
 
     <!-- Menu item that creates a new directory/folder at the current location [CHAR LIMIT=24] -->
-    <string name="menu_create_dir">Create folder</string>
+    <string name="menu_create_dir">New folder</string>
     <!-- Menu item that switches view to show documents as a large-format grid of thumbnails [CHAR LIMIT=24] -->
     <string name="menu_grid">Grid view</string>
     <!-- Menu item that switches view to show documents as a list [CHAR LIMIT=24] -->
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index a6a45e5..caaa2b9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -20,6 +20,7 @@
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
 import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
 import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.internal.util.Preconditions.checkArgument;
 
 import android.app.Activity;
@@ -127,10 +128,10 @@
 
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
-        boolean shown = super.onPrepareOptionsMenu(menu);
+        super.onPrepareOptionsMenu(menu);
 
         final RootInfo root = getCurrentRoot();
-        final DocumentInfo cwd = getCurrentDirectory();
+        final boolean inRecents = getCurrentDirectory() == null;
 
         final MenuItem sort = menu.findItem(R.id.menu_sort);
         final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
@@ -140,26 +141,28 @@
         final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
         final MenuItem settings = menu.findItem(R.id.menu_settings);
 
-        mSearchManager.update(root);
+        // I'm thinkin' this isn't necesary here. If it is...'cuz of a bug....
+        // then uncomment the linke and let's get a proper bug reference here.
+        // mSearchManager.update(root);
 
         // Search uses backend ranking; no sorting
-        sort.setVisible(cwd != null && !mSearchManager.isSearching());
+        sort.setVisible(!inRecents && !mSearchManager.isSearching());
+
+        // grid/list is effectively a toggle.
+        grid.setVisible(mState.derivedMode != State.MODE_GRID);
+        list.setVisible(mState.derivedMode != State.MODE_LIST);
+
+        sortSize.setVisible(mState.showSize); // Only sort by size when visible
+        fileSize.setVisible(!mState.forceSize);
+        advanced.setVisible(!mState.forceAdvanced);
+        settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
 
         advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
                 ? R.string.menu_advanced_hide : R.string.menu_advanced_show);
         fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
                 ? R.string.menu_file_size_hide : R.string.menu_file_size_show);
 
-        State state = getDisplayState();
-
-        sortSize.setVisible(state.showSize); // Only sort by size when visible
-        fileSize.setVisible(!state.showSize);
-        grid.setVisible(state.derivedMode != State.MODE_GRID);
-        list.setVisible(state.derivedMode != State.MODE_LIST);
-        advanced.setVisible(!mState.showAdvanced);
-        settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
-
-        return shown;
+        return true;
     }
 
     State buildDefaultState() {
@@ -185,12 +188,10 @@
     void onStackRestored(boolean restored, boolean external) {}
 
     void onRootPicked(RootInfo root) {
-        State state = getDisplayState();
-
         // Clear entire backstack and start in new root
-        state.stack.root = root;
-        state.stack.clear();
-        state.stackTouched = true;
+        mState.stack.root = root;
+        mState.stack.clear();
+        mState.stackTouched = true;
 
         mSearchManager.update(root);
 
@@ -280,6 +281,7 @@
         return cwd != null
                 && cwd.isCreateSupported()
                 && !mSearchManager.isSearching()
+                && !root.isRecents()
                 && !root.isDownloads();
     }
 
@@ -289,8 +291,8 @@
     }
 
     void openDirectory(DocumentInfo doc) {
-        getDisplayState().stack.push(doc);
-        getDisplayState().stackTouched = true;
+        mState.stack.push(doc);
+        mState.stackTouched = true;
         onCurrentDirectoryChanged(ANIM_DOWN);
     }
 
@@ -367,16 +369,15 @@
     }
 
     void setDisplayAdvancedDevices(boolean display) {
-        State state = getDisplayState();
         LocalPreferences.setDisplayAdvancedDevices(this, display);
-        state.showAdvanced = state.forceAdvanced | display;
+        mState.showAdvanced = mState.forceAdvanced | display;
         RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
         invalidateOptionsMenu();
     }
 
     void setDisplayFileSize(boolean display) {
         LocalPreferences.setDisplayFileSize(this, display);
-        getDisplayState().showSize = display;
+        mState.showSize = display;
         DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
         invalidateOptionsMenu();
     }
@@ -389,7 +390,7 @@
      * Set state sort order based on explicit user action.
      */
     void setUserSortOrder(int sortOrder) {
-        getDisplayState().userSortOrder = sortOrder;
+        mState.userSortOrder = sortOrder;
         DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
     }
 
@@ -397,7 +398,7 @@
      * Set state mode based on explicit user action.
      */
     void setUserMode(int mode) {
-        getDisplayState().userMode = mode;
+        mState.userMode = mode;
         DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
     }
 
@@ -411,7 +412,7 @@
     @Override
     protected void onSaveInstanceState(Bundle state) {
         super.onSaveInstanceState(state);
-        state.putParcelable(EXTRA_STATE, getDisplayState());
+        state.putParcelable(EXTRA_STATE, mState);
     }
 
     @Override
@@ -420,16 +421,15 @@
     }
 
     RootInfo getCurrentRoot() {
-        State state = getDisplayState();
-        if (state.stack.root != null) {
-            return state.stack.root;
+        if (mState.stack.root != null) {
+            return mState.stack.root;
         } else {
             return mRoots.getRecentsRoot();
         }
     }
 
     public DocumentInfo getCurrentDirectory() {
-        return getDisplayState().stack.peek();
+        return mState.stack.peek();
     }
 
     public Executor getExecutorForCurrentDirectory() {
@@ -470,9 +470,8 @@
             // Update the restored stack to ensure we have freshest data
             stack.updateDocuments(getContentResolver());
 
-            State state = getDisplayState();
-            state.stack = stack;
-            state.stackTouched = true;
+            mState.stack = stack;
+            mState.stackTouched = true;
             onCurrentDirectoryChanged(ANIM_SIDE);
 
         } catch (FileNotFoundException e) {
@@ -502,9 +501,8 @@
         @Override
         protected void onPostExecute(DocumentInfo result) {
             if (result != null) {
-                State state = getDisplayState();
-                state.stack.push(result);
-                state.stackTouched = true;
+                mState.stack.push(result);
+                mState.stackTouched = true;
                 onCurrentDirectoryChanged(ANIM_SIDE);
             }
         }
@@ -516,7 +514,9 @@
 
         @Override
         protected Void doInBackground(Void... params) {
-            State state = getDisplayState();
+            if (DEBUG && !mState.stack.isEmpty()) {
+                Log.w(mTag, "Overwriting existing stack.");
+            }
             RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this);
 
             // Restore last stack for calling package
@@ -528,7 +528,7 @@
                     mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
                     final byte[] rawStack = cursor.getBlob(
                             cursor.getColumnIndex(ResumeColumns.STACK));
-                    DurableUtils.readFromArray(rawStack, state.stack);
+                    DurableUtils.readFromArray(rawStack, mState.stack);
                     mRestoredStack = true;
                 }
             } catch (IOException e) {
@@ -539,13 +539,13 @@
 
             if (mRestoredStack) {
                 // Update the restored stack to ensure we have freshest data
-                final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(state);
+                final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState);
                 try {
-                    state.stack.updateRoot(matchingRoots);
-                    state.stack.updateDocuments(getContentResolver());
+                    mState.stack.updateRoot(matchingRoots);
+                    mState.stack.updateDocuments(getContentResolver());
                 } catch (FileNotFoundException e) {
                     Log.w(mTag, "Failed to restore stack: " + e);
-                    state.stack.reset();
+                    mState.stack.reset();
                     mRestoredStack = false;
                 }
             }
@@ -556,7 +556,7 @@
         @Override
         protected void onPostExecute(Void result) {
             if (isDestroyed()) return;
-            getDisplayState().restored = true;
+            mState.restored = true;
             onCurrentDirectoryChanged(ANIM_NONE);
             onStackRestored(mRestoredStack, mExternal);
         }
@@ -600,10 +600,9 @@
                 return;
             }
 
-            State state = getDisplayState();
-            while (state.stack.size() > position + 1) {
-                state.stackTouched = true;
-                state.stack.pop();
+            while (mState.stack.size() > position + 1) {
+                mState.stackTouched = true;
+                mState.stack.pop();
             }
             onCurrentDirectoryChanged(ANIM_UP);
         }
@@ -620,13 +619,12 @@
     final class StackAdapter extends BaseAdapter {
         @Override
         public int getCount() {
-            return getDisplayState().stack.size();
+            return mState.stack.size();
         }
 
         @Override
         public DocumentInfo getItem(int position) {
-            State state = getDisplayState();
-            return state.stack.get(state.stack.size() - position - 1);
+            return mState.stack.get(mState.stack.size() - position - 1);
         }
 
         @Override
@@ -714,13 +712,12 @@
                 return;
             }
 
-            State state = getDisplayState();
-            if (state.currentSearch != null) {
+            if (mState.currentSearch != null) {
                 mMenu.expandActionView();
 
                 mView.setIconified(false);
                 mView.clearFocus();
-                mView.setQuery(state.currentSearch, false);
+                mView.setQuery(mState.currentSearch, false);
             } else {
                 mView.clearFocus();
                 if (!mView.isIconified()) {
@@ -746,7 +743,7 @@
 
             mMenu.setVisible(visible);
             if (!visible) {
-                getDisplayState().currentSearch = null;
+                mState.currentSearch = null;
             }
         }
 
@@ -764,7 +761,7 @@
         }
 
         boolean isSearching() {
-            return getDisplayState().currentSearch != null;
+            return mState.currentSearch != null;
         }
 
         boolean isExpanded() {
@@ -779,7 +776,7 @@
                 return false;
             }
 
-            getDisplayState().currentSearch = null;
+            mState.currentSearch = null;
             onCurrentDirectoryChanged(ANIM_NONE);
             return false;
         }
@@ -798,7 +795,7 @@
                 mIgnoreNextCollapse = false;
                 return true;
             }
-            getDisplayState().currentSearch = null;
+            mState.currentSearch = null;
             onCurrentDirectoryChanged(ANIM_NONE);
             return true;
         }
@@ -806,7 +803,7 @@
         @Override
         public boolean onQueryTextSubmit(String query) {
             mSearchExpanded = true;
-            getDisplayState().currentSearch = query;
+            mState.currentSearch = query;
             mView.clearFocus();
             onCurrentDirectoryChanged(ANIM_NONE);
             return true;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index f1492dc..66f8acd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -20,6 +20,7 @@
 import static com.android.documentsui.model.DocumentInfo.getCursorLong;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
 
+import android.app.Activity;
 import android.app.IntentService;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -37,6 +38,7 @@
 import android.os.SystemClock;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
+import android.support.design.widget.Snackbar;
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.widget.Toast;
@@ -60,7 +62,6 @@
 
     private static final String EXTRA_CANCEL = "com.android.documentsui.CANCEL";
     public static final String EXTRA_SRC_LIST = "com.android.documentsui.SRC_LIST";
-    public static final String EXTRA_STACK = "com.android.documentsui.STACK";
     public static final String EXTRA_FAILURE = "com.android.documentsui.FAILURE";
     public static final String EXTRA_TRANSFER_MODE = "com.android.documentsui.TRANSFER_MODE";
 
@@ -107,21 +108,21 @@
      * @param srcDocs A list of src files to copy.
      * @param dstStack The copy destination stack.
      */
-    public static void start(Context context, List<DocumentInfo> srcDocs, DocumentStack dstStack,
+    public static void start(Activity activity, List<DocumentInfo> srcDocs, DocumentStack dstStack,
             int mode) {
-        final Resources res = context.getResources();
-        final Intent copyIntent = new Intent(context, CopyService.class);
+        final Resources res = activity.getResources();
+        final Intent copyIntent = new Intent(activity, CopyService.class);
         copyIntent.putParcelableArrayListExtra(
                 EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(srcDocs));
-        copyIntent.putExtra(EXTRA_STACK, (Parcelable) dstStack);
+        copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) dstStack);
         copyIntent.putExtra(EXTRA_TRANSFER_MODE, mode);
 
         int toastMessage = (mode == TRANSFER_MODE_COPY) ? R.plurals.copy_begin
                 : R.plurals.move_begin;
-        Toast.makeText(context,
+        Snackbars.makeSnackbar(activity,
                 res.getQuantityString(toastMessage, srcDocs.size(), srcDocs.size()),
-                Toast.LENGTH_SHORT).show();
-        context.startService(copyIntent);
+                Snackbar.LENGTH_SHORT).show();
+        activity.startService(copyIntent);
     }
 
     @Override
@@ -140,7 +141,7 @@
         }
 
         final ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST);
-        final DocumentStack stack = intent.getParcelableExtra(EXTRA_STACK);
+        final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
         // Copy by default.
         final int transferMode = intent.getIntExtra(EXTRA_TRANSFER_MODE, TRANSFER_MODE_COPY);
 
@@ -171,7 +172,7 @@
                 Log.e(TAG, mFailedFiles.size() + " files failed to copy");
                 final Context context = getApplicationContext();
                 final Intent navigateIntent = new Intent(context, FilesActivity.class);
-                navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack);
+                navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
                 navigateIntent.putExtra(EXTRA_FAILURE, FAILURE_COPY);
                 navigateIntent.putExtra(EXTRA_TRANSFER_MODE, transferMode);
                 navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, mFailedFiles);
@@ -219,7 +220,7 @@
 
         final Context context = getApplicationContext();
         final Intent navigateIntent = new Intent(context, FilesActivity.class);
-        navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack);
+        navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
 
         final String contentTitle = getString(copying ? R.string.copy_notification_title
                 : R.string.move_notification_title);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index e408e6e..c6425a6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -32,6 +32,7 @@
 import android.os.Bundle;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
+import android.support.design.widget.Snackbar;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -39,7 +40,6 @@
 import android.widget.EditText;
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
-import android.widget.Toast;
 
 import com.android.documentsui.model.DocumentInfo;
 
@@ -147,7 +147,7 @@
                 // Navigate into newly created child
                 mActivity.onDirectoryCreated(result);
             } else {
-                Toast.makeText(mActivity, R.string.create_error, Toast.LENGTH_SHORT).show();
+                Snackbars.makeSnackbar(mActivity, R.string.create_error, Snackbar.LENGTH_SHORT).show();
             }
 
             mActivity.setPending(false);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 5eacf21..0abbf4e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -44,7 +44,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.Loader;
-import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -54,7 +53,6 @@
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.CancellationSignal;
-import android.os.Handler;
 import android.os.Looper;
 import android.os.OperationCanceledException;
 import android.os.Parcelable;
@@ -91,7 +89,6 @@
 import android.view.ViewParent;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.documentsui.BaseActivity.DocumentContext;
 import com.android.documentsui.MultiSelectManager.Selection;
@@ -135,8 +132,6 @@
     private Model mModel;
     private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
 
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-
     private View mEmptyView;
     private RecyclerView mRecView;
 
@@ -218,8 +213,6 @@
     @Override
     public View onCreateView(
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        final Context context = inflater.getContext();
-        final Resources res = context.getResources();
         final View view = inflater.inflate(R.layout.fragment_directory, container, false);
 
         mMessageBar = MessageBar.create(getChildFragmentManager());
@@ -424,7 +417,7 @@
         }
 
         CopyService.start(getActivity(), getDisplayState(this).selectedDocumentsForCopy,
-                (DocumentStack) data.getParcelableExtra(CopyService.EXTRA_STACK),
+                (DocumentStack) data.getParcelableExtra(Shared.EXTRA_STACK),
                 data.getIntExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_NONE));
     }
 
@@ -679,6 +672,7 @@
             checkNotNull(mMenu);
             // Delegate update logic to our owning action, since specialized logic is desired.
             mFragmentTuner.updateActionMenu(mMenu, mType, mNoDeleteCount == 0);
+            Menus.disableHiddenItems(mMenu);
         }
 
         @Override
@@ -800,13 +794,12 @@
 
     private void deleteDocuments(final Selection selected) {
         Context context = getActivity();
-        ContentResolver resolver = context.getContentResolver();
         String message = Shared.getQuantityString(context, R.plurals.deleting, selected.size());
 
         mModel.markForDeletion(selected);
 
-        Activity activity = getActivity();
-        Snackbar.make(this.getView(), message, Snackbar.LENGTH_LONG)
+        final Activity activity = getActivity();
+        Snackbars.makeSnackbar(activity, message, Snackbar.LENGTH_LONG)
                 .setAction(
                         R.string.undo,
                         new android.view.View.OnClickListener() {
@@ -821,11 +814,11 @@
                                     mModel.undoDeletion();
                                 } else {
                                     mModel.finalizeDeletion(
-                                            new Runnable() {
+                                            new Model.DeletionListener() {
                                                 @Override
-                                                public void run() {
-                                                    Snackbar.make(
-                                                            DirectoryFragment.this.getView(),
+                                                public void onError() {
+                                                    Snackbars.makeSnackbar(
+                                                            activity,
                                                             R.string.toast_failed_delete,
                                                             Snackbar.LENGTH_LONG)
                                                             .show();
@@ -1245,9 +1238,11 @@
 
     private void copyDocuments(final List<DocumentInfo> docs, final DocumentInfo destination) {
         if (!canCopy(docs, destination)) {
-            Toast.makeText(
+            Snackbars.makeSnackbar(
                     getActivity(),
-                    R.string.clipboard_files_cannot_paste, Toast.LENGTH_SHORT).show();
+                    R.string.clipboard_files_cannot_paste,
+                    Snackbar.LENGTH_SHORT)
+                    .show();
             return;
         }
 
@@ -1297,10 +1292,10 @@
             void onDocumentsReady(List<DocumentInfo> docs) {
                 mClipper.clipDocuments(docs);
                 Activity activity = getActivity();
-                Toast.makeText(activity,
+                Snackbars.makeSnackbar(activity,
                         activity.getResources().getQuantityString(
                                 R.plurals.clipboard_files_clipped, docs.size(), docs.size()),
-                                Toast.LENGTH_SHORT).show();
+                                Snackbar.LENGTH_SHORT).show();
             }
         }.execute(items);
     }
@@ -1607,23 +1602,25 @@
 
         @Override
         public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+            boolean copyEnabled = mManaging && dirType != TYPE_RECENT_OPEN;
+            // TODO: The selection needs to be deletable.
+            boolean moveEnabled =
+                    SystemProperties.getBoolean("debug.documentsui.enable_move", false);
+            menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(copyEnabled);
 
             final MenuItem open = menu.findItem(R.id.menu_open);
             final MenuItem share = menu.findItem(R.id.menu_share);
             final MenuItem delete = menu.findItem(R.id.menu_delete);
             final MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
             final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
-            final MenuItem copyToClipboard = menu.findItem(R.id.menu_copy_to_clipboard);
 
             open.setVisible(!mManaging);
             share.setVisible(mManaging);
             delete.setVisible(mManaging && canDelete);
-            // Disable copying from the Recents view.
-            copyTo.setVisible(mManaging && dirType != TYPE_RECENT_OPEN);
-            moveTo.setVisible(SystemProperties.getBoolean("debug.documentsui.enable_move", false));
-
-            // Only shown in files mode.
-            copyToClipboard.setVisible(false);
+            copyTo.setVisible(copyEnabled);
+            copyTo.setEnabled(copyEnabled);
+            moveTo.setVisible(moveEnabled);
+            moveTo.setEnabled(moveEnabled);
         }
 
         @Override
@@ -1637,13 +1634,14 @@
         @Override
         public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
 
+            menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(dirType != TYPE_RECENT_OPEN);
+
             menu.findItem(R.id.menu_share).setVisible(true);
             menu.findItem(R.id.menu_delete).setVisible(canDelete);
-            menu.findItem(R.id.menu_copy_to_clipboard).setVisible(true);
 
             menu.findItem(R.id.menu_open).setVisible(false);
-            menu.findItem(R.id.menu_copy_to).setVisible(false);
-            menu.findItem(R.id.menu_move_to).setVisible(false);
+            menu.findItem(R.id.menu_copy_to).setVisible(true);
+            menu.findItem(R.id.menu_move_to).setVisible(true);
         }
 
         @Override
@@ -1864,9 +1862,9 @@
          * @param view The view which will be used to interact with the user (e.g. surfacing
          * snackbars) for errors, info, etc.
          */
-        void finalizeDeletion(Runnable errorCallback) {
+        void finalizeDeletion(DeletionListener listener) {
             final ContentResolver resolver = mContext.getContentResolver();
-            DeleteFilesTask task = new DeleteFilesTask(resolver, errorCallback);
+            DeleteFilesTask task = new DeleteFilesTask(resolver, listener);
             task.execute();
         }
 
@@ -1876,16 +1874,16 @@
          */
         private class DeleteFilesTask extends AsyncTask<Void, Void, List<DocumentInfo>> {
             private ContentResolver mResolver;
-            private Runnable mErrorCallback;
+            private DeletionListener mListener;
 
             /**
              * @param resolver A ContentResolver for performing the actual file deletions.
              * @param errorCallback A Runnable that is executed in the event that one or more errors
              *     occured while copying files.  Execution will occur on the UI thread.
              */
-            public DeleteFilesTask(ContentResolver resolver, Runnable errorCallback) {
+            public DeleteFilesTask(ContentResolver resolver, DeletionListener listener) {
                 mResolver = resolver;
-                mErrorCallback = errorCallback;
+                mListener = listener;
             }
 
             @Override
@@ -1919,15 +1917,29 @@
 
                 if (hadTrouble) {
                     // TODO show which files failed? b/23720103
-                    mErrorCallback.run();
+                    mListener.onError();
                     if (DEBUG) Log.d(TAG, "Deletion task completed.  Some deletions failed.");
                 } else {
                     if (DEBUG) Log.d(TAG, "Deletion task completed successfully.");
                 }
                 mMarkedForDeletion.clear();
+
+                mListener.onCompletion();
             }
         }
 
+        static class DeletionListener {
+            /**
+             * Called when deletion has completed (regardless of whether an error occurred).
+             */
+            void onCompletion() {}
+
+            /**
+             * Called at the end of a deletion operation that produced one or more errors.
+             */
+            void onError() {}
+        }
+
         void addUpdateListener(UpdateListener listener) {
             checkState(mUpdateListener == null);
             mUpdateListener = listener;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 4658fe3..6b428f5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -41,6 +41,7 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.provider.DocumentsContract;
+import android.support.design.widget.Snackbar;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -48,7 +49,6 @@
 import android.view.WindowManager;
 import android.widget.BaseAdapter;
 import android.widget.Spinner;
-import android.widget.Toast;
 import android.widget.Toolbar;
 
 import com.android.documentsui.RecentsProvider.RecentColumns;
@@ -89,8 +89,6 @@
             setTheme(R.style.DocumentsNonDialogTheme);
         }
 
-        final Context context = this;
-
         if (mShowAsDialog) {
             mDrawer = DrawerController.createDummy();
 
@@ -314,41 +312,35 @@
     public boolean onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
 
-        final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
 
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
         final MenuItem grid = menu.findItem(R.id.menu_grid);
         final MenuItem list = menu.findItem(R.id.menu_list);
-        final MenuItem advanced = menu.findItem(R.id.menu_advanced);
         final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
         final MenuItem settings = menu.findItem(R.id.menu_settings);
 
-        boolean fileSizeVisible = mState.showSize && !mState.forceSize;
-        if (mState.action == ACTION_CREATE
+        boolean recents = cwd == null;
+        boolean picking = mState.action == ACTION_CREATE
                 || mState.action == ACTION_OPEN_TREE
-                || mState.action == ACTION_OPEN_COPY_DESTINATION) {
-            createDir.setVisible(cwd != null && cwd.isCreateSupported());
-            mSearchManager.showMenu(false);
+                || mState.action == ACTION_OPEN_COPY_DESTINATION;
 
-            // No display options in recent directories
-            if (cwd == null) {
-                grid.setVisible(false);
-                list.setVisible(false);
-                fileSizeVisible = false;
-            }
+        createDir.setVisible(picking && !recents && cwd.isCreateSupported());
+        mSearchManager.showMenu(!picking);
 
-            if (mState.action == ACTION_CREATE) {
-                final FragmentManager fm = getFragmentManager();
-                SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
-            }
-        } else {
-            createDir.setVisible(false);
+        // No display options in recent directories
+        grid.setVisible(!(picking && recents));
+        list.setVisible(!(picking && recents));
+
+        fileSize.setVisible(fileSize.isVisible() && !picking);
+        settings.setVisible(false);
+
+        if (mState.action == ACTION_CREATE) {
+            final FragmentManager fm = getFragmentManager();
+            SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
         }
 
-        advanced.setVisible(!mState.forceAdvanced);
-        fileSize.setVisible(fileSizeVisible);
-        settings.setVisible(false);
+        Menus.disableHiddenItems(menu);
 
         return true;
     }
@@ -510,7 +502,7 @@
         } else if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
             // Picking a copy destination is only used internally by us, so we
             // don't need to extend permissions to the caller.
-            intent.putExtra(CopyService.EXTRA_STACK, (Parcelable) mState.stack);
+            intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
             intent.putExtra(CopyService.EXTRA_TRANSFER_MODE, mState.transferMode);
         } else {
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -611,8 +603,8 @@
             if (result != null) {
                 onTaskFinished(result);
             } else {
-                Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
-                        .show();
+                Snackbars.makeSnackbar(
+                    DocumentsActivity.this, R.string.save_error, Snackbar.LENGTH_SHORT).show();
             }
 
             setPending(false);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
index ea0c18a..120f610 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
@@ -66,7 +66,7 @@
         if (whichButton == DialogInterface.BUTTON_POSITIVE) {
             CopyService.start(getActivity(), mFailedSrcList,
                     (DocumentStack) getActivity().getIntent().getParcelableExtra(
-                            CopyService.EXTRA_STACK),
+                            Shared.EXTRA_STACK),
                             mTransferMode);
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index e8d1088..70ddf59 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -30,7 +30,10 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Parcelable;
+import android.provider.DocumentsContract;
 import android.support.annotation.Nullable;
+import android.support.design.widget.Snackbar;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.Menu;
@@ -38,7 +41,6 @@
 import android.view.View;
 import android.widget.BaseAdapter;
 import android.widget.Spinner;
-import android.widget.Toast;
 import android.widget.Toolbar;
 
 import com.android.documentsui.RecentsProvider.ResumeColumns;
@@ -85,32 +87,49 @@
         mDrawer = DrawerController.create(this);
 
         RootsFragment.show(getFragmentManager(), null);
-        if (!mState.restored) {
-            Intent intent = getIntent();
-            Uri rootUri = intent.getData();
 
-            // If we've got a specific root to display, restore that root using a dedicated
-            // authority. That way a misbehaving provider won't result in an ANR.
-            if (rootUri != null && !LauncherActivity.isLaunchUri(rootUri)) {
-                new RestoreRootTask(rootUri).executeOnExecutor(
-                        ProviderExecutor.forAuthority(rootUri.getAuthority()));
+        if (mState.restored) {
+            onCurrentDirectoryChanged(ANIM_NONE);
+        } else {
+            Intent intent = getIntent();
+            Uri uri = intent.getData();
+
+            // If a non-empty stack is present in our state it was read (presumably)
+            // from EXTRA_STACK intent extra. In this case, we'll skip other means of
+            // loading or restoring the stack.
+            if (!mState.stack.isEmpty()) {
+                // When restoring from a stack, if a URI is present, it should only ever
+                // be a launch URI. Launch URIs support sensible activity management, but
+                // don't specify an real content target.
+                if (uri != null) {
+                    checkState(LauncherActivity.isLaunchUri(uri));
+                }
+                onCurrentDirectoryChanged(ANIM_NONE);
+            } else if (DocumentsContract.isRootUri(this, uri)) {
+                // If we've got a specific root to display, restore that root using a dedicated
+                // authority. That way a misbehaving provider won't result in an ANR.
+                new RestoreRootTask(uri).executeOnExecutor(
+                        ProviderExecutor.forAuthority(uri.getAuthority()));
             } else {
+                // Finally, we try to restore a stack from recents.
                 new RestoreStackTask().execute();
             }
 
+            // TODO: Ensure we're handling CopyService errors correctly across all activities.
             // Show a failure dialog if there was a failed operation.
-            final DocumentStack dstStack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
             final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
             final int transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
                     CopyService.TRANSFER_MODE_NONE);
             if (failure != 0) {
                 final ArrayList<DocumentInfo> failedSrcList =
                         intent.getParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST);
-                FailureDialogFragment.show(getFragmentManager(), failure, failedSrcList, dstStack,
+                FailureDialogFragment.show(
+                        getFragmentManager(),
+                        failure,
+                        failedSrcList,
+                        mState.stack,
                         transferMode);
             }
-        } else {
-            onCurrentDirectoryChanged(ANIM_NONE);
         }
     }
 
@@ -126,7 +145,7 @@
         // Options specific to the DocumentsActivity.
         checkArgument(!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
 
-        final DocumentStack stack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
+        final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
         if (stack != null) {
             state.stack = stack;
         }
@@ -206,30 +225,23 @@
 
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
-        boolean shown = super.onPrepareOptionsMenu(menu);
-
-        menu.findItem(R.id.menu_file_size).setVisible(true);
-        menu.findItem(R.id.menu_advanced).setVisible(true);
+        super.onPrepareOptionsMenu(menu);
 
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
         final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
         final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
 
-        boolean canCreateDir = canCreateDirectory();
-
         createDir.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-        createDir.setVisible(canCreateDir);
-        createDir.setEnabled(canCreateDir);
+        createDir.setVisible(true);
+        createDir.setEnabled(canCreateDirectory());
+
+        pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
 
         newWindow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
         newWindow.setVisible(mProductivityDevice);
-        newWindow.setEnabled(mProductivityDevice);
 
-        pasteFromCb.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-        pasteFromCb.setVisible(true);
-        pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
-
-        return shown;
+        Menus.disableHiddenItems(menu, pasteFromCb);
+        return true;
     }
 
     @Override
@@ -240,7 +252,7 @@
                 showCreateDirectoryDialog();
                 return true;
             case R.id.menu_new_window:
-                startActivity(LauncherActivity.createLaunchIntent(this));
+                createNewWindow();
                 return true;
             case R.id.menu_paste_from_clipboard:
                 DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
@@ -252,6 +264,12 @@
         return super.onOptionsItemSelected(item);
     }
 
+    private void createNewWindow() {
+        Intent intent = LauncherActivity.createLaunchIntent(this);
+        intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
+        startActivity(intent);
+    }
+
     @Override
     void onDirectoryChanged(int anim) {
         final FragmentManager fm = getFragmentManager();
@@ -322,7 +340,7 @@
         try {
             startActivity(intent);
         } catch (ActivityNotFoundException ex2) {
-            Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
+            Snackbars.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
index 4754899..14a33f9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
@@ -31,12 +31,12 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.DocumentsContract;
+import android.support.design.widget.Snackbar;
 import android.util.Log;
 import android.view.Menu;
 import android.view.View;
 import android.widget.BaseAdapter;
 import android.widget.Spinner;
-import android.widget.Toast;
 import android.widget.Toolbar;
 
 import com.android.documentsui.RecentsProvider.ResumeColumns;
@@ -140,6 +140,7 @@
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
+        Menus.disableHiddenItems(menu);
         return true;
     }
 
@@ -184,7 +185,8 @@
                 try {
                     startActivity(view);
                 } catch (ActivityNotFoundException ex2) {
-                    Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
+                    Snackbars.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT)
+                            .show();
                 }
             }
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Menus.java b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
new file mode 100644
index 0000000..3f43a3d
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.view.Menu;
+import android.view.MenuItem;
+
+final class Menus {
+
+    private Menus() {}
+
+    /**
+     * Disables hidden menu items so that they are not invokable via command shortcuts
+     */
+    static void disableHiddenItems(Menu menu, MenuItem... exclusions) {
+        for (int i = 0; i < menu.size(); i++) {
+            MenuItem item = menu.getItem(i);
+            if (item.isVisible()) {
+              continue;
+            }
+            if (contains(exclusions, item)) {
+                continue;
+            }
+            item.setEnabled(false);
+        }
+    }
+
+    private static boolean contains(MenuItem[] exclusions, MenuItem item) {
+        for (int x = 0; x < exclusions.length; x++) {
+            if (exclusions[x] == item) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index 9c884d4..a4d6dc5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -18,12 +18,11 @@
 
 import android.content.Context;
 
-/**
- * @hide
- */
+/** @hide */
 public final class Shared {
     public static final boolean DEBUG = true;
     public static final String TAG = "Documents";
+    public static final String EXTRA_STACK = "com.android.documentsui.STACK";
 
     /**
      * Generates a formatted quantity string.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java b/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java
new file mode 100644
index 0000000..f48b298
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.app.Activity;
+import android.support.design.widget.Snackbar;
+import android.view.View;
+
+final class Snackbars {
+    private Snackbars() {}
+
+    public static final Snackbar makeSnackbar(Activity activity, int messageId, int duration) {
+        return Snackbars.makeSnackbar(activity, activity.getResources().getText(messageId), duration);
+    }
+
+    public static final Snackbar makeSnackbar(Activity activity, CharSequence message, int duration)
+    {
+        final View view = checkNotNull(activity.findViewById(R.id.coordinator_layout));
+        return Snackbar.make(view, message, duration);
+    }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
index 568e9e4..fc42c3b 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
@@ -425,7 +425,7 @@
         stack.push(DocumentInfo.fromUri(mResolver, dst));
         final Intent copyIntent = new Intent(mContext, CopyService.class);
         copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, srcDocs);
-        copyIntent.putExtra(CopyService.EXTRA_STACK, (Parcelable) stack);
+        copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
 
         // startService(copyIntent);
         return copyIntent;
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
index 1895a6e..98ffb77 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
@@ -34,6 +34,9 @@
 import com.android.documentsui.model.DocumentInfo;
 
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 
 
 public class DirectoryFragmentModelTest extends AndroidTestCase {
@@ -77,14 +80,6 @@
         delete(2, 4);
 
         assertEquals(ITEM_COUNT - 2, model.getItemCount());
-
-        // Finalize the deletion.  Provide a callback that just ignores errors.
-        model.finalizeDeletion(
-              new Runnable() {
-                  @Override
-                  public void run() {}
-              });
-        assertEquals(ITEM_COUNT - 2, model.getItemCount());
     }
 
     // Tests that the item count is correct after a deletion is undone.
@@ -95,7 +90,6 @@
         // Undo the deletion
         model.undoDeletion();
         assertEquals(ITEM_COUNT, model.getItemCount());
-
     }
 
     // Tests that the right things are marked for deletion.
@@ -125,6 +119,15 @@
         assertEquals("0", docs.get(0).documentId);
         assertEquals("1", docs.get(1).documentId);
         assertEquals("4", docs.get(2).documentId);
+
+        TestDeletionListener testListener = new TestDeletionListener();
+        model.finalizeDeletion(testListener);
+        testListener.waitForDone();
+
+        docs = getDocumentInfo(0, 1, 2);
+        assertEquals("0", docs.get(0).documentId);
+        assertEquals("1", docs.get(1).documentId);
+        assertEquals("2", docs.get(2).documentId);
     }
 
     // Tests that Model.getItem returns the right items after a deletion is undone.
@@ -176,4 +179,20 @@
             return null;
         }
     }
+
+    private static class TestDeletionListener extends Model.DeletionListener {
+        final CountDownLatch mSignal = new CountDownLatch(1);
+
+        @Override
+        public void onCompletion() {
+            mSignal.countDown();
+        }
+
+        public void waitForDone() {
+            try {
+                boolean timeout = mSignal.await(10, TimeUnit.SECONDS);
+                assertTrue("Timed out waiting for deletion completion", timeout);
+            } catch (InterruptedException e) {}
+        }
+    }
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java
index 25d4ed4..2447469 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManagerTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.support.v7.widget.RecyclerView;
+import android.test.AndroidTestCase;
 import android.util.SparseBooleanArray;
 import android.view.MotionEvent;
 import android.view.View;
@@ -27,8 +28,6 @@
 
 import com.android.documentsui.MultiSelectManager.Selection;
 
-import org.junit.Before;
-import org.junit.Test;
 import org.mockito.Mockito;
 
 import java.util.ArrayList;
@@ -36,7 +35,7 @@
 import java.util.List;
 import java.util.Set;
 
-public class MultiSelectManagerTest {
+public class MultiSelectManagerTest extends AndroidTestCase {
 
     private static final List<String> items;
     static {
@@ -54,7 +53,6 @@
     private TestCallback mCallback;
     private EventHelper mEventHelper;
 
-    @Before
     public void setUp() throws Exception {
         mAdapter = new TestAdapter(items);
         mCallback = new TestCallback();
@@ -63,65 +61,61 @@
         mManager.addCallback(mCallback);
     }
 
-    @Test
-    public void mouseClick_StartsSelectionMode() {
+    public void testMouseClick_StartsSelectionMode() {
         click(7);
         assertSelection(7);
     }
 
-    @Test
-    public void mouseClick_ShiftClickExtendsSelection() {
+    public void testMouseClick_NotifiesSelectionChanged() {
+        click(7);
+        mCallback.assertSelectionChanged();
+    }
+
+    public void testMouseClick_ShiftClickExtendsSelection() {
         longPress(7);
         shiftClick(11);
         assertRangeSelection(7, 11);
     }
 
-    @Test
-    public void mouseClick_NoPosition_ClearsSelection() {
+    public void testMouseClick_NoPosition_ClearsSelection() {
         longPress(7);
         click(11);
         click(RecyclerView.NO_POSITION);
         assertSelection();
     }
 
-    @Test
-    public void setSelectionFocusBegin() {
+    public void testSetSelectionFocusBegin() {
         mManager.setItemSelected(7, true);
         mManager.setSelectionFocusBegin(7);
         shiftClick(11);
         assertRangeSelection(7, 11);
     }
 
-    @Test
-    public void longPress_StartsSelectionMode() {
+    public void testLongPress_StartsSelectionMode() {
         longPress(7);
         assertSelection(7);
     }
 
-    @Test
-    public void longPress_SecondPressExtendsSelection() {
+    public void testLongPress_SecondPressExtendsSelection() {
         longPress(7);
         longPress(99);
         assertSelection(7, 99);
     }
 
-    @Test
-    public void singleTapUp_UnselectsSelectedItem() {
+    public void testSingleTapUp_UnselectsSelectedItem() {
         longPress(7);
         tap(7);
         assertSelection();
     }
 
-    @Test
-    public void singleTapUp_NoPosition_ClearsSelection() {
+    public void testSingleTapUp_NoPosition_ClearsSelection() {
         longPress(7);
         tap(11);
         tap(RecyclerView.NO_POSITION);
         assertSelection();
     }
 
-    @Test
-    public void singleTapUp_ExtendsSelection() {
+    public void testSingleTapUp_ExtendsSelection() {
         longPress(99);
         tap(7);
         tap(13);
@@ -129,30 +123,26 @@
         assertSelection(7, 99, 13, 129899);
     }
 
-    @Test
-    public void singleTapUp_ShiftCreatesRangeSelection() {
+    public void testSingleTapUp_ShiftCreatesRangeSelection() {
         longPress(7);
         shiftTap(17);
         assertRangeSelection(7, 17);
     }
 
-    @Test
-    public void singleTapUp_ShiftCreatesRangeSeletion_Backwards() {
+    public void testSingleTapUp_ShiftCreatesRangeSeletion_Backwards() {
         longPress(17);
         shiftTap(7);
         assertRangeSelection(7, 17);
     }
 
-    @Test
-    public void singleTapUp_SecondShiftClickExtendsSelection() {
+    public void testSingleTapUp_SecondShiftClickExtendsSelection() {
         longPress(7);
         shiftTap(11);
         shiftTap(17);
         assertRangeSelection(7, 17);
     }
 
-    @Test
-    public void singleTapUp_MultipleContiguousRangesSelected() {
+    public void testSingleTapUp_MultipleContiguousRangesSelected() {
         longPress(7);
         shiftTap(11);
         tap(20);
@@ -162,16 +152,14 @@
         assertSelectionSize(11);
     }
 
-    @Test
-    public void singleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick() {
+    public void testSingleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick() {
         longPress(7);
         shiftTap(17);
         shiftTap(10);
         assertRangeSelection(7, 10);
     }
 
-    @Test
-    public void singleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick_Backwards() {
+    public void testSingleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick_Backwards() {
         mManager.onLongPress(TestInputEvent.tap(17));
         shiftTap(7);
         shiftTap(14);
@@ -179,16 +167,14 @@
     }
 
 
-    @Test
-    public void singleTapUp_ShiftReversesSelectionDirection() {
+    public void testSingleTapUp_ShiftReversesSelectionDirection() {
         longPress(7);
         shiftTap(17);
         shiftTap(0);
         assertRangeSelection(0, 7);
     }
 
-    @Test
-    public void singleSelectMode() {
+    public void testSingleSelectMode() {
         mManager = new MultiSelectManager(mAdapter, mEventHelper, MultiSelectManager.MODE_SINGLE);
         mManager.addCallback(mCallback);
         longPress(20);
@@ -196,8 +182,7 @@
         assertSelection(13);
     }
 
-    @Test
-    public void singleSelectMode_ShiftTap() {
+    public void testSingleSelectMode_ShiftTap() {
         mManager = new MultiSelectManager(mAdapter, mEventHelper, MultiSelectManager.MODE_SINGLE);
         mManager.addCallback(mCallback);
         longPress(13);
@@ -205,8 +190,7 @@
         assertSelection(20);
     }
 
-    @Test
-    public void provisionaSelection() {
+    public void testProvisionalSelection() {
         Selection s = mManager.getSelection();
         assertSelection();
 
@@ -298,6 +282,7 @@
         Set<Integer> ignored = new HashSet<>();
         private int mLastChangedPosition;
         private boolean mLastChangedSelected;
+        private boolean mSelectionChanged = false;
 
         @Override
         public void onItemStateChanged(int position, boolean selected) {
@@ -311,7 +296,13 @@
         }
 
         @Override
-        public void onSelectionChanged() {}
+        public void onSelectionChanged() {
+            mSelectionChanged = true;
+        }
+
+        void assertSelectionChanged() {
+            assertTrue(mSelectionChanged);
+        }
     }
 
     private static final class TestHolder extends RecyclerView.ViewHolder {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java
index 87d7e15..aa50b48 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_GridModelTest.java
@@ -22,14 +22,12 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.support.v7.widget.RecyclerView.OnScrollListener;
+import android.test.AndroidTestCase;
 import android.util.SparseBooleanArray;
 
 import com.android.documentsui.MultiSelectManager.GridModel;
 
-import org.junit.After;
-import org.junit.Test;
-
-public class MultiSelectManager_GridModelTest {
+public class MultiSelectManager_GridModelTest extends AndroidTestCase {
 
     private static final int VIEW_PADDING_PX = 5;
     private static final int CHILD_VIEW_EDGE_PX = 100;
@@ -53,14 +51,13 @@
                 });
     }
 
-    @After
+    @Override
     public void tearDown() {
         model = null;
         helper = null;
         lastSelection = null;
     }
 
-    @Test
     public void testSelectionLeftOfItems() {
         setUp(20, 5);
         model.startSelection(new Point(0, 10));
@@ -69,7 +66,6 @@
         assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
-    @Test
     public void testSelectionRightOfItems() {
         setUp(20, 4);
         model.startSelection(new Point(viewWidth - 1, 10));
@@ -78,7 +74,6 @@
         assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
-    @Test
     public void testSelectionAboveItems() {
         setUp(20, 4);
         model.startSelection(new Point(10, 0));
@@ -87,7 +82,6 @@
         assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
-    @Test
     public void testSelectionBelowItems() {
         setUp(5, 4);
         model.startSelection(new Point(10, VIEWPORT_HEIGHT - 1));
@@ -96,7 +90,6 @@
         assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
-    @Test
     public void testVerticalSelectionBetweenItems() {
         setUp(20, 4);
         model.startSelection(new Point(106, 0));
@@ -105,7 +98,6 @@
         assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
-    @Test
     public void testHorizontalSelectionBetweenItems() {
         setUp(20, 4);
         model.startSelection(new Point(0, 105));
@@ -114,7 +106,6 @@
         assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
-    @Test
     public void testGrowingAndShrinkingSelection() {
         setUp(20, 4);
         model.startSelection(new Point(0, 0));
@@ -145,7 +136,6 @@
         assertEquals(GridModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
-    @Test
     public void testSelectionMovingAroundOrigin() {
         setUp(16, 4);
         model.startSelection(new Point(210, 210));
@@ -160,7 +150,6 @@
         assertEquals(10, model.getPositionNearestOrigin());
     }
 
-    @Test
     public void testScrollingBandSelect() {
         setUp(40, 4);
         model.startSelection(new Point(0, 0));
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_SelectionTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_SelectionTest.java
index 51b542b..eddf4ef 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_SelectionTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/MultiSelectManager_SelectionTest.java
@@ -18,16 +18,16 @@
 
 import static org.junit.Assert.*;
 
+import android.test.AndroidTestCase;
+
 import com.android.documentsui.MultiSelectManager.Selection;
 
-import org.junit.Before;
-import org.junit.Test;
 
-public class MultiSelectManager_SelectionTest {
+public class MultiSelectManager_SelectionTest extends AndroidTestCase{
 
     private Selection selection;
 
-    @Before
+    @Override
     public void setUp() throws Exception {
         selection = new Selection();
         selection.add(3);
@@ -35,8 +35,7 @@
         selection.add(9);
     }
 
-    @Test
-    public void add() {
+    public void testAdd() {
         // We added in setUp.
         assertEquals(3, selection.size());
         assertContains(3);
@@ -44,29 +43,25 @@
         assertContains(9);
     }
 
-    @Test
-    public void remove() {
+    public void testRemove() {
         selection.remove(3);
         selection.remove(5);
         assertEquals(1, selection.size());
         assertContains(9);
     }
 
-    @Test
-    public void clear() {
+    public void testClear() {
         selection.clear();
         assertEquals(0, selection.size());
     }
 
-    @Test
-    public void isEmpty() {
+    public void testIsEmpty() {
         assertTrue(new Selection().isEmpty());
         selection.clear();
         assertTrue(selection.isEmpty());
     }
 
-    @Test
-    public void sizeAndGet() {
+    public void testSizeAndGet() {
         Selection other = new Selection();
         for (int i = 0; i < selection.size(); i++) {
             other.add(selection.get(i));
@@ -74,13 +69,11 @@
         assertEquals(selection.size(), other.size());
     }
 
-    @Test
-    public void equalsSelf() {
+    public void testEqualsSelf() {
         assertEquals(selection, selection);
     }
 
-    @Test
-    public void equalsOther() {
+    public void testEqualsOther() {
         Selection other = new Selection();
         other.add(3);
         other.add(5);
@@ -89,23 +82,20 @@
         assertEquals(selection.hashCode(), other.hashCode());
     }
 
-    @Test
-    public void equalsCopy() {
+    public void testEqualsCopy() {
         Selection other = new Selection();
         other.copyFrom(selection);
         assertEquals(selection, other);
         assertEquals(selection.hashCode(), other.hashCode());
     }
 
-    @Test
-    public void notEquals() {
+    public void testNotEquals() {
         Selection other = new Selection();
         other.add(789);
         assertFalse(selection.equals(other));
     }
 
-    @Test
-    public void expandBefore() {
+    public void testExpandBefore() {
         selection.expand(2, 10);
         assertEquals(3, selection.size());
         assertContains(13);
@@ -113,8 +103,7 @@
         assertContains(19);
     }
 
-    @Test
-    public void expandAfter() {
+    public void testExpandAfter() {
         selection.expand(10, 10);
         assertEquals(3, selection.size());
         assertContains(3);
@@ -122,8 +111,7 @@
         assertContains(9);
     }
 
-    @Test
-    public void expandSplit() {
+    public void testExpandSplit() {
         selection.expand(5, 10);
         assertEquals(3, selection.size());
         assertContains(3);
@@ -131,8 +119,7 @@
         assertContains(19);
     }
 
-    @Test
-    public void expandEncompased() {
+    public void testExpandEncompased() {
         selection.expand(2, 10);
         assertEquals(3, selection.size());
         assertContains(13);
@@ -140,8 +127,7 @@
         assertContains(19);
     }
 
-    @Test
-    public void collapseBefore() {
+    public void testCollapseBefore() {
         selection.collapse(0, 2);
         assertEquals(3, selection.size());
         assertContains(1);
@@ -149,8 +135,7 @@
         assertContains(7);
     }
 
-    @Test
-    public void collapseAfter() {
+    public void testCollapseAfter() {
         selection.collapse(10, 10);
         assertEquals(3, selection.size());
         assertContains(3);
@@ -158,8 +143,7 @@
         assertContains(9);
     }
 
-    @Test
-    public void collapseAcross() {
+    public void testCollapseAcross() {
         selection.collapse(0, 10);
         assertEquals(0, selection.size());
     }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java
deleted file mode 100644
index be3f251..0000000
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.documentsui;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-import org.junit.runners.Suite.SuiteClasses;
-
-@RunWith(Suite.class)
-@SuiteClasses({
-        MultiSelectManager_GridModelTest.class,
-        MultiSelectManager_SelectionTest.class,
-        MultiSelectManagerTest.class
-})
-
-/**
- * This test suite can be run using the "art" runtime (which can be built
- * via the `build-art-host` target.) You'll also need to "mma -j32" the
- * DocumentsUI package to ensure all deps are built.
- *
- * <p>Once the dependencies have been built, the tests can be executed as follows:
- *
- * <pre>
- *  CP=$OUT/system/framework/framework.jar:\
- *      $OUT/system/framework/core-junit.jar:\
- *      $OUT/system/app/DocumentsUI/DocumentsUI.apk:\
- *      $OUT/data/app/DocumentsUITests/DocumentsUITests.apk
- *
- *  art -cp $CP org.junit.runner.JUnitCore com.android.documentsui.UnitTests
- * </pre>
- */
-public class UnitTests {}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 393771a..18335b6 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -38,6 +38,7 @@
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Root;
 import android.provider.DocumentsProvider;
+import android.provider.MediaStore;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.DebugUtils;
@@ -380,12 +381,31 @@
     @Override
     public void deleteDocument(String docId) throws FileNotFoundException {
         final File file = getFileForDocId(docId);
-        if (file.isDirectory()) {
+        final boolean isDirectory = file.isDirectory();
+        if (isDirectory) {
             FileUtils.deleteContents(file);
         }
         if (!file.delete()) {
             throw new IllegalStateException("Failed to delete " + file);
         }
+
+        final ContentResolver resolver = getContext().getContentResolver();
+        final Uri externalUri = MediaStore.Files.getContentUri("external");
+
+        // Remove media store entries for any files inside this directory, using
+        // path prefix match. Logic borrowed from MtpDatabase.
+        if (isDirectory) {
+            final String path = file.getAbsolutePath() + "/";
+            resolver.delete(externalUri,
+                    "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
+                    new String[] { path + "%", Integer.toString(path.length()), path });
+        }
+
+        // Remove media store entry for this exact file.
+        final String path = file.getAbsolutePath();
+        resolver.delete(externalUri,
+                "_data LIKE ?1 AND lower(_data)=lower(?2)",
+                new String[] { path, path });
     }
 
     @Override
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 1cd2908..c324abd 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -204,9 +204,6 @@
     <!-- Default for Settings.Secure.WAKE_GESTURE_ENABLED -->
     <bool name="def_wake_gesture_enabled">true</bool>
 
-    <!-- Default for Settings.Global.GUEST_USER_ENABLED -->
-    <bool name="def_guest_user_enabled">true</bool>
-
     <!-- Default state of tap to wake -->
     <bool name="def_double_tap_to_wake">true</bool>
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index ee296d9..d4e428e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -1693,20 +1693,7 @@
         }
 
         if (upgradeVersion < 105) {
-            if (mUserHandle == UserHandle.USER_SYSTEM) {
-                db.beginTransaction();
-                SQLiteStatement stmt = null;
-                try {
-                    stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)"
-                            + " VALUES(?,?);");
-                    loadBooleanSetting(stmt, Settings.Global.GUEST_USER_ENABLED,
-                            R.bool.def_guest_user_enabled);
-                    db.setTransactionSuccessful();
-                } finally {
-                    db.endTransaction();
-                    if (stmt != null) stmt.close();
-                }
-            }
+            // No-op: GUEST_USER_ENABLED setting was removed
             upgradeVersion = 105;
         }
 
@@ -2705,8 +2692,6 @@
 
             loadSetting(stmt, Settings.Global.DEVICE_NAME, getDefaultDeviceName());
 
-            loadBooleanSetting(stmt, Settings.Global.GUEST_USER_ENABLED,
-                    R.bool.def_guest_user_enabled);
             loadSetting(stmt, Settings.Global.ENHANCED_4G_MODE_ENABLED,
                     ImsConfig.FeatureValueConstants.ON);
 
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 47e24e8..368f9f7 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -13,3 +13,11 @@
 -keep class com.android.systemui.statusbar.phone.PhoneStatusBar
 -keep class com.android.systemui.statusbar.tv.TvStatusBar
 -keep class com.android.systemui.recents.*
+
+-keepclassmembers class ** {
+    public void onBusEvent(**);
+    public void onInterprocessBusEvent(**);
+}
+-keepclassmembers class ** extends **.EventBus$InterprocessEvent {
+	public <init>(android.os.Bundle);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_lock.xml b/packages/SystemUI/res/drawable/ic_qs_lock.xml
new file mode 100644
index 0000000..204af7e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_lock.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+
+    <path
+        android:fillColor="@color/keyguard_affordance"
+        android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_lock_open.xml b/packages/SystemUI/res/drawable/ic_qs_lock_open.xml
new file mode 100644
index 0000000..c877f06
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_lock_open.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+
+    <path
+        android:fillColor="@color/keyguard_affordance"
+        android:pathData="M12.0,17.0c1.1,0.0 2.0,-0.9 2.0,-2.0s-0.9,-2.0 -2.0,-2.0c-1.1,0.0 -2.0,0.9 -2.0,2.0S10.9,17.0 12.0,17.0zM18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l1.9,0.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM18.0,20.0L6.0,20.0L6.0,10.0l12.0,0.0L18.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 1473f24..c7c7f1a 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Net\nprioriteit"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Net\nwekkers"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laai tans (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tot vol)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Laai tans vinnig (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tot vol)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Laai tans stadig (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tot vol)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Wissel gebruiker"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Wissel gebruiker, huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 7ab6af9..f49f5bc 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ቅድሚያ ተሰጪ\nብቻ"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ማንቂያዎች\nብቻ"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ሃይል በመሙላት ላይ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> እስከሚሞላ ድረስ)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ኃይል በፍጥነት በመሙላት ላይ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> እስከሚሞላ ድረስ)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ኃይል በዝግታ በመሙላት ላይ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> እስከሚሞላ ድረስ)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ተጠቃሚ ቀይር"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ተጠቃሚ ይለውጡ፣ የአሁን ተጠቃሚ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"የአሁን ተጠቃሚ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index bd02650..aaf20cc 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -329,6 +329,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"الأولوية \nفقط"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"التنبيهات\nفقط"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"جارٍ الشحن (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> حتى الامتلاء)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"جارٍ الشحن سريعًا (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> حتى الاكتمال)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"جارٍ الشحن ببطء (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> حتى الاكتمال)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"تبديل المستخدم"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"تبديل المستخدم، المستخدم الحالي <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"المستخدم الحالي <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index a0d7d2b..0efc82b 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Yalnız\nprioritet"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Yalnız\nalarmlar"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Qidalanır (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> dolana kimi)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Sürətli qidalanır (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> dolana kimi)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Ləng qidalanır (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> dolana kimi)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Switch user"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"İstifadəçiləri dəyişin, indiki istifadəçi: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Cari istifadəçi <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index a14d0fc..6805779 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Само\nс приоритет"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Само\nбудилници"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарежда се (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до пълно зареждане)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Зарежда се бързо (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до пълно зареждане)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Зарежда се бавно (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до пълно зареждане)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Превключване между потребителите"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Превключване на потребителя – текущият е <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Текущ потребител – <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index e819d54..c0ffe2c 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"শুধুমাত্র\nঅগ্রাধিকার"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"শুধুমাত্র\nঅ্যালার্মগুলি"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"চার্জ হচ্ছে (পূর্ণ হতে <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> সময় বাকি)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"দ্রুত চার্জ হচ্ছে (পূর্ণ হতে <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> সময় বাকি)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ধীরে ধীরে চার্জ হচ্ছে (পূর্ণ হতে <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> সময় বাকি)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ব্যবহারকারী পাল্টে দিন"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ব্যবহারকারী পাল্টান, বর্তমান ব্যবহারকারী <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"<xliff:g id="CURRENT_USER_NAME">%s</xliff:g> হল বর্তমান ব্যবহারকারী"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 8b2951a..c955f34 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Només\ninterr. prior."</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Només\nalarmes"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregant (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar la càrrega)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Càrrega ràpida (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Càrrega lenta (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar-se)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Canvia d\'usuari"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Canvia l\'usuari. Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index a73947f..18fd994 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -329,6 +329,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Pouze\nprioritní"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Pouze\nbudíky"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nabíjení (plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Rychlé nabíjení (plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Pomalé nabíjení (plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Přepnout uživatele"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Přepnout uživatele, aktuální uživatel: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Aktuální uživatel <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 91d975b..5a2dd33 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Kun\nprioritet"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Kun\nalarmer"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Oplader (fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Hurtig opladning (fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Langsom opladning (fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Skift bruger"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Skift bruger. Nuværende bruger er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Nuværende bruger: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index a659077..a8143a4 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Nur\nwichtige"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Nur\nWecker"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Wird aufgeladen (voll in <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Wird schnell aufgeladen (voll in <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Wird langsam aufgeladen (voll in <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Nutzer wechseln"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Nutzer wechseln. Aktueller Nutzer: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Aktueller Nutzer <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index bcfc458..6031d3b 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Μόνο\nπροτεραιότητας"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Μόνο\nειδοποιήσεις"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Φόρτιση (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> για πλήρη φόρτιση)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Γρήγορη φόρτιση (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> για πλήρη φόρτιση)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Αργή φόρτιση (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> για πλήρη φόρτιση)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Εναλλαγή χρήστη"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Εναλλαγή χρήστη, τρέχων χρήστης <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Τρέχων χρήστης <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index fb781d4..bef7661 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priority\nonly"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarms\nonly"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charging (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charging rapidly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charging slowly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Switch user"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Switch user, current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index fb781d4..bef7661 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priority\nonly"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarms\nonly"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charging (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charging rapidly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charging slowly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Switch user"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Switch user, current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index fb781d4..bef7661 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priority\nonly"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarms\nonly"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charging (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charging rapidly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charging slowly (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Switch user"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Switch user, current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Current user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 10834a2..1590265 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Solo\nprioridad"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Solo\nalarmas"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Cargando (faltan <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para completar)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Carga rápida (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para completar la carga)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Carga lenta (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para completar la carga)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Cambiar de usuario (usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"El usuario actual es <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 37c81f4..082e762 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Solo\ncon prioridad"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Solo\nalarmas"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Cargando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para completar)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Cargando rápidamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hasta completar)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Cargando lentamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hasta completar)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Cambiar de usuario"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Cambiar de usuario (usuario actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index f4218a3..4add147 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Ainult\nprioriteetsed"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Ainult\nalarmid"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laadimine (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>, kuni seade on täis)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Kiirlaadimine (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>, kuni seade on täis)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Aeglane laadimine (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>, kuni seade on täis)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Kasutaja vahetamine"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Kasutaja vahetamine, praegune kasutaja: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Praegune kasutaja <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 0e5532c..4f5c9f2 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Lehentasunezkoak\nsoilik"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarmak\nsoilik"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Kargatzen (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> guztiz kargatu arte)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Bizkor kargatzen (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> guztiz kargatu arte)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Mantso kargatzen (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> guztiz kargatu arte)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Aldatu erabiltzailea"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Aldatu erabiltzailez. <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> da saioa hasita duena."</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Uneko erabiltzailea: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 147cbe4..6fa7e1c 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"فقط\nاولویت‌دار"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"فقط\nهشدارها"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"در حال شارژ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> تا شارژ کامل)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"در حال شارژ سریع (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> تا شارژ کامل)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"در حال شارژ آهسته (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> تا شارژ کامل)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"تغییر کاربر"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"تعویض کاربر، کاربر کنونی <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"کاربر کنونی <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 6c4a029..655cf31 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Vain\ntärkeät"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Vain\nherätykset"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Ladataan (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kunnes täynnä)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Nopea lataus (latausaikaa jäljellä <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Hidas lataus (latausaikaa jäljellä <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Vaihda käyttäjää"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Vaihda käyttäjä (nyt <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Nykyinen käyttäjä: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 07a2fc3..2f2c331 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priorités\nuniquement"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarmes\nuniquement"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours... (chargée à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charge rapide en cours... (chargé dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charge lente en cours... (chargé dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Changer d\'utilisateur (utilisateur actuel <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 0aa45ae..9f34431 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priorité\nuniquement"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alarmes\nuniquement"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours… (chargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Charge rapide… (chargé à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Charge lente… (chargé à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Changer d\'utilisateur (utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index ec64f22..df90ef4 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Só\nprioridade"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Só\nalarmas"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Cargando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para finalizar a carga)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Cargando rápido (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para rematar a carga)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Cargando lento (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para rematar a carga)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Cambiar usuario, usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuario actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index e873c95..42ee460 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ફક્ત\nપ્રાધાન્યતા"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ફક્ત\nએલાર્મ્સ"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ચાર્જ થઈ રહ્યું છે (પૂર્ણ થવામાં <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> બાકી)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ઝડપથી ચાર્જિંગ થઇ રહી છે (પૂર્ણ થવામાં <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> બાકી)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ધીમેથી ચાર્જિંગ થઇ રહી છે (પૂર્ણ થવામાં <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> બાકી)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"વપરાશકર્તા સ્વિચ કરો"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"વપરાશકર્તાને સ્વિચ કરો, વર્તમાન વપરાશકર્તા <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"વર્તમાન વપરાશકર્તા <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 83ac46e..86f536d 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"केवल\nप्राथमिकता"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"केवल\nअलार्म"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"चार्ज हो रहा है (पूरा होने में <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> बाकी)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"तेज़ी से चार्ज हो रहा है (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> में हो जाएगा)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"धीरे चार्ज हो रहा है (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> में पूरा हो जाएगा)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"उपयोगकर्ता स्विच करें"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"उपयोगकर्ता स्विच करें, वर्तमान उपयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"वर्तमान उपयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index b5d9931..043e7ad 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -326,6 +326,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Samo\nprioritetno"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Samo\nalarmi"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Punjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napunjenosti)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Brzo punjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napunjenosti)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Sporo punjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napunjenosti)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Promjena korisnika"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Promjena korisnika, trenutačni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Trenutačan korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 79390a6..e2b0812 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Csak\nprioritás"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Csak\nriasztások"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Töltés (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> a teljes töltöttségig)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Gyors töltés (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> a teljes töltöttségig)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Lassú töltés (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> a teljes töltöttségig)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Felhasználóváltás"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Felhasználóváltás (a jelenlegi felhasználó: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Jelenlegi felhasználó (<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 9f48b26..f8fc232 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Միայն\nկարևորները"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Միայն\nզարթուցիչ"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> մինչև լրիվ լիցքավորումը)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Արագ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև լցվելը)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Դանդաղ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև լցվելը)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Անջատել օգտվողին"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Փոխել օգտվողին. ներկայիս օգտվողն է՝ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Ընթացիկ օգտվողը՝ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 60024115..7dbcda9 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Hanya\nprioritas"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Hanya\nalarm"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengisi daya (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hingga penuh)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Mengisi daya dengan cepat (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hingga penuh)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Mengisi daya dengan lambat (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hingga penuh)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Beralih pengguna"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Ganti pengguna, pengguna saat ini <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Pengguna saat ini <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index e8e0530..b28b6fd 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Aðeins\nforgangur"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Aðeins\nvekjarar"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Í hleðslu (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> fram að fullri hleðslu)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Í hraðri hleðslu (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> fram að fullri hleðslu)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Í hægri hleðslu (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> fram að fullri hleðslu)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Skipta um notanda"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Skipta um notanda; núverandi notandi er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Núverandi notandi er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 0d06636..54e6f56 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Solo con\npriorità"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Solo\nsveglie"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"In carica (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> al termine)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Ricarica veloce (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> al termine)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Ricarica lenta (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> al termine)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Cambio utente"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Cambia utente, utente corrente <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utente corrente <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 44058a3..c959feb 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"התראות בעדיפות\nבלבד"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"התראות\nבלבד"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"טוען (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> עד לסיום)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"בטעינה מהירה (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> עד למילוי)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"בטעינה איטית (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> עד למילוי)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"החלפת משתמש"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"החלף משתמש. המשתמש הנוכחי הוא <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"משתמש נוכחי <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index a034f4f..29476d8 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"重要な\n通知のみ"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"アラーム\nのみ"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中(フル充電まで<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"急速充電中(完了まで<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"低速充電中(完了まで<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ユーザーを切り替える"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ユーザーを切り替える、現在のユーザーは<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"現在のユーザー: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index f1035a8..d83c9e9 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"მხოლოდ\nპრიორიტეტულები"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"მხოლოდ\nგაფრთხილებები"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>-ის შეცვლა დასრულებამდე)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"იტენება სწრაფად (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> სრულ დატენვამდე)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"იტენება ნელა (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> სრულ დატენვამდე)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"მომხმარებლის გადართვა"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"მომხმარებლის გდართვა. ამჟამინდელი მომხმარებელი <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"ამჟამინდელი მომხმარებელი <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 8b82e5f..5c066b3 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Тек\nбасымдық"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Тек\nдабылдар"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарядталуда (толғанша <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Жылдам зарядталуда (толғанша <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Баяу зарядталуда (толғанша <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Пайдаланушыны ауыстыру"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Пайдаланушыны ауыстыру, ағымдағы пайдаланушы <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Ағымдағы пайдаланушы: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index b8db00f..4c469b0 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"អាទិភាព\nប៉ុណ្ណោះ"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"សំឡេងរោទ៍\nប៉ុណ្ណោះ"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"កំពុង​បញ្ចូល​ថ្ម (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ទើប​ពេញ)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ថ្មកំពុងសាកលឿន (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ទើបពេញ)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ថ្មកំពុងសាកយឺតៗ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ទើបពេញ)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ប្ដូរ​អ្នក​ប្រើ"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ប្ដូរ​អ្នកប្រើ ​អ្នកប្រើ​បច្ចុប្បន្ន <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"អ្នកប្រើបច្ចុប្បន្ន <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 316c04f..4215a91 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ಆದ್ಯತೆ\nಮಾತ್ರ"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ಅಲಾರಮ್‌ಗಳು\nಮಾತ್ರ"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ ( ಪೂರ್ತಿ ಆಗುವವರೆಗೆ <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ವೇಗವಾಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ (ಪೂರ್ಣಗೊಳ್ಳಲು <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ನಿಧಾನ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ (ಪೂರ್ಣಗೊಳ್ಳಲು <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ, ಪ್ರಸ್ತುತ ಬಳಕೆದಾರ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"<xliff:g id="CURRENT_USER_NAME">%s</xliff:g> ಪ್ರಸ್ತುತ ಬಳಕೆದಾರ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index d7f1543..8ee5946 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"중요 알림만\n허용"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"알람만\n"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"충전 중(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> 후 충전 완료)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"고속 충전 중(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> 후 충전 완료)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"저속 충전 중(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> 후 충전 완료)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"사용자 전환"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"사용자 전환, 현재 사용자 <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"현재 사용자: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 65f91a2..ce0afd4 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Артыкчылыктуу\nгана"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Ойготкучтар\nгана"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Кубатталууда (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> толгонго чейин)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Тез кубатталууда (толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> калды)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Жай кубатталууда (толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> калды)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Колдонуучуну которуу"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Колдонуучуну күйгүзүү, учурдагы колдонуучу <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Учурдагы колдонуучу <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 2893b99..6ef1ada 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -19,9 +19,6 @@
     <!-- thickness (width) of the navigation bar on phones that require it -->
     <dimen name="navigation_bar_size">@*android:dimen/navigation_bar_width</dimen>
 
-    <!-- The side padding for the task stack as a percentage of the width. -->
-    <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.26</item>
-
     <!-- Standard notification width + gravity -->
     <dimen name="notification_panel_width">@dimen/standard_notification_panel_width</dimen>
     <integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 50b4522..b45395e 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ບຸ​ລິ​ມະ​ສິດ\nເທົ່າ​ນັ້ນ"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ໂມງ​ປຸກ\nເທົ່າ​ນັ້ນ"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ກຳ​ລັງ​ສາກ​ໄຟ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ກວ່າ​ຈ​ະ​ເຕັມ)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ກຳ​ລັງ​ສາກ​ໄຟ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ກວ່າ​ຈ​ະ​ເຕັມ)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ກຳ​ລັງ​ສາກ​ໄຟ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ກວ່າ​ຈ​ະ​ເຕັມ)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ສະ​ລັບ​ຜູ່ໃຊ້"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ປ່ຽນຜູ່ໃຊ້, ຜູ່ໃຊ້ປະຈຸບັນ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"ຜູ້ໃຊ້ປະຈຸບັນ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 9082778..194668b 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Tik\nprioritetiniai"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Tik\nsignalai"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Kraunama (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> iki visiško įkrovimo)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Greitai kraunama (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> iki visiško įkrovimo)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Lėtai kraunama (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> iki visiško įkrovimo)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Perjungti naudotoją"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Perjungti naudotoją, dabartinis naudotojas <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Dabartinis naudotojas <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 5fb4bb0..2b3be58 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -326,6 +326,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Tikai\nprioritārie"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Tikai\nsignāli"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Notiek uzlāde (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> līdz pilnīgai uzlādei)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Ātra uzlāde (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> līdz pilnīgai uzlādei)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Lēna uzlāde (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> līdz pilnīgai uzlādei)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Mainīt lietotāju"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Pārslēgt lietotāju; pašreizējais lietotājs: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Pašreizējais lietotājs: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 8571b3f..912c30f 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Само\nприоритетни"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Само\nаларми"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Се полни (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> додека не се наполни)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Брзо полнење (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> додека не се наполни)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Бавно полнење (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> додека не се наполни)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Промени го корисникот"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Промени го корисникот, тековен корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Тековен корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 4b11962..c85ecc7 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"മുൻഗണന\nമാത്രം"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"അലാറങ്ങൾ\nമാത്രം"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ചാർജ്ജുചെയ്യുന്നു (പൂർണ്ണമാകുന്നതിന്, <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"വേഗത്തിൽ ചാർജുചെയ്യുന്നു (പൂർണ്ണമാകാൻ <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"പതുക്കെ ചാർജുചെയ്യുന്നു (പൂർണ്ണമാകാൻ <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ഉപയോക്താവ് മാറുക"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ഉപയോക്താവിനെ മാറ്റുക, <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> എന്നയാളാണ് നിലവിലുള്ള ഉപയോക്താവ്"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"നിലവിലെ ഉപയോക്താവ് <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index d1dbd6d..6b29aff 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -323,6 +323,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Зөвхөн\nхамгийн чухлыг"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Зөвхөн\nсэрүүлэг"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Цэнэглэж байна (дүүргэхэд <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Цэнэглэж байна (дүүргэхэд <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> шаардлагатай)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Цэнэглэж байна (дүүргэхэд <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> шаардлагатай)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Хэрэглэгчийг сэлгэх"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Хэрэглэгчийг сэлгэх, одоогийн хэрэглэгч <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Одоогийн хэрэглэгч <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 2b9d953..9d518a0 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"केवळ\nप्राधान्य"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"केवळ\nअलार्म"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण होईपर्यंत) चार्ज होत आहे"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण होईपर्यंत) वेगाने चार्ज होत आहे"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण होईपर्यंत) हळूहळू चार्ज होत आहे"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"वापरकर्ता स्विच करा"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"वापरकर्ता स्विच करा, वर्तमान वापरकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"वर्तमान वापरकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 01f7edf..10dca3e 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Keutamaan\nsahaja"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Penggera\nsahaja"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengecas (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> sehingga penuh)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Mengecas cepat (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> sehingga penuh)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Mengecas perlahan (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> sehingga penuh)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Tukar pengguna"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Tukar pengguna, pengguna semasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Pengguna semasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index d0eaccf..adfd9f8 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ဦးစားပေးမှု\nသာ"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"နှိုးစက်များ\nသာ"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> အပြည့် အထိ) အားသွင်းနေ"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"လျှင်မြန်စွာအားသွင်းခြင်း (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ပြည့်သည်အထိ)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"နှေးကွေးစွာ အားသွင်းခြင်း (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ပြည့်သည်အထိ)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"အသုံးပြုသူကို ပြောင်းရန်၊ လက်ရှိ အသုံးပြုသူ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"လတ်တလော သုံးစွဲသူ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index e582039..ecfb7f2 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Bare\nPrioritet"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Bare\nalarmer"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Lader (fulladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Lader raskt (fulladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Lader sakte (fulladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Bytt bruker"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Bytt bruker, gjeldende bruker er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Gjeldende bruker: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index c3dc703..d997aff 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"प्राथमिकता \nमात्र"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"अलार्महरू \nमात्र"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"चार्ज हुँदै (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण भएसम्म)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"छिटो चार्ज हुँदै (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण नभएसम्म)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"बिस्तारै चार्ज हुँदै (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> पूर्ण नभएसम्म)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"प्रयोगकर्ता फेर्नुहोस्"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"प्रयोगकर्ता, हालको प्रयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> मा स्विच गर्नुहोस्"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"हालको प्रयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 79f3333..c6bab06 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Alleen\nprioriteit"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Alleen\nalarmen"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Opladen (vol over <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Snel opladen (vol over <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Langzaam opladen (vol over <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Gebruiker wijzigen"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Schakelen tussen gebruikers, huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index ef3d2f3..74e5fcb 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ਕੇਵਲ\nਤਰਜੀਹੀ"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ਕੇਵਲ\nਅਲਾਰਮ"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ਚਾਰਜਿੰਗ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ਪੂਰਾ ਹੋਣ ਤੱਕ)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ਤੇਜ਼ੀ ਨਾਲ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ਪੂਰੀ ਹੋਣ ਤੱਕ)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ਪੂਰੀ ਹੋਣ ਤੱਕ)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"ਉਪਭੋਗਤਾ ਸਵਿਚ ਕਰੋ"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"ਉਪਭੋਗਤਾ, ਵਰਤਮਾਨ ਉਪਭੋਗਤਾ ਸਵਿਚ ਕਰੋ<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"ਮੌਜੂਦਾ ਉਪਭੋਗਤਾ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 8c2c5c4..e04fd71 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Tylko\npriorytetowe"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Tylko\nalarmy"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Ładuje się (pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Szybkie ładowanie (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do końca)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Wolne ładowanie (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do końca)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Przełącz użytkownika"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Przełącz użytkownika. Bieżący użytkownik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Bieżący użytkownik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 3803b81..2a8e3ab 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Somente\nprioridade"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Somente\nalarmes"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até concluir)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Carregando rapidamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para conclusão)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Carregando lentamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para conclusão)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Trocar usuário"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Alternar usuário. Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 12609c7..8453e5b 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Apenas\nprioridade"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Apenas\nalarmes"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"A carregar (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até à carga máxima)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"A carregar rapid. (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até à carga máxima)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"A carregar lentam. (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até à carga máxima)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Mudar utilizador"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Mudar de utilizador; o utilizador atual é <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilizador atual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 3803b81..2a8e3ab 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Somente\nprioridade"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Somente\nalarmes"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até concluir)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Carregando rapidamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para conclusão)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Carregando lentamente (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para conclusão)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Trocar usuário"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Alternar usuário. Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index bb8f2c3..d85e791 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -326,6 +326,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Numai\ncu prioritate"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Numai\nalarme"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Se încarcă (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> până la finalizare)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Se încarcă rapid (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> până la finalizare)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Se încarcă lent (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> până la finalizare)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Comutați între utilizatori"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Schimbați utilizatorul (utilizator actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Utilizator actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 9609f40..39d8617 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -329,6 +329,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Только\nважные"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Только\nбудильник"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарядка батареи (осталось <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Быстрая зарядка (осталось <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Медленная зарядка (осталось <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Сменить пользователя."</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Сменить аккаунт. Вход выполнен под именем <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>."</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Выбран аккаунт пользователя <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 9e78834..51bb047 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ප්‍රමුඛතා\nපමණි"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"ඇඟවීම්\nපමණි"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ආරෝපණය වෙමින් (සම්පුර්ණ වන තෙක් <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"ඉක්මනින් ආරෝපණය වෙමින් (සම්පුර්ණ වන තෙක් <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"සෙමින් ආරෝපණය වෙමින් (සම්පුර්ණ වන තෙක් <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"පරිශීලක මාරුව"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"පරිශීලකයා මාරු කරන්න,දැන් සිටින පරිශීලකයා <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"වත්මන් පරිශීලක <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 2e41b29..2886e36 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -329,6 +329,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Iba\nprioritné"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Iba\nbudíky"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nabíja sa (úplné nabitie o <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Nabíja sa rýchlo (úplné nabitie o <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Nabíja sa pomaly (úplné nabitie o <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Prepnutie používateľa"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Prepnúť používateľa (súčasný používateľ: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Aktuálny používateľ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 4dffcfa..e325ef1 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Samo\nprednostno"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Samo\nalarmi"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Polnjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napolnjenosti)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Hitro polnjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napolnjenosti)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Počasno polnjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napolnjenosti)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Preklop med uporabniki"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Preklop med uporabniki, trenutni uporabnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Trenutni uporabnik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index 20c3426..5bcf941 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Vetëm\nme prioritet"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Vetëm\nalarmet"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Po ngarkohet (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> deri sa të mbushet)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Po ngarkon me shpejtësi (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> derisa të mbushet)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Po ngarkon me ngadalë (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> derisa të mbushet)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Ndërro përdorues"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Ndërro përdoruesin. Përdoruesi aktual është <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Përdoruesi aktual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 1dcde3a..522b2af 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -326,6 +326,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Само\nприорит. прекиди"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Само\nаларми"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Пуњење (пун је за <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Брзо се пуни (напуниће се за <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Споро се пуни (напуниће се за <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Замени корисника"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Промените корисника, актуелни корисник је <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Актуелни корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 0abeb1e..d91335c 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Endast\nprioriterade"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Endast\nalarm"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laddar (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tills batteriet är fulladdat)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Laddas snabbt (batteriet fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Laddas sakta (batteriet fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Byt användare"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Byt användare. Aktuell användare: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Aktuell användare <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index d000004..1cf4293 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Kipaumbele\npekee"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Kengele\npekee"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Inachaji (Imebakisha <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ijae)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Inachaji kwa kasi (itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Inachaji pole pole (itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Badili mtumiaji"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Badili mtumiaji, mtumiaji wa sasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Mtumiaji wa sasa <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 81ca86b..f084bc2 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -16,10 +16,6 @@
 */
 -->
 <resources>
-    <!-- Recent Applications parameters -->
-    <!-- The side padding for the task stack as a percentage of the width. -->
-    <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.25</item>
-
     <fraction name="keyguard_clock_y_fraction_max">37%</fraction>
     <fraction name="keyguard_clock_y_fraction_min">20%</fraction>
 
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 83477c0..4f6d209 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -32,9 +32,4 @@
 
     <!-- Set to true to enable the user switcher on the keyguard. -->
     <bool name="config_keyguardUserSwitcher">true</bool>
-
-    <!-- Transposes the search bar layout in landscape. -->
-    <bool name="recents_has_transposed_search_bar">true</bool>
-    <!-- Transposes the nav bar in landscape (only used for purposes of layout). -->
-    <bool name="recents_has_transposed_nav_bar">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 6deb818..49dbac2 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -39,12 +39,6 @@
     <!-- On tablets this is just the close_handle_height -->
     <dimen name="peek_height">@dimen/close_handle_height</dimen>
 
-    <!-- The side padding for the task stack as a percentage of the width. -->
-    <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.075</item>
-
-    <!-- The height of the search bar space. -->
-    <dimen name="recents_search_bar_space_height">72dp</dimen>
-
     <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
          max value is used when no notifications are displaying, and the min value is when the
          highest possible number of notifications are showing. -->
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index 1efae42..64e2760 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -25,10 +25,5 @@
     <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
          card. -->
     <integer name="keyguard_max_notification_count">5</integer>
-
-    <!-- Transposes the search bar layout in landscape. -->
-    <bool name="recents_has_transposed_search_bar">false</bool>
-    <!-- Transposes the nav bar in landscape (only used for purposes of layout). -->
-    <bool name="recents_has_transposed_nav_bar">false</bool>
 </resources>
 
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index 7b1ded3..670bccc 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"முன்னுரிமைகள்\nமட்டும்"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"அலாரங்கள்\nமட்டும்"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"சார்ஜாகிறது (முழு சார்ஜிற்கு <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ஆகும்)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"வேகமாக சார்ஜாகிறது (முழு சார்ஜிற்கு: <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"மெதுவாக சார்ஜாகிறது (முழு சார்ஜிற்கு: <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"பயனரை மாற்று"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"பயனரை மாற்று, தற்போதைய பயனர் <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"தற்போதைய பயனர்: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 06762bd..bbd9979 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"ప్రాధాన్యమైనవి\nమాత్రమే"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"అలారాలు\nమాత్రమే"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ఛార్జ్ అవుతోంది (పూర్తిగా నిండటానికి <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"వేగంగా ఛార్జ్ అవుతోంది (నిండటానికి <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"నెమ్మదిగా ఛార్జ్ అవుతోంది (నిండటానికి <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"వినియోగదారుని మార్చు"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"వినియోగదారుని మార్చు, ప్రస్తుత వినియోగదారు <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"ప్రస్తుత వినియోగదారు <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 52c7af7..2486519 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"เฉพาะเรื่อง\nสำคัญ"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"เฉพาะปลุก\nเท่านั้น"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"กำลังชาร์จ (อีก <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> เต็ม)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"กำลังชาร์จอย่างรวดเร็ว (อีก <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> จะเต็ม)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"กำลังชาร์จอย่างช้าๆ (อีก <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> จะเต็ม)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"สลับผู้ใช้"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"เปลี่ยนผู้ใช้จากผู้ใช้ปัจจุบัน <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"ผู้ใช้ปัจจุบัน <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 66056d1..3d1243e 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Priyoridad\nlang"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Mga alarm\nlang"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nagtsa-charge (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hanggang mapuno)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Mabilis mag-charge (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hanggang sa mapuno)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Mabagal mag-charge (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hanggang sa mapuno)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Magpalit ng user"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Magpalit ng user, kasalukuyang user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Kasalukuyang user <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 49edb57..4dadfbc 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Yalnızca\nöncelik"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Yalnızca\nalarmlar"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Şarj oluyor (tamamen dolmasına <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kaldı)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Hızlı şarj oluyor (tam dolmasına <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kaldı)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Yavaş şarj oluyor (tam dolmasına <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kaldı)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Kullanıcı değiştirme"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Kullanıcı değiştir. Geçerli kullanıcı: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Geçerli kullanıcı: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 9dde801..d158dbe 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Лише\nприорітетні"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Лише\nсигнали"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Заряджання (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до повного зарядження)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Швидке заряджання (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до повного заряду)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Повільне заряджання (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до повного заряду)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Змінити користувача"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Змінити користувача, поточний користувач – <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Поточний користувач: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index a4ef16a..f8d8c71 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"صرف\nترجیحی"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"صرف\nالارمز"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"چارج ہو رہا ہے (مکمل ہونے تک <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> باقی ہیں)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"تیزی سے چارج ہو رہا ہے (مکمل ہونے میں <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"آہستہ چارج ہو رہا ہے (مکمل ہونے میں <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"صارف سوئچ کریں"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"صارف سوئچ کریں، موجودہ صارف <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"موجودہ صارف <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 9f9fe5f..0186809 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Faqat\nmuhimlar"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Faqat\nsignallar"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Quvvat olmoqda (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>da to‘ladi)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Tez quvvat olmoqda (to‘lishiga <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> qoldi)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Sekin quvvat olmoqda (to‘lishiga <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> qoldi)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Foydalanuvchini almashtirish"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Foydalanuvchini o‘zgartirish. Joriy foydalanuvchi – <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Joriy foydalanuvchi <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 460d57a..49b5d1d 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Chỉ\nưu tiên"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Chỉ\nbáo thức"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Đang sạc (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> cho đến khi đầy)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Sạc nhanh (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> cho tới khi đầy)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Sạc chậm (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> cho tới khi đầy)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Chuyển đổi người dùng"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Chuyển người dùng, người dùng hiện tại <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Người dùng hiện tại <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 4d70f24..93ed5ca 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"仅限\n优先打扰"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"仅限\n闹钟"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"正在充电(还需<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>充满)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"正在快速充电(还需 <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>才能充满)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"正在慢速充电(还需 <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>才能充满)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"切换用户"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"切换用户,当前用户为<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"当前用户为<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index f7cac90..2196111 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"僅限\n優先"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"僅限\n鬧鐘"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後完成充電)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"正在快速充電 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後完成充電)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"正在緩慢充電 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後完成充電)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"切換使用者"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"切換使用者,目前使用者是<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"目前的使用者是 <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 369172f..fe4fe06 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -327,6 +327,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"僅允許\n優先通知"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"僅允許\n鬧鐘"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後充飽)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"快速充電中 (充飽需要 <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"慢速充電中 (充飽需要 <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"切換使用者"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"切換使用者,目前使用者是<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"目前使用者是「<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>」"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 2a14a9f..2f068af 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -325,6 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Okubalulekile\nkuphela"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Ama-alamu\nkuphela"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Iyashaja (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ize igcwale)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Iyashaja ngokushesha (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ize igcwale)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Iyashaja kancane (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ize igcwale)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Shintsha umsebenzisi"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Shintsha umsebenzisi, umsebenzisi wamanje ngu-<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Umsebenzisi wamanje <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f28c9d3..1d19589 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -189,12 +189,6 @@
     <!-- The delay to enforce between each alt-tab key press. -->
     <integer name="recents_alt_tab_key_delay">200</integer>
 
-    <!-- Transposes the search bar layout in landscape. -->
-    <bool name="recents_has_transposed_search_bar">true</bool>
-
-    <!-- Transposes the nav bar in landscape (only used for purposes of layout). -->
-    <bool name="recents_has_transposed_nav_bar">true</bool>
-
     <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
     <integer name="recents_svelte_level">0</integer>
 
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
new file mode 100755
index 0000000..3eb1271
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.animation.ArgbEvaluator;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.database.ContentObserver;
+import android.graphics.*;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Settings;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+public class BatteryMeterDrawable extends Drawable implements DemoMode,
+        BatteryController.BatteryStateChangeCallback {
+
+    private static final float ASPECT_RATIO = 9.5f / 14.5f;
+    public static final String TAG = BatteryMeterDrawable.class.getSimpleName();
+    public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent";
+
+    private static final boolean SINGLE_DIGIT_PERCENT = false;
+
+    private static final int FULL = 96;
+
+    private static final float BOLT_LEVEL_THRESHOLD = 0.3f;  // opaque bolt below this fraction
+
+    private final int[] mColors;
+
+    private boolean mShowPercent;
+    private float mButtonHeightFraction;
+    private float mSubpixelSmoothingLeft;
+    private float mSubpixelSmoothingRight;
+    private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
+    private float mTextHeight, mWarningTextHeight;
+    private int mIconTint = Color.WHITE;
+
+    private int mHeight;
+    private int mWidth;
+    private String mWarningString;
+    private final int mCriticalLevel;
+    private int mChargeColor;
+    private final float[] mBoltPoints;
+    private final Path mBoltPath = new Path();
+
+    private final RectF mFrame = new RectF();
+    private final RectF mButtonFrame = new RectF();
+    private final RectF mBoltFrame = new RectF();
+
+    private final Path mShapePath = new Path();
+    private final Path mClipPath = new Path();
+    private final Path mTextPath = new Path();
+
+    private BatteryController mBatteryController;
+    private boolean mPowerSaveEnabled;
+
+    private int mDarkModeBackgroundColor;
+    private int mDarkModeFillColor;
+
+    private int mLightModeBackgroundColor;
+    private int mLightModeFillColor;
+
+    private final SettingObserver mSettingObserver = new SettingObserver();
+
+    private final Context mContext;
+    private final Handler mHandler;
+
+    private int mLevel = -1;
+    private boolean mPluggedIn;
+    private boolean mListening;
+
+    public BatteryMeterDrawable(Context context, Handler handler, int frameColor) {
+        mContext = context;
+        mHandler = handler;
+        final Resources res = context.getResources();
+        TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
+        TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);
+
+        final int N = levels.length();
+        mColors = new int[2*N];
+        for (int i=0; i<N; i++) {
+            mColors[2*i] = levels.getInt(i, 0);
+            mColors[2*i+1] = colors.getColor(i, 0);
+        }
+        levels.recycle();
+        colors.recycle();
+        updateShowPercent();
+        mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
+        mCriticalLevel = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
+        mButtonHeightFraction = context.getResources().getFraction(
+                R.fraction.battery_button_height_fraction, 1, 1);
+        mSubpixelSmoothingLeft = context.getResources().getFraction(
+                R.fraction.battery_subpixel_smoothing_left, 1, 1);
+        mSubpixelSmoothingRight = context.getResources().getFraction(
+                R.fraction.battery_subpixel_smoothing_right, 1, 1);
+
+        mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mFramePaint.setColor(frameColor);
+        mFramePaint.setDither(true);
+        mFramePaint.setStrokeWidth(0);
+        mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);
+
+        mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mBatteryPaint.setDither(true);
+        mBatteryPaint.setStrokeWidth(0);
+        mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+
+        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD);
+        mTextPaint.setTypeface(font);
+        mTextPaint.setTextAlign(Paint.Align.CENTER);
+
+        mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mWarningTextPaint.setColor(mColors[1]);
+        font = Typeface.create("sans-serif", Typeface.BOLD);
+        mWarningTextPaint.setTypeface(font);
+        mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
+
+        mChargeColor = context.getColor(R.color.batterymeter_charge_color);
+
+        mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mBoltPaint.setColor(context.getColor(R.color.batterymeter_bolt_color));
+        mBoltPoints = loadBoltPoints(res);
+
+        mDarkModeBackgroundColor =
+                context.getColor(R.color.dark_mode_icon_color_dual_tone_background);
+        mDarkModeFillColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_fill);
+        mLightModeBackgroundColor =
+                context.getColor(R.color.light_mode_icon_color_dual_tone_background);
+        mLightModeFillColor = context.getColor(R.color.light_mode_icon_color_dual_tone_fill);
+    }
+
+    public void startListening() {
+        mListening = true;
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
+        if (mDemoMode) return;
+        mBatteryController.addStateChangedCallback(this);
+    }
+
+    public void stopListening() {
+        mListening = false;
+        mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
+        if (mDemoMode) return;
+        mBatteryController.removeStateChangedCallback(this);
+    }
+
+    private void postInvalidate() {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                invalidateSelf();
+            }
+        });
+    }
+
+    public void setBatteryController(BatteryController batteryController) {
+        mBatteryController = batteryController;
+        mPowerSaveEnabled = mBatteryController.isPowerSave();
+    }
+
+    @Override
+    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+        mLevel = level;
+        mPluggedIn = pluggedIn;
+
+        postInvalidate();
+    }
+
+    @Override
+    public void onPowerSaveChanged() {
+        mPowerSaveEnabled = mBatteryController.isPowerSave();
+        invalidateSelf();
+    }
+
+    private static float[] loadBoltPoints(Resources res) {
+        final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);
+        int maxX = 0, maxY = 0;
+        for (int i = 0; i < pts.length; i += 2) {
+            maxX = Math.max(maxX, pts[i]);
+            maxY = Math.max(maxY, pts[i + 1]);
+        }
+        final float[] ptsF = new float[pts.length];
+        for (int i = 0; i < pts.length; i += 2) {
+            ptsF[i] = (float)pts[i] / maxX;
+            ptsF[i + 1] = (float)pts[i + 1] / maxY;
+        }
+        return ptsF;
+    }
+
+    @Override
+    public void setBounds(int left, int top, int right, int bottom) {
+        super.setBounds(left, top, right, bottom);
+        mHeight = bottom - top;
+        mWidth = right - left;
+        mWarningTextPaint.setTextSize(mHeight * 0.75f);
+        mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;
+    }
+
+    private void updateShowPercent() {
+        mShowPercent = 0 != Settings.System.getInt(mContext.getContentResolver(),
+                SHOW_PERCENT_SETTING, 0);
+    }
+
+    private int getColorForLevel(int percent) {
+
+        // If we are in power save mode, always use the normal color.
+        if (mPowerSaveEnabled) {
+            return mColors[mColors.length-1];
+        }
+        int thresh, color = 0;
+        for (int i=0; i<mColors.length; i+=2) {
+            thresh = mColors[i];
+            color = mColors[i+1];
+            if (percent <= thresh) {
+
+                // Respect tinting for "normal" level
+                if (i == mColors.length-2) {
+                    return mIconTint;
+                } else {
+                    return color;
+                }
+            }
+        }
+        return color;
+    }
+
+    public void setDarkIntensity(float darkIntensity) {
+        int backgroundColor = getBackgroundColor(darkIntensity);
+        int fillColor = getFillColor(darkIntensity);
+        mIconTint = fillColor;
+        mFramePaint.setColor(backgroundColor);
+        mBoltPaint.setColor(fillColor);
+        mChargeColor = fillColor;
+        invalidateSelf();
+    }
+
+    private int getBackgroundColor(float darkIntensity) {
+        return getColorForDarkIntensity(
+                darkIntensity, mLightModeBackgroundColor, mDarkModeBackgroundColor);
+    }
+
+    private int getFillColor(float darkIntensity) {
+        return getColorForDarkIntensity(
+                darkIntensity, mLightModeFillColor, mDarkModeFillColor);
+    }
+
+    private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) {
+        return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor);
+    }
+
+    @Override
+    public void draw(Canvas c) {
+        final int level = mLevel;
+
+        if (level == -1) return;
+
+        float drawFrac = (float) level / 100f;
+        final int height = mHeight;
+        final int width = (int) (ASPECT_RATIO * mHeight);
+        int px = (mWidth - width) / 2;
+
+        final int buttonHeight = (int) (height * mButtonHeightFraction);
+
+        mFrame.set(0, 0, width, height);
+        mFrame.offset(px, 0);
+
+        // button-frame: area above the battery body
+        mButtonFrame.set(
+                mFrame.left + Math.round(width * 0.25f),
+                mFrame.top,
+                mFrame.right - Math.round(width * 0.25f),
+                mFrame.top + buttonHeight);
+
+        mButtonFrame.top += mSubpixelSmoothingLeft;
+        mButtonFrame.left += mSubpixelSmoothingLeft;
+        mButtonFrame.right -= mSubpixelSmoothingRight;
+
+        // frame: battery body area
+        mFrame.top += buttonHeight;
+        mFrame.left += mSubpixelSmoothingLeft;
+        mFrame.top += mSubpixelSmoothingLeft;
+        mFrame.right -= mSubpixelSmoothingRight;
+        mFrame.bottom -= mSubpixelSmoothingRight;
+
+        // set the battery charging color
+        mBatteryPaint.setColor(mPluggedIn ? mChargeColor : getColorForLevel(level));
+
+        if (level >= FULL) {
+            drawFrac = 1f;
+        } else if (level <= mCriticalLevel) {
+            drawFrac = 0f;
+        }
+
+        final float levelTop = drawFrac == 1f ? mButtonFrame.top
+                : (mFrame.top + (mFrame.height() * (1f - drawFrac)));
+
+        // define the battery shape
+        mShapePath.reset();
+        mShapePath.moveTo(mButtonFrame.left, mButtonFrame.top);
+        mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top);
+        mShapePath.lineTo(mButtonFrame.right, mFrame.top);
+        mShapePath.lineTo(mFrame.right, mFrame.top);
+        mShapePath.lineTo(mFrame.right, mFrame.bottom);
+        mShapePath.lineTo(mFrame.left, mFrame.bottom);
+        mShapePath.lineTo(mFrame.left, mFrame.top);
+        mShapePath.lineTo(mButtonFrame.left, mFrame.top);
+        mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top);
+
+        if (mPluggedIn) {
+            // define the bolt shape
+            final float bl = mFrame.left + mFrame.width() / 4.5f;
+            final float bt = mFrame.top + mFrame.height() / 6f;
+            final float br = mFrame.right - mFrame.width() / 7f;
+            final float bb = mFrame.bottom - mFrame.height() / 10f;
+            if (mBoltFrame.left != bl || mBoltFrame.top != bt
+                    || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
+                mBoltFrame.set(bl, bt, br, bb);
+                mBoltPath.reset();
+                mBoltPath.moveTo(
+                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
+                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
+                for (int i = 2; i < mBoltPoints.length; i += 2) {
+                    mBoltPath.lineTo(
+                            mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
+                            mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
+                }
+                mBoltPath.lineTo(
+                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
+                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
+            }
+
+            float boltPct = (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top);
+            boltPct = Math.min(Math.max(boltPct, 0), 1);
+            if (boltPct <= BOLT_LEVEL_THRESHOLD) {
+                // draw the bolt if opaque
+                c.drawPath(mBoltPath, mBoltPaint);
+            } else {
+                // otherwise cut the bolt out of the overall shape
+                mShapePath.op(mBoltPath, Path.Op.DIFFERENCE);
+            }
+        }
+
+        // compute percentage text
+        boolean pctOpaque = false;
+        float pctX = 0, pctY = 0;
+        String pctText = null;
+        if (!mPluggedIn && level > mCriticalLevel && mShowPercent) {
+            mTextPaint.setColor(getColorForLevel(level));
+            mTextPaint.setTextSize(height *
+                    (SINGLE_DIGIT_PERCENT ? 0.75f
+                            : (mLevel == 100 ? 0.38f : 0.5f)));
+            mTextHeight = -mTextPaint.getFontMetrics().ascent;
+            pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);
+            pctX = mWidth * 0.5f;
+            pctY = (mHeight + mTextHeight) * 0.47f;
+            pctOpaque = levelTop > pctY;
+            if (!pctOpaque) {
+                mTextPath.reset();
+                mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath);
+                // cut the percentage text out of the overall shape
+                mShapePath.op(mTextPath, Path.Op.DIFFERENCE);
+            }
+        }
+
+        // draw the battery shape background
+        c.drawPath(mShapePath, mFramePaint);
+
+        // draw the battery shape, clipped to charging level
+        mFrame.top = levelTop;
+        mClipPath.reset();
+        mClipPath.addRect(mFrame,  Path.Direction.CCW);
+        mShapePath.op(mClipPath, Path.Op.INTERSECT);
+        c.drawPath(mShapePath, mBatteryPaint);
+
+        if (!mPluggedIn) {
+            if (level <= mCriticalLevel) {
+                // draw the warning text
+                final float x = mWidth * 0.5f;
+                final float y = (mHeight + mWarningTextHeight) * 0.48f;
+                c.drawText(mWarningString, x, y, mWarningTextPaint);
+            } else if (pctOpaque) {
+                // draw the percentage text
+                c.drawText(pctText, pctX, pctY, mTextPaint);
+            }
+        }
+    }
+
+    // Some stuff required by Drawable.
+    @Override
+    public void setAlpha(int alpha) {
+    }
+
+    @Override
+    public void setColorFilter(@Nullable ColorFilter colorFilter) {
+    }
+
+    @Override
+    public int getOpacity() {
+        return 0;
+    }
+
+    private boolean mDemoMode;
+
+    @Override
+    public void dispatchDemoCommand(String command, Bundle args) {
+        if (!mDemoMode && command.equals(COMMAND_ENTER)) {
+            mBatteryController.removeStateChangedCallback(this);
+            mDemoMode = true;
+            if (mListening) {
+                mBatteryController.removeStateChangedCallback(this);
+            }
+        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
+            mDemoMode = false;
+            postInvalidate();
+            if (mListening) {
+                mBatteryController.addStateChangedCallback(this);
+            }
+        } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
+           String level = args.getString("level");
+           String plugged = args.getString("plugged");
+           if (level != null) {
+               mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
+           }
+           if (plugged != null) {
+               mPluggedIn = Boolean.parseBoolean(plugged);
+           }
+           postInvalidate();
+        }
+    }
+
+    private final class SettingObserver extends ContentObserver {
+        public SettingObserver() {
+            super(new Handler());
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            super.onChange(selfChange, uri);
+            updateShowPercent();
+            postInvalidate();
+        }
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
old mode 100755
new mode 100644
index 95b58e5..6cb8da4
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -13,82 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.systemui;
 
-import android.animation.ArgbEvaluator;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.database.ContentObserver;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.RectF;
-import android.graphics.Typeface;
-import android.net.Uri;
-import android.os.BatteryManager;
-import android.os.Bundle;
 import android.os.Handler;
-import android.provider.Settings;
 import android.util.AttributeSet;
-import android.view.View;
-
+import android.widget.ImageView;
 import com.android.systemui.statusbar.policy.BatteryController;
 
-public class BatteryMeterView extends View implements DemoMode,
-        BatteryController.BatteryStateChangeCallback {
-    public static final String TAG = BatteryMeterView.class.getSimpleName();
-    public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
-    public static final String SHOW_PERCENT_SETTING = "status_bar_show_battery_percent";
+public class BatteryMeterView extends ImageView implements BatteryController.BatteryStateChangeCallback {
 
-    private static final boolean SINGLE_DIGIT_PERCENT = false;
-
-    private static final int FULL = 96;
-
-    private static final float BOLT_LEVEL_THRESHOLD = 0.3f;  // opaque bolt below this fraction
-
-    private final int[] mColors;
-
-    private boolean mShowPercent;
-    private float mButtonHeightFraction;
-    private float mSubpixelSmoothingLeft;
-    private float mSubpixelSmoothingRight;
-    private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
-    private float mTextHeight, mWarningTextHeight;
-    private int mIconTint = Color.WHITE;
-
-    private int mHeight;
-    private int mWidth;
-    private String mWarningString;
-    private final int mCriticalLevel;
-    private int mChargeColor;
-    private final float[] mBoltPoints;
-    private final Path mBoltPath = new Path();
-
-    private final RectF mFrame = new RectF();
-    private final RectF mButtonFrame = new RectF();
-    private final RectF mBoltFrame = new RectF();
-
-    private final Path mShapePath = new Path();
-    private final Path mClipPath = new Path();
-    private final Path mTextPath = new Path();
-
+    private final BatteryMeterDrawable mDrawable;
     private BatteryController mBatteryController;
-    private boolean mPowerSaveEnabled;
-
-    private int mDarkModeBackgroundColor;
-    private int mDarkModeFillColor;
-
-    private int mLightModeBackgroundColor;
-    private int mLightModeFillColor;
-
-    private BatteryTracker mTracker = new BatteryTracker();
-    private final SettingObserver mSettingObserver = new SettingObserver();
 
     public BatteryMeterView(Context context) {
         this(context, null, 0);
@@ -101,326 +38,14 @@
     public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        final Resources res = context.getResources();
         TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView,
                 defStyle, 0);
         final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
                 context.getColor(R.color.batterymeter_frame_color));
-        TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
-        TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);
-
-        final int N = levels.length();
-        mColors = new int[2*N];
-        for (int i=0; i<N; i++) {
-            mColors[2*i] = levels.getInt(i, 0);
-            mColors[2*i+1] = colors.getColor(i, 0);
-        }
-        levels.recycle();
-        colors.recycle();
+        mDrawable = new BatteryMeterDrawable(context, new Handler(), frameColor);
         atts.recycle();
-        updateShowPercent();
-        mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
-        mCriticalLevel = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
-        mButtonHeightFraction = context.getResources().getFraction(
-                R.fraction.battery_button_height_fraction, 1, 1);
-        mSubpixelSmoothingLeft = context.getResources().getFraction(
-                R.fraction.battery_subpixel_smoothing_left, 1, 1);
-        mSubpixelSmoothingRight = context.getResources().getFraction(
-                R.fraction.battery_subpixel_smoothing_right, 1, 1);
 
-        mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mFramePaint.setColor(frameColor);
-        mFramePaint.setDither(true);
-        mFramePaint.setStrokeWidth(0);
-        mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);
-
-        mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mBatteryPaint.setDither(true);
-        mBatteryPaint.setStrokeWidth(0);
-        mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);
-
-        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD);
-        mTextPaint.setTypeface(font);
-        mTextPaint.setTextAlign(Paint.Align.CENTER);
-
-        mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mWarningTextPaint.setColor(mColors[1]);
-        font = Typeface.create("sans-serif", Typeface.BOLD);
-        mWarningTextPaint.setTypeface(font);
-        mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
-
-        mChargeColor = context.getColor(R.color.batterymeter_charge_color);
-
-        mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mBoltPaint.setColor(context.getColor(R.color.batterymeter_bolt_color));
-        mBoltPoints = loadBoltPoints(res);
-
-        mDarkModeBackgroundColor =
-                context.getColor(R.color.dark_mode_icon_color_dual_tone_background);
-        mDarkModeFillColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_fill);
-        mLightModeBackgroundColor =
-                context.getColor(R.color.light_mode_icon_color_dual_tone_background);
-        mLightModeFillColor = context.getColor(R.color.light_mode_icon_color_dual_tone_fill);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-        filter.addAction(ACTION_LEVEL_TEST);
-        final Intent sticky = getContext().registerReceiver(mTracker, filter);
-        if (sticky != null) {
-            // preload the battery level
-            mTracker.onReceive(getContext(), sticky);
-        }
-        mBatteryController.addStateChangedCallback(this);
-        getContext().getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-
-        getContext().unregisterReceiver(mTracker);
-        mBatteryController.removeStateChangedCallback(this);
-        getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
-    }
-
-    public void setBatteryController(BatteryController batteryController) {
-        mBatteryController = batteryController;
-        mPowerSaveEnabled = mBatteryController.isPowerSave();
-    }
-
-    @Override
-    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
-        // TODO: Use this callback instead of own broadcast receiver.
-    }
-
-    @Override
-    public void onPowerSaveChanged() {
-        mPowerSaveEnabled = mBatteryController.isPowerSave();
-        invalidate();
-    }
-
-    private static float[] loadBoltPoints(Resources res) {
-        final int[] pts = res.getIntArray(R.array.batterymeter_bolt_points);
-        int maxX = 0, maxY = 0;
-        for (int i = 0; i < pts.length; i += 2) {
-            maxX = Math.max(maxX, pts[i]);
-            maxY = Math.max(maxY, pts[i + 1]);
-        }
-        final float[] ptsF = new float[pts.length];
-        for (int i = 0; i < pts.length; i += 2) {
-            ptsF[i] = (float)pts[i] / maxX;
-            ptsF[i + 1] = (float)pts[i + 1] / maxY;
-        }
-        return ptsF;
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        mHeight = h;
-        mWidth = w;
-        mWarningTextPaint.setTextSize(h * 0.75f);
-        mWarningTextHeight = -mWarningTextPaint.getFontMetrics().ascent;
-    }
-
-    private void updateShowPercent() {
-        mShowPercent = 0 != Settings.System.getInt(getContext().getContentResolver(),
-                SHOW_PERCENT_SETTING, 0);
-    }
-
-    private int getColorForLevel(int percent) {
-
-        // If we are in power save mode, always use the normal color.
-        if (mPowerSaveEnabled) {
-            return mColors[mColors.length-1];
-        }
-        int thresh, color = 0;
-        for (int i=0; i<mColors.length; i+=2) {
-            thresh = mColors[i];
-            color = mColors[i+1];
-            if (percent <= thresh) {
-
-                // Respect tinting for "normal" level
-                if (i == mColors.length-2) {
-                    return mIconTint;
-                } else {
-                    return color;
-                }
-            }
-        }
-        return color;
-    }
-
-    public void setDarkIntensity(float darkIntensity) {
-        int backgroundColor = getBackgroundColor(darkIntensity);
-        int fillColor = getFillColor(darkIntensity);
-        mIconTint = fillColor;
-        mFramePaint.setColor(backgroundColor);
-        mBoltPaint.setColor(fillColor);
-        mChargeColor = fillColor;
-        invalidate();
-    }
-
-    private int getBackgroundColor(float darkIntensity) {
-        return getColorForDarkIntensity(
-                darkIntensity, mLightModeBackgroundColor, mDarkModeBackgroundColor);
-    }
-
-    private int getFillColor(float darkIntensity) {
-        return getColorForDarkIntensity(
-                darkIntensity, mLightModeFillColor, mDarkModeFillColor);
-    }
-
-    private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) {
-        return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor);
-    }
-
-    @Override
-    public void draw(Canvas c) {
-        BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;
-        final int level = tracker.level;
-
-        if (level == BatteryTracker.UNKNOWN_LEVEL) return;
-
-        float drawFrac = (float) level / 100f;
-        final int pt = getPaddingTop();
-        final int pl = getPaddingLeft();
-        final int pr = getPaddingRight();
-        final int pb = getPaddingBottom();
-        final int height = mHeight - pt - pb;
-        final int width = mWidth - pl - pr;
-
-        final int buttonHeight = (int) (height * mButtonHeightFraction);
-
-        mFrame.set(0, 0, width, height);
-        mFrame.offset(pl, pt);
-
-        // button-frame: area above the battery body
-        mButtonFrame.set(
-                mFrame.left + Math.round(width * 0.25f),
-                mFrame.top,
-                mFrame.right - Math.round(width * 0.25f),
-                mFrame.top + buttonHeight);
-
-        mButtonFrame.top += mSubpixelSmoothingLeft;
-        mButtonFrame.left += mSubpixelSmoothingLeft;
-        mButtonFrame.right -= mSubpixelSmoothingRight;
-
-        // frame: battery body area
-        mFrame.top += buttonHeight;
-        mFrame.left += mSubpixelSmoothingLeft;
-        mFrame.top += mSubpixelSmoothingLeft;
-        mFrame.right -= mSubpixelSmoothingRight;
-        mFrame.bottom -= mSubpixelSmoothingRight;
-
-        // set the battery charging color
-        mBatteryPaint.setColor(tracker.plugged ? mChargeColor : getColorForLevel(level));
-
-        if (level >= FULL) {
-            drawFrac = 1f;
-        } else if (level <= mCriticalLevel) {
-            drawFrac = 0f;
-        }
-
-        final float levelTop = drawFrac == 1f ? mButtonFrame.top
-                : (mFrame.top + (mFrame.height() * (1f - drawFrac)));
-
-        // define the battery shape
-        mShapePath.reset();
-        mShapePath.moveTo(mButtonFrame.left, mButtonFrame.top);
-        mShapePath.lineTo(mButtonFrame.right, mButtonFrame.top);
-        mShapePath.lineTo(mButtonFrame.right, mFrame.top);
-        mShapePath.lineTo(mFrame.right, mFrame.top);
-        mShapePath.lineTo(mFrame.right, mFrame.bottom);
-        mShapePath.lineTo(mFrame.left, mFrame.bottom);
-        mShapePath.lineTo(mFrame.left, mFrame.top);
-        mShapePath.lineTo(mButtonFrame.left, mFrame.top);
-        mShapePath.lineTo(mButtonFrame.left, mButtonFrame.top);
-
-        if (tracker.plugged) {
-            // define the bolt shape
-            final float bl = mFrame.left + mFrame.width() / 4.5f;
-            final float bt = mFrame.top + mFrame.height() / 6f;
-            final float br = mFrame.right - mFrame.width() / 7f;
-            final float bb = mFrame.bottom - mFrame.height() / 10f;
-            if (mBoltFrame.left != bl || mBoltFrame.top != bt
-                    || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
-                mBoltFrame.set(bl, bt, br, bb);
-                mBoltPath.reset();
-                mBoltPath.moveTo(
-                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
-                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
-                for (int i = 2; i < mBoltPoints.length; i += 2) {
-                    mBoltPath.lineTo(
-                            mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
-                            mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
-                }
-                mBoltPath.lineTo(
-                        mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
-                        mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
-            }
-
-            float boltPct = (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top);
-            boltPct = Math.min(Math.max(boltPct, 0), 1);
-            if (boltPct <= BOLT_LEVEL_THRESHOLD) {
-                // draw the bolt if opaque
-                c.drawPath(mBoltPath, mBoltPaint);
-            } else {
-                // otherwise cut the bolt out of the overall shape
-                mShapePath.op(mBoltPath, Path.Op.DIFFERENCE);
-            }
-        }
-
-        // compute percentage text
-        boolean pctOpaque = false;
-        float pctX = 0, pctY = 0;
-        String pctText = null;
-        if (!tracker.plugged && level > mCriticalLevel && mShowPercent) {
-            mTextPaint.setColor(getColorForLevel(level));
-            mTextPaint.setTextSize(height *
-                    (SINGLE_DIGIT_PERCENT ? 0.75f
-                            : (tracker.level == 100 ? 0.38f : 0.5f)));
-            mTextHeight = -mTextPaint.getFontMetrics().ascent;
-            pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level/10) : level);
-            pctX = mWidth * 0.5f;
-            pctY = (mHeight + mTextHeight) * 0.47f;
-            pctOpaque = levelTop > pctY;
-            if (!pctOpaque) {
-                mTextPath.reset();
-                mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath);
-                // cut the percentage text out of the overall shape
-                mShapePath.op(mTextPath, Path.Op.DIFFERENCE);
-            }
-        }
-
-        // draw the battery shape background
-        c.drawPath(mShapePath, mFramePaint);
-
-        // draw the battery shape, clipped to charging level
-        mFrame.top = levelTop;
-        mClipPath.reset();
-        mClipPath.addRect(mFrame,  Path.Direction.CCW);
-        mShapePath.op(mClipPath, Path.Op.INTERSECT);
-        c.drawPath(mShapePath, mBatteryPaint);
-
-        if (!tracker.plugged) {
-            if (level <= mCriticalLevel) {
-                // draw the warning text
-                final float x = mWidth * 0.5f;
-                final float y = (mHeight + mWarningTextHeight) * 0.48f;
-                c.drawText(mWarningString, x, y, mWarningTextPaint);
-            } else if (pctOpaque) {
-                // draw the percentage text
-                c.drawText(pctText, pctX, pctY, mTextPaint);
-            }
-        }
+        setImageDrawable(mDrawable);
     }
 
     @Override
@@ -428,116 +53,37 @@
         return false;
     }
 
-    private boolean mDemoMode;
-    private BatteryTracker mDemoTracker = new BatteryTracker();
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mBatteryController.addStateChangedCallback(this);
+        mDrawable.startListening();
+    }
 
     @Override
-    public void dispatchDemoCommand(String command, Bundle args) {
-        if (!mDemoMode && command.equals(COMMAND_ENTER)) {
-            mDemoMode = true;
-            mDemoTracker.level = mTracker.level;
-            mDemoTracker.plugged = mTracker.plugged;
-        } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
-            mDemoMode = false;
-            postInvalidate();
-        } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
-           String level = args.getString("level");
-           String plugged = args.getString("plugged");
-           if (level != null) {
-               mDemoTracker.level = Math.min(Math.max(Integer.parseInt(level), 0), 100);
-           }
-           if (plugged != null) {
-               mDemoTracker.plugged = Boolean.parseBoolean(plugged);
-           }
-           postInvalidate();
-        }
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mBatteryController.removeStateChangedCallback(this);
+        mDrawable.stopListening();
     }
 
-    private final class BatteryTracker extends BroadcastReceiver {
-        public static final int UNKNOWN_LEVEL = -1;
-
-        // current battery status
-        int level = UNKNOWN_LEVEL;
-        String percentStr;
-        int plugType;
-        boolean plugged;
-        int health;
-        int status;
-        String technology;
-        int voltage;
-        int temperature;
-        boolean testmode = false;
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
-                if (testmode && ! intent.getBooleanExtra("testmode", false)) return;
-
-                level = (int)(100f
-                        * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
-                        / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
-
-                plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
-                plugged = plugType != 0;
-                health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH,
-                        BatteryManager.BATTERY_HEALTH_UNKNOWN);
-                status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
-                        BatteryManager.BATTERY_STATUS_UNKNOWN);
-                technology = intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
-                voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0);
-                temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
-
-                setContentDescription(
-                        context.getString(R.string.accessibility_battery_level, level));
-                postInvalidate();
-            } else if (action.equals(ACTION_LEVEL_TEST)) {
-                testmode = true;
-                post(new Runnable() {
-                    int curLevel = 0;
-                    int incr = 1;
-                    int saveLevel = level;
-                    int savePlugged = plugType;
-                    Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
-                    @Override
-                    public void run() {
-                        if (curLevel < 0) {
-                            testmode = false;
-                            dummy.putExtra("level", saveLevel);
-                            dummy.putExtra("plugged", savePlugged);
-                            dummy.putExtra("testmode", false);
-                        } else {
-                            dummy.putExtra("level", curLevel);
-                            dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC
-                                    : 0);
-                            dummy.putExtra("testmode", true);
-                        }
-                        getContext().sendBroadcast(dummy);
-
-                        if (!testmode) return;
-
-                        curLevel += incr;
-                        if (curLevel == 100) {
-                            incr *= -1;
-                        }
-                        postDelayed(this, 200);
-                    }
-                });
-            }
-        }
+    @Override
+    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+        setContentDescription(
+                getContext().getString(R.string.accessibility_battery_level, level));
     }
 
-    private final class SettingObserver extends ContentObserver {
-        public SettingObserver() {
-            super(new Handler());
-        }
+    @Override
+    public void onPowerSaveChanged() {
 
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            super.onChange(selfChange, uri);
-            updateShowPercent();
-            postInvalidate();
-        }
     }
 
+    public void setBatteryController(BatteryController mBatteryController) {
+        this.mBatteryController = mBatteryController;
+        mDrawable.setBatteryController(mBatteryController);
+    }
+
+    public void setDarkIntensity(float f) {
+        mDrawable.setDarkIntensity(f);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 3b3593b..e562682 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -30,16 +30,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.qs.QSTile.State;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.Listenable;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.*;
 
 import java.util.Collection;
 import java.util.Objects;
@@ -349,6 +340,9 @@
         CastController getCastController();
         FlashlightController getFlashlightController();
         KeyguardMonitor getKeyguardMonitor();
+        UserSwitcherController getUserSwitcherController();
+        UserInfoController getUserInfoController();
+        BatteryController getBatteryController();
 
         public interface Callback {
             void onTilesChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 08cdc1e..e575923 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -267,7 +267,7 @@
         final int w = MeasureSpec.getSize(widthMeasureSpec);
         final int h = MeasureSpec.getSize(heightMeasureSpec);
         final int iconSpec = exactly(mIconSizePx);
-        mIcon.measure(MeasureSpec.makeMeasureSpec(w, MeasureSpec.AT_MOST), iconSpec);
+        mIcon.measure(MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY), iconSpec);
         switch (mType) {
             case QS_TYPE_QUICK:
                 mCircle.measure(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
index 84b05d0..f676ea3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
@@ -44,7 +44,8 @@
                 host.getRotationLockController(), host.getNetworkController(),
                 host.getZenModeController(), host.getHotspotController(), host.getCastController(),
                 host.getFlashlightController(), host.getUserSwitcherController(),
-                host.getKeyguardMonitor(), new BlankSecurityController());
+                host.getUserInfoController(), host.getKeyguardMonitor(),
+                new BlankSecurityController(), host.getBatteryController());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
new file mode 100644
index 0000000..8f9655d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.tiles;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.BatteryMeterDrawable;
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import java.text.NumberFormat;
+
+public class BatteryTile extends QSTile<QSTile.State> implements BatteryController.BatteryStateChangeCallback {
+
+    private final BatteryMeterDrawable mDrawable;
+    private final BatteryController mBatteryController;
+
+    private int mLevel;
+
+    public BatteryTile(Host host) {
+        super(host);
+        mBatteryController = host.getBatteryController();
+        mDrawable = new BatteryMeterDrawable(host.getContext(), new Handler(),
+                host.getContext().getColor(R.color.batterymeter_frame_color));
+        mDrawable.setBatteryController(mBatteryController);
+    }
+
+    @Override
+    protected State newTileState() {
+        return new QSTile.State();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_BATTERY_TILE;
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        if (listening) {
+            mDrawable.startListening();
+            mBatteryController.addStateChangedCallback(this);
+        } else {
+            mDrawable.stopListening();
+            mBatteryController.removeStateChangedCallback(this);
+        }
+    }
+
+    @Override
+    protected void handleClick() {
+        mHost.startActivityDismissingKeyguard(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
+    }
+
+    @Override
+    protected void handleUpdateState(State state, Object arg) {
+        int level = (arg != null) ? (Integer) arg : mLevel;
+        String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
+
+        state.visible = true;
+        state.icon = new Icon() {
+            @Override
+            public Drawable getDrawable(Context context) {
+                return mDrawable;
+            }
+        };
+        state.label = percentage;
+    }
+
+    @Override
+    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+        mLevel = level;
+        refreshState((Integer) level);
+    }
+
+    @Override
+    public void onPowerSaveChanged() {
+
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java
new file mode 100644
index 0000000..3675f02
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QLockTile.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.tiles;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.QSTileView;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.R;
+
+public class QLockTile extends QSTile<QSTile.State> implements KeyguardMonitor.Callback {
+
+    private final KeyguardMonitor mKeyguard;
+
+    public QLockTile(Host host) {
+        super(host);
+        mKeyguard = host.getKeyguardMonitor();
+    }
+
+    @Override
+    public int getTileType() {
+        return QSTileView.QS_TYPE_QUICK;
+    }
+
+    @Override
+    protected State newTileState() {
+        return new State();
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        if (listening) {
+            mKeyguard.addCallback(this);
+        } else {
+            mKeyguard.removeCallback(this);
+        }
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_LOCK_TILE;
+    }
+
+    @Override
+    public void onKeyguardChanged() {
+        refreshState();
+    }
+
+    @Override
+    protected void handleClick() {
+        if (mKeyguard.isShowing()) {
+            mKeyguard.unlock();
+        } else {
+            mKeyguard.lock();
+        }
+    }
+
+    @Override
+    protected void handleUpdateState(State state, Object arg) {
+        // TOD: Content description.
+        state.visible = true;
+        if (mKeyguard.isShowing()) {
+            state.icon = ResourceIcon.get(R.drawable.ic_qs_lock);
+        } else {
+            state.icon = ResourceIcon.get(R.drawable.ic_qs_lock_open);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
new file mode 100644
index 0000000..3c5ab8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.tiles;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.Pair;
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+
+public class UserTile extends QSTile<QSTile.State> implements UserInfoController.OnUserInfoChangedListener {
+
+    private final UserSwitcherController mUserSwitcherController;
+    private final UserInfoController mUserInfoController;
+    private Pair<String, Drawable> mLastUpdate;
+
+    public UserTile(Host host) {
+        super(host);
+        mUserSwitcherController = host.getUserSwitcherController();
+        mUserInfoController = host.getUserInfoController();
+    }
+
+    @Override
+    protected State newTileState() {
+        return new QSTile.State();
+    }
+
+    @Override
+    protected void handleClick() {
+        showDetail(true);
+    }
+
+    @Override
+    public DetailAdapter getDetailAdapter() {
+        return mUserSwitcherController.userDetailAdapter;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsLogger.QS_USER_TILE;
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        if (listening) {
+            mUserInfoController.addListener(this);
+        } else {
+            mUserInfoController.remListener(this);
+        }
+    }
+
+    @Override
+    protected void handleUpdateState(State state, Object arg) {
+        final Pair<String, Drawable> p = arg != null ? (Pair<String, Drawable>) arg : mLastUpdate;
+        state.visible = p != null;
+        if (!state.visible) return;
+        state.label = p.first;
+        // TODO: Better content description.
+        state.contentDescription = p.first;
+        state.icon = new Icon() {
+            @Override
+            public Drawable getDrawable(Context context) {
+                return p.second;
+            }
+        };
+    }
+
+    @Override
+    public void onUserInfoChanged(String name, Drawable picture) {
+        mLastUpdate = new Pair<>(name, picture);
+        refreshState(mLastUpdate);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index a3e89f2..a78351a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -38,7 +38,6 @@
 import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
@@ -75,6 +74,8 @@
 public class Recents extends SystemUI
         implements ActivityOptions.OnAnimationStartedListener, RecentsComponent {
 
+    public final static int EVENT_BUS_PRIORITY = 1;
+
     final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab";
     final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey";
     final public static String EXTRA_RECENTS_VISIBILITY = "recentsVisibility";
@@ -164,7 +165,6 @@
     static RecentsTaskLoadPlan sInstanceLoadPlan;
     static Recents sInstance;
 
-    LayoutInflater mInflater;
     SystemServicesProxy mSystemServicesProxy;
     Handler mHandler;
     TaskStackListenerImpl mTaskStackListener;
@@ -176,12 +176,14 @@
 
     // Task launching
     RecentsConfiguration mConfig;
+    Rect mSearchBarBounds = new Rect();
     Rect mTaskStackBounds = new Rect();
-    Rect mSystemInsets = new Rect();
+    Rect mLastTaskViewBounds = new Rect();
     TaskViewTransform mTmpTransform = new TaskViewTransform();
     int mStatusBarHeight;
     int mNavBarHeight;
     int mNavBarWidth;
+    int mTaskBarHeight;
 
     // Header (for transition)
     TaskViewHeader mHeaderBar;
@@ -229,11 +231,11 @@
         if (sInstance == null) {
             sInstance = this;
         }
+        Resources res = mContext.getResources();
         RecentsTaskLoader.initialize(mContext);
-        mInflater = LayoutInflater.from(mContext);
+        LayoutInflater inflater = LayoutInflater.from(mContext);
         mSystemServicesProxy = new SystemServicesProxy(mContext);
         mHandler = new Handler();
-        mTaskStackBounds = new Rect();
         mAppWidgetHost = new RecentsAppWidgetHost(mContext, Constants.Values.App.AppWidgetHostId);
 
         // Register the task stack listener
@@ -241,7 +243,7 @@
         mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
 
         // Only the owner has the callback to update the SysUI visibility flags, so all non-owner
-        // instances of AlternateRecentsComponent needs to notify the owner when the visibility
+        // instances of RecentsComponent needs to notify the owner when the visibility
         // changes.
         if (mSystemServicesProxy.isForegroundUserSystem()) {
             mProxyBroadcastReceiver = new RecentsOwnerEventProxyReceiver();
@@ -254,8 +256,16 @@
 
         // Initialize some static datastructures
         TaskStackViewLayoutAlgorithm.initializeCurve();
-        // Load the header bar layout
-        reloadHeaderBarLayout();
+        // Initialize the static configuration resources
+        mConfig = RecentsConfiguration.initialize(mContext, mSystemServicesProxy);
+        mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+        mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
+        mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
+        mTaskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
+        mDummyStackView = new TaskStackView(mContext, new TaskStack());
+        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
+                null, false);
+        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
 
         // When we start, preload the data associated with the previous recent tasks.
         // We can use a new plan since the caches will be the same.
@@ -273,6 +283,7 @@
     @Override
     public void onBootCompleted() {
         mBootCompleted = true;
+        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
     }
 
     /** Shows the Recents. */
@@ -293,7 +304,7 @@
         mTriggeredFromAltTab = triggeredFromAltTab;
 
         try {
-            startRecentsActivity();
+            showRecentsActivity();
         } catch (ActivityNotFoundException e) {
             Console.logRawError("Failed to launch RecentAppsIntent", e);
         }
@@ -488,56 +499,52 @@
     void configurationChanged() {
         // Don't reuse task stack views if the configuration changes
         mCanReuseTaskStackViews = false;
-        // Reload the header bar layout
-        reloadHeaderBarLayout();
+        mConfig.updateOnConfigurationChange();
     }
 
-    /** Prepares the header bar layout. */
-    void reloadHeaderBarLayout() {
-        Resources res = mContext.getResources();
+    /**
+     * Prepares the header bar layout for the next transition, if the task view bounds has changed
+     * since the last call, it will attempt to re-measure and layout the header bar to the new size.
+     *
+     * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one
+     *                               is not already bound (can be expensive)
+     */
+    void reloadHeaderBarLayout(boolean tryAndBindSearchWidget) {
         Rect windowRect = mSystemServicesProxy.getWindowRect();
 
-        mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
-        mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
-        mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
-        // TODO: We can't rely on this anymore since the activity context will yield different
-        //      resources while multiwindow is enabled
-        mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
-        mConfig.updateOnConfigurationChange();
-        Rect searchBarBounds = new Rect();
-        // Try and pre-emptively bind the search widget on startup to ensure that we
-        // have the right thumbnail bounds to animate to.
-        // Note: We have to reload the widget id before we get the task stack bounds below
-        if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
-            mConfig.getSearchBarBounds(windowRect,
-                    mStatusBarHeight, searchBarBounds);
+        // Update the configuration for the current state
+        mConfig.update(mContext, mSystemServicesProxy, mSystemServicesProxy.getWindowRect());
+
+        if (tryAndBindSearchWidget) {
+            // Try and pre-emptively bind the search widget on startup to ensure that we
+            // have the right thumbnail bounds to animate to.
+            // Note: We have to reload the widget id before we get the task stack bounds below
+            if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
+                mConfig.getSearchBarBounds(windowRect,
+                        mStatusBarHeight, mSearchBarBounds);
+            }
         }
         mConfig.getAvailableTaskStackBounds(windowRect,
-                mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0), searchBarBounds,
-                mTaskStackBounds);
-        if (mConfig.isLandscape && mConfig.hasTransposedNavBar) {
-            mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
-        } else {
-            mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight);
-        }
+                mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
+                mSearchBarBounds, mTaskStackBounds);
+        int systemBarBottomInset = mConfig.hasTransposedNavBar ? 0 : mNavBarHeight;
 
-        // Inflate the header bar layout so that we can rebind and draw it for the transition
-        TaskStack stack = new TaskStack();
-        mDummyStackView = new TaskStackView(mContext, stack);
+        // Rebind the header bar and draw it for the transition
         TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
         Rect taskStackBounds = new Rect(mTaskStackBounds);
-        taskStackBounds.bottom -= mSystemInsets.bottom;
+        taskStackBounds.bottom -= systemBarBottomInset;
         algo.computeRects(windowRect.width(), windowRect.height(), taskStackBounds);
-        Rect taskViewSize = algo.getUntransformedTaskViewSize();
-        int taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
-        synchronized (mHeaderBarLock) {
-            mHeaderBar = (TaskViewHeader) mInflater.inflate(R.layout.recents_task_view_header, null,
-                    false);
-            mHeaderBar.measure(
-                    View.MeasureSpec.makeMeasureSpec(taskViewSize.width(), View.MeasureSpec.EXACTLY),
-                    View.MeasureSpec.makeMeasureSpec(taskBarHeight, View.MeasureSpec.EXACTLY));
-            // TODO: may not be needed
-            mHeaderBar.layout(0, 0, taskViewSize.width(), taskBarHeight);
+        Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
+        if (!taskViewBounds.equals(mLastTaskViewBounds)) {
+            mLastTaskViewBounds.set(taskViewBounds);
+
+            int taskViewWidth = taskViewBounds.width();
+            synchronized (mHeaderBarLock) {
+                mHeaderBar.measure(
+                    View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
+                    View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
+                mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
+            }
         }
     }
 
@@ -562,17 +569,17 @@
             return;
         } else {
             // Otherwise, start the recents activity
-            startRecentsActivity(topTask, isTopTaskHome.value);
+            showRecentsActivity(topTask, isTopTaskHome.value);
         }
     }
 
-    /** Starts the recents activity if it is not already running */
-    void startRecentsActivity() {
+    /** Shows the recents activity if it is not already running */
+    void showRecentsActivity() {
         // Check if the top task is in the home stack, and start the recents activity
         ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
         MutableBoolean isTopTaskHome = new MutableBoolean(true);
         if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
-            startRecentsActivity(topTask, isTopTaskHome.value);
+            showRecentsActivity(topTask, isTopTaskHome.value);
         }
     }
 
@@ -734,33 +741,18 @@
         return mTmpTransform;
     }
 
-    /** Starts the recents activity */
-    void startRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
+    /** Shows the recents activity */
+    void showRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-        // Don't reinitialize the configuration completely here, since it has the wrong context,
-        // only update the parts that we can get from any context
-        RecentsConfiguration config = RecentsConfiguration.getInstance();
-        config.reinitializeWithApplicationContext(mContext, mSystemServicesProxy);
+
+        // Update the header bar if necessary
+        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
 
         if (sInstanceLoadPlan == null) {
             // Create a new load plan if onPreloadRecents() was never triggered
             sInstanceLoadPlan = loader.createLoadPlan(mContext);
         }
 
-        // Temporarily skip the transition (use a dummy fade) if multi stack is enabled.
-        // For multi-stack we need to figure out where each of the tasks are going.
-        if (mConfig.multiWindowEnabled) {
-            loader.preloadTasks(sInstanceLoadPlan, true);
-            TaskStack stack = sInstanceLoadPlan.getTaskStack();
-            mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, true);
-            TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
-                    mDummyStackView.computeStackVisibilityReport();
-            ActivityOptions opts = getUnknownTransitionActivityOptions();
-            startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
-                    false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
-            return;
-        }
-
         if (!sInstanceLoadPlan.hasTasks()) {
             loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
         }
@@ -779,7 +771,7 @@
             ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
                     mDummyStackView);
             if (opts != null) {
-                startAlternateRecentsActivity(topTask, opts, false /* fromHome */,
+                startRecentsActivity(topTask, opts, false /* fromHome */,
                         false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
             } else {
                 // Fall through below to the non-thumbnail transition
@@ -799,12 +791,12 @@
                 boolean fromSearchHome = (homeActivityPackage != null) &&
                         homeActivityPackage.equals(searchWidgetPackage);
                 ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
-                startAlternateRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
+                startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
                         false /* fromThumbnail */, stackVr);
             } else {
                 // Otherwise we do the normal fade from an unknown source
                 ActivityOptions opts = getUnknownTransitionActivityOptions();
-                startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
+                startRecentsActivity(topTask, opts, true /* fromHome */,
                         false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
             }
         }
@@ -812,19 +804,20 @@
     }
 
     /** Starts the recents activity */
-    void startAlternateRecentsActivity(ActivityManager.RunningTaskInfo topTask,
-            ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
-            TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
+    void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+              ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
+              TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
         // Update the configuration based on the launch options
-        mConfig.launchedFromHome = fromSearchHome || fromHome;
-        mConfig.launchedFromSearchHome = fromSearchHome;
-        mConfig.launchedFromAppWithThumbnail = fromThumbnail;
-        mConfig.launchedToTaskId = (topTask != null) ? topTask.id : -1;
-        mConfig.launchedWithAltTab = mTriggeredFromAltTab;
-        mConfig.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
-        mConfig.launchedNumVisibleTasks = vr.numVisibleTasks;
-        mConfig.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
-        mConfig.launchedHasConfigurationChanged = false;
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+        launchState.launchedFromHome = fromSearchHome || fromHome;
+        launchState.launchedFromSearchHome = fromSearchHome;
+        launchState.launchedFromAppWithThumbnail = fromThumbnail;
+        launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
+        launchState.launchedWithAltTab = mTriggeredFromAltTab;
+        launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
+        launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
+        launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
+        launchState.launchedHasConfigurationChanged = false;
 
         Intent intent = new Intent(sToggleRecentsAction);
         intent.setClassName(sRecentsPackage, sRecentsActivity);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index c53e573..1a0eb24 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -25,21 +25,20 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewStub;
-import android.widget.Toast;
-
 import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
 import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsPackageMonitor;
 import com.android.systemui.recents.model.RecentsTaskLoadPlan;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
@@ -48,16 +47,17 @@
 import com.android.systemui.recents.views.SystemBarScrimViews;
 import com.android.systemui.recents.views.ViewAnimation;
 
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
 /**
  * The main Recents activity that is started from AlternateRecentsComponent.
  */
-public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
-        RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
+public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks {
+
+    public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
 
     RecentsConfiguration mConfig;
+    RecentsPackageMonitor mPackageMonitor;
     long mLastTabKeyEventTime;
 
     // Top level views
@@ -182,18 +182,19 @@
         }
 
         // Start loading tasks according to the load plan
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
         if (!plan.hasTasks()) {
-            loader.preloadTasks(plan, mConfig.launchedFromHome);
+            loader.preloadTasks(plan, launchState.launchedFromHome);
         }
         RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
-        loadOpts.runningTaskId = mConfig.launchedToTaskId;
-        loadOpts.numVisibleTasks = mConfig.launchedNumVisibleTasks;
-        loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails;
+        loadOpts.runningTaskId = launchState.launchedToTaskId;
+        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
         loader.loadTasks(this, plan, loadOpts);
 
         TaskStack stack = plan.getTaskStack();
-        mConfig.launchedWithNoRecentTasks = !plan.hasTasks();
-        if (!mConfig.launchedWithNoRecentTasks) {
+        launchState.launchedWithNoRecentTasks = !plan.hasTasks();
+        if (!launchState.launchedWithNoRecentTasks) {
             mRecentsView.setTaskStack(stack);
         }
 
@@ -204,19 +205,19 @@
                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
         mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent,
             ActivityOptions.makeCustomAnimation(this,
-                mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
+                    launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
                         R.anim.recents_to_launcher_enter,
-                    mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
+                    launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
                         R.anim.recents_to_launcher_exit));
 
         // Mark the task that is the launch target
         int launchTaskIndexInStack = 0;
-        if (mConfig.launchedToTaskId != -1) {
+        if (launchState.launchedToTaskId != -1) {
             ArrayList<Task> tasks = stack.getTasks();
             int taskCount = tasks.size();
             for (int j = 0; j < taskCount; j++) {
                 Task t = tasks.get(j);
-                if (t.key.id == mConfig.launchedToTaskId) {
+                if (t.key.id == launchState.launchedToTaskId) {
                     t.isLaunchTarget = true;
                     launchTaskIndexInStack = tasks.size() - j - 1;
                     break;
@@ -225,7 +226,7 @@
         }
 
         // Update the top level view's visibilities
-        if (mConfig.launchedWithNoRecentTasks) {
+        if (launchState.launchedWithNoRecentTasks) {
             if (mEmptyView == null) {
                 mEmptyView = mEmptyViewStub.inflate();
             }
@@ -246,13 +247,13 @@
         mScrimViews.prepareEnterRecentsAnimation();
 
         // Keep track of whether we launched from the nav bar button or via alt-tab
-        if (mConfig.launchedWithAltTab) {
+        if (launchState.launchedWithAltTab) {
             MetricsLogger.count(this, "overview_trigger_alttab", 1);
         } else {
             MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
         }
         // Keep track of whether we launched from an app or from home
-        if (mConfig.launchedFromAppWithThumbnail) {
+        if (launchState.launchedFromAppWithThumbnail) {
             MetricsLogger.count(this, "overview_source_app", 1);
             // If from an app, track the stack index of the app in the stack (for affiliated tasks)
             MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
@@ -266,6 +267,7 @@
 
     /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
     boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
         SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
         if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
             // If we currently have filtered stacks, then unfilter those first
@@ -274,7 +276,7 @@
             // If we have a focused Task, launch that Task now
             if (mRecentsView.launchFocusedTask()) return true;
             // If we launched from Home, then return to Home
-            if (mConfig.launchedFromHome) {
+            if (launchState.launchedFromHome) {
                 dismissRecentsToHomeRaw(true);
                 return true;
             }
@@ -320,11 +322,17 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        // Register this activity with the event bus
+        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
+
         // For the non-primary user, ensure that the SystemServicesProxy and configuration is
         // initialized
         RecentsTaskLoader.initialize(this);
         SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
-        mConfig = RecentsConfiguration.reinitialize(this, ssp);
+        mConfig = RecentsConfiguration.initialize(this, ssp);
+        mConfig.update(this, ssp, ssp.getWindowRect());
+        mPackageMonitor = new RecentsPackageMonitor();
 
         // Initialize the widget host (the host id is static and does not change)
         mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
@@ -337,7 +345,7 @@
                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
         mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
-        mScrimViews = new SystemBarScrimViews(this, mConfig);
+        mScrimViews = new SystemBarScrimViews(this);
 
         // Bind the search app widget when we first start up
         mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
@@ -358,6 +366,7 @@
     @Override
     protected void onStart() {
         super.onStart();
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
         MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY);
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         SystemServicesProxy ssp = loader.getSystemServicesProxy();
@@ -371,7 +380,7 @@
         registerReceiver(mServiceBroadcastReceiver, filter);
 
         // Register any broadcast receivers for the task loader
-        loader.registerReceivers(this, mRecentsView);
+        mPackageMonitor.register(this);
 
         // Update the recent tasks
         updateRecentsTasks();
@@ -379,12 +388,13 @@
         // If this is a new instance from a configuration change, then we have to manually trigger
         // the enter animation state, or if recents was relaunched by AM, without going through
         // the normal mechanisms
-        boolean wasLaunchedByAm = !mConfig.launchedFromHome && !mConfig.launchedFromAppWithThumbnail;
-        if (mConfig.launchedHasConfigurationChanged || wasLaunchedByAm) {
+        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
+                !launchState.launchedFromAppWithThumbnail;
+        if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
             onEnterAnimationTriggered();
         }
 
-        if (!mConfig.launchedHasConfigurationChanged) {
+        if (!launchState.launchedHasConfigurationChanged) {
             mRecentsView.disableLayersForOneFrame();
         }
     }
@@ -402,6 +412,7 @@
     protected void onStop() {
         super.onStop();
         MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         SystemServicesProxy ssp = loader.getSystemServicesProxy();
         Recents.notifyVisibilityChanged(this, ssp, false);
@@ -413,17 +424,17 @@
         unregisterReceiver(mServiceBroadcastReceiver);
 
         // Unregister any broadcast receivers for the task loader
-        loader.unregisterReceivers();
+        mPackageMonitor.unregister();
 
         // Workaround for b/22542869, if the RecentsActivity is started again, but without going
         // through SystemUI, we need to reset the config launch flags to ensure that we do not
         // wait on the system to send a signal that was never queued.
-        mConfig.launchedFromHome = false;
-        mConfig.launchedFromSearchHome = false;
-        mConfig.launchedFromAppWithThumbnail = false;
-        mConfig.launchedToTaskId = -1;
-        mConfig.launchedWithAltTab = false;
-        mConfig.launchedHasConfigurationChanged = false;
+        launchState.launchedFromHome = false;
+        launchState.launchedFromSearchHome = false;
+        launchState.launchedFromAppWithThumbnail = false;
+        launchState.launchedToTaskId = -1;
+        launchState.launchedWithAltTab = false;
+        launchState.launchedHasConfigurationChanged = false;
     }
 
     @Override
@@ -435,6 +446,7 @@
 
         // Stop listening for widget package changes if there was one bound
         mAppWidgetHost.stopListening();
+        EventBus.getDefault().unregister(this);
     }
 
     public void onEnterAnimationTriggered() {
@@ -444,16 +456,12 @@
         mRecentsView.startEnterRecentsAnimation(ctx);
 
         if (mSearchWidgetInfo != null) {
-            final WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks> cbRef =
-                    new WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks>(
-                            RecentsActivity.this);
             ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
                 @Override
                 public void run() {
                     // Start listening for widget package changes if there is one bound
-                    RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks cb = cbRef.get();
-                    if (cb != null) {
-                        mAppWidgetHost.startListening(cb);
+                    if (mAppWidgetHost != null) {
+                        mAppWidgetHost.startListening();
                     }
                 }
             });
@@ -475,8 +483,9 @@
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_TAB: {
+                int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay);
                 boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
-                        mLastTabKeyEventTime) > mConfig.altTabKeyDelay;
+                        mLastTabKeyEventTime) > altTabKeyDelay;
                 if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
                     // Focus the next task in the stack
                     final boolean backward = event.isShiftPressed();
@@ -514,9 +523,6 @@
 
     @Override
     public void onBackPressed() {
-        // Test mode where back does not do anything
-        if (mConfig.debugModeEnabled) return;
-
         // Dismiss Recents to the focused Task or Home
         dismissRecentsToFocusedTaskOrHome(true);
     }
@@ -572,10 +578,13 @@
         mAfterPauseRunnable = r;
     }
 
-    /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/
+    /**** EventBus events ****/
 
-    @Override
-    public void refreshSearchWidgetView() {
+    public final void onBusEvent(AppWidgetProviderChangedEvent event) {
+        refreshSearchWidgetView();
+    }
+
+    private void refreshSearchWidgetView() {
         if (mSearchWidgetInfo != null) {
             SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
             int searchWidgetId = ssp.getSearchAppWidgetId(this);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
new file mode 100644
index 0000000..e2e0e918
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+/**
+ * The launch state of the RecentsActivity.
+ *
+ * TODO: We will be refactoring this out RecentsConfiguration.
+ * Current Constraints:
+ *  - needed in onStart() before onNewIntent()
+ *  - needs to be reset when Recents is hidden
+ *  - needs to be computed in Recents component
+ *  - needs to be accessible by views
+ */
+public class RecentsActivityLaunchState {
+
+    public RecentsConfiguration mConfig;
+
+    public boolean launchedWithAltTab;
+    public boolean launchedWithNoRecentTasks;
+    public boolean launchedFromAppWithThumbnail;
+    public boolean launchedFromHome;
+    public boolean launchedFromSearchHome;
+    public boolean launchedReuseTaskStackViews;
+    public boolean launchedHasConfigurationChanged;
+    public int launchedToTaskId;
+    public int launchedNumVisibleTasks;
+    public int launchedNumVisibleThumbnails;
+
+    RecentsActivityLaunchState(RecentsConfiguration config) {
+        mConfig = config;
+    }
+
+    /** Called when the configuration has changed, and we want to reset any configuration specific
+     * members. */
+    public void updateOnConfigurationChange() {
+        // Reset this flag on configuration change to ensure that we recreate new task views
+        launchedReuseTaskStackViews = false;
+        // Set this flag to indicate that the configuration has changed since Recents last launched
+        launchedHasConfigurationChanged = true;
+    }
+
+    /** Returns whether the status bar scrim should be animated when shown for the first time. */
+    public boolean shouldAnimateStatusBarScrim() {
+        return launchedFromHome;
+    }
+
+    /** Returns whether the status bar scrim should be visible. */
+    public boolean hasStatusBarScrim() {
+        return !launchedWithNoRecentTasks;
+    }
+
+    /** Returns whether the nav bar scrim should be animated when shown for the first time. */
+    public boolean shouldAnimateNavBarScrim() {
+        return true;
+    }
+
+    /** Returns whether the nav bar scrim should be visible. */
+    public boolean hasNavBarScrim() {
+        // Only show the scrim if we have recent tasks, and if the nav bar is not transposed
+        return !launchedWithNoRecentTasks && mConfig.hasTransposedNavBar;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
index 0102332..fc96c11 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
@@ -20,24 +20,19 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
 
 /** Our special app widget host for the Search widget */
 public class RecentsAppWidgetHost extends AppWidgetHost {
 
-    /* Callbacks to notify when an app package changes */
-    interface RecentsAppWidgetHostCallbacks {
-        void refreshSearchWidgetView();
-    }
-
-    RecentsAppWidgetHostCallbacks mCb;
     boolean mIsListening;
 
     public RecentsAppWidgetHost(Context context, int hostId) {
         super(context, hostId);
     }
 
-    public void startListening(RecentsAppWidgetHostCallbacks cb) {
-        mCb = cb;
+    public void startListening() {
         if (!mIsListening) {
             mIsListening = true;
             super.startListening();
@@ -47,11 +42,9 @@
     @Override
     public void stopListening() {
         if (mIsListening) {
+            mIsListening = false;
             super.stopListening();
         }
-        // Ensure that we release any references to the callbacks
-        mCb = null;
-        mIsListening = false;
     }
 
     @Override
@@ -66,8 +59,8 @@
     @Override
     protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
         super.onProviderChanged(appWidgetId, appWidgetInfo);
-        if (mIsListening && mCb != null) {
-            mCb.refreshSearchWidgetView();
+        if (mIsListening) {
+            EventBus.getDefault().send(new AppWidgetProviderChangedEvent());
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index a59eb30..52b9521 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -16,27 +16,29 @@
 
 package com.android.systemui.recents;
 
-import android.app.ActivityManager;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.provider.Settings;
-import android.util.DisplayMetrics;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
-
-/** A static Recents configuration for the current context
- * NOTE: We should not hold any references to a Context from a static instance */
+/**
+ * Application resources that can be retrieved from the application context and are not specifically
+ * tied to the current activity.
+ */
 public class RecentsConfiguration {
     static RecentsConfiguration sInstance;
-    static int sPrevConfigurationHashCode;
+
+    private static final int LARGE_SCREEN_MIN_DP = 600;
+    private static final int XLARGE_SCREEN_MIN_DP = 720;
+
+    // Variables that are used for global calculations
+    private static final float STACK_SIDE_PADDING_PHONES_PCT = 0.03333f;
+    private static final float STACK_SIZE_PADDING_TABLETS_PCT = 0.075f;
+    private static final float STACK_SIZE_PADDING_LARGE_TABLETS_PCT = 0.15f;
+    private static final int SEARCH_BAR_SPACE_HEIGHT_PHONES_DPS = 64;
+    private static final int SEARCH_BAR_SPACE_HEIGHT_TABLETS_DPS = 72;
 
     /** Levels of svelte in increasing severity/austerity. */
     // No svelting.
@@ -50,123 +52,81 @@
     // Disable all thumbnail loading.
     public static final int SVELTE_DISABLE_LOADING = 3;
 
-    /** Interpolators */
-    public Interpolator fastOutSlowInInterpolator;
-    public Interpolator fastOutLinearInInterpolator;
-    public Interpolator linearOutSlowInInterpolator;
-    public Interpolator quintOutInterpolator;
+    // Launch states
+    public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState(this);
 
-    /** Filtering */
-    public int filteringCurrentViewsAnimDuration;
-    public int filteringNewViewsAnimDuration;
-
-    /** Insets */
-    public Rect systemInsets = new Rect();
-    public Rect displayRect = new Rect();
-
-    /** Layout */
-    boolean isLandscape;
+    // TODO: Values determined by the current context, needs to be refactored into something that is
+    //       agnostic of the activity context, but still calculable from the Recents component for
+    //       the transition into recents
     boolean hasTransposedSearchBar;
     boolean hasTransposedNavBar;
-
-    /** Loading */
-    public int maxNumTasksToLoad;
-
-    /** Search bar */
-    public int searchBarSpaceHeightPx;
-
-    /** Task stack */
-    public int taskStackScrollDuration;
-    public int taskStackMaxDim;
-    public int taskStackTopPaddingPx;
-    public int dismissAllButtonSizePx;
     public float taskStackWidthPaddingPct;
-    public float taskStackOverscrollPct;
 
-    /** Transitions */
-    public int transitionEnterFromAppDelay;
-    public int transitionEnterFromHomeDelay;
-
-    /** Task view animation and styles */
-    public int taskViewEnterFromAppDuration;
-    public int taskViewEnterFromHomeDuration;
-    public int taskViewEnterFromHomeStaggerDelay;
-    public int taskViewExitToAppDuration;
-    public int taskViewExitToHomeDuration;
-    public int taskViewRemoveAnimDuration;
-    public int taskViewRemoveAnimTranslationXPx;
-    public int taskViewTranslationZMinPx;
-    public int taskViewTranslationZMaxPx;
-    public int taskViewRoundedCornerRadiusPx;
-    public int taskViewHighlightPx;
-    public int taskViewAffiliateGroupEnterOffsetPx;
-    public float taskViewThumbnailAlpha;
-
-    /** Task bar colors */
-    public int taskBarViewDefaultBackgroundColor;
-    public int taskBarViewLightTextColor;
-    public int taskBarViewDarkTextColor;
-    public int taskBarViewHighlightColor;
-    public float taskBarViewAffiliationColorMinAlpha;
-
-    /** Task bar size & animations */
-    public int taskBarHeight;
-    public int taskBarDismissDozeDelaySeconds;
-
-    /** Nav bar scrim */
-    public int navBarScrimEnterDuration;
-
-    /** Launch states */
-    public boolean launchedWithAltTab;
-    public boolean launchedWithNoRecentTasks;
-    public boolean launchedFromAppWithThumbnail;
-    public boolean launchedFromHome;
-    public boolean launchedFromSearchHome;
-    public boolean launchedReuseTaskStackViews;
-    public boolean launchedHasConfigurationChanged;
-    public int launchedToTaskId;
-    public int launchedNumVisibleTasks;
-    public int launchedNumVisibleThumbnails;
+    // Since the positions in Recents has to be calculated globally (before the RecentsActivity
+    // starts), we need to calculate some resource values ourselves, instead of relying on framework
+    // resources.
+    public final boolean isLargeScreen;
+    public final boolean isXLargeScreen;
+    public final int smallestWidth;
 
     /** Misc **/
     public boolean useHardwareLayers;
-    public int altTabKeyDelay;
     public boolean fakeShadows;
+    public int svelteLevel;
+    public int searchBarSpaceHeightPx;
 
     /** Dev options and global settings */
     public boolean multiWindowEnabled;
     public boolean lockToAppEnabled;
-    public boolean developerOptionsEnabled;
-    public boolean debugModeEnabled;
-    public int svelteLevel;
 
     /** Private constructor */
-    private RecentsConfiguration(Context context) {
-        // Properties that don't have to be reloaded with each configuration change can be loaded
-        // here.
+    private RecentsConfiguration(Context context, SystemServicesProxy ssp) {
+        // Load only resources that can not change after the first load either through developer
+        // settings or via multi window
+        Context appContext = context.getApplicationContext();
+        Resources res = appContext.getResources();
+        useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
+        fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
+        svelteLevel = res.getInteger(R.integer.recents_svelte_level);
 
-        // Interpolators
-        fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_slow_in);
-        fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_linear_in);
-        linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.linear_out_slow_in);
-        quintOutInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.decelerate_quint);
+        float density = context.getResources().getDisplayMetrics().density;
+        smallestWidth = ssp.getDeviceSmallestWidth();
+        isLargeScreen = smallestWidth >= (int) (density * LARGE_SCREEN_MIN_DP);
+        isXLargeScreen = smallestWidth >= (int) (density * XLARGE_SCREEN_MIN_DP);
+        searchBarSpaceHeightPx = isLargeScreen ?
+                (int) (density * SEARCH_BAR_SPACE_HEIGHT_TABLETS_DPS) :
+                (int) (density * SEARCH_BAR_SPACE_HEIGHT_PHONES_DPS);
+        if (isLargeScreen) {
+            taskStackWidthPaddingPct = STACK_SIZE_PADDING_TABLETS_PCT;
+        } else if (isXLargeScreen) {
+            taskStackWidthPaddingPct = STACK_SIZE_PADDING_LARGE_TABLETS_PCT;
+        } else {
+            taskStackWidthPaddingPct = STACK_SIDE_PADDING_PHONES_PCT;
+        }
+    }
+
+    /**
+     * Updates the configuration based on the current state of the system
+     */
+    void update(Context context, SystemServicesProxy ssp, Rect windowRect) {
+        // Only update resources that can change after the first load, either through developer
+        // settings or via multi window
+        lockToAppEnabled = ssp.getSystemSetting(context,
+                Settings.System.LOCK_TO_APP_ENABLED) != 0;
+        multiWindowEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
+
+        // Recompute some values based on the given state, since we can not rely on the resource
+        // system to get certain values.
+        boolean isLandscape = windowRect.width() > windowRect.height();
+        hasTransposedNavBar = isLandscape && isLargeScreen && !isXLargeScreen;
+        hasTransposedSearchBar = isLandscape && isLargeScreen && !isXLargeScreen;
     }
 
     /** Updates the configuration to the current context */
-    public static RecentsConfiguration reinitialize(Context context, SystemServicesProxy ssp) {
+    public static RecentsConfiguration initialize(Context context, SystemServicesProxy ssp) {
         if (sInstance == null) {
-            sInstance = new RecentsConfiguration(context);
+            sInstance = new RecentsConfiguration(context, ssp);
         }
-        int configHashCode = context.getResources().getConfiguration().hashCode();
-        if (sPrevConfigurationHashCode != configHashCode) {
-            sInstance.update(context);
-            sPrevConfigurationHashCode = configHashCode;
-        }
-        sInstance.reinitializeWithApplicationContext(context.getApplicationContext(), ssp);
         return sInstance;
     }
 
@@ -175,145 +135,18 @@
         return sInstance;
     }
 
-    /** Updates the state, given the specified context */
-    void update(Context context) {
-        Resources res = context.getResources();
-        DisplayMetrics dm = res.getDisplayMetrics();
-
-        // Debug mode
-        debugModeEnabled = Prefs.getBoolean(context, Prefs.Key.DEBUG_MODE_ENABLED,
-                false /* defaultValue */);
-        if (debugModeEnabled) {
-            Console.Enabled = true;
-        }
-
-        // Layout
-        isLandscape = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
-        hasTransposedSearchBar = res.getBoolean(R.bool.recents_has_transposed_search_bar);
-        hasTransposedNavBar = res.getBoolean(R.bool.recents_has_transposed_nav_bar);
-
-        // Insets
-        displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
-
-        // Filtering
-        filteringCurrentViewsAnimDuration =
-                res.getInteger(R.integer.recents_filter_animate_current_views_duration);
-        filteringNewViewsAnimDuration =
-                res.getInteger(R.integer.recents_filter_animate_new_views_duration);
-
-        // Loading
-        maxNumTasksToLoad = ActivityManager.getMaxRecentTasksStatic();
-
-        // Search Bar
-        searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
-
-        // Task stack
-        taskStackScrollDuration =
-                res.getInteger(R.integer.recents_animate_task_stack_scroll_duration);
-        taskStackWidthPaddingPct = res.getFloat(R.dimen.recents_stack_width_padding_percentage);
-        taskStackOverscrollPct = res.getFloat(R.dimen.recents_stack_overscroll_percentage);
-        taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
-        taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
-        dismissAllButtonSizePx = res.getDimensionPixelSize(R.dimen.recents_dismiss_all_button_size);
-
-        // Transition
-        transitionEnterFromAppDelay =
-                res.getInteger(R.integer.recents_enter_from_app_transition_duration);
-        transitionEnterFromHomeDelay =
-                res.getInteger(R.integer.recents_enter_from_home_transition_duration);
-
-        // Task view animation and styles
-        taskViewEnterFromAppDuration =
-                res.getInteger(R.integer.recents_task_enter_from_app_duration);
-        taskViewEnterFromHomeDuration =
-                res.getInteger(R.integer.recents_task_enter_from_home_duration);
-        taskViewEnterFromHomeStaggerDelay =
-                res.getInteger(R.integer.recents_task_enter_from_home_stagger_delay);
-        taskViewExitToAppDuration =
-                res.getInteger(R.integer.recents_task_exit_to_app_duration);
-        taskViewExitToHomeDuration =
-                res.getInteger(R.integer.recents_task_exit_to_home_duration);
-        taskViewRemoveAnimDuration =
-                res.getInteger(R.integer.recents_animate_task_view_remove_duration);
-        taskViewRemoveAnimTranslationXPx =
-                res.getDimensionPixelSize(R.dimen.recents_task_view_remove_anim_translation_x);
-        taskViewRoundedCornerRadiusPx =
-                res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
-        taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
-        taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
-        taskViewTranslationZMaxPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
-        taskViewAffiliateGroupEnterOffsetPx =
-                res.getDimensionPixelSize(R.dimen.recents_task_view_affiliate_group_enter_offset);
-        taskViewThumbnailAlpha = res.getFloat(R.dimen.recents_task_view_thumbnail_alpha);
-
-        // Task bar colors
-        taskBarViewDefaultBackgroundColor = context.getColor(
-                R.color.recents_task_bar_default_background_color);
-        taskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
-        taskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
-        taskBarViewHighlightColor = context.getColor(R.color.recents_task_bar_highlight_color);
-        taskBarViewAffiliationColorMinAlpha = res.getFloat(
-                R.dimen.recents_task_affiliation_color_min_alpha_percentage);
-
-        // Task bar size & animations
-        taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
-        taskBarDismissDozeDelaySeconds =
-                res.getInteger(R.integer.recents_task_bar_dismiss_delay_seconds);
-
-        // Nav bar scrim
-        navBarScrimEnterDuration =
-                res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
-
-        // Misc
-        useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
-        altTabKeyDelay = res.getInteger(R.integer.recents_alt_tab_key_delay);
-        fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
-        svelteLevel = res.getInteger(R.integer.recents_svelte_level);
-    }
-
-    /** Updates the system insets */
-    public void updateSystemInsets(Rect insets) {
-        systemInsets.set(insets);
-    }
-
-    /** Updates the states that need to be re-read from the application context. */
-    void reinitializeWithApplicationContext(Context context, SystemServicesProxy ssp) {
-        // Check if the developer options are enabled
-        developerOptionsEnabled = ssp.getGlobalSetting(context,
-                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0;
-        lockToAppEnabled = ssp.getSystemSetting(context,
-                Settings.System.LOCK_TO_APP_ENABLED) != 0;
-        multiWindowEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
+    /**
+     * Returns the activity launch state.
+     * TODO: This will be refactored out of RecentsConfiguration.
+     */
+    public RecentsActivityLaunchState getLaunchState() {
+        return mLaunchState;
     }
 
     /** Called when the configuration has changed, and we want to reset any configuration specific
      * members. */
     public void updateOnConfigurationChange() {
-        // Reset this flag on configuration change to ensure that we recreate new task views
-        launchedReuseTaskStackViews = false;
-        // Set this flag to indicate that the configuration has changed since Recents last launched
-        launchedHasConfigurationChanged = true;
-    }
-
-    /** Returns whether the status bar scrim should be animated when shown for the first time. */
-    public boolean shouldAnimateStatusBarScrim() {
-        return launchedFromHome;
-    }
-
-    /** Returns whether the status bar scrim should be visible. */
-    public boolean hasStatusBarScrim() {
-        return !launchedWithNoRecentTasks;
-    }
-
-    /** Returns whether the nav bar scrim should be animated when shown for the first time. */
-    public boolean shouldAnimateNavBarScrim() {
-        return true;
-    }
-
-    /** Returns whether the nav bar scrim should be visible. */
-    public boolean hasNavBarScrim() {
-        // Only show the scrim if we have recent tasks, and if the nav bar is not transposed
-        return !launchedWithNoRecentTasks && (!hasTransposedNavBar || !isLandscape);
+        mLaunchState.updateOnConfigurationChange();
     }
 
     /**
@@ -322,14 +155,17 @@
      */
     public void getAvailableTaskStackBounds(Rect windowBounds, int topInset,
             int rightInset, Rect searchBarBounds, Rect taskStackBounds) {
-        if (isLandscape && hasTransposedSearchBar) {
-            // In landscape, the search bar appears on the left, but we overlay it on top
-            taskStackBounds.set(windowBounds.left, windowBounds.top + topInset,
-                    windowBounds.right - rightInset, windowBounds.bottom);
+        if (hasTransposedNavBar) {
+            // In landscape phones, the search bar appears on the left, but we overlay it on top
+            int swInset = getInsetToSmallestWidth(windowBounds.right - rightInset -
+                    windowBounds.left);
+            taskStackBounds.set(windowBounds.left + swInset, windowBounds.top + topInset,
+                    windowBounds.right - swInset - rightInset, windowBounds.bottom);
         } else {
             // In portrait, the search bar appears on the top (which already has the inset)
-            taskStackBounds.set(windowBounds.left, searchBarBounds.bottom,
-                    windowBounds.right, windowBounds.bottom);
+            int swInset = getInsetToSmallestWidth(windowBounds.right - windowBounds.left);
+            taskStackBounds.set(windowBounds.left + swInset, searchBarBounds.bottom,
+                    windowBounds.right - swInset, windowBounds.bottom);
         }
     }
 
@@ -340,8 +176,8 @@
     public void getSearchBarBounds(Rect windowBounds, int topInset, Rect searchBarSpaceBounds) {
         // Return empty rects if search is not enabled
         int searchBarSize = searchBarSpaceHeightPx;
-        if (isLandscape && hasTransposedSearchBar) {
-            // In landscape, the search bar appears on the left
+        if (hasTransposedSearchBar) {
+            // In landscape phones, the search bar appears on the left
             searchBarSpaceBounds.set(windowBounds.left, windowBounds.top + topInset,
                     windowBounds.left + searchBarSize, windowBounds.bottom);
         } else {
@@ -350,4 +186,14 @@
                     windowBounds.right, windowBounds.top + topInset + searchBarSize);
         }
     }
+
+    /**
+     * Constrain the width of the landscape stack to the smallest width of the device.
+     */
+    private int getInsetToSmallestWidth(int availableWidth) {
+        if (availableWidth > smallestWidth) {
+            return (availableWidth - smallestWidth) / 2;
+        }
+        return 0;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index 85f5b5d..59df293 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -21,7 +21,6 @@
 import android.app.Dialog;
 import android.app.DialogFragment;
 import android.app.FragmentManager;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.graphics.Rect;
 import android.os.Bundle;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index cbf5c05..231843e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -39,7 +39,6 @@
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-
 import com.android.systemui.R;
 
 import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
new file mode 100644
index 0000000..4addfa5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -0,0 +1,844 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.MutableBoolean;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Represents a subscriber, which implements various event bus handler methods.
+ */
+class Subscriber {
+    private WeakReference<Object> mSubscriber;
+
+    long registrationTime;
+
+    Subscriber(Object subscriber, long registrationTime) {
+        mSubscriber = new WeakReference<>(subscriber);
+        this.registrationTime = registrationTime;
+    }
+
+    public String toString(int priority) {
+        Object sub = mSubscriber.get();
+        String id = Integer.toHexString(System.identityHashCode(sub));
+        return sub.getClass().getSimpleName() + " [0x" + id + ", P" + priority + "]";
+    }
+
+    public Object getReference() {
+        return mSubscriber.get();
+    }
+}
+
+/**
+ * Represents an event handler with a priority.
+ */
+class EventHandler {
+    int priority;
+    Subscriber subscriber;
+    EventHandlerMethod method;
+
+    EventHandler(Subscriber subscriber, EventHandlerMethod method, int priority) {
+        this.subscriber = subscriber;
+        this.method = method;
+        this.priority = priority;
+    }
+
+    @Override
+    public String toString() {
+        return subscriber.toString(priority) + " " + method.toString();
+    }
+}
+
+/**
+ * Represents the low level method handling a particular event.
+ */
+class EventHandlerMethod {
+    private Method mMethod;
+    Class<? extends EventBus.Event> eventType;
+
+    EventHandlerMethod(Method method, Class<? extends EventBus.Event> eventType) {
+        mMethod = method;
+        mMethod.setAccessible(true);
+        this.eventType = eventType;
+    }
+
+    public void invoke(Object target, EventBus.Event event)
+            throws InvocationTargetException, IllegalAccessException {
+        mMethod.invoke(target, event);
+    }
+
+    @Override
+    public String toString() {
+        return mMethod.getName() + "(" + eventType.getSimpleName() + ")";
+    }
+}
+
+/**
+ * A simple in-process event bus.  It is simple because we can make assumptions about the state of
+ * SystemUI and Recent's lifecycle.
+ *
+ * <p>
+ * Currently, there is a single EventBus that handles {@link EventBus.Event}s for each subscriber
+ * on the main application thread.  Publishers can send() events to synchronously call subscribers
+ * of that event, or post() events to be processed in the next run of the {@link Looper}.  In
+ * addition, the EventBus supports sending and handling {@link EventBus.InterprocessEvent}s
+ * (within the same package) implemented using standard {@link BroadcastReceiver} mechanism.
+ * Interprocess events must be posted using postInterprocess() to ensure that it is dispatched
+ * correctly across processes.
+ *
+ * <p>
+ * Subscribers must be registered with a particular EventBus before they will receive events, and
+ * handler methods must match a specific signature.
+ *
+ * <p>
+ * Event method signature:<ul>
+ * <li>Methods must be public final
+ * <li>Methods must return void
+ * <li>Methods must be called "onBusEvent"
+ * <li>Methods must take one parameter, of class type deriving from {@link EventBus.Event}
+ * </ul>
+ *
+ * <p>
+ * Interprocess-Event method signature:<ul>
+ * <li>Methods must be public final
+ * <li>Methods must return void
+ * <li>Methods must be called "onInterprocessBusEvent"
+ * <li>Methods must take one parameter, of class type deriving from {@link EventBus.InterprocessEvent}
+ * </ul>
+ * </p>
+ *
+ * </p>
+ * Each subscriber can be registered with a given priority (default 1), and events will be dispatch
+ * in decreasing order of priority.  For subscribers with the same priority, events will be
+ * dispatched by latest registration time to earliest.
+ *
+ * <p>
+ * Interprocess events must extend {@link EventBus.InterprocessEvent}, have a constructor which
+ * takes a {@link Bundle} and implement toBundle().  This allows us to serialize events to be sent
+ * across processes.
+ *
+ * <p>
+ * Caveats:<ul>
+ * <li>The EventBus keeps a {@link WeakReference} to the publisher to prevent memory leaks, so
+ * there must be another strong reference to the publisher for it to not get garbage-collected and
+ * continue receiving events.
+ * <li>Because the event handlers are called back using reflection, the EventBus is not intended
+ * for use in tight, performance criticial loops.  For most user input/system callback events, this
+ * is generally of low enough frequency to use the EventBus.
+ * <li>Because the event handlers are called back using reflection, there will often be no
+ * references to them from actual code.  The proguard configuration will be need to be updated to
+ * keep these extra methods:
+ *
+ * -keepclassmembers class ** {
+ * public void onBusEvent(**);
+ * public void onInterprocessBusEvent(**);
+ * }
+ * -keepclassmembers class ** extends **.EventBus$InterprocessEvent {
+ * public <init>(android.os.Bundle);
+ * }
+ *
+ * <li>Subscriber registration can be expensive depending on the subscriber's {@link Class}.  This
+ * is only done once per class type, but if possible, it is best to pre-register an instance of
+ * that class beforehand or when idle.
+ * <li>Each event should be sent once.  Events may hold internal information about the current
+ * dispatch, or may be queued to be dispatched on another thread (if posted from a non-main thread),
+ * so it may be unsafe to edit, change, or re-send the event again.
+ * <li>Events should follow a pattern of public-final POD (plain old data) objects, where they are
+ * initialized by the constructor and read by each subscriber of that event.  Subscribers should
+ * never alter events as they are processed, and this enforces that pattern.
+ * </ul>
+ *
+ * <p>
+ * Future optimizations:
+ * <li>throw exception/log when a subscriber loses the reference
+ * <li>trace cost per registration & invocation
+ * <li>trace cross-process invocation
+ * <li>register(subscriber, Class&lt;?&gt;...) -- pass in exact class types you want registered
+ * <li>setSubscriberEventHandlerPriority(subscriber, Class<Event>, priority)
+ * <li>allow subscribers to implement interface, ie. EventBus.Subscriber, which lets then test a
+ * message before invocation (ie. check if task id == this task id)
+ * <li>add postOnce() which automatically debounces
+ * <li>add postDelayed() which delays / postDelayedOnce() which delays and bounces
+ * <li>consolidate register() and registerInterprocess()
+ * <li>sendForResult&lt;ReturnType&gt;(Event) to send and get a result, but who will send the
+ * result?
+ * </p>
+ */
+public class EventBus extends BroadcastReceiver {
+
+    public static final String TAG = "EventBus";
+
+    /**
+     * An event super class that allows us to track internal event state across subscriber
+     * invocations.
+     *
+     * Events should not be edited by subscribers.
+     */
+    public static class Event {
+        // Indicates that this event's dispatch should be traced and logged to logcat
+        boolean trace;
+        // Indicates that this event must be posted on the EventBus's looper thread before invocation
+        boolean requiresPost;
+        // Not currently exposed, allows a subscriber to cancel further dispatch of this event
+        boolean cancelled;
+
+        // Only accessible from derived events
+        protected Event() {}
+    }
+
+    /**
+     * An inter-process event super class that allows us to track user state across subscriber
+     * invocations.
+     */
+    public static class InterprocessEvent extends Event {
+        private static final String EXTRA_USER = "_user";
+
+        // The user which this event originated from
+        public final int user;
+
+        // Only accessible from derived events
+        protected InterprocessEvent(int user) {
+            this.user = user;
+        }
+
+        /**
+         * Called from the event bus
+         */
+        protected InterprocessEvent(Bundle b) {
+            user = b.getInt(EXTRA_USER);
+        }
+
+        protected Bundle toBundle() {
+            Bundle b = new Bundle();
+            b.putInt(EXTRA_USER, user);
+            return b;
+        }
+    }
+
+    /**
+     * Proguard must also know, and keep, all methods matching this signature.
+     *
+     * -keepclassmembers class ** {
+     *     public void onBusEvent(**);
+     *     public void onInterprocessBusEvent(**);
+     * }
+     */
+    private static final String METHOD_PREFIX = "onBusEvent";
+    private static final String INTERPROCESS_METHOD_PREFIX = "onInterprocessBusEvent";
+
+    // Ensures that interprocess events can only be sent from a process holding this permission. */
+    private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+
+    // Used for passing event data across process boundaries
+    private static final String EXTRA_INTERPROCESS_EVENT_BUNDLE = "interprocess_event_bundle";
+
+    // The default priority of all subscribers
+    private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1;
+
+    // Used for debugging everything
+    private static final boolean DEBUG_TRACE_ALL = false;
+
+    // Orders the handlers by priority and registration time
+    private static final Comparator<EventHandler> EVENT_HANDLER_COMPARATOR = new Comparator<EventHandler>() {
+        @Override
+        public int compare(EventHandler h1, EventHandler h2) {
+            // Rank the handlers by priority descending, followed by registration time descending.
+            // aka. the later registered
+            if (h1.priority != h2.priority) {
+                return h2.priority - h1.priority;
+            } else {
+                return Long.compare(h2.subscriber.registrationTime, h1.subscriber.registrationTime);
+            }
+        }
+    };
+
+    // Used for initializing the default bus
+    private static final Object sLock = new Object();
+    private static EventBus sDefaultBus;
+
+    // The handler to post all events
+    private Handler mHandler;
+
+    // Keep track of whether we have registered a broadcast receiver already, so that we can
+    // unregister ourselves before re-registering again with a new IntentFilter.
+    private boolean mHasRegisteredReceiver;
+
+    /**
+     * Map from event class -> event handler list.  Keeps track of the actual mapping from event
+     * to subscriber method.
+     */
+    private HashMap<Class<? extends Event>, ArrayList<EventHandler>> mEventTypeMap = new HashMap<>();
+
+    /**
+     * Map from subscriber class -> event handler method lists.  Used to determine upon registration
+     * of a new subscriber whether we need to read all the subscriber's methods again using
+     * reflection or whether we can just add the subscriber to the event type map.
+     */
+    private HashMap<Class<? extends Object>, ArrayList<EventHandlerMethod>> mSubscriberTypeMap = new HashMap<>();
+
+    /**
+     * Map from interprocess event name -> interprocess event class.  Used for mapping the event
+     * name after receiving the broadcast, to the event type.  After which a new instance is created
+     * and posted in the local process.
+     */
+    private HashMap<String, Class<? extends InterprocessEvent>> mInterprocessEventNameMap = new HashMap<>();
+
+    /**
+     * Set of all currently registered subscribers
+     */
+    private ArrayList<Subscriber> mSubscribers = new ArrayList<>();
+
+    // For tracing
+    private int mCallCount;
+    private long mCallDurationMicros;
+
+    /**
+     * Private constructor to create an event bus for a given looper.
+     */
+    private EventBus(Looper looper) {
+        mHandler = new Handler(looper);
+    }
+
+    /**
+     * @return the default event bus for the application's main thread.
+     */
+    public static EventBus getDefault() {
+        if (sDefaultBus == null)
+        synchronized (sLock) {
+            if (sDefaultBus == null) {
+                if (DEBUG_TRACE_ALL) {
+                    logWithPid("New EventBus");
+                }
+                sDefaultBus = new EventBus(Looper.getMainLooper());
+            }
+        }
+        return sDefaultBus;
+    }
+
+    /**
+     * Registers a subscriber to receive events with the default priority.
+     *
+     * @param subscriber the subscriber to handle events.  If this is the first instance of the
+     *                   subscriber's class type that has been registered, the class's methods will
+     *                   be scanned for appropriate event handler methods.
+     */
+    public void register(Object subscriber) {
+        registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY, null);
+    }
+
+    /**
+     * Registers a subscriber to receive events with the given priority.
+     *
+     * @param subscriber the subscriber to handle events.  If this is the first instance of the
+     *                   subscriber's class type that has been registered, the class's methods will
+     *                   be scanned for appropriate event handler methods.
+     * @param priority the priority that this subscriber will receive events relative to other
+     *                 subscribers
+     */
+    public void register(Object subscriber, int priority) {
+        registerSubscriber(subscriber, priority, null);
+    }
+
+    /**
+     * Explicitly registers a subscriber to receive interprocess events with the default priority.
+     *
+     * @param subscriber the subscriber to handle events.  If this is the first instance of the
+     *                   subscriber's class type that has been registered, the class's methods will
+     *                   be scanned for appropriate event handler methods.
+     */
+    public void registerInterprocessAsCurrentUser(Context context, Object subscriber) {
+        registerInterprocessAsCurrentUser(context, subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
+    }
+
+    /**
+     * Registers a subscriber to receive interprocess events with the given priority.
+     *
+     * @param subscriber the subscriber to handle events.  If this is the first instance of the
+     *                   subscriber's class type that has been registered, the class's methods will
+     *                   be scanned for appropriate event handler methods.
+     * @param priority the priority that this subscriber will receive events relative to other
+     *                 subscribers
+     */
+    public void registerInterprocessAsCurrentUser(Context context, Object subscriber, int priority) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("registerInterprocessAsCurrentUser(" + subscriber.getClass().getSimpleName() + ")");
+        }
+
+        // Register the subscriber normally, and update the broadcast receiver filter if this is
+        // a new subscriber type with interprocess events
+        MutableBoolean hasInterprocessEventsChanged = new MutableBoolean(false);
+        registerSubscriber(subscriber, priority, hasInterprocessEventsChanged);
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("hasInterprocessEventsChanged: " + hasInterprocessEventsChanged.value);
+        }
+        if (hasInterprocessEventsChanged.value) {
+            registerReceiverForInterprocessEvents(context);
+        }
+    }
+
+    /**
+     * Remove all EventHandlers pointing to the specified subscriber.  This does not remove the
+     * mapping of subscriber type to event handler method, in case new instances of this subscriber
+     * are registered.
+     */
+    public void unregister(Object subscriber) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("unregister()");
+        }
+
+        // Fail immediately if we are being called from the non-main thread
+        long callingThreadId = Thread.currentThread().getId();
+        if (callingThreadId != mHandler.getLooper().getThread().getId()) {
+            throw new RuntimeException("Can not unregister() a subscriber from a non-main thread.");
+        }
+
+        // Return early if this is not a registered subscriber
+        if (!findRegisteredSubscriber(subscriber, true /* removeFoundSubscriber */)) {
+            return;
+        }
+
+        Class<?> subscriberType = subscriber.getClass();
+        ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
+        if (subscriberMethods != null) {
+            // For each of the event handlers the subscriber handles, remove all references of that
+            // handler
+            for (EventHandlerMethod method : subscriberMethods) {
+                ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(method.eventType);
+                for (int i = eventHandlers.size() - 1; i >= 0; i--) {
+                    if (eventHandlers.get(i).subscriber.getReference() == subscriber) {
+                        eventHandlers.remove(i);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Explicit unregistration for interprocess event subscribers.  This actually behaves exactly
+     * the same as unregister() since we also do not want to stop listening for specific
+     * inter-process messages in case new instances of that subscriber is registered.
+     */
+    public void unregisterInterprocess(Context context, Object subscriber) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("unregisterInterprocess()");
+        }
+        unregister(subscriber);
+    }
+
+    /**
+     * Sends an event to the subscribers of the given event type immediately.  This can only be
+     * called from the same thread as the EventBus's looper thread (for the default EventBus, this
+     * is the main application thread).
+     */
+    public void send(Event event) {
+        // Fail immediately if we are being called from the non-main thread
+        long callingThreadId = Thread.currentThread().getId();
+        if (callingThreadId != mHandler.getLooper().getThread().getId()) {
+            throw new RuntimeException("Can not send() a message from a non-main thread.");
+        }
+
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("send(" + event.getClass().getSimpleName() + ")");
+        }
+
+        // Reset the event's cancelled state
+        event.requiresPost = false;
+        event.cancelled = false;
+        queueEvent(event);
+    }
+
+    /**
+     * Post a message to the subscribers of the given event type.  The messages will be posted on
+     * the EventBus's looper thread (for the default EventBus, this is the main application thread).
+     */
+    public void post(Event event) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("post(" + event.getClass().getSimpleName() + ")");
+        }
+
+        // Reset the event's cancelled state
+        event.requiresPost = true;
+        event.cancelled = false;
+        queueEvent(event);
+    }
+
+    /** Prevent post()ing an InterprocessEvent */
+    @Deprecated
+    public void post(InterprocessEvent event) {
+        throw new RuntimeException("Not supported, use postInterprocess");
+    }
+
+    /** Prevent send()ing an InterprocessEvent */
+    @Deprecated
+    public void send(InterprocessEvent event) {
+        throw new RuntimeException("Not supported, use postInterprocess");
+    }
+
+    /**
+     * Posts an interprocess event.
+     */
+    public void postInterprocess(Context context, final InterprocessEvent event) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("postInterprocess(" + event.getClass().getSimpleName() + ")");
+        }
+        String eventType = event.getClass().getName();
+        Bundle eventBundle = event.toBundle();
+        Intent intent = new Intent(eventType);
+        intent.setPackage(context.getPackageName());
+        intent.putExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE, eventBundle);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+                Intent.FLAG_RECEIVER_FOREGROUND);
+        context.sendBroadcastAsUser(intent, UserHandle.ALL);
+    }
+
+    /**
+     * Receiver for interprocess events.
+     */
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("onReceive(" + intent.getAction() + ", user " + UserHandle.myUserId() + ")");
+        }
+
+        Bundle eventBundle = intent.getBundleExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE);
+        Class<? extends InterprocessEvent> eventType = mInterprocessEventNameMap.get(intent.getAction());
+        try {
+            Constructor<? extends InterprocessEvent> ctor = eventType.getConstructor(Bundle.class);
+            send((Event) ctor.newInstance(eventBundle));
+        } catch (NoSuchMethodException|
+                InvocationTargetException|
+                InstantiationException|
+                IllegalAccessException e) {
+            Log.e(TAG, "Failed to create InterprocessEvent", e);
+        }
+    }
+
+    /**
+     * @return a dump of the current state of the EventBus
+     */
+    public String dump() {
+        StringBuilder output = new StringBuilder();
+        output.append("Registered class types:");
+        output.append("\n");
+        for (Class<?> clz : mSubscriberTypeMap.keySet()) {
+            output.append("\t");
+            output.append(clz.getSimpleName());
+            output.append("\n");
+        }
+        output.append("Event map:");
+        output.append("\n");
+        for (Class<?> clz : mEventTypeMap.keySet()) {
+            output.append("\t");
+            output.append(clz.getSimpleName());
+            output.append(" -> ");
+            output.append("\n");
+            ArrayList<EventHandler> handlers = mEventTypeMap.get(clz);
+            for (EventHandler handler : handlers) {
+                Object subscriber = handler.subscriber.getReference();
+                if (subscriber != null) {
+                    String id = Integer.toHexString(System.identityHashCode(subscriber));
+                    output.append("\t\t");
+                    output.append(subscriber.getClass().getSimpleName());
+                    output.append(" [0x" + id + ", #" + handler.priority + "]");
+                    output.append("\n");
+                }
+            }
+        }
+        return output.toString();
+    }
+
+    /**
+     * Registers a new subscriber.
+     *
+     * @return return whether or not this
+     */
+    private void registerSubscriber(Object subscriber, int priority,
+            MutableBoolean hasInterprocessEventsChangedOut) {
+        // Fail immediately if we are being called from the non-main thread
+        long callingThreadId = Thread.currentThread().getId();
+        if (callingThreadId != mHandler.getLooper().getThread().getId()) {
+            throw new RuntimeException("Can not register() a subscriber from a non-main thread.");
+        }
+
+        // Return immediately if this exact subscriber is already registered
+        if (findRegisteredSubscriber(subscriber, false /* removeFoundSubscriber */)) {
+            return;
+        }
+
+        long t1 = 0;
+        if (DEBUG_TRACE_ALL) {
+            t1 = SystemClock.currentTimeMicro();
+            logWithPid("registerSubscriber(" + subscriber.getClass().getSimpleName() + ")");
+        }
+        Subscriber sub = new Subscriber(subscriber, SystemClock.uptimeMillis());
+        Class<?> subscriberType = subscriber.getClass();
+        ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
+        if (subscriberMethods != null) {
+            if (DEBUG_TRACE_ALL) {
+                logWithPid("Subscriber class type already registered");
+            }
+
+            // If we've parsed this subscriber type before, just add to the set for all the known
+            // events
+            for (EventHandlerMethod method : subscriberMethods) {
+                ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(method.eventType);
+                eventTypeHandlers.add(new EventHandler(sub, method, priority));
+                sortEventHandlersByPriority(eventTypeHandlers);
+            }
+            mSubscribers.add(sub);
+            return;
+        } else {
+            if (DEBUG_TRACE_ALL) {
+                logWithPid("Subscriber class type requires registration");
+            }
+
+            // If we are parsing this type from scratch, ensure we add it to the subscriber type
+            // map, and pull out he handler methods below
+            subscriberMethods = new ArrayList<>();
+            mSubscriberTypeMap.put(subscriberType, subscriberMethods);
+            mSubscribers.add(sub);
+        }
+
+        // Find all the valid event bus handler methods of the subscriber
+        MutableBoolean isInterprocessEvent = new MutableBoolean(false);
+        Method[] methods = subscriberType.getMethods();
+        for (Method m : methods) {
+            Class<?>[] parameterTypes = m.getParameterTypes();
+            isInterprocessEvent.value = false;
+            if (isValidEventBusHandlerMethod(m, parameterTypes, isInterprocessEvent)) {
+                Class<? extends Event> eventType = (Class<? extends Event>) parameterTypes[0];
+                ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(eventType);
+                if (eventTypeHandlers == null) {
+                    eventTypeHandlers = new ArrayList<>();
+                    mEventTypeMap.put(eventType, eventTypeHandlers);
+                }
+                if (isInterprocessEvent.value) {
+                    try {
+                        // Enforce that the event must have a Bundle constructor
+                        eventType.getConstructor(Bundle.class);
+
+                        mInterprocessEventNameMap.put(eventType.getName(),
+                                (Class<? extends InterprocessEvent>) eventType);
+                        if (hasInterprocessEventsChangedOut != null) {
+                            hasInterprocessEventsChangedOut.value = true;
+                        }
+                    } catch (NoSuchMethodException e) {
+                        throw new RuntimeException("Expected InterprocessEvent to have a Bundle constructor");
+                    }
+                }
+                EventHandlerMethod method = new EventHandlerMethod(m, eventType);
+                EventHandler handler = new EventHandler(sub, method, priority);
+                eventTypeHandlers.add(handler);
+                subscriberMethods.add(method);
+                sortEventHandlersByPriority(eventTypeHandlers);
+
+                if (DEBUG_TRACE_ALL) {
+                    logWithPid("  * Method: " + m.getName() +
+                            " event: " + parameterTypes[0].getSimpleName() +
+                            " interprocess? " + isInterprocessEvent.value);
+                }
+            }
+        }
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("Registered " + subscriber.getClass().getSimpleName() + " in " +
+                    (SystemClock.currentTimeMicro() - t1) + " microseconds");
+        }
+    }
+
+    /**
+     * Adds a new message.
+     */
+    private void queueEvent(final Event event) {
+        ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(event.getClass());
+        if (eventHandlers == null) {
+            return;
+        }
+        // We need to clone the list in case a subscriber unregisters itself during traversal
+        eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
+        for (final EventHandler eventHandler : eventHandlers) {
+            if (eventHandler.subscriber.getReference() != null) {
+                if (event.requiresPost) {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            processEvent(eventHandler, event);
+                        }
+                    });
+                } else {
+                    processEvent(eventHandler, event);
+                }
+            }
+        }
+    }
+
+    /**
+     * Processes and dispatches the given event to the given event handler, on the thread of whoever
+     * calls this method.
+     */
+    private void processEvent(final EventHandler eventHandler, final Event event) {
+        // Skip if the event was already cancelled
+        if (event.cancelled) {
+            if (event.trace || DEBUG_TRACE_ALL) {
+                logWithPid("Event dispatch cancelled");
+            }
+            return;
+        }
+
+        try {
+            if (event.trace || DEBUG_TRACE_ALL) {
+                logWithPid(" -> " + eventHandler.toString());
+            }
+            Object sub = eventHandler.subscriber.getReference();
+            if (sub != null) {
+                long t1 = 0;
+                if (DEBUG_TRACE_ALL) {
+                    t1 = SystemClock.currentTimeMicro();
+                }
+                eventHandler.method.invoke(sub, event);
+                if (DEBUG_TRACE_ALL) {
+                    long duration = (SystemClock.currentTimeMicro() - t1);
+                    mCallDurationMicros += duration;
+                    mCallCount++;
+                    logWithPid(eventHandler.method.toString() + " duration: " + duration +
+                            " microseconds, avg: " + (mCallDurationMicros / mCallCount));
+                }
+            } else {
+                Log.e(TAG, "Failed to deliver event to null subscriber");
+            }
+        } catch (IllegalAccessException e) {
+            Log.e(TAG, "Failed to invoke method", e);
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e.getCause());
+        }
+    }
+
+    /**
+     * Re-registers the broadcast receiver for any new messages that we want to listen for.
+     */
+    private void registerReceiverForInterprocessEvents(Context context) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("registerReceiverForInterprocessEvents()");
+        }
+        // Rebuild the receiver filter with the new interprocess events
+        IntentFilter filter = new IntentFilter();
+        for (String eventName : mInterprocessEventNameMap.keySet()) {
+            filter.addAction(eventName);
+            if (DEBUG_TRACE_ALL) {
+                logWithPid("  filter: " + eventName);
+            }
+        }
+        // Re-register the receiver with the new filter
+        if (mHasRegisteredReceiver) {
+            context.unregisterReceiver(this);
+        }
+        context.registerReceiverAsUser(this, UserHandle.ALL, filter, PERMISSION_SELF, mHandler);
+        mHasRegisteredReceiver = true;
+    }
+
+    /**
+     * Returns whether this subscriber is currently registered.  If {@param removeFoundSubscriber}
+     * is true, then remove the subscriber before returning.
+     */
+    private boolean findRegisteredSubscriber(Object subscriber, boolean removeFoundSubscriber) {
+        for (int i = mSubscribers.size() - 1; i >= 0; i--) {
+            Subscriber sub = mSubscribers.get(i);
+            if (sub.getReference() == subscriber) {
+                if (removeFoundSubscriber) {
+                    mSubscribers.remove(i);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return whether {@param method} is a valid (normal or interprocess) event bus handler method
+     */
+    private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes,
+            MutableBoolean isInterprocessEventOut) {
+        int modifiers = method.getModifiers();
+        if (Modifier.isPublic(modifiers) &&
+                Modifier.isFinal(modifiers) &&
+                method.getReturnType().equals(Void.TYPE) &&
+                parameterTypes.length == 1) {
+            if (EventBus.InterprocessEvent.class.isAssignableFrom(parameterTypes[0]) &&
+                    method.getName().startsWith(INTERPROCESS_METHOD_PREFIX)) {
+                isInterprocessEventOut.value = true;
+                return true;
+            } else if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
+                            method.getName().startsWith(METHOD_PREFIX)) {
+                isInterprocessEventOut.value = false;
+                return true;
+            } else {
+                if (DEBUG_TRACE_ALL) {
+                    if (!EventBus.Event.class.isAssignableFrom(parameterTypes[0])) {
+                        logWithPid("  Expected method take an Event-based parameter: " + method.getName());
+                    } else if (!method.getName().startsWith(INTERPROCESS_METHOD_PREFIX) &&
+                            !method.getName().startsWith(METHOD_PREFIX)) {
+                        logWithPid("  Expected method start with method prefix: " + method.getName());
+                    }
+                }
+            }
+        } else {
+            if (DEBUG_TRACE_ALL) {
+                if (!Modifier.isPublic(modifiers)) {
+                    logWithPid("  Expected method to be public: " + method.getName());
+                } else if (!Modifier.isFinal(modifiers)) {
+                    logWithPid("  Expected method to be final: " + method.getName());
+                } else if (!method.getReturnType().equals(Void.TYPE)) {
+                    logWithPid("  Expected method to return null: " + method.getName());
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sorts the event handlers by priority and registration time.
+     */
+    private void sortEventHandlersByPriority(List<EventHandler> eventHandlers) {
+        Collections.sort(eventHandlers, EVENT_HANDLER_COMPARATOR);
+    }
+
+    /**
+     * Helper method to log the given {@param text} with the current process and user id.
+     */
+    private static void logWithPid(String text) {
+        Log.d(TAG, "[" + android.os.Process.myPid() + ", u" + UserHandle.myUserId() + "] " + text);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java
new file mode 100644
index 0000000..52cfe18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.RecentsAppWidgetHost;
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent by the {@link RecentsAppWidgetHost} whenever the search provider widget changes, and
+ * subscribers can update accordingly.
+ */
+public class AppWidgetProviderChangedEvent extends EventBus.Event {
+    // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
new file mode 100644
index 0000000..3b68574
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.RecentsPackageMonitor;
+import com.android.systemui.recents.views.TaskStackView;
+
+/**
+ * This event is sent by {@link RecentsPackageMonitor} when a package on the the system changes.
+ * {@link TaskStackView}s listen for this event, and remove the tasks associated with the removed
+ * packages.
+ */
+public class PackagesChangedEvent extends EventBus.Event {
+
+    public final RecentsPackageMonitor monitor;
+    public final String packageName;
+    public final int userId;
+
+    public PackagesChangedEvent(RecentsPackageMonitor monitor, String packageName, int userId) {
+        this.monitor = monitor;
+        this.packageName = packageName;
+        this.userId = userId;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index a2d7d01..b6d25f5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -32,7 +32,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -56,18 +55,18 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.util.MutableBoolean;
+import android.util.MutableFloat;
+import android.util.MutableInt;
 import android.util.Pair;
-import android.util.SparseArray;
+import android.util.Size;
 import android.view.Display;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
-
 import com.android.internal.app.AssistUtils;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsConfiguration;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -662,6 +661,18 @@
     }
 
     /**
+     * Returns the smallest width/height.
+     */
+    public int getDeviceSmallestWidth() {
+        if (mWm == null) return 0;
+
+        Point smallestSizeRange = new Point();
+        Point largestSizeRange = new Point();
+        mWm.getDefaultDisplay().getCurrentSizeRange(smallestSizeRange, largestSizeRange);
+        return smallestSizeRange.x;
+    }
+
+    /**
      * Returns the display rect.
      */
     public Rect getDisplayRect() {
@@ -675,7 +686,7 @@
     }
 
     /**
-     * Returns the window rect.
+     * Returns the window rect for the RecentsActivity, based on the dimensions of the home stack.
      */
     public Rect getWindowRect() {
         Rect windowRect = new Rect();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index e48e5f0..8f9a293 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -21,6 +21,8 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import com.android.internal.content.PackageMonitor;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.PackagesChangedEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
 import java.util.HashSet;
@@ -31,18 +33,9 @@
  * Recents list.
  */
 public class RecentsPackageMonitor extends PackageMonitor {
-    public interface PackageCallbacks {
-        public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName,
-                                      int userId);
-    }
-
-    PackageCallbacks mCb;
-    SystemServicesProxy mSystemServicesProxy;
 
     /** Registers the broadcast receivers with the specified callbacks. */
-    public void register(Context context, PackageCallbacks cb) {
-        mSystemServicesProxy = new SystemServicesProxy(context);
-        mCb = cb;
+    public void register(Context context) {
         try {
             // We register for events from all users, but will cross-reference them with
             // packages for the current user and any profiles they have
@@ -60,17 +53,13 @@
         } catch (IllegalStateException e) {
             e.printStackTrace();
         }
-        mSystemServicesProxy = null;
-        mCb = null;
     }
 
     @Override
     public void onPackageRemoved(String packageName, int uid) {
-        if (mCb == null) return;
-
         // Notify callbacks that a package has changed
         final int eventUserId = getChangingUserId();
-        mCb.onPackagesChanged(this, packageName, eventUserId);
+        EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId));
     }
 
     @Override
@@ -81,11 +70,9 @@
 
     @Override
     public void onPackageModified(String packageName) {
-        if (mCb == null) return;
-
         // Notify callbacks that a package has changed
         final int eventUserId = getChangingUserId();
-        mCb.onPackagesChanged(this, packageName, eventUserId);
+        EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId));
     }
 
     /**
@@ -108,7 +95,8 @@
                     // If we know that the component still exists in the package, then skip
                     continue;
                 }
-                if (mSystemServicesProxy.getActivityInfo(cn, userId) != null) {
+                SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+                if (ssp.getActivityInfo(cn, userId) != null) {
                     existingComponents.add(cn);
                 } else {
                     removedComponents.add(cn);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 649cb4d..6ef7253 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -23,7 +23,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.util.Log;
-import android.util.SparseArray;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
@@ -76,7 +75,7 @@
      * An optimization to preload the raw list of tasks.
      */
     public synchronized void preloadRawTasks(boolean isTopTaskHome) {
-        mRawTasks = mSystemServicesProxy.getRecentTasks(mConfig.maxNumTasksToLoad,
+        mRawTasks = mSystemServicesProxy.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
                 UserHandle.CURRENT.getIdentifier(), isTopTaskHome);
         Collections.reverse(mRawTasks);
 
@@ -125,7 +124,7 @@
                     activityLabel, mSystemServicesProxy, res);
             Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
                     mSystemServicesProxy, res, infoHandle, false);
-            int activityColor = loader.getActivityPrimaryColor(t.taskDescription, mConfig);
+            int activityColor = loader.getActivityPrimaryColor(t.taskDescription, res);
 
             // Update the activity info cache
             if (!hadCachedActivityInfo && infoHandle.info != null) {
@@ -153,7 +152,7 @@
         // Initialize the stacks
         mStack = new TaskStack();
         mStack.setTasks(stackTasks);
-        mStack.createAffiliatedGroupings(mConfig);
+        mStack.createAffiliatedGroupings(mContext);
     }
 
     /**
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 ad25c85..39bef81 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -27,7 +27,6 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.util.Log;
-
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
@@ -248,7 +247,7 @@
 }
 
 /* Recents task loader
- * NOTE: We should not hold any references to a Context from a static instance */
+ * NOTE: We should not hold any references to non-application Context from a static instance */
 public class RecentsTaskLoader {
     private static final String TAG = "RecentsTaskLoader";
 
@@ -263,8 +262,6 @@
     TaskResourceLoadQueue mLoadQueue;
     TaskResourceLoader mLoader;
 
-    RecentsPackageMonitor mPackageMonitor;
-
     int mMaxThumbnailCacheSize;
     int mMaxIconCacheSize;
     int mNumVisibleTasksLoaded;
@@ -294,7 +291,6 @@
 
         // Initialize the proxy, cache and loaders
         mSystemServicesProxy = new SystemServicesProxy(context);
-        mPackageMonitor = new RecentsPackageMonitor();
         mLoadQueue = new TaskResourceLoadQueue();
         mApplicationIconCache = new DrawableLruCache(iconCacheSize);
         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
@@ -438,12 +434,11 @@
     }
 
     /** Returns the activity's primary color. */
-    public int getActivityPrimaryColor(ActivityManager.TaskDescription td,
-            RecentsConfiguration config) {
+    public int getActivityPrimaryColor(ActivityManager.TaskDescription td, Resources res) {
         if (td != null && td.getPrimaryColor() != 0) {
             return td.getPrimaryColor();
         }
-        return config.taskBarViewDefaultBackgroundColor;
+        return res.getColor(R.color.recents_task_bar_default_background_color);
     }
 
     /** Returns the size of the app icon cache. */
@@ -521,17 +516,6 @@
         mLoadQueue.clearTasks();
     }
 
-    /** Registers any broadcast receivers. */
-    public void registerReceivers(Context context, RecentsPackageMonitor.PackageCallbacks cb) {
-        // Register the broadcast receiver to handle messages related to packages being added/removed
-        mPackageMonitor.register(context, cb);
-    }
-
-    /** Unregisters any broadcast receivers. */
-    public void unregisterReceivers() {
-        mPackageMonitor.unregister();
-    }
-
     /**
      * Handles signals from the system, trimming memory when requested to prevent us from running
      * out of memory.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 515e578..20d9203 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,11 +16,12 @@
 
 package com.android.systemui.recents.model;
 
+import android.content.Context;
 import android.graphics.Color;
 import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.NamedCounter;
 import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.R;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -367,7 +368,7 @@
     /**
      * Temporary: This method will simulate affiliation groups by
      */
-    public void createAffiliatedGroupings(RecentsConfiguration config) {
+    public void createAffiliatedGroupings(Context context) {
         if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) {
             HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
             // Sort all tasks by increasing firstActiveTime of the task
@@ -452,7 +453,8 @@
                 tasksMap.put(t.key, t);
             }
             // Update the task colors for each of the groups
-            float minAlpha = config.taskBarViewAffiliationColorMinAlpha;
+            float minAlpha = context.getResources().getFloat(
+                    R.dimen.recents_task_affiliation_color_min_alpha_percentage);
             int taskGroupCount = mGroups.size();
             for (int i = 0; i < taskGroupCount; i++) {
                 TaskGrouping group = mGroups.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
index 41adbed..682fd8f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java
@@ -89,7 +89,8 @@
         mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
         mCornerShadowPaint.setStyle(Paint.Style.FILL);
         mCornerShadowPaint.setDither(true);
-        mCornerRadius = config.taskViewRoundedCornerRadiusPx;
+        mCornerRadius = resources.getDimensionPixelSize(
+                R.dimen.recents_task_view_rounded_corners_radius);
         mCardBounds = new RectF();
         mEdgeShadowPaint = new Paint(mCornerShadowPaint);
     }
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 9685a24..68faccc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -38,8 +38,9 @@
 import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowManagerGlobal;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
@@ -58,8 +59,7 @@
  * This view is the the top level layout that contains TaskStacks (which are laid out according
  * to their SpaceNode bounds.
  */
-public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks,
-        RecentsPackageMonitor.PackageCallbacks {
+public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks {
 
     private static final String TAG = "RecentsView";
 
@@ -83,6 +83,9 @@
     TaskStackView mTaskStackView;
     RecentsAppWidgetHostView mSearchBar;
     RecentsViewCallbacks mCb;
+    Interpolator mFastOutSlowInInterpolator;
+
+    Rect mSystemInsets = new Rect();
 
     public RecentsView(Context context) {
         super(context);
@@ -100,6 +103,8 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         mConfig = RecentsConfiguration.getInstance();
         mInflater = LayoutInflater.from(context);
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_slow_in);
     }
 
     /** Sets the callbacks */
@@ -109,7 +114,7 @@
 
     /** Set/get the bsp root node */
     public void setTaskStack(TaskStack stack) {
-        if (mConfig.launchedReuseTaskStackViews) {
+        if (mConfig.getLaunchState().launchedReuseTaskStackViews) {
             if (mTaskStackView != null) {
                 // If onRecentsHidden is not triggered, we need to the stack view again here
                 mTaskStackView.reset();
@@ -278,7 +283,7 @@
         // Get the search bar bounds and measure the search bar layout
         Rect searchBarSpaceBounds = new Rect();
         if (mSearchBar != null) {
-            mConfig.getSearchBarBounds(new Rect(0, 0, width, height), mConfig.systemInsets.top,
+            mConfig.getSearchBarBounds(new Rect(0, 0, width, height), mSystemInsets.top,
                     searchBarSpaceBounds);
             mSearchBar.measure(
                     MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
@@ -286,8 +291,8 @@
         }
 
         Rect taskStackBounds = new Rect();
-        mConfig.getAvailableTaskStackBounds(new Rect(0, 0, width, height), mConfig.systemInsets.top,
-                mConfig.systemInsets.right, searchBarSpaceBounds, taskStackBounds);
+        mConfig.getAvailableTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
+                mSystemInsets.right, searchBarSpaceBounds, taskStackBounds);
         if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
             mTaskStackView.setTaskStackBounds(taskStackBounds);
             mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
@@ -306,7 +311,7 @@
         Rect searchBarSpaceBounds = new Rect();
         if (mSearchBar != null) {
             mConfig.getSearchBarBounds(measuredRect,
-                    mConfig.systemInsets.top, searchBarSpaceBounds);
+                    mSystemInsets.top, searchBarSpaceBounds);
             mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top,
                     searchBarSpaceBounds.right, searchBarSpaceBounds.bottom);
         }
@@ -318,8 +323,7 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        // Update the configuration with the latest system insets and trigger a relayout
-        mConfig.updateSystemInsets(insets.getSystemWindowInsets());
+        mSystemInsets.set(insets.getSystemWindowInsets());
         requestLayout();
         return insets.consumeSystemWindowInsets();
     }
@@ -548,7 +552,7 @@
             // outside the display rect (to ensure we don't animate from too far away)
             sourceView = stackView;
             offsetX = transform.rect.left;
-            offsetY = mConfig.displayRect.height();
+            offsetY = getMeasuredHeight();
         } else {
             sourceView = tv.mThumbnailView;
         }
@@ -579,17 +583,10 @@
             }
             postDrawHeaderThumbnailTransitionRunnable(stackView, tv, offsetX, offsetY, stackScroll,
                     animStartedListener);
-            if (mConfig.multiWindowEnabled) {
-                opts = ActivityOptions.makeCustomAnimation(sourceView.getContext(),
-                        R.anim.recents_from_unknown_enter,
-                        R.anim.recents_from_unknown_exit,
-                        sourceView.getHandler(), animStartedListener);
-            } else {
-                opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
-                        Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(),
-                        offsetX, offsetY, transform.rect.width(), transform.rect.height(),
-                        sourceView.getHandler(), animStartedListener);
-            }
+            opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
+                    Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(),
+                    offsetX, offsetY, transform.rect.width(), transform.rect.height(),
+                    sourceView.getHandler(), animStartedListener);
         } else {
             opts = ActivityOptions.makeBasic();
         }
@@ -700,11 +697,13 @@
     public void onTaskStackFilterTriggered() {
         // Hide the search bar
         if (mSearchBar != null) {
+            int filterDuration = getResources().getInteger(
+                    R.integer.recents_filter_animate_current_views_duration);
             mSearchBar.animate()
                     .alpha(0f)
                     .setStartDelay(0)
-                    .setInterpolator(mConfig.fastOutSlowInInterpolator)
-                    .setDuration(mConfig.filteringCurrentViewsAnimDuration)
+                    .setInterpolator(mFastOutSlowInInterpolator)
+                    .setDuration(filterDuration)
                     .withLayer()
                     .start();
         }
@@ -714,11 +713,13 @@
     public void onTaskStackUnfilterTriggered() {
         // Show the search bar
         if (mSearchBar != null) {
+            int filterDuration = getResources().getInteger(
+                    R.integer.recents_filter_animate_new_views_duration);
             mSearchBar.animate()
                     .alpha(1f)
                     .setStartDelay(0)
-                    .setInterpolator(mConfig.fastOutSlowInInterpolator)
-                    .setDuration(mConfig.filteringNewViewsAnimDuration)
+                    .setInterpolator(mFastOutSlowInInterpolator)
+                    .setDuration(filterDuration)
                     .withLayer()
                     .start();
         }
@@ -730,14 +731,4 @@
             mCb.onTaskResize(t);
         }
     }
-
-    /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
-
-    @Override
-    public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
-        // Propagate this event down to each task stack view
-        if (mTaskStackView != null) {
-            mTaskStackView.onPackagesChanged(monitor, packageName, userId);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index 0428b48..e04699c1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -22,13 +22,15 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.TargetApi;
+import android.content.Context;
 import android.os.Build;
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
-import com.android.systemui.recents.RecentsConfiguration;
 
 /**
  * This class facilitates swipe to dismiss. It defines an interface to be implemented by the
@@ -46,6 +48,7 @@
     public static final int Y = 1;
 
     private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
+    private Interpolator mLinearOutSlowInInterpolator;
 
     private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
     private int DEFAULT_ESCAPE_ANIMATION_DURATION = 75; // ms
@@ -74,13 +77,15 @@
     public boolean mAllowSwipeTowardsEnd = true;
     private boolean mRtl;
 
-    public SwipeHelper(int swipeDirection, Callback callback, float densityScale,
+    public SwipeHelper(Context context, int swipeDirection, Callback callback, float densityScale,
             float pagingTouchSlop) {
         mCallback = callback;
         mSwipeDirection = swipeDirection;
         mVelocityTracker = VelocityTracker.obtain();
         mDensityScale = densityScale;
         mPagingTouchSlop = pagingTouchSlop;
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.linear_out_slow_in);
     }
 
     public void setDensityScale(float densityScale) {
@@ -265,7 +270,7 @@
         ValueAnimator anim = createTranslationAnimation(view, 0);
         int duration = SNAP_ANIM_LEN;
         anim.setDuration(duration);
-        anim.setInterpolator(RecentsConfiguration.getInstance().linearOutSlowInInterpolator);
+        anim.setInterpolator(mLinearOutSlowInInterpolator);
         anim.addUpdateListener(new AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 1086160..7ce50d8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -17,13 +17,19 @@
 package com.android.systemui.recents.views;
 
 import android.app.Activity;
+import android.content.Context;
 import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import com.android.systemui.R;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.RecentsActivityLaunchState;
 import com.android.systemui.recents.RecentsConfiguration;
 
 /** Manages the scrims for the various system bars. */
 public class SystemBarScrimViews {
 
+    Context mContext;
     RecentsConfiguration mConfig;
 
     View mStatusBarScrimView;
@@ -34,10 +40,22 @@
     boolean mHasStatusBarScrim;
     boolean mShouldAnimateNavBarScrim;
 
-    public SystemBarScrimViews(Activity activity, RecentsConfiguration config) {
-        mConfig = config;
+    int mNavBarScrimEnterDuration;
+
+    Interpolator mFastOutSlowInInterpolator;
+    Interpolator mQuintOutInterpolator;
+
+    public SystemBarScrimViews(Activity activity) {
+        mContext = activity;
+        mConfig = RecentsConfiguration.getInstance();
         mStatusBarScrimView = activity.findViewById(R.id.status_bar_scrim);
         mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
+        mNavBarScrimEnterDuration = activity.getResources().getInteger(
+                R.integer.recents_nav_bar_scrim_enter_duration);
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(activity,
+                        com.android.internal.R.interpolator.fast_out_slow_in);
+        mQuintOutInterpolator = AnimationUtils.loadInterpolator(activity,
+                com.android.internal.R.interpolator.decelerate_quint);
     }
 
     /**
@@ -45,10 +63,11 @@
      * the first draw.
      */
     public void prepareEnterRecentsAnimation() {
-        mHasNavBarScrim = mConfig.hasNavBarScrim();
-        mShouldAnimateNavBarScrim = mConfig.shouldAnimateNavBarScrim();
-        mHasStatusBarScrim = mConfig.hasStatusBarScrim();
-        mShouldAnimateStatusBarScrim = mConfig.shouldAnimateStatusBarScrim();
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+        mHasNavBarScrim = launchState.hasNavBarScrim();
+        mShouldAnimateNavBarScrim = launchState.shouldAnimateNavBarScrim();
+        mHasStatusBarScrim = launchState.hasStatusBarScrim();
+        mShouldAnimateStatusBarScrim = launchState.shouldAnimateStatusBarScrim();
 
         mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
                 View.VISIBLE : View.INVISIBLE);
@@ -60,15 +79,21 @@
      * Starts animating the scrim views when entering Recents.
      */
     public void startEnterRecentsAnimation() {
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+        int transitionEnterFromAppDelay = mContext.getResources().getInteger(
+                R.integer.recents_enter_from_app_transition_duration);
+        int transitionEnterFromHomeDelay = mContext.getResources().getInteger(
+                R.integer.recents_enter_from_home_transition_duration);
+
         if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
             mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
             mStatusBarScrimView.animate()
                     .translationY(0)
-                    .setStartDelay(mConfig.launchedFromHome ?
-                            mConfig.transitionEnterFromHomeDelay :
-                            mConfig.transitionEnterFromAppDelay)
-                    .setDuration(mConfig.navBarScrimEnterDuration)
-                    .setInterpolator(mConfig.quintOutInterpolator)
+                    .setStartDelay(launchState.launchedFromHome ?
+                            transitionEnterFromHomeDelay :
+                            transitionEnterFromAppDelay)
+                    .setDuration(mNavBarScrimEnterDuration)
+                    .setInterpolator(mQuintOutInterpolator)
                     .withStartAction(new Runnable() {
                         @Override
                         public void run() {
@@ -81,11 +106,11 @@
             mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
             mNavBarScrimView.animate()
                     .translationY(0)
-                    .setStartDelay(mConfig.launchedFromHome ?
-                            mConfig.transitionEnterFromHomeDelay :
-                            mConfig.transitionEnterFromAppDelay)
-                    .setDuration(mConfig.navBarScrimEnterDuration)
-                    .setInterpolator(mConfig.quintOutInterpolator)
+                    .setStartDelay(launchState.launchedFromHome ?
+                            transitionEnterFromHomeDelay :
+                            transitionEnterFromAppDelay)
+                    .setDuration(mNavBarScrimEnterDuration)
+                    .setInterpolator(mQuintOutInterpolator)
                     .withStartAction(new Runnable() {
                         @Override
                         public void run() {
@@ -101,20 +126,22 @@
      * going home).
      */
     public void startExitRecentsAnimation() {
+        int taskViewExitToAppDuration = mContext.getResources().getInteger(
+                R.integer.recents_task_exit_to_app_duration);
         if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
             mStatusBarScrimView.animate()
                     .translationY(-mStatusBarScrimView.getMeasuredHeight())
                     .setStartDelay(0)
-                    .setDuration(mConfig.taskViewExitToAppDuration)
-                    .setInterpolator(mConfig.fastOutSlowInInterpolator)
+                    .setDuration(taskViewExitToAppDuration)
+                    .setInterpolator(mFastOutSlowInInterpolator)
                     .start();
         }
         if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
             mNavBarScrimView.animate()
                     .translationY(mNavBarScrimView.getMeasuredHeight())
                     .setStartDelay(0)
-                    .setDuration(mConfig.taskViewExitToAppDuration)
-                    .setInterpolator(mConfig.fastOutSlowInInterpolator)
+                    .setDuration(taskViewExitToAppDuration)
+                    .setInterpolator(mFastOutSlowInInterpolator)
                     .start();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 4db8b37..76b091c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -29,15 +29,17 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsActivityLaunchState;
+import com.android.systemui.recents.RecentsActivity;
 import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.PackagesChangedEvent;
 import com.android.systemui.recents.misc.DozeTrigger;
 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.Task;
 import com.android.systemui.recents.model.TaskStack;
@@ -54,7 +56,7 @@
 /* The visual representation of a task stack view */
 public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
         TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
-        ViewPool.ViewPoolConsumer<TaskView, Task>, RecentsPackageMonitor.PackageCallbacks {
+        ViewPool.ViewPoolConsumer<TaskView, Task> {
 
     /** The TaskView callbacks */
     interface TaskStackViewCallbacks {
@@ -77,7 +79,7 @@
     TaskStackViewTouchHandler mTouchHandler;
     TaskStackViewCallbacks mCb;
     ViewPool<TaskView, Task> mViewPool;
-    ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<TaskViewTransform>();
+    ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
     DozeTrigger mUIDozeTrigger;
     DismissView mDismissAllButton;
     boolean mDismissAllButtonAnimating;
@@ -97,9 +99,9 @@
     Matrix mTmpMatrix = new Matrix();
     Rect mTmpRect = new Rect();
     TaskViewTransform mTmpTransform = new TaskViewTransform();
-    HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<Task, TaskView>();
-    ArrayList<TaskView> mTaskViews = new ArrayList<TaskView>();
-    List<TaskView> mImmutableTaskViews = new ArrayList<TaskView>();
+    HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<>();
+    ArrayList<TaskView> mTaskViews = new ArrayList<>();
+    List<TaskView> mImmutableTaskViews = new ArrayList<>();
     LayoutInflater mInflater;
     boolean mLayersDisabled;
 
@@ -117,14 +119,17 @@
         // Set the stack first
         setStack(stack);
         mConfig = RecentsConfiguration.getInstance();
-        mViewPool = new ViewPool<TaskView, Task>(context, this);
+        mViewPool = new ViewPool<>(context, this);
         mInflater = LayoutInflater.from(context);
-        mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig);
-        mFilterAlgorithm = new TaskStackViewFilterAlgorithm(mConfig, this, mViewPool);
-        mStackScroller = new TaskStackViewScroller(context, mConfig, mLayoutAlgorithm);
+        mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(context, mConfig);
+        mFilterAlgorithm = new TaskStackViewFilterAlgorithm(this, mViewPool);
+        mStackScroller = new TaskStackViewScroller(context, mLayoutAlgorithm);
         mStackScroller.setCallbacks(this);
-        mTouchHandler = new TaskStackViewTouchHandler(context, this, mConfig, mStackScroller);
-        mUIDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
+        mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
+
+        int taskBarDismissDozeDelaySeconds = getResources().getInteger(
+                R.integer.recents_task_bar_dismiss_delay_seconds);
+        mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
             @Override
             public void run() {
                 // Show the task bar dismiss buttons
@@ -144,6 +149,18 @@
         mCb = cb;
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+        super.onAttachedToWindow();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        EventBus.getDefault().unregister(this);
+    }
+
     /** Sets the task stack */
     void setStack(TaskStack stack) {
         // Set the new stack
@@ -731,8 +748,9 @@
         int height = MeasureSpec.getSize(heightMeasureSpec);
 
         // Compute our stack/task rects
-        computeRects(width, height, mTaskStackBounds, mConfig.launchedWithAltTab,
-                mConfig.launchedFromHome);
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+        computeRects(width, height, mTaskStackBounds, launchState.launchedWithAltTab,
+                launchState.launchedFromHome);
 
         // If this is the first layout, then scroll to the front of the stack and synchronize the
         // stack views immediately to load all the views
@@ -764,9 +782,11 @@
         // Measure the dismiss button
         if (mDismissAllButton != null) {
             int taskRectWidth = mLayoutAlgorithm.mTaskRect.width();
+            int dismissAllButtonHeight = getResources().getDimensionPixelSize(
+                    R.dimen.recents_dismiss_all_button_size);
             mDismissAllButton.measure(
                     MeasureSpec.makeMeasureSpec(taskRectWidth, MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(mConfig.dismissAllButtonSizePx, MeasureSpec.EXACTLY));
+                    MeasureSpec.makeMeasureSpec(dismissAllButtonHeight, MeasureSpec.EXACTLY));
         }
 
         setMeasuredDimension(width, height);
@@ -847,20 +867,19 @@
 
         // When Alt-Tabbing, focus the previous task (but leave the animation until we finish the
         // enter animation).
-        if (mConfig.launchedWithAltTab) {
-            if (mConfig.launchedFromAppWithThumbnail) {
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+        if (launchState.launchedWithAltTab) {
+            if (launchState.launchedFromAppWithThumbnail) {
                 focusTask(Math.max(0, mStack.getTaskCount() - 2), false,
-                        mConfig.launchedHasConfigurationChanged);
+                        launchState.launchedHasConfigurationChanged);
             } else {
                 focusTask(Math.max(0, mStack.getTaskCount() - 1), false,
-                        mConfig.launchedHasConfigurationChanged);
+                        launchState.launchedHasConfigurationChanged);
             }
         }
 
         // Start dozing
-        if (!mConfig.multiWindowEnabled) {
-            mUIDozeTrigger.startDozing();
-        }
+        mUIDozeTrigger.startDozing();
     }
 
     /** Requests this task stacks to start it's enter-recents animation */
@@ -924,7 +943,9 @@
 
                     // Start the focus animation when alt-tabbing
                     ArrayList<Task> tasks = mStack.getTasks();
-                    if (mConfig.launchedWithAltTab && !mConfig.launchedHasConfigurationChanged &&
+                    RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+                    if (launchState.launchedWithAltTab &&
+                            !launchState.launchedHasConfigurationChanged &&
                             0 <= mFocusedTaskIndex && mFocusedTaskIndex < tasks.size()) {
                         TaskView tv = getChildViewForTask(tasks.get(mFocusedTaskIndex));
                         if (tv != null) {
@@ -1115,7 +1136,8 @@
         }
 
         // Update the min/max scroll and animate other task views into their new positions
-        updateMinMaxScroll(true, mConfig.launchedWithAltTab, mConfig.launchedFromHome);
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+        updateMinMaxScroll(true, launchState.launchedWithAltTab, launchState.launchedFromHome);
 
         // Offset the stack by as much as the anchor task would otherwise move back
         if (pullStackForward) {
@@ -1133,7 +1155,8 @@
             TaskView frontTv = getChildViewForTask(newFrontMostTask);
             if (frontTv != null) {
                 frontTv.onTaskBound(newFrontMostTask);
-                frontTv.fadeInActionButton(0, mConfig.taskViewEnterFromAppDuration);
+                frontTv.fadeInActionButton(0, getResources().getInteger(
+                        R.integer.recents_task_enter_from_app_duration));
             }
         }
 
@@ -1282,7 +1305,7 @@
         RecentsTaskLoader.getInstance().loadTaskData(task);
 
         // If the doze trigger has already fired, then update the state for this task view
-        if (mConfig.multiWindowEnabled || mUIDozeTrigger.hasTriggered()) {
+        if (mUIDozeTrigger.hasTriggered()) {
             tv.setNoUserInteractionState();
         }
 
@@ -1384,7 +1407,8 @@
                 if (nextTv != null) {
                     // Focus the next task, and only animate the visible state if we are launched
                     // from Alt-Tab
-                    nextTv.setFocusedTask(mConfig.launchedWithAltTab);
+                    RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+                    nextTv.setFocusedTask(launchState.launchedWithAltTab);
                 }
             }
         }
@@ -1420,13 +1444,12 @@
         postInvalidateOnAnimation();
     }
 
-    /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
+    /**** EventBus Events ****/
 
-    @Override
-    public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
+    public final void onBusEvent(PackagesChangedEvent event) {
         // Compute which components need to be removed
-        HashSet<ComponentName> removedComponents = monitor.computeComponentsRemoved(
-                mStack.getTaskKeys(), packageName, userId);
+        HashSet<ComponentName> removedComponents = event.monitor.computeComponentsRemoved(
+                mStack.getTaskKeys(), event.packageName, event.userId);
 
         // For other tasks, just remove them directly if they no longer exist
         ArrayList<Task> tasks = mStack.getTasks();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
index 614ca53..e9f6a46 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
@@ -17,8 +17,8 @@
 package com.android.systemui.recents.views;
 
 import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.model.Task;
+import com.android.systemui.R;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -27,13 +27,10 @@
 /* The layout logic for a TaskStackView */
 public class TaskStackViewFilterAlgorithm {
 
-    RecentsConfiguration mConfig;
     TaskStackView mStackView;
     ViewPool<TaskView, Task> mViewPool;
 
-    public TaskStackViewFilterAlgorithm(RecentsConfiguration config, TaskStackView stackView,
-                                        ViewPool<TaskView, Task> viewPool) {
-        mConfig = config;
+    public TaskStackViewFilterAlgorithm(TaskStackView stackView, ViewPool<TaskView, Task> viewPool) {
         mStackView = stackView;
         mViewPool = viewPool;
     }
@@ -126,7 +123,8 @@
                 }
             }
         }
-        return mConfig.filteringNewViewsAnimDuration;
+        return mStackView.getResources().getInteger(
+                R.integer.recents_filter_animate_new_views_duration);
     }
 
     /**
@@ -172,7 +170,8 @@
             childViewTransformsOut.put(tv, toTransform);
             offset++;
         }
-        return mConfig.filteringCurrentViewsAnimDuration;
+        return mStackView.getResources().getInteger(
+                R.integer.recents_filter_animate_current_views_duration);
     }
 
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index f6df881..7f4c0a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -16,11 +16,13 @@
 
 package com.android.systemui.recents.views;
 
+import android.content.Context;
 import android.graphics.Rect;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
+import com.android.systemui.R;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -48,6 +50,7 @@
         }
     }
 
+    Context mContext;
     RecentsConfiguration mConfig;
 
     // The various rects that define the stack view
@@ -71,7 +74,8 @@
     static float[] xp;
     static float[] px;
 
-    public TaskStackViewLayoutAlgorithm(RecentsConfiguration config) {
+    public TaskStackViewLayoutAlgorithm(Context context, RecentsConfiguration config) {
+        mContext = context;
         mConfig = config;
 
         // Precompute the path
@@ -87,7 +91,8 @@
         mStackVisibleRect.bottom = mViewRect.bottom;
 
         int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
-        int heightPadding = mConfig.taskStackTopPaddingPx;
+        int heightPadding = mContext.getResources().getDimensionPixelSize(
+                R.dimen.recents_stack_top_padding);
         mStackRect.inset(widthPadding, heightPadding);
 
         // Compute the task rect
@@ -98,7 +103,8 @@
 
         // Update the affiliation offsets
         float visibleTaskPct = 0.5f;
-        mWithinAffiliationOffset = mConfig.taskBarHeight;
+        mWithinAffiliationOffset = mContext.getResources().getDimensionPixelSize(
+                R.dimen.recents_task_bar_height);
         mBetweenAffiliationOffset = (int) (visibleTaskPct * mTaskRect.height());
     }
 
@@ -134,8 +140,10 @@
                         mStackRect.bottom));
         float pDismissAllButtonOffset = 0f;
         if (Constants.DebugFlags.App.EnableDismissAll) {
+            int dismissAllButtonHeight = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.recents_dismiss_all_button_size);
             pDismissAllButtonOffset = pAtBottomOfStackRect -
-                screenYToCurveProgress(mStackVisibleRect.bottom - mConfig.dismissAllButtonSizePx);
+                screenYToCurveProgress(mStackVisibleRect.bottom - dismissAllButtonHeight);
         }
 
         // Update the task offsets
@@ -177,6 +185,8 @@
 
         // Walk backwards in the task stack and count the number of tasks and visible thumbnails
         int taskHeight = mTaskRect.height();
+        int taskBarHeight = mContext.getResources().getDimensionPixelSize(
+                R.dimen.recents_task_bar_height);
         int numVisibleTasks = 1;
         int numVisibleThumbnails = 1;
         float progress = mTaskProgressMap.get(tasks.get(tasks.size() - 1).key) - mInitialScrollP;
@@ -192,7 +202,7 @@
                 float scaleAtP = curveProgressToScale(progress);
                 int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2);
                 int screenY = curveProgressToScreenY(progress) + scaleYOffsetAtP;
-                boolean hasVisibleThumbnail = (prevScreenY - screenY) > mConfig.taskBarHeight;
+                boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
                 if (hasVisibleThumbnail) {
                     numVisibleThumbnails++;
                     numVisibleTasks++;
@@ -251,8 +261,8 @@
         }
         float scale = curveProgressToScale(pBounded);
         int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
-        int minZ = mConfig.taskViewTranslationZMinPx;
-        int maxZ = mConfig.taskViewTranslationZMaxPx;
+        int minZ = mContext.getResources().getDimensionPixelSize(R.dimen.recents_task_view_z_min);
+        int maxZ = mContext.getResources().getDimensionPixelSize(R.dimen.recents_task_view_z_max);
         transformOut.scale = scale;
         transformOut.translationY = curveProgressToScreenY(pBounded) - mStackVisibleRect.top -
                 scaleYOffset;
@@ -265,11 +275,9 @@
         return transformOut;
     }
 
-    /** Returns the untransformed task view size. */
-    public Rect getUntransformedTaskViewSize() {
-        Rect tvSize = new Rect(mTaskRect);
-        tvSize.offsetTo(0, 0);
-        return tvSize;
+    /** Returns the untransformed task view bounds. */
+    public Rect getUntransformedTaskViewBounds() {
+        return new Rect(mTaskRect);
     }
 
     /** Returns the scroll to such task top = 1f; */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index fabc86d..f0ae87f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -21,9 +21,11 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.OverScroller;
-import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.R;
 
 /* The scrolling logic for a TaskStackView */
 public class TaskStackViewScroller {
@@ -31,7 +33,7 @@
         public void onScrollChanged(float p);
     }
 
-    RecentsConfiguration mConfig;
+    Context mContext;
     TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
     TaskStackViewScrollerCallbacks mCb;
 
@@ -41,10 +43,14 @@
     ObjectAnimator mScrollAnimator;
     float mFinalAnimatedScroll;
 
-    public TaskStackViewScroller(Context context, RecentsConfiguration config, TaskStackViewLayoutAlgorithm layoutAlgorithm) {
-        mConfig = config;
+    Interpolator mLinearOutSlowInInterpolator;
+
+    public TaskStackViewScroller(Context context, TaskStackViewLayoutAlgorithm layoutAlgorithm) {
+        mContext = context;
         mScroller = new OverScroller(context);
         mLayoutAlgorithm = layoutAlgorithm;
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.linear_out_slow_in);
         setStackScroll(getStackScroll());
     }
 
@@ -140,8 +146,9 @@
 
         mFinalAnimatedScroll = newScroll;
         mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
-        mScrollAnimator.setDuration(mConfig.taskStackScrollDuration);
-        mScrollAnimator.setInterpolator(mConfig.linearOutSlowInInterpolator);
+        mScrollAnimator.setDuration(mContext.getResources().getInteger(
+                R.integer.recents_animate_task_stack_scroll_duration));
+        mScrollAnimator.setInterpolator(mLinearOutSlowInInterpolator);
         mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 7d079d9..86eced8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -26,7 +26,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.R;
 
 import java.util.List;
 
@@ -34,7 +34,7 @@
 class TaskStackViewTouchHandler implements SwipeHelper.Callback {
     static int INACTIVE_POINTER_ID = -1;
 
-    RecentsConfiguration mConfig;
+    Context mContext;
     TaskStackView mSv;
     TaskStackViewScroller mScroller;
     VelocityTracker mVelocityTracker;
@@ -62,7 +62,8 @@
     boolean mInterceptedBySwipeHelper;
 
     public TaskStackViewTouchHandler(Context context, TaskStackView sv,
-            RecentsConfiguration config, TaskStackViewScroller scroller) {
+            TaskStackViewScroller scroller) {
+        mContext = context;
         ViewConfiguration configuration = ViewConfiguration.get(context);
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
@@ -71,10 +72,9 @@
         mWindowTouchSlop = configuration.getScaledWindowTouchSlop();
         mSv = sv;
         mScroller = scroller;
-        mConfig = config;
 
         float densityScale = context.getResources().getDisplayMetrics().density;
-        mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
+        mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale, mPagingTouchSlop);
         mSwipeHelper.setMinAlpha(1f);
     }
 
@@ -268,7 +268,8 @@
                     if (Float.compare(overScrollAmount, 0f) != 0) {
                         // Bound the overscroll to a fixed amount, and inversely scale the y-movement
                         // relative to how close we are to the max overscroll
-                        float maxOverScroll = mConfig.taskStackOverscrollPct;
+                        float maxOverScroll = mContext.getResources().getFloat(
+                                R.dimen.recents_stack_overscroll_percentage);
                         deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount)
                                 / maxOverScroll));
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 373fe7b..bbbaccf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -20,16 +20,25 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.graphics.*;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityManager;
 import android.view.View;
 import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsActivityLaunchState;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
@@ -75,6 +84,10 @@
     View mActionButtonView;
     TaskViewCallbacks mCb;
 
+    Interpolator mFastOutSlowInInterpolator;
+    Interpolator mFastOutLinearInInterpolator;
+    Interpolator mQuintOutInterpolator;
+
     // Optimizations
     ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
             new ValueAnimator.AnimatorUpdateListener() {
@@ -99,14 +112,22 @@
 
     public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        Resources res = context.getResources();
         mConfig = RecentsConfiguration.getInstance();
-        mMaxDimScale = mConfig.taskStackMaxDim / 255f;
+        mMaxDimScale = res.getInteger(R.integer.recents_max_task_stack_view_dim) / 255f;
         mClipViewInStack = true;
-        mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx);
+        mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize(
+                R.dimen.recents_task_view_rounded_corners_radius));
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_slow_in);
+        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_linear_in);
+        mQuintOutInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.decelerate_quint);
         setTaskProgress(getTaskProgress());
         setDim(getDim());
         if (mConfig.fakeShadows) {
-            setBackground(new FakeShadowDrawable(context.getResources(), mConfig));
+            setBackground(new FakeShadowDrawable(res, mConfig));
         }
         setOutlineProvider(mViewBounds);
     }
@@ -159,6 +180,7 @@
 
         int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
         int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;
+        int taskBarHeight = getResources().getDimensionPixelSize(R.dimen.recents_task_bar_height);
 
         // Measure the content
         mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
@@ -166,7 +188,7 @@
 
         // Measure the bar view, and action button
         mHeaderView.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY));
+                MeasureSpec.makeMeasureSpec(taskBarHeight, MeasureSpec.EXACTLY));
         mActionButtonView.measure(
                 MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.AT_MOST),
                 MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.AT_MOST));
@@ -186,7 +208,7 @@
     void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration,
                                              ValueAnimator.AnimatorUpdateListener updateCallback) {
         // Apply the transform
-        toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false,
+        toTransform.applyToTaskView(this, duration, mFastOutSlowInInterpolator, false,
                 !mConfig.fakeShadows, updateCallback);
 
         // Update the task progress
@@ -238,10 +260,11 @@
      * first layout because the actual animation into recents may take a long time. */
     void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask,
                                              boolean occludesLaunchTarget, int offscreenY) {
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
         int initialDim = getDim();
-        if (mConfig.launchedHasConfigurationChanged) {
+        if (launchState.launchedHasConfigurationChanged) {
             // Just load the views as-is
-        } else if (mConfig.launchedFromAppWithThumbnail) {
+        } else if (launchState.launchedFromAppWithThumbnail) {
             if (isTaskViewLaunchTargetTask) {
                 // Set the dim to 0 so we can animate it in
                 initialDim = 0;
@@ -252,7 +275,7 @@
                 setTranslationY(offscreenY);
             }
 
-        } else if (mConfig.launchedFromHome) {
+        } else if (launchState.launchedFromHome) {
             // Move the task view off screen (below) so we can animate it in
             setTranslationY(offscreenY);
             setTranslationZ(0);
@@ -267,45 +290,59 @@
 
     /** Animates this task view as it enters recents */
     void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+        Resources res = mContext.getResources();
         final TaskViewTransform transform = ctx.currentTaskTransform;
+        final int transitionEnterFromAppDelay = res.getInteger(
+                R.integer.recents_enter_from_app_transition_duration);
+        final int transitionEnterFromHomeDelay = res.getInteger(
+                R.integer.recents_enter_from_home_transition_duration);
+        final int taskViewEnterFromAppDuration = res.getInteger(
+                R.integer.recents_task_enter_from_app_duration);
+        final int taskViewEnterFromHomeDuration = res.getInteger(
+                R.integer.recents_task_enter_from_home_duration);
+        final int taskViewEnterFromHomeStaggerDelay = res.getInteger(
+                R.integer.recents_task_enter_from_home_stagger_delay);
+        final int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
+                R.dimen.recents_task_view_affiliate_group_enter_offset);
         int startDelay = 0;
 
-        if (mConfig.launchedFromAppWithThumbnail) {
+        if (launchState.launchedFromAppWithThumbnail) {
             if (mTask.isLaunchTarget) {
                 // Animate the dim/overlay
                 if (Constants.DebugFlags.App.EnableThumbnailAlphaOnFrontmost) {
                     // Animate the thumbnail alpha before the dim animation (to prevent updating the
                     // hardware layer)
-                    mThumbnailView.startEnterRecentsAnimation(mConfig.transitionEnterFromAppDelay,
+                    mThumbnailView.startEnterRecentsAnimation(transitionEnterFromAppDelay,
                             new Runnable() {
                                 @Override
                                 public void run() {
-                                    animateDimToProgress(0, mConfig.taskViewEnterFromAppDuration,
+                                    animateDimToProgress(0, taskViewEnterFromAppDuration,
                                             ctx.postAnimationTrigger.decrementOnAnimationEnd());
                                 }
                             });
                 } else {
                     // Immediately start the dim animation
-                    animateDimToProgress(mConfig.transitionEnterFromAppDelay,
-                            mConfig.taskViewEnterFromAppDuration,
+                    animateDimToProgress(transitionEnterFromAppDelay,
+                            taskViewEnterFromAppDuration,
                             ctx.postAnimationTrigger.decrementOnAnimationEnd());
                 }
                 ctx.postAnimationTrigger.increment();
 
                 // Animate the action button in
-                fadeInActionButton(mConfig.transitionEnterFromAppDelay,
-                        mConfig.taskViewEnterFromAppDuration);
+                fadeInActionButton(transitionEnterFromAppDelay,
+                        taskViewEnterFromAppDuration);
             } else {
                 // Animate the task up if it was occluding the launch target
                 if (ctx.currentTaskOccludesLaunchTarget) {
-                    setTranslationY(transform.translationY + mConfig.taskViewAffiliateGroupEnterOffsetPx);
+                    setTranslationY(transform.translationY + taskViewAffiliateGroupEnterOffset);
                     setAlpha(0f);
                     animate().alpha(1f)
                             .translationY(transform.translationY)
-                            .setStartDelay(mConfig.transitionEnterFromAppDelay)
+                            .setStartDelay(transitionEnterFromAppDelay)
                             .setUpdateListener(null)
-                            .setInterpolator(mConfig.fastOutSlowInInterpolator)
-                            .setDuration(mConfig.taskViewEnterFromHomeDuration)
+                            .setInterpolator(mFastOutSlowInInterpolator)
+                            .setDuration(taskViewEnterFromHomeDuration)
                             .withEndAction(new Runnable() {
                                 @Override
                                 public void run() {
@@ -317,13 +354,13 @@
                     ctx.postAnimationTrigger.increment();
                 }
             }
-            startDelay = mConfig.transitionEnterFromAppDelay;
+            startDelay = transitionEnterFromAppDelay;
 
-        } else if (mConfig.launchedFromHome) {
+        } else if (launchState.launchedFromHome) {
             // Animate the tasks up
             int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
-            int delay = mConfig.transitionEnterFromHomeDelay +
-                    frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay;
+            int delay = transitionEnterFromHomeDelay +
+                    frontIndex * taskViewEnterFromHomeStaggerDelay;
 
             setScaleX(transform.scale);
             setScaleY(transform.scale);
@@ -334,9 +371,9 @@
                     .translationY(transform.translationY)
                     .setStartDelay(delay)
                     .setUpdateListener(ctx.updateListener)
-                    .setInterpolator(mConfig.quintOutInterpolator)
-                    .setDuration(mConfig.taskViewEnterFromHomeDuration +
-                            frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay)
+                    .setInterpolator(mQuintOutInterpolator)
+                    .setDuration(taskViewEnterFromHomeDuration +
+                            frontIndex * taskViewEnterFromHomeStaggerDelay)
                     .withEndAction(new Runnable() {
                         @Override
                         public void run() {
@@ -373,12 +410,14 @@
 
     /** Animates this task view as it leaves recents by pressing home. */
     void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
+        int taskViewExitToHomeDuration = getResources().getInteger(
+                R.integer.recents_task_exit_to_home_duration);
         animate()
                 .translationY(ctx.offscreenTranslationY)
                 .setStartDelay(0)
                 .setUpdateListener(null)
-                .setInterpolator(mConfig.fastOutLinearInInterpolator)
-                .setDuration(mConfig.taskViewExitToHomeDuration)
+                .setInterpolator(mFastOutLinearInInterpolator)
+                .setDuration(taskViewExitToHomeDuration)
                 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
                 .start();
         ctx.postAnimationTrigger.increment();
@@ -392,6 +431,11 @@
     /** Animates this task view as it exits recents */
     void startLaunchTaskAnimation(final Runnable postAnimRunnable, boolean isLaunchingTask,
             boolean occludesLaunchTarget, boolean lockToTask) {
+        final int taskViewExitToAppDuration = mContext.getResources().getInteger(
+                R.integer.recents_task_exit_to_app_duration);
+        final int taskViewAffiliateGroupEnterOffset = mContext.getResources().getDimensionPixelSize(
+                R.dimen.recents_task_view_affiliate_group_enter_offset);
+
         if (isLaunchingTask) {
             // Animate the thumbnail alpha back into full opacity for the window animation out
             mThumbnailView.startLaunchTaskAnimation(postAnimRunnable);
@@ -399,8 +443,8 @@
             // Animate the dim
             if (mDimAlpha > 0) {
                 ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
-                anim.setDuration(mConfig.taskViewExitToAppDuration);
-                anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+                anim.setDuration(taskViewExitToAppDuration);
+                anim.setInterpolator(mFastOutLinearInInterpolator);
                 anim.start();
             }
 
@@ -414,8 +458,8 @@
             mActionButtonView.animate()
                     .alpha(0f)
                     .setStartDelay(0)
-                    .setDuration(mConfig.taskViewExitToAppDuration)
-                    .setInterpolator(mConfig.fastOutLinearInInterpolator)
+                    .setDuration(taskViewExitToAppDuration)
+                    .setInterpolator(mFastOutLinearInInterpolator)
                     .start();
         } else {
             // Hide the dismiss button
@@ -424,11 +468,11 @@
             // animate it away first
             if (occludesLaunchTarget) {
                 animate().alpha(0f)
-                    .translationY(getTranslationY() + mConfig.taskViewAffiliateGroupEnterOffsetPx)
+                    .translationY(getTranslationY() + taskViewAffiliateGroupEnterOffset)
                     .setStartDelay(0)
                     .setUpdateListener(null)
-                    .setInterpolator(mConfig.fastOutLinearInInterpolator)
-                    .setDuration(mConfig.taskViewExitToAppDuration)
+                    .setInterpolator(mFastOutLinearInInterpolator)
+                    .setDuration(taskViewExitToAppDuration)
                     .start();
             }
         }
@@ -436,15 +480,20 @@
 
     /** Animates the deletion of this task view */
     void startDeleteTaskAnimation(final Runnable r, int delay) {
+        int taskViewRemoveAnimDuration = getResources().getInteger(
+                R.integer.recents_animate_task_view_remove_duration);
+        int taskViewRemoveAnimTranslationXPx = getResources().getDimensionPixelSize(
+                R.dimen.recents_task_view_remove_anim_translation_x);
+
         // Disabling clipping with the stack while the view is animating away
         setClipViewInStack(false);
 
-        animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
+        animate().translationX(taskViewRemoveAnimTranslationXPx)
             .alpha(0f)
             .setStartDelay(delay)
             .setUpdateListener(null)
-            .setInterpolator(mConfig.fastOutSlowInInterpolator)
-            .setDuration(mConfig.taskViewRemoveAnimDuration)
+            .setInterpolator(mFastOutSlowInInterpolator)
+            .setDuration(taskViewRemoveAnimDuration)
             .withEndAction(new Runnable() {
                 @Override
                 public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 3e9410e..f68dd64 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -26,19 +26,21 @@
 import android.content.res.ColorStateList;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewOutlineProvider;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -67,6 +69,8 @@
     boolean mCurrentPrimaryColorIsDark;
     int mCurrentPrimaryColor;
     int mBackgroundColor;
+    int mCornerRadius;
+    int mHighlightHeight;
     Drawable mLightDismissDrawable;
     Drawable mDarkDismissDrawable;
     RippleDrawable mBackground;
@@ -81,6 +85,9 @@
     Paint mDimLayerPaint = new Paint();
     PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
 
+    Interpolator mFastOutSlowInInterpolator;
+    Interpolator mFastOutLinearInInterpolator;
+
     boolean mLayersDisabled;
 
     public TaskViewHeader(Context context) {
@@ -113,13 +120,21 @@
         mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
         mDismissContentDescription =
                 context.getString(R.string.accessibility_recents_item_will_be_dismissed);
+        mCornerRadius = getResources().getDimensionPixelSize(
+                R.dimen.recents_task_view_rounded_corners_radius);
+        mHighlightHeight = getResources().getDimensionPixelSize(
+                R.dimen.recents_task_view_highlight);
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_slow_in);
+        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_linear_in);
 
         // Configure the highlight paint
         if (sHighlightPaint == null) {
             sHighlightPaint = new Paint();
             sHighlightPaint.setStyle(Paint.Style.STROKE);
-            sHighlightPaint.setStrokeWidth(mConfig.taskViewHighlightPx);
-            sHighlightPaint.setColor(mConfig.taskBarViewHighlightColor);
+            sHighlightPaint.setStrokeWidth(mHighlightHeight);
+            sHighlightPaint.setColor(context.getColor(R.color.recents_task_bar_highlight_color));
             sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
             sHighlightPaint.setAntiAlias(true);
         }
@@ -154,8 +169,8 @@
     @Override
     protected void onDraw(Canvas canvas) {
         // Draw the highlight at the top edge (but put the bottom edge just out of view)
-        float offset = (float) Math.ceil(mConfig.taskViewHighlightPx / 2f);
-        float radius = mConfig.taskViewRoundedCornerRadiusPx;
+        float offset = (float) Math.ceil(mHighlightHeight / 2f);
+        float radius = mCornerRadius;
         int count = canvas.save(Canvas.CLIP_SAVE_FLAG);
         canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
         canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
@@ -207,10 +222,15 @@
             mBackgroundColorDrawable.setColor(t.colorPrimary);
             mBackgroundColor = t.colorPrimary;
         }
+
+        int taskBarViewLightTextColor = getResources().getColor(
+                R.color.recents_task_bar_light_text_color);
+        int taskBarViewDarkTextColor = getResources().getColor(
+                R.color.recents_task_bar_dark_text_color);
         mCurrentPrimaryColor = t.colorPrimary;
         mCurrentPrimaryColorIsDark = t.useLightOnPrimaryColor;
         mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
-                mConfig.taskBarViewLightTextColor : mConfig.taskBarViewDarkTextColor);
+                taskBarViewLightTextColor : taskBarViewDarkTextColor);
         mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
                 mLightDismissDrawable : mDarkDismissDrawable);
         mDismissButton.setContentDescription(String.format(mDismissContentDescription,
@@ -262,12 +282,14 @@
     /** Animates this task bar dismiss button when launching a task. */
     void startLaunchTaskDismissAnimation() {
         if (mDismissButton.getVisibility() == View.VISIBLE) {
+            int taskViewExitToAppDuration = mContext.getResources().getInteger(
+                    R.integer.recents_task_exit_to_app_duration);
             mDismissButton.animate().cancel();
             mDismissButton.animate()
                     .alpha(0f)
                     .setStartDelay(0)
-                    .setInterpolator(mConfig.fastOutSlowInInterpolator)
-                    .setDuration(mConfig.taskViewExitToAppDuration)
+                    .setInterpolator(mFastOutSlowInInterpolator)
+                    .setDuration(taskViewExitToAppDuration)
                     .start();
         }
     }
@@ -280,8 +302,9 @@
             mDismissButton.animate()
                     .alpha(1f)
                     .setStartDelay(0)
-                    .setInterpolator(mConfig.fastOutLinearInInterpolator)
-                    .setDuration(mConfig.taskViewEnterFromAppDuration)
+                    .setInterpolator(mFastOutLinearInInterpolator)
+                    .setDuration(getResources().getInteger(
+                            R.integer.recents_task_enter_from_app_duration))
                     .start();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 117a7d3..6c83bee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -32,9 +32,11 @@
 import android.graphics.Shader;
 import android.util.AttributeSet;
 import android.view.View;
-import com.android.systemui.recents.RecentsConfiguration;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
+import com.android.systemui.R;
 
 
 /**
@@ -43,9 +45,8 @@
  */
 public class TaskViewThumbnail extends View {
 
-    RecentsConfiguration mConfig;
-
     // Drawing
+    int mCornerRadius;
     float mDimAlpha;
     Matrix mScaleMatrix = new Matrix();
     Paint mDrawPaint = new Paint();
@@ -54,6 +55,8 @@
     BitmapShader mBitmapShader;
     LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
 
+    Interpolator mFastOutSlowInInterpolator;
+
     // Thumbnail alpha
     float mThumbnailAlpha;
     ValueAnimator mThumbnailAlphaAnimator;
@@ -89,15 +92,18 @@
 
     public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        mConfig = RecentsConfiguration.getInstance();
         mDrawPaint.setColorFilter(mLightingColorFilter);
         mDrawPaint.setFilterBitmap(true);
         mDrawPaint.setAntiAlias(true);
+        mCornerRadius = getResources().getDimensionPixelSize(
+                R.dimen.recents_task_view_rounded_corners_radius);
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_slow_in);
     }
 
     @Override
     protected void onFinishInflate() {
-        mThumbnailAlpha = mConfig.taskViewThumbnailAlpha;
+        mThumbnailAlpha = getResources().getFloat(R.dimen.recents_task_view_thumbnail_alpha);
         updateThumbnailPaintFilter();
     }
 
@@ -117,8 +123,8 @@
         }
         // Draw the thumbnail with the rounded corners
         canvas.drawRoundRect(0, 0, getWidth(), getHeight(),
-                mConfig.taskViewRoundedCornerRadiusPx,
-                mConfig.taskViewRoundedCornerRadiusPx, mDrawPaint);
+                mCornerRadius,
+                mCornerRadius, mDrawPaint);
     }
 
     /** Sets the thumbnail to a given bitmap. */
@@ -215,8 +221,10 @@
                 startFadeAnimation(1f, 0, 150, null);
             }
         } else {
-            if (Float.compare(getAlpha(), mConfig.taskViewThumbnailAlpha) != 0) {
-                startFadeAnimation(mConfig.taskViewThumbnailAlpha, 0, 150, null);
+            float taskViewThumbnailAlpha = getResources().getFloat(
+                    R.dimen.recents_task_view_thumbnail_alpha);
+            if (Float.compare(getAlpha(), taskViewThumbnailAlpha) != 0) {
+                startFadeAnimation(taskViewThumbnailAlpha, 0, 150, null);
             }
         }
     }
@@ -229,20 +237,26 @@
         if (isTaskViewLaunchTargetTask) {
             mThumbnailAlpha = 1f;
         } else {
-            mThumbnailAlpha = mConfig.taskViewThumbnailAlpha;
+            mThumbnailAlpha = getResources().getFloat(
+                    R.dimen.recents_task_view_thumbnail_alpha);
         }
         updateThumbnailPaintFilter();
     }
 
     /** Animates this task thumbnail as it enters Recents. */
     void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) {
-        startFadeAnimation(mConfig.taskViewThumbnailAlpha, delay,
-                mConfig.taskViewEnterFromAppDuration, postAnimRunnable);
+        float taskViewThumbnailAlpha = getResources().getFloat(
+                R.dimen.recents_task_view_thumbnail_alpha);
+        startFadeAnimation(taskViewThumbnailAlpha, delay,
+                getResources().getInteger(R.integer.recents_task_enter_from_app_duration),
+                postAnimRunnable);
     }
 
     /** Animates this task thumbnail as it exits Recents. */
     void startLaunchTaskAnimation(Runnable postAnimRunnable) {
-        startFadeAnimation(1f, 0, mConfig.taskViewExitToAppDuration, postAnimRunnable);
+        int taskViewExitToAppDuration = mContext.getResources().getInteger(
+                R.integer.recents_task_exit_to_app_duration);
+        startFadeAnimation(1f, 0, taskViewExitToAppDuration, postAnimRunnable);
     }
 
     /** Starts a new thumbnail alpha animation. */
@@ -251,7 +265,7 @@
         mThumbnailAlphaAnimator = ValueAnimator.ofFloat(mThumbnailAlpha, finalAlpha);
         mThumbnailAlphaAnimator.setStartDelay(delay);
         mThumbnailAlphaAnimator.setDuration(duration);
-        mThumbnailAlphaAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
+        mThumbnailAlphaAnimator.setInterpolator(mFastOutSlowInInterpolator);
         mThumbnailAlphaAnimator.addUpdateListener(mThumbnailAlphaUpdateListener);
         if (postAnimRunnable != null) {
             mThumbnailAlphaAnimator.addListener(new AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index e70d146..71267cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -81,7 +81,7 @@
     }
 
     private void registerListener() {
-        if (UserSwitcherController.isUserSwitcherAvailable(mUserManager) && mUserListener == null) {
+        if (mUserManager.isUserSwitcherEnabled() && mUserListener == null) {
 
             final UserSwitcherController controller = mUserSwitcherController;
             if (controller != null) {
@@ -103,7 +103,7 @@
 
     @Override
     public void onClick(View v) {
-        if (UserSwitcherController.isUserSwitcherAvailable(mUserManager)) {
+        if (mUserManager.isUserSwitcherEnabled()) {
             if (mKeyguardMode) {
                 if (mKeyguardUserSwitcher != null) {
                     mKeyguardUserSwitcher.show(true /* animate */);
@@ -135,14 +135,14 @@
 
     private void refreshContentDescription() {
         String currentUser = null;
-        if (UserSwitcherController.isUserSwitcherAvailable(mUserManager)
+        if (mUserManager.isUserSwitcherEnabled()
                 && mUserSwitcherController != null) {
             currentUser = mUserSwitcherController.getCurrentUserName(mContext);
         }
 
         String text = null;
         if (isClickable()) {
-            if (UserSwitcherController.isUserSwitcherAvailable(mUserManager)) {
+            if (mUserManager.isUserSwitcherEnabled()) {
                 if (TextUtils.isEmpty(currentUser)) {
                     text = mContext.getString(R.string.accessibility_multi_user_switch_switcher);
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 98f5444..37edc28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -882,7 +882,7 @@
         mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
         mNextAlarmController = new NextAlarmController(mContext);
         mKeyguardMonitor = new KeyguardMonitor(mContext);
-        if (UserSwitcherController.isUserSwitcherAvailable(UserManager.get(mContext))) {
+        if (UserManager.get(mContext).isUserSwitcherEnabled()) {
             mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,
                     mHandler);
             if (mUserSwitcherController.useFullscreenUserSwitcher()) {
@@ -904,8 +904,8 @@
                     mBluetoothController, mLocationController, mRotationLockController,
                     mNetworkController, mZenModeController, mHotspotController,
                     mCastController, mFlashlightController,
-                    mUserSwitcherController, mKeyguardMonitor,
-                    mSecurityController);
+                    mUserSwitcherController, mUserInfoController, mKeyguardMonitor,
+                    mSecurityController, mBatteryController);
             mQSPanel.setHost(qsh);
             mQSPanel.setTiles(qsh.getTiles());
             mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
@@ -3001,6 +3001,10 @@
         dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone);
     }
 
+    public void dismissKeyguard() {
+        mStatusBarKeyguardViewManager.dismiss();
+    }
+
     private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
             boolean afterKeyguardGone) {
         if (mStatusBarKeyguardViewManager.isShowing()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 385c5d5..6ec9494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -26,34 +26,8 @@
 
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.tiles.AirplaneModeTile;
-import com.android.systemui.qs.tiles.BluetoothTile;
-import com.android.systemui.qs.tiles.CastTile;
-import com.android.systemui.qs.tiles.CellularTile;
-import com.android.systemui.qs.tiles.ColorInversionTile;
-import com.android.systemui.qs.tiles.DndTile;
-import com.android.systemui.qs.tiles.FlashlightTile;
-import com.android.systemui.qs.tiles.HotspotTile;
-import com.android.systemui.qs.tiles.IntentTile;
-import com.android.systemui.qs.tiles.LocationTile;
-import com.android.systemui.qs.tiles.QAirplaneTile;
-import com.android.systemui.qs.tiles.QBluetoothTile;
-import com.android.systemui.qs.tiles.QFlashlightTile;
-import com.android.systemui.qs.tiles.QRotationLockTile;
-import com.android.systemui.qs.tiles.QWifiTile;
-import com.android.systemui.qs.tiles.RotationLockTile;
-import com.android.systemui.qs.tiles.WifiTile;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.qs.tiles.*;
+import com.android.systemui.statusbar.policy.*;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
@@ -85,8 +59,10 @@
     private final Looper mLooper;
     private final FlashlightController mFlashlight;
     private final UserSwitcherController mUserSwitcherController;
+    private final UserInfoController mUserInfoController;
     private final KeyguardMonitor mKeyguard;
     private final SecurityController mSecurity;
+    private final BatteryController mBattery;
 
     private Callback mCallback;
 
@@ -95,8 +71,8 @@
             RotationLockController rotation, NetworkController network,
             ZenModeController zen, HotspotController hotspot,
             CastController cast, FlashlightController flashlight,
-            UserSwitcherController userSwitcher, KeyguardMonitor keyguard,
-            SecurityController security) {
+            UserSwitcherController userSwitcher, UserInfoController userInfo, KeyguardMonitor keyguard,
+            SecurityController security, BatteryController battery) {
         mContext = context;
         mStatusBar = statusBar;
         mBluetooth = bluetooth;
@@ -108,8 +84,10 @@
         mCast = cast;
         mFlashlight = flashlight;
         mUserSwitcherController = userSwitcher;
+        mUserInfoController = userInfo;
         mKeyguard = keyguard;
         mSecurity = security;
+        mBattery = battery;
 
         final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(),
                 Process.THREAD_PRIORITY_BACKGROUND);
@@ -203,10 +181,21 @@
         return mKeyguard;
     }
 
+    @Override
     public UserSwitcherController getUserSwitcherController() {
         return mUserSwitcherController;
     }
 
+    @Override
+    public UserInfoController getUserInfoController() {
+        return mUserInfoController;
+    }
+
+    @Override
+    public BatteryController getBatteryController() {
+        return mBattery;
+    }
+
     public SecurityController getSecurityController() {
         return mSecurity;
     }
@@ -259,6 +248,8 @@
         else if (tileSpec.equals("location")) return new LocationTile(this);
         else if (tileSpec.equals("cast")) return new CastTile(this);
         else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
+        else if (tileSpec.equals("user")) return new UserTile(this);
+        else if (tileSpec.equals("battery")) return new BatteryTile(this);
         // Detail only versions of wifi and bluetooth.
         else if (tileSpec.equals("dwifi")) return new WifiTile(this, true);
         else if (tileSpec.equals("dbt")) return new BluetoothTile(this, true);
@@ -268,6 +259,7 @@
         else if (tileSpec.equals("qairplane")) return new QAirplaneTile(this);
         else if (tileSpec.equals("qrotation")) return new QRotationLockTile(this);
         else if (tileSpec.equals("qflashlight")) return new QFlashlightTile(this);
+        else if (tileSpec.equals("qlock")) return new QLockTile(this);
         // Intent tiles.
         else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
         else throw new IllegalArgumentException("Bad tile spec: " + tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index d1b69ab..5071df0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.BatteryManager;
+import android.os.Handler;
 import android.os.PowerManager;
 import android.util.Log;
 
@@ -30,24 +31,31 @@
 
 public class BatteryController extends BroadcastReceiver {
     private static final String TAG = "BatteryController";
+
+    public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
+
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
     private final PowerManager mPowerManager;
+    private final Handler mHandler;
 
     private int mLevel;
     private boolean mPluggedIn;
     private boolean mCharging;
     private boolean mCharged;
     private boolean mPowerSave;
+    private boolean mTestmode = false;
 
     public BatteryController(Context context) {
+        mHandler = new Handler();
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
         filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
         filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
+        filter.addAction(ACTION_LEVEL_TEST);
         context.registerReceiver(this, filter);
 
         updatePowerSave();
@@ -71,9 +79,10 @@
         mChangeCallbacks.remove(cb);
     }
 
-    public void onReceive(Context context, Intent intent) {
+    public void onReceive(final Context context, Intent intent) {
         final String action = intent.getAction();
         if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+            if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;
             mLevel = (int)(100f
                     * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
                     / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
@@ -89,6 +98,38 @@
             updatePowerSave();
         } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)) {
             setPowerSave(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
+        } else if (action.equals(ACTION_LEVEL_TEST)) {
+            mTestmode = true;
+            mHandler.post(new Runnable() {
+                int curLevel = 0;
+                int incr = 1;
+                int saveLevel = mLevel;
+                boolean savePlugged = mPluggedIn;
+                Intent dummy = new Intent(Intent.ACTION_BATTERY_CHANGED);
+                @Override
+                public void run() {
+                    if (curLevel < 0) {
+                        mTestmode = false;
+                        dummy.putExtra("level", saveLevel);
+                        dummy.putExtra("plugged", savePlugged);
+                        dummy.putExtra("testmode", false);
+                    } else {
+                        dummy.putExtra("level", curLevel);
+                        dummy.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC
+                                : 0);
+                        dummy.putExtra("testmode", true);
+                    }
+                    context.sendBroadcast(dummy);
+
+                    if (!mTestmode) return;
+
+                    curLevel += incr;
+                    if (curLevel == 100) {
+                        incr *= -1;
+                    }
+                    mHandler.postDelayed(this, 200);
+                }
+            });
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
index d907b00..cec0c0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
@@ -18,7 +18,8 @@
 
 import android.app.ActivityManager;
 import android.content.Context;
-
+import android.os.RemoteException;
+import android.view.WindowManagerGlobal;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.settings.CurrentUserTracker;
@@ -83,6 +84,20 @@
         return mCanSkipBouncer;
     }
 
+    public void unlock() {
+        try {
+            WindowManagerGlobal.getWindowManagerService().dismissKeyguard();
+        } catch (RemoteException e) {
+        }
+    }
+
+    public void lock() {
+        try {
+            WindowManagerGlobal.getWindowManagerService().lockNow(null /* options */);
+        } catch (RemoteException e) {
+        }
+    }
+
     public void notifyKeyguardState(boolean showing, boolean secure) {
         if (mShowing == showing && mSecure == secure) return;
         mShowing = showing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
index a8d4f13..6931d1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -71,6 +71,11 @@
 
     public void addListener(OnUserInfoChangedListener callback) {
         mCallbacks.add(callback);
+        callback.onUserInfoChanged(mUserName, mUserDrawable);
+    }
+
+    public void remListener(OnUserInfoChangedListener callback) {
+        mCallbacks.remove(callback);
     }
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
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 8165894..e00b890 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -752,9 +752,4 @@
             }
         }
     }
-
-    public static boolean isUserSwitcherAvailable(UserManager um) {
-        return UserManager.supportsMultipleUsers() && um.isUserSwitcherEnabled();
-    }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
index 4387b33..04a51f0 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/QSPagingSwitch.java
@@ -9,8 +9,8 @@
 public class QSPagingSwitch extends TunerSwitch {
 
     public static final String QS_PAGE_TILES =
-            "dwifi,dbt,inversion,dnd,cell,airplane,rotation,flashlight,location,"
-             + "hotspot,qwifi,qbt,qrotation,qflashlight,qairplane,cast";
+            "dwifi,dbt,dnd,cell,battery,user,rotation,flashlight,location,"
+             + "hotspot,qwifi,qbt,qlock,qflashlight,qairplane,inversion,cast";
 
     public QSPagingSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
index 772f866..703ee661 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
@@ -206,8 +206,8 @@
     private static class CustomHost extends QSTileHost {
 
         public CustomHost(Context context) {
-            super(context, null, null, null, null, null, null, null, null, null,
-                    null, null, new BlankSecurityController());
+            super(context, null, null, null, null, null, null, null, null, null, null,
+                    null, null, new BlankSecurityController(), null);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 920f875..9a78b6c 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -39,7 +39,7 @@
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.tuner.TunerService.Tunable;
 
-import static com.android.systemui.BatteryMeterView.SHOW_PERCENT_SETTING;
+import static com.android.systemui.BatteryMeterDrawable.SHOW_PERCENT_SETTING;
 
 public class TunerFragment extends PreferenceFragment {
 
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 50234b2..71559389 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -33,7 +33,7 @@
 import android.provider.Settings;
 import android.util.ArrayMap;
 
-import com.android.systemui.BatteryMeterView;
+import com.android.systemui.BatteryMeterDrawable;
 import com.android.systemui.DemoMode;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
@@ -135,7 +135,7 @@
     public void clearAll() {
         // A couple special cases.
         Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null);
-        Settings.System.putString(mContentResolver, BatteryMeterView.SHOW_PERCENT_SETTING, null);
+        Settings.System.putString(mContentResolver, BatteryMeterDrawable.SHOW_PERCENT_SETTING, null);
         Intent intent = new Intent(DemoMode.ACTION_DEMO);
         intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT);
         mContext.sendBroadcast(intent);
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 8c830e8..3759c91 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -611,7 +611,7 @@
     in_allocs[2] = (RsAllocation)C;
 
     rsScriptForEachMulti((RsContext)con, (RsScript)id, 0,
-                         in_allocs, sizeof(in_allocs), nullptr,
+                         in_allocs, NELEM(in_allocs), nullptr,
                          &call, sizeof(call), nullptr, 0);
 }
 
@@ -646,7 +646,7 @@
     in_allocs[2] = (RsAllocation)C;
 
     rsScriptForEachMulti((RsContext)con, (RsScript)id, 0,
-                         in_allocs, sizeof(in_allocs), nullptr,
+                         in_allocs, NELEM(in_allocs), nullptr,
                          &call, sizeof(call), nullptr, 0);
 }
 
@@ -681,7 +681,7 @@
     in_allocs[2] = (RsAllocation)C;
 
     rsScriptForEachMulti((RsContext)con, (RsScript)id, 0,
-                         in_allocs, sizeof(in_allocs), nullptr,
+                         in_allocs, NELEM(in_allocs), nullptr,
                          &call, sizeof(call), nullptr, 0);
 }
 
@@ -707,7 +707,7 @@
     in_allocs[2] = (RsAllocation)C;
 
     rsScriptForEachMulti((RsContext)con, (RsScript)id, 0,
-                         in_allocs, sizeof(in_allocs), nullptr,
+                         in_allocs, NELEM(in_allocs), nullptr,
                          &call, sizeof(call), nullptr, 0);
 }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 87a0b80..b52687a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -45,28 +45,28 @@
     /**
      * Flag for enabling the screen magnification feature.
      *
-     * @see #setEnabledFeatures(int)
+     * @see #setUserAndEnabledFeatures(int, int)
      */
     static final int FLAG_FEATURE_SCREEN_MAGNIFIER = 0x00000001;
 
     /**
      * Flag for enabling the touch exploration feature.
      *
-     * @see #setEnabledFeatures(int)
+     * @see #setUserAndEnabledFeatures(int, int)
      */
     static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002;
 
     /**
      * Flag for enabling the filtering key events feature.
      *
-     * @see #setEnabledFeatures(int)
+     * @see #setUserAndEnabledFeatures(int, int)
      */
     static final int FLAG_FEATURE_FILTER_KEY_EVENTS = 0x00000004;
 
     /**
      * Flag for enabling "Automatically click on mouse stop" feature.
      *
-     * @see #setEnabledFeatures(int)
+     * @see #setUserAndEnabledFeatures(int, int)
      */
     static final int FLAG_FEATURE_AUTOCLICK = 0x00000008;
 
@@ -97,6 +97,8 @@
 
     private boolean mInstalled;
 
+    private int mUserId;
+
     private int mEnabledFeatures;
 
     private TouchExplorer mTouchExplorer;
@@ -327,13 +329,14 @@
         /* do nothing */
     }
 
-    void setEnabledFeatures(int enabledFeatures) {
-        if (mEnabledFeatures == enabledFeatures) {
+    void setUserAndEnabledFeatures(int userId, int enabledFeatures) {
+        if (mEnabledFeatures == enabledFeatures && mUserId == userId) {
             return;
         }
         if (mInstalled) {
             disableFeatures();
         }
+        mUserId = userId;
         mEnabledFeatures = enabledFeatures;
         if (mInstalled) {
             enableFeatures();
@@ -350,7 +353,7 @@
         resetStreamState();
 
         if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
-            mAutoclickController = new AutoclickController(mContext);
+            mAutoclickController = new AutoclickController(mContext, mUserId);
             addFirstEventHandler(mAutoclickController);
         }
 
@@ -360,7 +363,7 @@
         }
 
         if ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) {
-            mScreenMagnifier = new ScreenMagnifier(mContext,
+            mScreenMagnifier = new ScreenMagnifier(mContext, mUserId,
                     Display.DEFAULT_DISPLAY, mAms);
             addFirstEventHandler(mScreenMagnifier);
         }
@@ -386,7 +389,7 @@
         mEventHandler = handler;
     }
 
-    void disableFeatures() {
+    private void disableFeatures() {
         if (mAutoclickController != null) {
             mAutoclickController.onDestroy();
             mAutoclickController = null;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ff2a2ee..749a080 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1296,11 +1296,11 @@
                     inputFilter = mInputFilter;
                     setInputFilter = true;
                 }
-                mInputFilter.setEnabledFeatures(flags);
+                mInputFilter.setUserAndEnabledFeatures(userState.mUserId, flags);
             } else {
                 if (mHasInputFilter) {
                     mHasInputFilter = false;
-                    mInputFilter.disableFeatures();
+                    mInputFilter.setUserAndEnabledFeatures(userState.mUserId, 0);
                     inputFilter = null;
                     setInputFilter = true;
                 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index 8989625..892e9da 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -32,6 +32,7 @@
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 
 /**
  * Implements "Automatically click on mouse stop" feature.
@@ -51,22 +52,24 @@
  *
  * It is expected that each instance will receive mouse events from a single mouse device. User of
  * the class should handle cases where multiple mouse devices are present.
+ *
+ * Each instance is associated to a single user (and it does not handle user switch itself).
  */
 public class AutoclickController implements EventStreamTransformation {
 
-    public static final int DEFAULT_CLICK_DELAY_MS = 600;
-
     private static final String LOG_TAG = AutoclickController.class.getSimpleName();
 
     private EventStreamTransformation mNext;
     private final Context mContext;
+    private final int mUserId;
 
     // Lazily created on the first mouse motion event.
     private ClickScheduler mClickScheduler;
     private ClickDelayObserver mClickDelayObserver;
 
-    public AutoclickController(Context context) {
+    public AutoclickController(Context context, int userId) {
         mContext = context;
+        mUserId = userId;
     }
 
     @Override
@@ -74,8 +77,9 @@
         if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
             if (mClickScheduler == null) {
                 Handler handler = new Handler(mContext.getMainLooper());
-                mClickScheduler = new ClickScheduler(handler, DEFAULT_CLICK_DELAY_MS);
-                mClickDelayObserver = new ClickDelayObserver(handler);
+                mClickScheduler =
+                        new ClickScheduler(handler, AccessibilityManager.AUTOCLICK_DELAY_DEFAULT);
+                mClickDelayObserver = new ClickDelayObserver(mUserId, handler);
                 mClickDelayObserver.start(mContext.getContentResolver(), mClickScheduler);
             }
 
@@ -168,9 +172,11 @@
 
         private ContentResolver mContentResolver;
         private ClickScheduler mClickScheduler;
+        private final int mUserId;
 
-        public ClickDelayObserver(Handler handler) {
+        public ClickDelayObserver(int userId, Handler handler) {
             super(handler);
+            mUserId = userId;
         }
 
         /**
@@ -199,7 +205,7 @@
             mContentResolver = contentResolver;
             mClickScheduler = clickScheduler;
             mContentResolver.registerContentObserver(mAutoclickDelaySettingUri, false, this,
-                    UserHandle.USER_ALL);
+                    mUserId);
 
             // Initialize mClickScheduler's initial delay value.
             onChange(true, mAutoclickDelaySettingUri);
@@ -222,10 +228,9 @@
         @Override
         public void onChange(boolean selfChange, Uri uri) {
             if (mAutoclickDelaySettingUri.equals(uri)) {
-                // TODO: Plumb current user id down to here and use getIntForUser.
-                int delay = Settings.Secure.getInt(
+                int delay = Settings.Secure.getIntForUser(
                         mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
-                        DEFAULT_CLICK_DELAY_MS);
+                        AccessibilityManager.AUTOCLICK_DELAY_DEFAULT, mUserId);
                 mClickScheduler.updateDelay(delay);
             }
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
index 37276bd..8845bc0 100644
--- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -29,7 +29,6 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
-import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Property;
@@ -137,6 +136,8 @@
 
     private final AccessibilityManagerService mAms;
 
+    private final int mUserId;
+
     private final int mTapTimeSlop = ViewConfiguration.getJumpTapTimeout();
     private final int mMultiTapTimeSlop;
     private final int mTapDistanceSlop;
@@ -188,8 +189,10 @@
         }
     };
 
-    public ScreenMagnifier(Context context, int displayId, AccessibilityManagerService service) {
+    public ScreenMagnifier(Context context, int userId, int displayId,
+            AccessibilityManagerService service) {
         mContext = context;
+        mUserId = userId;
         mWindowManager = LocalServices.getService(WindowManagerInternal.class);
         mAms = service;
 
@@ -813,33 +816,12 @@
             while (mDelayedEventQueue != null) {
                 MotionEventInfo info = mDelayedEventQueue;
                 mDelayedEventQueue = info.mNext;
-                final long offset = SystemClock.uptimeMillis() - info.mCachedTimeMillis;
-                MotionEvent event = obtainEventWithOffsetTimeAndDownTime(info.mEvent, offset);
-                MotionEvent rawEvent = obtainEventWithOffsetTimeAndDownTime(info.mRawEvent, offset);
-                ScreenMagnifier.this.onMotionEvent(event, rawEvent, info.mPolicyFlags);
-                event.recycle();
-                rawEvent.recycle();
+                ScreenMagnifier.this.onMotionEvent(info.mEvent, info.mRawEvent,
+                        info.mPolicyFlags);
                 info.recycle();
             }
         }
 
-        private MotionEvent obtainEventWithOffsetTimeAndDownTime(MotionEvent event, long offset) {
-            final int pointerCount = event.getPointerCount();
-            PointerCoords[] coords = getTempPointerCoordsWithMinSize(pointerCount);
-            PointerProperties[] properties = getTempPointerPropertiesWithMinSize(pointerCount);
-            for (int i = 0; i < pointerCount; i++) {
-                event.getPointerCoords(i, coords[i]);
-                event.getPointerProperties(i, properties[i]);
-            }
-            final long downTime = event.getDownTime() + offset;
-            final long eventTime = event.getEventTime() + offset;
-            return MotionEvent.obtain(downTime, eventTime,
-                    event.getAction(), pointerCount, properties, coords,
-                    event.getMetaState(), event.getButtonState(),
-                    1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
-                    event.getSource(), event.getFlags());
-        }
-
         private void clearDelayedMotionEvents() {
             while (mDelayedEventQueue != null) {
                 MotionEventInfo info = mDelayedEventQueue;
@@ -882,17 +864,17 @@
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                Settings.Secure.putFloat(mContext.getContentResolver(),
-                        Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale);
+                Settings.Secure.putFloatForUser(mContext.getContentResolver(),
+                        Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale, mUserId);
                 return null;
             }
         }.execute();
     }
 
     private float getPersistedScale() {
-        return Settings.Secure.getFloat(mContext.getContentResolver(),
+        return Settings.Secure.getFloatForUser(mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
-                DEFAULT_MAGNIFICATION_SCALE);
+                DEFAULT_MAGNIFICATION_SCALE, mUserId);
     }
 
     private static boolean isScreenMagnificationAutoUpdateEnabled(Context context) {
@@ -915,7 +897,6 @@
         public MotionEvent mEvent;
         public MotionEvent mRawEvent;
         public int mPolicyFlags;
-        public long mCachedTimeMillis;
 
         public static MotionEventInfo obtain(MotionEvent event, MotionEvent rawEvent,
                 int policyFlags) {
@@ -940,7 +921,6 @@
             mEvent = MotionEvent.obtain(event);
             mRawEvent = MotionEvent.obtain(rawEvent);
             mPolicyFlags = policyFlags;
-            mCachedTimeMillis = SystemClock.uptimeMillis();
         }
 
         public void recycle() {
@@ -964,7 +944,6 @@
             mRawEvent.recycle();
             mRawEvent = null;
             mPolicyFlags = 0;
-            mCachedTimeMillis = 0;
         }
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index c3f5c43..f6b32f7 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -112,12 +112,6 @@
     // Timeout before trying to decide what the user is trying to do.
     private final int mDetermineUserIntentTimeout;
 
-    // Timeout within which we try to detect a tap.
-    private final int mTapTimeout;
-
-    // Slop between the down and up tap to be a tap.
-    private final int mTouchSlop;
-
     // Slop between the first and second tap to be a double tap.
     private final int mDoubleTapSlop;
 
@@ -142,9 +136,6 @@
     // Command for delayed sending of touch interaction end events.
     private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed;
 
-    // Command for delayed sending of a long press.
-    private final PerformLongPressDelayed mPerformLongPressDelayed;
-
     // Command for exiting gesture detection mode after a timeout.
     private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed;
 
@@ -173,9 +164,6 @@
     // Handle to the accessibility manager service.
     private final AccessibilityManagerService mAms;
 
-    // Temporary rectangle to avoid instantiation.
-    private final Rect mTempRect = new Rect();
-
     // Temporary point to avoid instantiation.
     private final Point mTempPoint = new Point();
 
@@ -226,12 +214,9 @@
         mAms = service;
         mReceivedPointerTracker = new ReceivedPointerTracker();
         mInjectedPointerTracker = new InjectedPointerTracker();
-        mTapTimeout = ViewConfiguration.getTapTimeout();
         mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
-        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
         mHandler = new Handler(context.getMainLooper());
-        mPerformLongPressDelayed = new PerformLongPressDelayed();
         mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
         mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
         mGestureLibrary.setOrientationStyle(8);
@@ -299,7 +284,6 @@
         // Remove all pending callbacks.
         mSendHoverEnterAndMoveDelayed.cancel();
         mSendHoverExitDelayed.cancel();
-        mPerformLongPressDelayed.cancel();
         mExitGestureDetectionModeDelayed.cancel();
         mSendTouchExplorationEndDelayed.cancel();
         mSendTouchInteractionEndDelayed.cancel();
@@ -437,7 +421,6 @@
                 // we resent the delayed callback and wait again.
                 mSendHoverEnterAndMoveDelayed.cancel();
                 mSendHoverExitDelayed.cancel();
-                mPerformLongPressDelayed.cancel();
 
                 if (mSendTouchExplorationEndDelayed.isPending()) {
                     mSendTouchExplorationEndDelayed.forceSendAndRemove();
@@ -447,18 +430,7 @@
                     mSendTouchInteractionEndDelayed.forceSendAndRemove();
                 }
 
-                // If we have the first tap, schedule a long press and break
-                // since we do not want to schedule hover enter because
-                // the delayed callback will kick in before the long click.
-                // This would lead to a state transition resulting in long
-                // pressing the item below the double taped area which is
-                // not necessary where accessibility focus is.
-                if (mDoubleTapDetector.firstTapDetected()) {
-                    // We got a tap now post a long press action.
-                    mPerformLongPressDelayed.post(event, policyFlags);
-                    break;
-                }
-                if (!mTouchExplorationInProgress) {
+                if (!mDoubleTapDetector.firstTapDetected() && !mTouchExplorationInProgress) {
                     if (!mSendHoverEnterAndMoveDelayed.isPending()) {
                         // Deliver hover enter with a delay to have a chance
                         // to detect what the user is trying to do.
@@ -478,7 +450,6 @@
                 // decide what we will actually do next.
                 mSendHoverEnterAndMoveDelayed.cancel();
                 mSendHoverExitDelayed.cancel();
-                mPerformLongPressDelayed.cancel();
             } break;
             case MotionEvent.ACTION_MOVE: {
                 final int pointerId = receivedTracker.getPrimaryPointerId();
@@ -521,7 +492,6 @@
                                     mVelocityTracker.clear();
                                     mSendHoverEnterAndMoveDelayed.cancel();
                                     mSendHoverExitDelayed.cancel();
-                                    mPerformLongPressDelayed.cancel();
                                     mExitGestureDetectionModeDelayed.post();
                                     // Send accessibility event to announce the start
                                     // of gesture recognition.
@@ -532,28 +502,12 @@
                                     // exploring so start sending events.
                                     mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
                                     mSendHoverExitDelayed.cancel();
-                                    mPerformLongPressDelayed.cancel();
                                     sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE,
                                             pointerIdBits, policyFlags);
                                 }
                                 break;
                             }
                         } else {
-                            // Cancel the long press if pending and the user
-                            // moved more than the slop.
-                            if (mPerformLongPressDelayed.isPending()) {
-                                final float deltaX =
-                                        receivedTracker.getReceivedPointerDownX(pointerId)
-                                        - rawEvent.getX(pointerIndex);
-                                final float deltaY =
-                                        receivedTracker.getReceivedPointerDownY(pointerId)
-                                        - rawEvent.getY(pointerIndex);
-                                final double moveDelta = Math.hypot(deltaX, deltaY);
-                                // The user has moved enough for us to decide.
-                                if (moveDelta > mTouchSlop) {
-                                    mPerformLongPressDelayed.cancel();
-                                }
-                            }
                             if (mTouchExplorationInProgress) {
                                 sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
                                 sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
@@ -569,9 +523,7 @@
                             // scheduled sending events.
                             mSendHoverEnterAndMoveDelayed.cancel();
                             mSendHoverExitDelayed.cancel();
-                            mPerformLongPressDelayed.cancel();
                         } else {
-                            mPerformLongPressDelayed.cancel();
                             if (mTouchExplorationInProgress) {
                                 // If the user is touch exploring the second pointer may be
                                 // performing a double tap to activate an item without need
@@ -620,9 +572,7 @@
                             // scheduled sending events.
                             mSendHoverEnterAndMoveDelayed.cancel();
                             mSendHoverExitDelayed.cancel();
-                            mPerformLongPressDelayed.cancel();
                         } else {
-                            mPerformLongPressDelayed.cancel();
                             // We are sending events so send exit and gesture
                             // end since we transition to another state.
                             sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
@@ -643,7 +593,6 @@
                 final int pointerId = event.getPointerId(event.getActionIndex());
                 final int pointerIdBits = (1 << pointerId);
 
-                mPerformLongPressDelayed.cancel();
                 mVelocityTracker.clear();
 
                 if (mSendHoverEnterAndMoveDelayed.isPending()) {
@@ -1110,6 +1059,7 @@
         private final GestureDetector mGestureDetector;
         private boolean mFirstTapDetected;
         private boolean mDoubleTapDetected;
+        private int mPolicyFlags;
 
         DoubleTapDetector(Context context) {
             mGestureDetector = new GestureDetector(context, this);
@@ -1117,6 +1067,7 @@
         }
 
         public void onMotionEvent(MotionEvent event, int policyFlags) {
+            mPolicyFlags = policyFlags;
             switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:
                     mDoubleTapDetected = false;
@@ -1135,6 +1086,11 @@
         }
 
         @Override
+        public void onLongPress(MotionEvent e) {
+            maybeSendLongPress(e, mPolicyFlags);
+        }
+
+        @Override
         public boolean onSingleTapUp(MotionEvent event) {
             mFirstTapDetected = true;
             return false;
@@ -1154,6 +1110,38 @@
             return true;
         }
 
+        private void maybeSendLongPress(MotionEvent event, int policyFlags) {
+            if (!mDoubleTapDetected) {
+                return;
+            }
+
+            clear();
+
+            // Pointers should not be zero when running this command.
+            if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
+                return;
+            }
+
+            final int pointerIndex = event.getActionIndex();
+            final int pointerId = event.getPointerId(pointerIndex);
+
+            Point clickLocation = mTempPoint;
+            final int result = computeClickLocation(clickLocation);
+
+            if (result == CLICK_LOCATION_NONE) {
+                return;
+            }
+
+            mLongPressingPointerId = pointerId;
+            mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
+            mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
+
+            sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
+
+            mCurrentState = STATE_DELEGATING;
+            sendDownForAllNotInjectedPointers(event, policyFlags);
+        }
+
         private void maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
             if (!mDoubleTapDetected) {
                 return;
@@ -1169,7 +1157,6 @@
             // Remove pending event deliveries.
             mSendHoverEnterAndMoveDelayed.cancel();
             mSendHoverExitDelayed.cancel();
-            mPerformLongPressDelayed.cancel();
 
             if (mSendTouchExplorationEndDelayed.isPending()) {
                 mSendTouchExplorationEndDelayed.forceSendAndRemove();
@@ -1178,8 +1165,8 @@
                 mSendTouchInteractionEndDelayed.forceSendAndRemove();
             }
 
-            final int pointerId = event.getPointerId(event.getActionIndex());
-            final int pointerIndex = event.findPointerIndex(pointerId);
+            final int pointerIndex = event.getActionIndex();
+            final int pointerId = event.getPointerId(pointerIndex);
 
             Point clickLocation = mTempPoint;
             final int result = computeClickLocation(clickLocation);
@@ -1306,65 +1293,6 @@
     }
 
     /**
-     * Class for delayed sending of long press.
-     */
-    private final class PerformLongPressDelayed implements Runnable {
-        private MotionEvent mEvent;
-        private int mPolicyFlags;
-
-        public void post(MotionEvent prototype, int policyFlags) {
-            mEvent = MotionEvent.obtain(prototype);
-            mPolicyFlags = policyFlags;
-            mHandler.postDelayed(this, ViewConfiguration.getLongPressTimeout());
-        }
-
-        public void cancel() {
-            if (mEvent != null) {
-                mHandler.removeCallbacks(this);
-                clear();
-            }
-        }
-
-        private boolean isPending() {
-            return mHandler.hasCallbacks(this);
-        }
-
-        @Override
-        public void run() {
-            // Pointers should not be zero when running this command.
-            if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
-                return;
-            }
-
-            final int pointerId = mEvent.getPointerId(mEvent.getActionIndex());
-            final int pointerIndex = mEvent.findPointerIndex(pointerId);
-
-            Point clickLocation = mTempPoint;
-            final int result = computeClickLocation(clickLocation);
-
-            if (result == CLICK_LOCATION_NONE) {
-                return;
-            }
-
-            mLongPressingPointerId = pointerId;
-            mLongPressingPointerDeltaX = (int) mEvent.getX(pointerIndex) - clickLocation.x;
-            mLongPressingPointerDeltaY = (int) mEvent.getY(pointerIndex) - clickLocation.y;
-
-            sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags);
-
-            mCurrentState = STATE_DELEGATING;
-            sendDownForAllNotInjectedPointers(mEvent, mPolicyFlags);
-            clear();
-        }
-
-        private void clear() {
-            mEvent.recycle();
-            mEvent = null;
-            mPolicyFlags = 0;
-        }
-    }
-
-    /**
      * Class for delayed sending of hover enter and move events.
      */
     class SendHoverEnterAndMoveDelayed implements Runnable {
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index f245985..2aa0390 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -55,7 +55,8 @@
      * Time in milliseconds in which the power button must be pressed twice so it will be considered
      * as a camera launch.
      */
-    private static final long CAMERA_POWER_DOUBLE_TAP_TIME_MS = 300;
+    private static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
+    private static final long CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS = 120;
 
     /** The listener that receives the gesture event. */
     private final GestureEventListener mGestureListener = new GestureEventListener();
@@ -256,14 +257,16 @@
         synchronized (this) {
             doubleTapInterval = event.getEventTime() - mLastPowerDown;
             if (mCameraDoubleTapPowerEnabled
-                    && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_TIME_MS) {
+                    && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
+                    && doubleTapInterval > CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS) {
                 launched = true;
                 intercept = interactive;
             }
             mLastPowerDown = event.getEventTime();
         }
         if (launched) {
-            Slog.i(TAG, "Power button double tap gesture detected, launching camera.");
+            Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval="
+                    + doubleTapInterval + "ms");
             launched = handleCameraLaunchGesture(false /* useWakelock */,
                     StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
             if (launched) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 9dad7a1..ab1d775 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -37,6 +37,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManagerNative;
 import android.app.AlertDialog;
 import android.app.AppGlobals;
@@ -280,8 +281,19 @@
     boolean mSystemReady;
 
     /**
-     * Id of the currently selected input method.
+     * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method.
+     * method.  This is to be synchronized with the secure settings keyed with
+     * {@link Settings.Secure#DEFAULT_INPUT_METHOD}.
+     *
+     * <p>This can be transiently {@code null} when the system is re-initializing input method
+     * settings, e.g., the system locale is just changed.</p>
+     *
+     * <p>Note that {@link #mCurId} is used to track which IME is being connected to
+     * {@link InputMethodManagerService}.</p>
+     *
+     * @see #mCurId
      */
+    @Nullable
     String mCurMethodId;
 
     /**
@@ -311,9 +323,14 @@
     EditorInfo mCurAttribute;
 
     /**
-     * The input method ID of the input method service that we are currently
+     * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
      * connected to or in the process of connecting to.
+     *
+     * <p>This can be {@code null} when no input method is connected.</p>
+     *
+     * @see #mCurMethodId
      */
+    @Nullable
     String mCurId;
 
     /**
@@ -918,7 +935,6 @@
                 || (newLocale != null && !newLocale.equals(mLastSystemLocale))) {
             if (!updateOnlyWhenLocaleChanged) {
                 hideCurrentInputLocked(0, null);
-                mCurMethodId = null;
                 unbindCurrentMethodLocked(true, false);
             }
             if (DEBUG) {
@@ -1474,7 +1490,11 @@
         channel.dispose();
     }
 
-    void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) {
+    void unbindCurrentMethodLocked(boolean resetCurrentMethodAndClient, boolean savePosition) {
+        if (resetCurrentMethodAndClient) {
+            mCurMethodId = null;
+        }
+
         if (mVisibleBound) {
             mContext.unbindService(mVisibleConnection);
             mVisibleBound = false;
@@ -1501,9 +1521,8 @@
         mCurId = null;
         clearCurMethodLocked();
 
-        if (reportToClient && mCurClient != null) {
-            executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
-                    MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
+        if (resetCurrentMethodAndClient) {
+            unbindCurrentClientLocked();
         }
     }
 
@@ -1857,13 +1876,11 @@
                 setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
-                mCurMethodId = null;
                 unbindCurrentMethodLocked(true, false);
             }
             mShortcutInputMethodsAndSubtypes.clear();
         } else {
             // There is no longer an input method set, so stop any current one.
-            mCurMethodId = null;
             unbindCurrentMethodLocked(true, false);
         }
         // Here is not the perfect place to reset the switching controller. Ideally
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bd10c63..617264c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -20,8 +20,11 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.HOME_STACK_ID;
 import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -8685,7 +8688,32 @@
                     Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
                     return;
                 }
-                mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode);
+                // Place the task in the right stack if it isn't there already based on
+                // the requested bounds.
+                // The stack transition logic is:
+                // - a null bounds on a freeform task moves that task to fullscreen
+                // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
+                //   that task to freeform
+                // - otherwise the task is not moved
+                // Note it's not allowed to resize a home stack task, or a docked task.
+                int stackId = task.stack.mStackId;
+                if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID) {
+                    throw new IllegalArgumentException("trying to resizeTask on a "
+                            + "home or docked task");
+                }
+                if (bounds == null && stackId == FREEFORM_WORKSPACE_STACK_ID) {
+                    stackId = FULLSCREEN_WORKSPACE_STACK_ID;
+                } else if (bounds != null && stackId != FREEFORM_WORKSPACE_STACK_ID ) {
+                    stackId = FREEFORM_WORKSPACE_STACK_ID;
+                }
+                boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
+                if (stackId != task.stack.mStackId) {
+                    mStackSupervisor.moveTaskToStackUncheckedLocked(
+                            task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask");
+                    preserveWindow = false;
+                }
+
+                mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode, preserveWindow);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -11893,7 +11921,8 @@
             updateCurrentProfileIdsLocked();
 
             mRecentTasks.clear();
-            mRecentTasks.addAll(mTaskPersister.restoreTasksLocked());
+            mRecentTasks.addAll(mTaskPersister.restoreTasksLocked(
+                    getUserManagerLocked().getUserIds()));
             mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
             mTaskPersister.startPersisting();
 
@@ -20644,9 +20673,6 @@
                 // Kill all the processes for the user.
                 forceStopUserLocked(userId, "finish user");
             }
-
-            // Explicitly remove the old information in mRecentTasks.
-            mRecentTasks.removeTasksForUserLocked(userId);
         }
 
         for (int i=0; i<callbacks.size(); i++) {
@@ -20665,6 +20691,10 @@
         }
     }
 
+    void onUserRemovedLocked(int userId) {
+        mRecentTasks.removeTasksForUserLocked(userId);
+    }
+
     @Override
     public UserInfo getCurrentUser() {
         if ((checkCallingPermission(INTERACT_ACROSS_USERS)
@@ -20949,6 +20979,13 @@
                 return homeActivity == null ? null : homeActivity.realActivity;
             }
         }
+
+        @Override
+        public void onUserRemoved(int userId) {
+            synchronized (ActivityManagerService.this) {
+                ActivityManagerService.this.onUserRemovedLocked(userId);
+            }
+        }
     }
 
     private final class SleepTokenImpl extends SleepToken {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index ed92579..3a368ac 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3058,7 +3058,7 @@
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode) {
+    void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) {
         if (!task.mResizeable) {
             Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
             return;
@@ -3066,7 +3066,7 @@
 
         // If this is a forced resize, let it go through even if the bounds is not changing,
         // as we might need a relayout due to surface size change (to/from fullscreen).
-        final boolean forced = (resizeMode == RESIZE_MODE_FORCED);
+        final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
         if (task.mBounds != null && task.mBounds.equals(bounds) && !forced) {
             // Nothing to do here...
             return;
@@ -3084,25 +3084,14 @@
             return;
         }
 
+        // Do not move the task to another stack here.
+        // This method assumes that the task is already placed in the right stack.
+        // we do not mess with that decision and we only do the resize!
+
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + task.taskId);
 
-        // The stack of a task is determined by its size (fullscreen vs non-fullscreen).
-        // Place the task in the right stack if it isn't there already based on the requested
-        // bounds.
-        int stackId = task.stack.mStackId;
-        if (bounds == null && stackId != FULLSCREEN_WORKSPACE_STACK_ID) {
-            stackId = FULLSCREEN_WORKSPACE_STACK_ID;
-        } else if (bounds != null
-                && stackId != FREEFORM_WORKSPACE_STACK_ID && stackId != DOCKED_STACK_ID) {
-            stackId = FREEFORM_WORKSPACE_STACK_ID;
-        }
-        final boolean changedStacks = stackId != task.stack.mStackId;
-        if (changedStacks) {
-            moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask");
-        }
-
         final Configuration overrideConfig =  task.updateOverrideConfiguration(bounds);
-        // This variable holds information whether the configuration didn't change in a signficant
+        // This variable holds information whether the configuration didn't change in a significant
         // way and the activity was kept the way it was. If it's false, it means the activity had
         // to be relaunched due to configuration change.
         boolean kept = true;
@@ -3110,8 +3099,6 @@
             ActivityRecord r = task.topRunningActivityLocked(null);
             if (r != null) {
                 final ActivityStack stack = task.stack;
-                final boolean resizedByUser = resizeMode == RESIZE_MODE_USER;
-                final boolean preserveWindow = resizedByUser && !changedStacks;
                 kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
                 // All other activities must be made visible with their correct configuration.
                 ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
@@ -3203,7 +3190,7 @@
      * @param reason Reason the task is been moved.
      * @return The stack the task was moved to.
      */
-    private ActivityStack moveTaskToStackUncheckedLocked(
+    ActivityStack moveTaskToStackUncheckedLocked(
             TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) {
         final ActivityRecord r = task.getTopActivity();
         final boolean wasFocused = isFrontStack(task.stack) && (topRunningActivityLocked() == r);
@@ -3260,12 +3247,15 @@
 
         // Make sure the task has the appropriate bounds/size for the stack it is in.
         if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
-            resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM);
+            resizeTaskLocked(task, stack.mBounds,
+                    RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
         } else if (stackId == FREEFORM_WORKSPACE_STACK_ID
                 && task.mBounds == null && task.mLastNonFullscreenBounds != null) {
-            resizeTaskLocked(task, task.mLastNonFullscreenBounds, RESIZE_MODE_SYSTEM);
+            resizeTaskLocked(task, task.mLastNonFullscreenBounds,
+                    RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
         } else if (stackId == DOCKED_STACK_ID) {
-            resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM);
+            resizeTaskLocked(task, stack.mBounds,
+                    RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
         }
 
         // The task might have already been running and its visibility needs to be synchronized with
@@ -4626,16 +4616,6 @@
             return mActivityDisplay != null;
         }
 
-        void getBounds(Point outBounds) {
-            synchronized (mService) {
-                    if (mActivityDisplay != null) {
-                    mActivityDisplay.getBounds(outBounds);
-                } else {
-                    outBounds.set(0, 0);
-                }
-            }
-        }
-
         // TODO: Make sure every change to ActivityRecord.visible results in a call to this.
         void setVisible(boolean visible) {
             if (mVisible != visible) {
@@ -4807,12 +4787,6 @@
             mStacks.remove(stack);
         }
 
-        void getBounds(Point bounds) {
-            mDisplay.getDisplayInfo(mDisplayInfo);
-            bounds.x = mDisplayInfo.appWidth;
-            bounds.y = mDisplayInfo.appHeight;
-        }
-
         void setVisibleBehindActivity(ActivityRecord r) {
             mVisibleBehindActivity = r;
         }
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index aa154a7..871331b 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -35,6 +35,7 @@
 import android.util.Xml;
 import android.os.Process;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
@@ -330,7 +331,7 @@
         return null;
     }
 
-    ArrayList<TaskRecord> restoreTasksLocked() {
+    ArrayList<TaskRecord> restoreTasksLocked(final int [] validUserIds) {
         final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
         ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
 
@@ -362,15 +363,18 @@
                             if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" +
                                     task);
                             if (task != null) {
-                                task.isPersistable = true;
                                 // XXX Don't add to write queue... there is no reason to write
                                 // out the stuff we just read, if we don't write it we will
                                 // read the same thing again.
                                 //mWriteQueue.add(new TaskWriteQueueItem(task));
-                                tasks.add(task);
                                 final int taskId = task.taskId;
-                                recoveredTaskIds.add(taskId);
                                 mStackSupervisor.setNextTaskId(taskId);
+                                // Check if it's a valid user id. Don't add tasks for removed users.
+                                if (ArrayUtils.contains(validUserIds, task.userId)) {
+                                    task.isPersistable = true;
+                                    tasks.add(task);
+                                    recoveredTaskIds.add(taskId);
+                                }
                             } else {
                                 Slog.e(TAG, "Unable to restore taskFile=" + taskFile + ": " +
                                         fileToString(taskFile));
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 43f5baa..7e14b2b 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1193,7 +1193,7 @@
             mOverrideConfig = Configuration.EMPTY;
         } else {
             mBounds = new Rect(bounds);
-            if (stack.mStackId != DOCKED_STACK_ID) {
+            if (stack == null || stack.mStackId != DOCKED_STACK_ID) {
                 mLastNonFullscreenBounds = mBounds;
             }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e49a7e4..c4b57f1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -501,7 +501,6 @@
     private volatile IRingtonePlayer mRingtonePlayer;
 
     private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
-    private int mDeviceRotation = Surface.ROTATION_0;
 
     // Request to override default use of A2DP for media.
     private boolean mBluetoothA2dpEnabled;
@@ -545,8 +544,6 @@
     // If absolute volume is supported in AVRCP device
     private boolean mAvrcpAbsVolSupported = false;
 
-    private AudioOrientationEventListener mOrientationListener;
-
     private static Long mLastDeviceConnectMsgTime = new Long(0);
 
     private AudioManagerInternal.RingerModeDelegate mRingerModeDelegate;
@@ -669,15 +666,7 @@
         }
         mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
         if (mMonitorRotation) {
-            mDeviceRotation = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE))
-                    .getDefaultDisplay().getRotation();
-            Log.v(TAG, "monitoring device rotation, initial=" + mDeviceRotation);
-
-            mOrientationListener = new AudioOrientationEventListener(mContext);
-            mOrientationListener.enable();
-
-            // initialize rotation in AudioSystem
-            setRotationForAudioSystem();
+            RotationHelper.init(mContext, mAudioHandler);
         }
 
         context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
@@ -805,7 +794,7 @@
             setOrientationForAudioSystem();
         }
         if (mMonitorRotation) {
-            setRotationForAudioSystem();
+            RotationHelper.updateOrientation();
         }
 
         synchronized (mBluetoothA2dpEnabledLock) {
@@ -1058,25 +1047,6 @@
         return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
     }
 
-    private class AudioOrientationEventListener
-            extends OrientationEventListener {
-        public AudioOrientationEventListener(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void onOrientationChanged(int orientation) {
-            //Even though we're responding to phone orientation events,
-            //use display rotation so audio stays in sync with video/dialogs
-            int newRotation = ((WindowManager) mContext.getSystemService(
-                    Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
-            if (newRotation != mDeviceRotation) {
-                mDeviceRotation = newRotation;
-                setRotationForAudioSystem();
-            }
-        }
-    }
-
     ///////////////////////////////////////////////////////////////////////////
     // IPC methods
     ///////////////////////////////////////////////////////////////////////////
@@ -5066,14 +5036,13 @@
                 }
             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
                 if (mMonitorRotation) {
-                    mOrientationListener.onOrientationChanged(0); //argument is ignored anyway
-                    mOrientationListener.enable();
+                    RotationHelper.enable();
                 }
                 AudioSystem.setParameters("screen_state=on");
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                 if (mMonitorRotation) {
                     //reduce wakeups (save current) by only listening when display is on
-                    mOrientationListener.disable();
+                    RotationHelper.disable();
                 }
                 AudioSystem.setParameters("screen_state=off");
             } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
@@ -5322,6 +5291,7 @@
         }
     }
 
+    //TODO move to an external "orientation helper" class
     private void setOrientationForAudioSystem() {
         switch (mDeviceOrientation) {
             case Configuration.ORIENTATION_LANDSCAPE:
@@ -5345,26 +5315,6 @@
         }
     }
 
-    private void setRotationForAudioSystem() {
-        switch (mDeviceRotation) {
-            case Surface.ROTATION_0:
-                AudioSystem.setParameters("rotation=0");
-                break;
-            case Surface.ROTATION_90:
-                AudioSystem.setParameters("rotation=90");
-                break;
-            case Surface.ROTATION_180:
-                AudioSystem.setParameters("rotation=180");
-                break;
-            case Surface.ROTATION_270:
-                AudioSystem.setParameters("rotation=270");
-                break;
-            default:
-                Log.e(TAG, "Unknown device rotation");
-        }
-    }
-
-
     // Handles request to override default use of A2DP for media.
     // Must be called synchronized on mConnectedDevices
     public void setBluetoothA2dpOnInt(boolean on) {
diff --git a/services/core/java/com/android/server/audio/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java
new file mode 100644
index 0000000..f03e6c7
--- /dev/null
+++ b/services/core/java/com/android/server/audio/RotationHelper.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.audio;
+
+import android.content.Context;
+import android.media.AudioSystem;
+import android.os.Handler;
+import android.util.Log;
+import android.view.OrientationEventListener;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import com.android.server.policy.WindowOrientationListener;
+
+/**
+ * Class to handle device rotation events for AudioService, and forward device rotation
+ * to the audio HALs through AudioSystem.
+ *
+ * The role of this class is to monitor device orientation changes, and upon rotation,
+ * verify the UI orientation. In case of a change, send the new orientation, in increments
+ * of 90deg, through AudioSystem.
+ *
+ * Note that even though we're responding to device orientation events, we always
+ * query the display rotation so audio stays in sync with video/dialogs. This is
+ * done with .getDefaultDisplay().getRotation() from WINDOW_SERVICE.
+ */
+class RotationHelper {
+
+    private static final String TAG = "AudioService.RotationHelper";
+
+    private static AudioOrientationListener sOrientationListener;
+    private static AudioWindowOrientationListener sWindowOrientationListener;
+
+    private static final Object sRotationLock = new Object();
+    private static int sDeviceRotation = Surface.ROTATION_0; // R/W synchronized on sRotationLock
+
+    private static Context sContext;
+
+    /**
+     * post conditions:
+     * - (sWindowOrientationListener != null) xor (sOrientationListener != null)
+     * - sWindowOrientationListener xor sOrientationListener is enabled
+     * - sContext != null
+     */
+    static void init(Context context, Handler handler) {
+        if (context == null) {
+            throw new IllegalArgumentException("Invalid null context");
+        }
+        sContext = context;
+        sWindowOrientationListener = new AudioWindowOrientationListener(context, handler);
+        sWindowOrientationListener.enable();
+        if (!sWindowOrientationListener.canDetectOrientation()) {
+            // cannot use com.android.server.policy.WindowOrientationListener, revert to public
+            // orientation API
+            Log.i(TAG, "Not using WindowOrientationListener, reverting to OrientationListener");
+            sWindowOrientationListener.disable();
+            sWindowOrientationListener = null;
+            sOrientationListener = new AudioOrientationListener(context);
+            sOrientationListener.enable();
+        }
+    }
+
+    static void enable() {
+        if (sWindowOrientationListener != null) {
+            sWindowOrientationListener.enable();
+        } else {
+            sOrientationListener.enable();
+        }
+        updateOrientation();
+    }
+
+    static void disable() {
+        if (sWindowOrientationListener != null) {
+            sWindowOrientationListener.disable();
+        } else {
+            sOrientationListener.disable();
+        }
+    }
+
+    /**
+     * Query current display rotation and publish the change if any.
+     */
+    static void updateOrientation() {
+        // Even though we're responding to device orientation events,
+        // use display rotation so audio stays in sync with video/dialogs
+        int newRotation = ((WindowManager) sContext.getSystemService(
+                Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
+        synchronized(sRotationLock) {
+            if (newRotation != sDeviceRotation) {
+                sDeviceRotation = newRotation;
+                publishRotation(sDeviceRotation);
+            }
+        }
+    }
+
+    private static void publishRotation(int rotation) {
+        Log.v(TAG, "publishing device rotation =" + rotation + " (x90deg)");
+        switch (rotation) {
+            case Surface.ROTATION_0:
+                AudioSystem.setParameters("rotation=0");
+                break;
+            case Surface.ROTATION_90:
+                AudioSystem.setParameters("rotation=90");
+                break;
+            case Surface.ROTATION_180:
+                AudioSystem.setParameters("rotation=180");
+                break;
+            case Surface.ROTATION_270:
+                AudioSystem.setParameters("rotation=270");
+                break;
+            default:
+                Log.e(TAG, "Unknown device rotation");
+        }
+    }
+
+    /**
+     * Uses android.view.OrientationEventListener
+     */
+    final static class AudioOrientationListener extends OrientationEventListener {
+        AudioOrientationListener(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onOrientationChanged(int orientation) {
+            updateOrientation();
+        }
+    }
+
+    /**
+     * Uses com.android.server.policy.WindowOrientationListener
+     */
+    final static class AudioWindowOrientationListener extends WindowOrientationListener {
+        private static RotationCheckThread sRotationCheckThread;
+
+        AudioWindowOrientationListener(Context context, Handler handler) {
+            super(context, handler);
+        }
+
+        public void onProposedRotationChanged(int rotation) {
+            updateOrientation();
+            if (sRotationCheckThread != null) {
+                sRotationCheckThread.endCheck();
+            }
+            sRotationCheckThread = new RotationCheckThread();
+            sRotationCheckThread.beginCheck();
+        }
+    }
+
+    /**
+     * When com.android.server.policy.WindowOrientationListener report an orientation change,
+     * the UI may not have rotated yet. This thread polls with gradually increasing delays
+     * the new orientation.
+     */
+    final static class RotationCheckThread extends Thread {
+        // how long to wait between each rotation check
+        private final int[] WAIT_TIMES_MS = { 10, 20, 50, 100, 100, 200, 200, 500 };
+        private int mWaitCounter;
+        private final Object mCounterLock = new Object();
+
+        RotationCheckThread() {
+            super("RotationCheck");
+        }
+
+        void beginCheck() {
+            synchronized(mCounterLock) {
+                mWaitCounter = 0;
+            }
+            try {
+                start();
+            } catch (IllegalStateException e) { }
+        }
+
+        void endCheck() {
+            synchronized(mCounterLock) {
+                mWaitCounter = WAIT_TIMES_MS.length;
+            }
+        }
+
+        public void run() {
+            int newRotation;
+            while (mWaitCounter < WAIT_TIMES_MS.length) {
+                updateOrientation();
+                int waitTimeMs;
+                synchronized(mCounterLock) {
+                    waitTimeMs = WAIT_TIMES_MS[mWaitCounter];
+                    mWaitCounter++;
+                }
+                try {
+                    sleep(waitTimeMs);
+                } catch (InterruptedException e) { }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f6c9374..7b15aad 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -27,6 +27,7 @@
 import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.AutomaticZenRule;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.ITransientNotification;
@@ -101,6 +102,7 @@
 import com.android.internal.R;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -1172,8 +1174,8 @@
             // Don't allow client applications to cancel foreground service notis.
             cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
                     Binder.getCallingUid() == Process.SYSTEM_UID
-                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
-                    null);
+                            ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId,
+                    REASON_NOMAN_CANCEL, null);
         }
 
         @Override
@@ -1594,6 +1596,50 @@
         }
 
         @Override
+        public List<AutomaticZenRule> getAutomaticZenRules() throws RemoteException {
+            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
+            return mZenModeHelper.getAutomaticZenRules();
+        }
+
+        @Override
+        public AutomaticZenRule getAutomaticZenRule(String name) throws RemoteException {
+            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
+            return mZenModeHelper.getAutomaticZenRule(name);
+        }
+
+        @Override
+        public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule)
+                throws RemoteException {
+            Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
+            Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
+            Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
+            Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
+            enforcePolicyAccess(Binder.getCallingUid(), "addOrUpdateZenModeRule");
+
+            return mZenModeHelper.addOrUpdateAutomaticZenRule(automaticZenRule,
+                    "addOrUpdateAutomaticZenRule");
+        }
+
+        @Override
+        public boolean renameAutomaticZenRule(String oldName, String newName) {
+            Preconditions.checkNotNull(oldName, "oldName is null");
+            Preconditions.checkNotNull(newName, "newName is null");
+            enforcePolicyAccess(Binder.getCallingUid(), "renameAutomaticZenRule");
+
+            return mZenModeHelper.renameAutomaticZenRule(
+                    oldName, newName, "renameAutomaticZenRule");
+        }
+
+        @Override
+        public boolean removeAutomaticZenRule(String name) throws RemoteException {
+            Preconditions.checkNotNull(name, "Name is null");
+            // Verify that they can modify zen rules.
+            enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
+
+            return mZenModeHelper.removeAutomaticZenRule(name, "removeAutomaticZenRule");
+        }
+
+        @Override
         public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
             enforcePolicyAccess(pkg, "setInterruptionFilter");
             final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
@@ -1641,6 +1687,25 @@
                     message);
         }
 
+        private void enforcePolicyAccess(int uid, String method) {
+            if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
+                    android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
+                return;
+            }
+            boolean accessAllowed = false;
+            String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
+            final int packageCount = packages.length;
+            for (int i = 0; i < packageCount; i++) {
+                if (checkPolicyAccess(packages[i])) {
+                    accessAllowed = true;
+                }
+            }
+            if (!accessAllowed) {
+                Slog.w(TAG, "Notification policy access denied calling " + method);
+                throw new SecurityException("Notification policy access denied");
+            }
+        }
+
         private void enforcePolicyAccess(String pkg, String method) {
             if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
                     android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 9131e5e..4d41e3a 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,11 +21,13 @@
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 
 import android.app.AppOpsManager;
+import android.app.AutomaticZenRule;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.database.ContentObserver;
@@ -34,6 +36,7 @@
 import android.media.AudioSystem;
 import android.media.VolumePolicy;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -62,6 +65,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -196,6 +200,121 @@
         return mZenMode;
     }
 
+    public List<AutomaticZenRule> getAutomaticZenRules() {
+        List<AutomaticZenRule> rules = new ArrayList<>();
+        if (mConfig == null) return rules;
+        for(ZenRule rule : mConfig.automaticRules.values()) {
+            if (canManageAutomaticZenRule(rule)) {
+                rules.add(createAutomaticZenRule(rule));
+            }
+        }
+        return rules;
+    }
+
+    public AutomaticZenRule getAutomaticZenRule(String name) {
+        if (mConfig == null) return null;
+        for(ZenRule rule : mConfig.automaticRules.values()) {
+            if (canManageAutomaticZenRule(rule) && rule.name.equals(name)) {
+                return createAutomaticZenRule(rule);
+            }
+        }
+        return null;
+    }
+
+    public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
+        if (mConfig == null) return false;
+        if (DEBUG) {
+            Log.d(TAG, "addOrUpdateAutomaticZenRule zenRule=" + automaticZenRule
+                    + " reason=" + reason);
+        }
+        final ZenModeConfig newConfig = mConfig.copy();
+        String ruleId = findMatchingRuleId(newConfig, automaticZenRule.getName());
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        if (ruleId == null) {
+            ruleId = newConfig.newRuleId();
+            rule.name = automaticZenRule.getName();
+            rule.component = automaticZenRule.getOwner();
+        } else {
+            rule = newConfig.automaticRules.get(ruleId);
+            if (!canManageAutomaticZenRule(rule)) {
+                throw new SecurityException(
+                        "Cannot update rules not owned by your condition provider");
+            }
+        }
+        if (rule.enabled != automaticZenRule.isEnabled()) {
+            rule.snoozing = false;
+        }
+        rule.condition = null;
+        rule.conditionId = automaticZenRule.getConditionId();
+        rule.enabled = automaticZenRule.isEnabled();
+        rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
+                automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+        newConfig.automaticRules.put(ruleId, rule);
+        return setConfig(newConfig, reason, true);
+    }
+
+    public boolean renameAutomaticZenRule(String oldName, String newName, String reason) {
+        if (mConfig == null) return false;
+        if (DEBUG) {
+            Log.d(TAG, "renameAutomaticZenRule oldName=" + oldName + "  newName=" + newName
+                    + " reason=" + reason);
+        }
+        final ZenModeConfig newConfig = mConfig.copy();
+        String ruleId = findMatchingRuleId(newConfig, oldName);
+        if (ruleId == null) {
+            return false;
+        } else {
+            ZenRule rule = newConfig.automaticRules.get(ruleId);
+            if (!canManageAutomaticZenRule(rule)) {
+                throw new SecurityException(
+                        "Cannot update rules not owned by your condition provider");
+            }
+            rule.name = newName;
+            return setConfig(newConfig, reason, true);
+        }
+    }
+
+    public boolean removeAutomaticZenRule(String name, String reason) {
+        if (mConfig == null) return false;
+        final ZenModeConfig newConfig = mConfig.copy();
+        String ruleId = findMatchingRuleId(newConfig, name);
+        if (ruleId != null) {
+            ZenRule rule = newConfig.automaticRules.get(ruleId);
+            if (canManageAutomaticZenRule(rule)) {
+                newConfig.automaticRules.remove(ruleId);
+                if (DEBUG) Log.d(TAG, "removeZenRule zenRule=" + name + " reason=" + reason);
+            } else {
+                throw new SecurityException(
+                        "Cannot delete rules not owned by your condition provider");
+            }
+        }
+        return setConfig(newConfig, reason, true);
+    }
+
+    public boolean canManageAutomaticZenRule(ZenRule rule) {
+        if (mContext.checkCallingPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS)
+                == PackageManager.PERMISSION_GRANTED) {
+            return true;
+        } else {
+            String[] packages = mContext.getPackageManager().getPackagesForUid(
+                    Binder.getCallingUid());
+            if (packages != null) {
+                final int packageCount = packages.length;
+                for (int i = 0; i < packageCount; i++) {
+                    if (packages[i].equals(rule.component.getPackageName())) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    private AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
+        return new AutomaticZenRule(rule.name, rule.component, rule.conditionId,
+                NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled);
+    }
+
     public void setManualZenMode(int zenMode, Uri conditionId, String reason) {
         setManualZenMode(zenMode, conditionId, reason, true /*setRingerMode*/);
     }
@@ -225,6 +344,15 @@
         setConfig(newConfig, reason, setRingerMode);
     }
 
+    private String findMatchingRuleId(ZenModeConfig config, String ruleName) {
+        for (String ruleId : config.automaticRules.keySet()) {
+            if (config.automaticRules.get(ruleId).name.equals(ruleName)) {
+                return ruleId;
+            }
+        }
+        return null;
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mZenMode=");
         pw.println(Global.zenModeToString(mZenMode));
@@ -272,7 +400,7 @@
                 }
                 config.manualRule = null;  // don't restore the manual rule
                 if (config.automaticRules != null) {
-                    for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) {
+                    for (ZenRule automaticRule : config.automaticRules.values()) {
                         // don't restore transient state from restored automatic rules
                         automaticRule.snoozing = false;
                         automaticRule.condition = null;
@@ -319,36 +447,41 @@
     }
 
     private boolean setConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
-        if (config == null || !config.isValid()) {
-            Log.w(TAG, "Invalid config in setConfig; " + config);
-            return false;
-        }
-        if (config.user != mUser) {
-            // simply store away for background users
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (config == null || !config.isValid()) {
+                Log.w(TAG, "Invalid config in setConfig; " + config);
+                return false;
+            }
+            if (config.user != mUser) {
+                // simply store away for background users
+                mConfigs.put(config.user, config);
+                if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user);
+                return true;
+            }
+            mConditions.evaluateConfig(config, false /*processSubscriptions*/);  // may modify config
             mConfigs.put(config.user, config);
-            if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user);
+            if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
+            ZenLog.traceConfig(reason, mConfig, config);
+            final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
+                    getNotificationPolicy(config));
+            mConfig = config;
+            if (config.equals(mConfig)) {
+                dispatchOnConfigChanged();
+            }
+            if (policyChanged){
+                dispatchOnPolicyChanged();
+            }
+            final String val = Integer.toString(mConfig.hashCode());
+            Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
+            if (!evaluateZenMode(reason, setRingerMode)) {
+                applyRestrictions();  // evaluateZenMode will also apply restrictions if changed
+            }
+            mConditions.evaluateConfig(config, true /*processSubscriptions*/);
             return true;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        mConditions.evaluateConfig(config, false /*processSubscriptions*/);  // may modify config
-        mConfigs.put(config.user, config);
-        if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
-        ZenLog.traceConfig(reason, mConfig, config);
-        final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
-                getNotificationPolicy(config));
-        mConfig = config;
-        if (config.equals(mConfig)) {
-            dispatchOnConfigChanged();
-        }
-        if (policyChanged){
-            dispatchOnPolicyChanged();
-        }
-        final String val = Integer.toString(mConfig.hashCode());
-        Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
-        if (!evaluateZenMode(reason, setRingerMode)) {
-            applyRestrictions();  // evaluateZenMode will also apply restrictions if changed
-        }
-        mConditions.evaluateConfig(config, true /*processSubscriptions*/);
-        return true;
     }
 
     private int getZenModeSetting() {
@@ -506,6 +639,7 @@
                 .getString(R.string.zen_mode_default_weeknights_name);
         rule1.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
         rule1.zenMode = Global.ZEN_MODE_ALARMS;
+        rule1.component = ScheduleConditionProvider.COMPONENT;
         config.automaticRules.put(config.newRuleId(), rule1);
 
         final ScheduleInfo weekends = new ScheduleInfo();
@@ -519,6 +653,7 @@
                 .getString(R.string.zen_mode_default_weekends_name);
         rule2.conditionId = ZenModeConfig.toScheduleConditionId(weekends);
         rule2.zenMode = Global.ZEN_MODE_ALARMS;
+        rule2.component = ScheduleConditionProvider.COMPONENT;
         config.automaticRules.put(config.newRuleId(), rule2);
     }
 
@@ -533,6 +668,7 @@
         rule.name = mContext.getResources().getString(R.string.zen_mode_default_events_name);
         rule.conditionId = ZenModeConfig.toEventConditionId(events);
         rule.zenMode = Global.ZEN_MODE_ALARMS;
+        rule.component = EventConditionProvider.COMPONENT;
         config.automaticRules.put(config.newRuleId(), rule);
     }
 
@@ -573,6 +709,7 @@
                 rule.conditionId = ZenModeConfig.toScheduleConditionId(schedule);
                 rule.zenMode = v1.sleepNone ? Global.ZEN_MODE_NO_INTERRUPTIONS
                         : Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+                rule.component = ScheduleConditionProvider.COMPONENT;
                 rt.automaticRules.put(rt.newRuleId(), rule);
             } else {
                 Log.i(TAG, "No existing V1 downtime found, generating default schedules");
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0366fff..cf09b84 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -140,6 +140,7 @@
     private static final String ATTR_APP_ICON = "appIcon";
     private static final String ATTR_APP_LABEL = "appLabel";
     private static final String ATTR_ORIGINATING_URI = "originatingUri";
+    private static final String ATTR_ORIGINATING_UID = "originatingUid";
     private static final String ATTR_REFERRER_URI = "referrerUri";
     private static final String ATTR_ABI_OVERRIDE = "abiOverride";
     private static final String ATTR_VOLUME_UUID = "volumeUuid";
@@ -405,6 +406,8 @@
         params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
         params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
         params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
+        params.originatingUid =
+                readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
         params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
         params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
         params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
@@ -477,6 +480,7 @@
         writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
         writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
         writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
+        writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
         writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
         writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
         writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 6c6625d..6e32e5c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -222,11 +222,17 @@
         // waived if the installer is the device owner.
         DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
+        final boolean isPermissionGranted =
+                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
+                        == PackageManager.PERMISSION_GRANTED);
+        final boolean isInstallerRoot = (installerUid == Process.ROOT_UID);
+        final boolean forcePermissionPrompt =
+                (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
         mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(installerPackageName);
-        if ((mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
-                == PackageManager.PERMISSION_GRANTED)
-                || (installerUid == Process.ROOT_UID)
-                || mIsInstallerDeviceOwner) {
+        if ((isPermissionGranted
+                        || isInstallerRoot
+                        || mIsInstallerDeviceOwner)
+                && !forcePermissionPrompt) {
             mPermissionsAccepted = true;
         } else {
             mPermissionsAccepted = false;
@@ -955,7 +961,9 @@
 
         if (accepted) {
             // Mark and kick off another install pass
-            mPermissionsAccepted = true;
+            synchronized (mLock) {
+                mPermissionsAccepted = true;
+            }
             mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
         } else {
             destroyInternal();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1c523b2..99562f3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9687,7 +9687,8 @@
             IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
             String installerPackageName, int installerUid, UserHandle user) {
         final VerificationParams verifParams = new VerificationParams(
-                null, sessionParams.originatingUri, sessionParams.referrerUri, installerUid, null);
+                null, sessionParams.originatingUri, sessionParams.referrerUri,
+                sessionParams.originatingUid, null);
         verifParams.setInstallerUid(installerUid);
 
         final OriginInfo origin;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4989a9b..bdc84d5 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
 import android.app.IStopUserCallback;
 import android.app.admin.DevicePolicyManager;
@@ -64,7 +65,7 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
-import com.android.server.accounts.AccountManagerService;
+import com.android.server.LocalServices;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -244,13 +245,15 @@
             synchronized (mPackagesLock) {
                 // Prune out any partially created/partially removed users.
                 ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
-                for (int i = 0; i < mUsers.size(); i++) {
+                final int userSize = mUsers.size();
+                for (int i = 0; i < userSize; i++) {
                     UserInfo ui = mUsers.valueAt(i);
                     if ((ui.partial || ui.guestToRemove) && i != 0) {
                         partials.add(ui);
                     }
                 }
-                for (int i = 0; i < partials.size(); i++) {
+                final int partialsSize = partials.size();
+                for (int i = 0; i < partialsSize; i++) {
                     UserInfo ui = partials.get(i);
                     Slog.w(LOG_TAG, "Removing partially created user " + ui.id
                             + " (name=" + ui.name + ")");
@@ -274,7 +277,8 @@
     public UserInfo getPrimaryUser() {
         checkManageUsersPermission("query users");
         synchronized (mPackagesLock) {
-            for (int i = 0; i < mUsers.size(); i++) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
                 UserInfo ui = mUsers.valueAt(i);
                 if (ui.isPrimary()) {
                     return ui;
@@ -289,7 +293,8 @@
         checkManageUsersPermission("query users");
         synchronized (mPackagesLock) {
             ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
-            for (int i = 0; i < mUsers.size(); i++) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
                 UserInfo ui = mUsers.valueAt(i);
                 if (ui.partial) {
                     continue;
@@ -325,7 +330,8 @@
             // Probably a dying user
             return users;
         }
-        for (int i = 0; i < mUsers.size(); i++) {
+        final int userSize = mUsers.size();
+        for (int i = 0; i < userSize; i++) {
             UserInfo profile = mUsers.valueAt(i);
             if (!isProfileOf(user, profile)) {
                 continue;
@@ -1012,7 +1018,8 @@
             serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
             writeRestrictionsLocked(serializer, mGuestRestrictions);
             serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
-            for (int i = 0; i < mUsers.size(); i++) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
                 UserInfo user = mUsers.valueAt(i);
                 serializer.startTag(null, TAG_USER);
                 serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
@@ -1589,6 +1596,9 @@
                             }
                             new Thread() {
                                 public void run() {
+                                    // Clean up any ActivityManager state
+                                    LocalServices.getService(ActivityManagerInternal.class)
+                                            .onUserRemoved(userHandle);
                                     synchronized (mInstallLock) {
                                         synchronized (mPackagesLock) {
                                             removeUserStateLocked(userHandle);
@@ -1953,14 +1963,15 @@
      */
     private void updateUserIdsLocked() {
         int num = 0;
-        for (int i = 0; i < mUsers.size(); i++) {
+        final int userSize = mUsers.size();
+        for (int i = 0; i < userSize; i++) {
             if (!mUsers.valueAt(i).partial) {
                 num++;
             }
         }
         final int[] newUsers = new int[num];
         int n = 0;
-        for (int i = 0; i < mUsers.size(); i++) {
+        for (int i = 0; i < userSize; i++) {
             if (!mUsers.valueAt(i).partial) {
                 newUsers[n++] = mUsers.keyAt(i);
             }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index dbc3970..4f30a15 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -778,6 +778,8 @@
         private final Runnable mUpdateRotationRunnable = new Runnable() {
             @Override
             public void run() {
+                // send interaction hint to improve redraw performance
+                mPowerManagerInternal.powerHint(PowerManagerInternal.POWER_HINT_INTERACTION, 0);
                 updateRotation(false);
             }
         };
@@ -2105,6 +2107,8 @@
         case TYPE_WALLPAPER:
             // wallpaper is at the bottom, though the window manager may move it.
             return 2;
+        case TYPE_DOCK_DIVIDER:
+            return 2;
         case TYPE_PHONE:
             return 3;
         case TYPE_SEARCH_BAR:
@@ -2135,56 +2139,54 @@
         case TYPE_INPUT_METHOD_DIALOG:
             // on-screen keyboards and other such input method user interfaces go here.
             return 13;
-        case TYPE_DOCK_DIVIDER:
-            return 14;
         case TYPE_KEYGUARD_SCRIM:
             // the safety window that shows behind keyguard while keyguard is starting
-            return 15;
+            return 14;
         case TYPE_STATUS_BAR_SUB_PANEL:
-            return 16;
+            return 15;
         case TYPE_STATUS_BAR:
-            return 17;
+            return 16;
         case TYPE_STATUS_BAR_PANEL:
-            return 18;
+            return 17;
         case TYPE_KEYGUARD_DIALOG:
-            return 19;
+            return 18;
         case TYPE_VOLUME_OVERLAY:
             // the on-screen volume indicator and controller shown when the user
             // changes the device volume
-            return 20;
+            return 19;
         case TYPE_SYSTEM_OVERLAY:
             // the on-screen volume indicator and controller shown when the user
             // changes the device volume
-            return 21;
+            return 20;
         case TYPE_NAVIGATION_BAR:
             // the navigation bar, if available, shows atop most things
-            return 22;
+            return 21;
         case TYPE_NAVIGATION_BAR_PANEL:
             // some panels (e.g. search) need to show on top of the navigation bar
-            return 23;
+            return 22;
         case TYPE_SYSTEM_ERROR:
             // system-level error dialogs
-            return 24;
+            return 23;
         case TYPE_MAGNIFICATION_OVERLAY:
             // used to highlight the magnified portion of a display
-            return 25;
+            return 24;
         case TYPE_DISPLAY_OVERLAY:
             // used to simulate secondary display devices
-            return 26;
+            return 25;
         case TYPE_DRAG:
             // the drag layer: input for drag-and-drop is associated with this window,
             // which sits above all other focusable windows
-            return 27;
+            return 26;
         case TYPE_ACCESSIBILITY_OVERLAY:
             // overlay put by accessibility services to intercept user interaction
-            return 28;
+            return 27;
         case TYPE_SECURE_SYSTEM_OVERLAY:
-            return 29;
+            return 28;
         case TYPE_BOOT_PROGRESS:
-            return 30;
+            return 29;
         case TYPE_POINTER:
             // the (mouse) pointer layer
-            return 31;
+            return 30;
         }
         Log.e(TAG, "Unknown window type: " + type);
         return 2;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index a64cda6..cc51d20 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1032,6 +1032,7 @@
                 0, top - containingFrame.top, 0);
         set.addAnimation(translate);
         set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+        set.setZAdjustment(Animation.ZORDER_TOP);
         return set;
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d6d77e9..6b5ecdc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -126,6 +126,7 @@
         display.getMetrics(mDisplayMetrics);
         isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
         mService = service;
+        initializeDisplayBaseInfo();
         mDividerControllerLocked = new DockedStackDividerController(service.mContext, this);
     }
 
@@ -192,6 +193,21 @@
         }
     }
 
+    void initializeDisplayBaseInfo() {
+        synchronized(mDisplaySizeLock) {
+            // Bootstrap the default logical display from the display manager.
+            final DisplayInfo newDisplayInfo =
+                    mService.mDisplayManagerInternal.getDisplayInfo(mDisplayId);
+            if (newDisplayInfo != null) {
+                mDisplayInfo.copyFrom(newDisplayInfo);
+            }
+            mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth;
+            mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight;
+            mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
+            mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
+        }
+    }
+
     void getLogicalDisplayRect(Rect out) {
         // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked.
         final int orientation = mDisplayInfo.rotation;
@@ -568,12 +584,7 @@
     }
 
     TaskStack getDockedStackLocked() {
-        for (int i = mStacks.size() - 1; i >= 0; i--) {
-            TaskStack stack = mStacks.get(i);
-            if (stack.mStackId == DOCKED_STACK_ID && stack.isVisibleLocked()) {
-                return stack;
-            }
-        }
-        return null;
+        final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        return (stack != null && stack.isVisibleLocked()) ? stack : null;
     }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d1111f7..1f986dd 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,9 +17,11 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
 import static com.android.server.wm.WindowManagerService.TAG;
 import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
 import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
 import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
 
 import android.content.res.Configuration;
@@ -245,8 +247,32 @@
         return true;
     }
 
+    /** Return true if the current bound can get outputted to the rest of the system as-is. */
+    private boolean useCurrentBounds() {
+        final DisplayContent displayContent = mStack.getDisplayContent();
+        if (mFullscreen
+                || mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID
+                || mStack.mStackId == DOCKED_STACK_ID
+                || displayContent == null
+                || displayContent.getDockedStackLocked() != null) {
+            return true;
+        }
+        return false;
+    }
+
+    /** Bounds of the task with other system factors taken into consideration. */
     void getBounds(Rect out) {
-        out.set(mBounds);
+        if (useCurrentBounds()) {
+            // No need to adjust the output bounds if fullscreen or the docked stack is visible
+            // since it is already what we want to represent to the rest of the system.
+            out.set(mBounds);
+            return;
+        }
+
+        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
+        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
+        // system.
+        mStack.getDisplayContent().getLogicalDisplayRect(out);
     }
 
     void setDragResizing(boolean dragResizing) {
@@ -274,7 +300,13 @@
         // this happens, so update the task bounds so it stays in the same place.
         mTmpRect2.set(mBounds);
         displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
-        setBounds(mTmpRect2, mOverrideConfig);
+        if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) {
+            // Post message to inform activity manager of the bounds change simulating
+            // a one-way call. We do this to prevent a deadlock between window manager
+            // lock and activity manager lock been held.
+            mService.mH.sendMessage(mService.mH.obtainMessage(
+                            RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds));
+        }
     }
 
     /** Updates the dim layer bounds, recreating it if needed. */
@@ -433,10 +465,6 @@
         return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
     }
 
-    boolean inDockedWorkspace() {
-        return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
-    }
-
     WindowState getTopAppMainWindow() {
         final int tokensCount = mAppTokens.size();
         return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null;
@@ -444,7 +472,13 @@
 
     @Override
     public boolean isFullscreen() {
-        return mFullscreen;
+        if (useCurrentBounds()) {
+            return mFullscreen;
+        }
+        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
+        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
+        // system.
+        return true;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index df2e5e8..290c2ea 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -19,8 +19,8 @@
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.RESIZE_MODE_FORCED;
 import static android.app.ActivityManager.RESIZE_MODE_USER;
+import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
@@ -146,13 +146,18 @@
                         }
                         synchronized (mService.mWindowMap) {
                             mDragEnded = notifyMoveLocked(newX, newY);
+                            mTask.getBounds(mTmpRect);
                         }
-                        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.TaskPositioner.resizeTask");
-                        try {
-                            mService.mActivityManager.resizeTask(
-                                    mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
-                        } catch(RemoteException e) {}
-                        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+                        if (!mTmpRect.equals(mWindowDragBounds)) {
+                            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+                                    "wm.TaskPositioner.resizeTask");
+                            try {
+                                mService.mActivityManager.resizeTask(
+                                        mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
+                            } catch (RemoteException e) {
+                            }
+                            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+                        }
                     } break;
 
                     case MotionEvent.ACTION_UP: {
@@ -171,15 +176,16 @@
                 }
 
                 if (mDragEnded) {
+                    final boolean wasResizing = mResizing;
                     synchronized (mService.mWindowMap) {
                         endDragLocked();
                     }
                     try {
-                        if (mResizing) {
+                        if (wasResizing) {
                             // We were using fullscreen surface during resizing. Request
                             // resizeTask() one last time to restore surface to window size.
                             mService.mActivityManager.resizeTask(
-                                    mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_FORCED);
+                                    mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
                         }
 
                         if (mCurrentDimSide != CTRL_NONE) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 3b08284..f030b9a 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -18,13 +18,13 @@
 
 import static android.app.ActivityManager.*;
 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
+import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
 import static com.android.server.wm.WindowManagerService.TAG;
 
 import android.annotation.IntDef;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Debug;
-import android.os.RemoteException;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -58,7 +58,7 @@
 
     /** For comparison with DisplayContent bounds. */
     private Rect mTmpRect = new Rect();
-    private Rect TmpRect2 = new Rect();
+    private Rect mTmpRect2 = new Rect();
 
     /** Content limits relative to the DisplayContent this sits in. */
     private Rect mBounds = new Rect();
@@ -181,24 +181,60 @@
         return true;
     }
 
-    void getBounds(Rect out) {
+    /** Bounds of the stack without adjusting for other factors in the system like visibility
+     * of docked stack.
+     * Most callers should be using {@link #getBounds} as it take into consideration other system
+     * factors. */
+    void getRawBounds(Rect out) {
         out.set(mBounds);
     }
 
+    /** Return true if the current bound can get outputted to the rest of the system as-is. */
+    private boolean useCurrentBounds() {
+        if (mFullscreen
+                || mStackId == DOCKED_STACK_ID
+                || mDisplayContent == null
+                || mDisplayContent.getDockedStackLocked() != null) {
+            return true;
+        }
+        return false;
+    }
+
+    /** Bounds of the stack with other system factors taken into consideration. */
+    void getBounds(Rect out) {
+        if (useCurrentBounds()) {
+            // No need to adjust the output bounds if fullscreen or the docked stack is visible
+            // since it is already what we want to represent to the rest of the system.
+            out.set(mBounds);
+            return;
+        }
+
+        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
+        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
+        // system.
+        mDisplayContent.getLogicalDisplayRect(out);
+    }
+
     void updateDisplayInfo(Rect bounds) {
         if (mDisplayContent != null) {
+            for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+                mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
+            }
             if (bounds != null) {
                 setBounds(bounds);
             } else if (mFullscreen) {
                 setBounds(null);
             } else {
-                TmpRect2.set(mBounds);
+                mTmpRect2.set(mBounds);
                 mDisplayContent.rotateBounds(
-                        mRotation, mDisplayContent.getDisplayInfo().rotation, TmpRect2);
-                setBounds(TmpRect2);
-            }
-            for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
+                        mRotation, mDisplayContent.getDisplayInfo().rotation, mTmpRect2);
+                if (setBounds(mTmpRect2)) {
+                    // Post message to inform activity manager of the bounds change simulating
+                    // a one-way call. We do this to prevent a deadlock between window manager
+                    // lock and activity manager lock been held.
+                    mService.mH.sendMessage(
+                            mService.mH.obtainMessage(RESIZE_STACK, mStackId, -1, mBounds));
+                }
             }
         }
     }
@@ -346,14 +382,18 @@
         mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId());
 
         Rect bounds = null;
-        final boolean dockedStackExists = mService.mStackIdToStack.get(DOCKED_STACK_ID) != null;
-        if (mStackId == DOCKED_STACK_ID || (dockedStackExists
+        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        if (mStackId == DOCKED_STACK_ID || (dockedStack != null
                 && mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) {
             // The existence of a docked stack affects the size of any static stack created since
             // the docked stack occupies a dedicated region on screen.
             bounds = new Rect();
             displayContent.getLogicalDisplayRect(mTmpRect);
-            getInitialDockedStackBounds(mTmpRect, bounds, mStackId,
+            mTmpRect2.setEmpty();
+            if (dockedStack != null) {
+                dockedStack.getRawBounds(mTmpRect2);
+            }
+            getInitialDockedStackBounds(mTmpRect, bounds, mStackId, mTmpRect2,
                     mDisplayContent.mDividerControllerLocked.getWidthAdjustment());
         }
 
@@ -363,7 +403,7 @@
             // Attaching a docked stack to the display affects the size of all other static
             // stacks since the docked stack occupies a dedicated region on screen.
             // Resize existing static stacks so they are pushed to the side of the docked stack.
-            resizeNonDockedStacks(!FULLSCREEN);
+            resizeNonDockedStacks(!FULLSCREEN, mBounds);
         }
     }
 
@@ -373,29 +413,49 @@
      * @param displayRect The bounds of the display the docked stack is on.
      * @param outBounds Output bounds that should be used for the stack.
      * @param stackId Id of stack we are calculating the bounds for.
+     * @param dockedBounds Bounds of the docked stack.
      * @param adjustment
      */
-    private static void getInitialDockedStackBounds(Rect displayRect, Rect outBounds, int stackId,
-            int adjustment) {
-        // Docked stack start off occupying half the screen space.
+    private static void getInitialDockedStackBounds(
+            Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int adjustment) {
         final boolean dockedStack = stackId == DOCKED_STACK_ID;
         final boolean splitHorizontally = displayRect.width() > displayRect.height();
         final boolean topOrLeftCreateMode =
                 WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-        final boolean placeTopOrLeft = (dockedStack && topOrLeftCreateMode)
-                || (!dockedStack && !topOrLeftCreateMode);
+
         outBounds.set(displayRect);
-        if (placeTopOrLeft) {
-            if (splitHorizontally) {
-                outBounds.right = displayRect.centerX() - adjustment;
+        if (dockedStack) {
+            // The initial bounds of the docked stack when it is created half the screen space and
+            // its bounds can be adjusted after that. The bounds of all other stacks are adjusted
+            // to occupy whatever screen space the docked stack isn't occupying.
+            if (topOrLeftCreateMode) {
+                if (splitHorizontally) {
+                    outBounds.right = displayRect.centerX() - adjustment;
+                } else {
+                    outBounds.bottom = displayRect.centerY() - adjustment;
+                }
             } else {
-                outBounds.bottom = displayRect.centerY() - adjustment;
+                if (splitHorizontally) {
+                    outBounds.left = displayRect.centerX() + adjustment;
+                } else {
+                    outBounds.top = displayRect.centerY() + adjustment;
+                }
+            }
+            return;
+        }
+
+        // Other stacks occupy whatever space is left by the docked stack.
+        if (!topOrLeftCreateMode) {
+            if (splitHorizontally) {
+                outBounds.right = dockedBounds.left - adjustment;
+            } else {
+                outBounds.bottom = dockedBounds.top - adjustment;
             }
         } else {
             if (splitHorizontally) {
-                outBounds.left = displayRect.centerX() + adjustment;
+                outBounds.left = dockedBounds.right + adjustment;
             } else {
-                outBounds.top = displayRect.centerY() + adjustment;
+                outBounds.top = dockedBounds.bottom + adjustment;
             }
         }
     }
@@ -404,11 +464,14 @@
      * based on the presence of a docked stack.
      * @param fullscreen If true the stacks will be resized to fullscreen, else they will be
      *                   resized to the appropriate size based on the presence of a docked stack.
+     * @param dockedBounds Bounds of the docked stack.
      */
-    private void resizeNonDockedStacks(boolean fullscreen) {
-        mDisplayContent.getLogicalDisplayRect(mTmpRect);
+    private void resizeNonDockedStacks(boolean fullscreen, Rect dockedBounds) {
+        // Not using mTmpRect because we are posting the object in a message.
+        final Rect bounds = new Rect();
+        mDisplayContent.getLogicalDisplayRect(bounds);
         if (!fullscreen) {
-            getInitialDockedStackBounds(mTmpRect, mTmpRect, FULLSCREEN_WORKSPACE_STACK_ID,
+            getInitialDockedStackBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds,
                     mDisplayContent.mDividerControllerLocked.getWidth());
         }
 
@@ -419,11 +482,8 @@
             if (otherStackId != DOCKED_STACK_ID
                     && otherStackId >= FIRST_STATIC_STACK_ID
                     && otherStackId <= LAST_STATIC_STACK_ID) {
-                try {
-                    mService.mActivityManager.resizeStack(otherStackId, mTmpRect);
-                } catch (RemoteException e) {
-                    // This will not happen since we are in the same process.
-                }
+                mService.mH.sendMessage(
+                        mService.mH.obtainMessage(RESIZE_STACK, otherStackId, -1, bounds));
             }
         }
     }
@@ -451,7 +511,7 @@
         if (mStackId == DOCKED_STACK_ID) {
             // Docked stack was detached from the display, so we no longer need to restrict the
             // region of the screen other static stacks occupy. Go ahead and make them fullscreen.
-            resizeNonDockedStacks(FULLSCREEN);
+            resizeNonDockedStacks(FULLSCREEN, null);
         }
 
         close();
@@ -520,9 +580,23 @@
         }
     }
 
+    /** Fullscreen status of the stack without adjusting for other factors in the system like
+     * visibility of docked stack.
+     * Most callers should be using {@link #isFullscreen} as it take into consideration other
+     * system factors. */
+    boolean getRawFullscreen() {
+        return mFullscreen;
+    }
+
     @Override
     public boolean isFullscreen() {
-        return mFullscreen;
+        if (useCurrentBounds()) {
+            return mFullscreen;
+        }
+        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
+        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
+        // system.
+        return true;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ea26822..74572cf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -222,6 +222,7 @@
     static final boolean HIDE_STACK_CRAWLS = true;
     static final int LAYOUT_REPEAT_THRESHOLD = 4;
 
+
     static final boolean PROFILE_ORIENTATION = false;
     static final boolean localLOGV = DEBUG;
 
@@ -2175,10 +2176,7 @@
                 // The exit animation is running... wait for it!
                 win.mExiting = true;
                 win.mRemoveOnExit = true;
-                final DisplayContent displayContent = win.getDisplayContent();
-                if (displayContent != null) {
-                    displayContent.layoutNeeded = true;
-                }
+                win.setDisplayLayoutNeeded();
                 final boolean focusChanged = updateFocusedWindowLocked(
                         UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
                 mWindowPlacerLocked.performSurfacePlacement();
@@ -2301,10 +2299,7 @@
             windows.remove(win);
             if (!mWindowPlacerLocked.isInLayout()) {
                 assignLayersLocked(windows);
-                final DisplayContent displayContent = win.getDisplayContent();
-                if (displayContent != null) {
-                    displayContent.layoutNeeded = true;
-                }
+                win.setDisplayLayoutNeeded();
                 if (performLayout) {
                     mWindowPlacerLocked.performSurfacePlacement();
                 }
@@ -2386,10 +2381,7 @@
                         w.mGivenVisibleInsets.scale(w.mGlobalScale);
                         w.mGivenTouchableRegion.scale(w.mGlobalScale);
                     }
-                    final DisplayContent displayContent = w.getDisplayContent();
-                    if (displayContent != null) {
-                        displayContent.layoutNeeded = true;
-                    }
+                    w.setDisplayLayoutNeeded();
                     mWindowPlacerLocked.performSurfacePlacement();
                 }
             }
@@ -2731,10 +2723,7 @@
                         WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
             }
 
-            final DisplayContent displayContent = win.getDisplayContent();
-            if (displayContent != null) {
-                displayContent.layoutNeeded = true;
-            }
+            win.setDisplayLayoutNeeded();
             win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
             configChanged = updateOrientationFromAppTokensLocked(false);
             mWindowPlacerLocked.performSurfacePlacement();
@@ -2827,10 +2816,7 @@
                         getDefaultDisplayContentLocked().pendingLayoutChanges |=
                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                     }
-                    final DisplayContent displayContent = win.getDisplayContent();
-                    if (displayContent != null) {
-                        displayContent.layoutNeeded = true;
-                    }
+                    win.setDisplayLayoutNeeded();
                     mWindowPlacerLocked.requestTraversal();
                 }
             }
@@ -3929,10 +3915,7 @@
                             }
                         }
                         changed = true;
-                        final DisplayContent displayContent = win.getDisplayContent();
-                        if (displayContent != null) {
-                            displayContent.layoutNeeded = true;
-                        }
+                        win.setDisplayLayoutNeeded();
                     }
                 } else if (win.isVisibleNow()) {
                     if (!runningAppAnimation) {
@@ -3946,10 +3929,7 @@
                         }
                     }
                     changed = true;
-                    final DisplayContent displayContent = win.getDisplayContent();
-                    if (displayContent != null) {
-                        displayContent.layoutNeeded = true;
-                    }
+                    win.setDisplayLayoutNeeded();
                 }
             }
 
@@ -4117,10 +4097,7 @@
                     }
                     w.mLastFreezeDuration = 0;
                     unfrozeWindows = true;
-                    final DisplayContent displayContent = w.getDisplayContent();
-                    if (displayContent != null) {
-                        displayContent.layoutNeeded = true;
-                    }
+                    w.setDisplayLayoutNeeded();
                 }
             }
             if (force || unfrozeWindows) {
@@ -4423,7 +4400,6 @@
         mInputMonitor.setUpdateInputWindowsNeededLw();
         mWindowPlacerLocked.performSurfacePlacement();
         mInputMonitor.updateInputWindowsLw(false /*force*/);
-
         //dump();
     }
 
@@ -4508,20 +4484,16 @@
                     }
                     stack.attachDisplayContent(displayContent);
                     displayContent.attachStack(stack, onTop);
-                    if (stack.mStackId == DOCKED_STACK_ID) {
-                        mH.obtainMessage(H.UPDATE_DOCKED_STACK_DIVIDER,
-                                displayContent).sendToTarget();
-                    }
                     moveStackWindowsLocked(displayContent);
                     final WindowList windows = displayContent.getWindowList();
                     for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                         windows.get(winNdx).reportResized();
                     }
-                    if (stack.isFullscreen()) {
+                    if (stack.getRawFullscreen()) {
                         return null;
                     }
                     Rect bounds = new Rect();
-                    stack.getBounds(bounds);
+                    stack.getRawBounds(bounds);
                     return bounds;
                 }
             }
@@ -4534,11 +4506,6 @@
     void detachStackLocked(DisplayContent displayContent, TaskStack stack) {
         displayContent.detachStack(stack);
         stack.detachDisplay();
-        // We can't directly remove the divider, because only the WM thread can do these operations
-        // and we can be on AM thread.
-        if (stack.mStackId == DOCKED_STACK_ID) {
-            mH.obtainMessage(H.UPDATE_DOCKED_STACK_DIVIDER, displayContent).sendToTarget();
-        }
     }
 
     public void detachStack(int stackId) {
@@ -4644,7 +4611,7 @@
                 stack.getDisplayContent().layoutNeeded = true;
                 mWindowPlacerLocked.performSurfacePlacement();
             }
-            return stack.isFullscreen();
+            return stack.getRawFullscreen();
         }
     }
 
@@ -7131,22 +7098,7 @@
             final DisplayContent displayContent = getDisplayContentLocked(displayId);
             if (displayContent != null) {
                 mAnimator.addDisplayLocked(displayId);
-                synchronized(displayContent.mDisplaySizeLock) {
-                    // Bootstrap the default logical display from the display manager.
-                    final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-                    DisplayInfo newDisplayInfo = mDisplayManagerInternal.getDisplayInfo(displayId);
-                    if (newDisplayInfo != null) {
-                        displayInfo.copyFrom(newDisplayInfo);
-                    }
-                    displayContent.mInitialDisplayWidth = displayInfo.logicalWidth;
-                    displayContent.mInitialDisplayHeight = displayInfo.logicalHeight;
-                    displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi;
-                    displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth;
-                    displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight;
-                    displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity;
-                    displayContent.mBaseDisplayRect.set(0, 0,
-                            displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight);
-                }
+                displayContent.initializeDisplayBaseInfo();
             }
         }
     }
@@ -7209,6 +7161,9 @@
 
         public static final int UPDATE_DOCKED_STACK_DIVIDER = 42;
 
+        public static final int RESIZE_STACK = 43;
+        public static final int RESIZE_TASK = 44;
+
         @Override
         public void handleMessage(Message msg) {
             if (DEBUG_WINDOW_TRACE) {
@@ -7755,6 +7710,22 @@
                     }
                 }
                 break;
+                case RESIZE_TASK: {
+                    try {
+                        mActivityManager.resizeTask(msg.arg1, (Rect) msg.obj, msg.arg2);
+                    } catch (RemoteException e) {
+                        // This will not happen since we are in the same process.
+                    }
+                }
+                break;
+                case RESIZE_STACK: {
+                    try {
+                        mActivityManager.resizeStack(msg.arg1, (Rect) msg.obj);
+                    } catch (RemoteException e) {
+                        // This will not happen since we are in the same process.
+                    }
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG, "handleMessage: exit");
@@ -8366,6 +8337,13 @@
             } else if (wtoken != null) {
                 winAnimator.mAnimLayer =
                         w.mLayer + wtoken.mAppAnimator.animLayerAdjustment;
+                if (wtoken.mReplacingWindow && wtoken.mAnimateReplacingWindow) {
+                    // We know that we will be animating a relaunching window in the near future,
+                    // which will receive a z-order increase. We want the replaced window to
+                    // immediately receive the same treatment, e.g. to be above the dock divider.
+                    w.mLayer += TYPE_LAYER_OFFSET;
+                    winAnimator.mAnimLayer += TYPE_LAYER_OFFSET;
+                }
             } else {
                 winAnimator.mAnimLayer = w.mLayer;
             }
@@ -10083,8 +10061,6 @@
         @Override
         public void saveLastInputMethodWindowForTransition() {
             synchronized (mWindowMap) {
-                // TODO(multidisplay): Pass in the displayID.
-                DisplayContent displayContent = getDefaultDisplayContentLocked();
                 if (mInputMethodWindow != null) {
                     mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget);
                 }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ad9c382..55ddbc0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1323,6 +1323,12 @@
         }
     }
 
+    void setDisplayLayoutNeeded() {
+        if (mDisplayContent != null) {
+            mDisplayContent.layoutNeeded = true;
+        }
+    }
+
     private class DeathRecipient implements IBinder.DeathRecipient {
         @Override
         public void binderDied() {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0b9f2c6..60bf571 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerService.DEBUG_LAYERS;
@@ -868,14 +869,18 @@
                 mSurfaceW = width;
                 mSurfaceH = height;
 
-                final boolean isHwAccelerated = (attrs.flags &
-                        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
+                final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0;
                 final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
                 if (!PixelFormat.formatHasAlpha(attrs.format)
+                        // Don't make surface with surfaceInsets opaque as they display a
+                        // translucent shadow.
                         && attrs.surfaceInsets.left == 0
                         && attrs.surfaceInsets.top == 0
                         && attrs.surfaceInsets.right == 0
-                        && attrs.surfaceInsets.bottom  == 0) {
+                        && attrs.surfaceInsets.bottom == 0
+                        // Don't make surface opaque when resizing to reduce the amount of
+                        // artifacts shown in areas the app isn't drawing content to.
+                        && !w.isDragResizing()) {
                     flags |= SurfaceControl.OPAQUE;
                 }
 
@@ -1400,7 +1405,11 @@
 
     private void adjustCropToStackBounds(WindowState w, Rect clipRect) {
         final AppWindowToken appToken = w.mAppToken;
-        if (appToken != null && appToken.mCropWindowsToStack) {
+        // We don't apply the the stack bounds to the window that is being replaced, because it was
+        // living in a different stack. If we suddenly crop it to the new stack bounds, it might
+        // get cut off. We don't want it to happen, so we let it ignore the stack bounds until it
+        // gets removed. The window that will replace it will abide them.
+        if (appToken != null && appToken.mCropWindowsToStack && !appToken.mReplacingWindow) {
             TaskStack stack = w.getTask().mStack;
             stack.getBounds(mTmpStackBounds);
             final int surfaceX = (int) mSurfaceX;
@@ -1713,8 +1722,7 @@
             return false;
         }
         final LayoutParams attrs = mWin.getAttrs();
-        final boolean isHwAccelerated = (attrs.flags &
-                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
+        final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0;
         final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
         if (format == mSurfaceFormat) {
             setOpaqueLocked(!PixelFormat.formatHasAlpha(attrs.format));
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index d86a8af..df0a1c9 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -967,6 +967,7 @@
         }
 
         mService.mPolicy.finishLayoutLw();
+        mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER, displayContent).sendToTarget();
     }
 
     /**
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 982bae0..53f55cd 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -423,7 +423,7 @@
                 try {
                     byteBuffer = recvKernelReply();
                 } catch (ErrnoException e) {
-                    Log.w(TAG, "ErrnoException: ", e);
+                    if (stillRunning()) { Log.w(TAG, "ErrnoException: ", e); }
                     break;
                 }
                 final long whenMs = SystemClock.elapsedRealtime();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index c6c7497..f4ffe2e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -50,11 +50,11 @@
         private final File mDeviceOwnerFile;
         private final File mProfileOwnerBase;
 
-        public OwnersTestable(Context context, File dataDir) {
+        public OwnersTestable(DpmMockContext context) {
             super(context);
-            mLegacyFile = new File(dataDir, LEGACY_FILE);
-            mDeviceOwnerFile = new File(dataDir, DEVICE_OWNER_FILE);
-            mProfileOwnerBase = new File(dataDir, PROFILE_OWNER_FILE_BASE);
+            mLegacyFile = new File(context.dataDir, LEGACY_FILE);
+            mDeviceOwnerFile = new File(context.dataDir, DEVICE_OWNER_FILE);
+            mProfileOwnerBase = new File(context.dataDir, PROFILE_OWNER_FILE_BASE);
         }
 
         @Override
@@ -90,27 +90,15 @@
 
         public final File dataDir;
 
-        public final File systemUserDataDir;
-        public final File secondUserDataDir;
-
         private MockInjector(DpmMockContext context, File dataDir) {
             super(context);
             this.context = context;
             this.dataDir = dataDir;
-
-            systemUserDataDir = new File(dataDir, "user0");
-            DpmTestUtils.clearDir(dataDir);
-
-            secondUserDataDir = new File(dataDir, "user" + DpmMockContext.CALLER_USER_HANDLE);
-            DpmTestUtils.clearDir(secondUserDataDir);
-
-            when(context.environment.getUserSystemDirectory(
-                    eq(DpmMockContext.CALLER_USER_HANDLE))).thenReturn(secondUserDataDir);
         }
 
         @Override
         Owners newOwners() {
-            return new OwnersTestable(context, dataDir);
+            return new OwnersTestable(context);
         }
 
         @Override
@@ -165,7 +153,7 @@
 
         @Override
         String getDevicePolicyFilePathForSystemUser() {
-            return systemUserDataDir.getAbsolutePath();
+            return context.systemUserDataDir.getAbsolutePath();
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 0072f52..5b23798 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -91,11 +91,13 @@
         admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
         admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class);
 
-        setUpPackageManagerForAdmin(admin1);
-        setUpPackageManagerForAdmin(admin2);
-        setUpPackageManagerForAdmin(admin3);
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+        setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
+        setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
 
-        setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
+        setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
+                DpmMockContext.CALLER_UID);
+
         setUpPackageInfo();
         setUpUserManager();
     }
@@ -105,7 +107,7 @@
      * the actual ResolveInfo for the admin component, but we need to mock PM so it'll return
      * it for user {@link DpmMockContext#CALLER_USER_HANDLE}.
      */
-    private void setUpPackageManagerForAdmin(ComponentName admin) {
+    private void setUpPackageManagerForAdmin(ComponentName admin, int packageUid) {
         final Intent resolveIntent = new Intent();
         resolveIntent.setComponent(admin);
         final List<ResolveInfo> realResolveInfo =
@@ -115,32 +117,36 @@
         assertNotNull(realResolveInfo);
         assertEquals(1, realResolveInfo.size());
 
+        // We need to change AI, so set a clone.
+        realResolveInfo.set(0, DpmTestUtils.cloneParcelable(realResolveInfo.get(0)));
+
         // We need to rewrite the UID in the activity info.
-        realResolveInfo.get(0).activityInfo.applicationInfo.uid = DpmMockContext.CALLER_UID;
+        realResolveInfo.get(0).activityInfo.applicationInfo.uid = packageUid;
 
         doReturn(realResolveInfo).when(mContext.packageManager).queryBroadcastReceivers(
                 MockUtils.checkIntentComponent(admin),
                 eq(PackageManager.GET_META_DATA
                         | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
-                eq(DpmMockContext.CALLER_USER_HANDLE)
-        );
+                eq(UserHandle.getUserId(packageUid)));
     }
 
     /**
      * Set up a mock result for {@link IPackageManager#getApplicationInfo} for user
      * {@link DpmMockContext#CALLER_USER_HANDLE}.
      */
-    private void setUpApplicationInfo(int enabledSetting) throws Exception {
-        final ApplicationInfo ai = mRealTestContext.getPackageManager().getApplicationInfo(
-                admin1.getPackageName(),
-                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
+    private void setUpApplicationInfo(int enabledSetting, int packageUid) throws Exception {
+        final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
+                mRealTestContext.getPackageManager().getApplicationInfo(
+                        admin1.getPackageName(),
+                        PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
 
         ai.enabledSetting = enabledSetting;
+        ai.uid = packageUid;
 
         doReturn(ai).when(mContext.ipackageManager).getApplicationInfo(
                 eq(admin1.getPackageName()),
                 eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
-                eq(DpmMockContext.CALLER_USER_HANDLE));
+                eq(UserHandle.getUserId(packageUid)));
     }
 
     /**
@@ -193,16 +199,8 @@
         }).when(mContext.userManager).getApplicationRestrictions(
                 anyString(), any(UserHandle.class));
 
-        // System user is always running.
-        when(mContext.userManager.isUserRunning(MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)))
-                .thenReturn(true);
-
-        // Set up (default) UserInfo for CALLER_USER_HANDLE.
-        final UserInfo uh = new UserInfo(DpmMockContext.CALLER_USER_HANDLE,
-                "user" + DpmMockContext.CALLER_USER_HANDLE, 0);
-
-        when(mContext.userManager.getUserInfo(eq(DpmMockContext.CALLER_USER_HANDLE)))
-                .thenReturn(uh);
+        // Add the first secondary user.
+        mContext.addUser(DpmMockContext.CALLER_USER_HANDLE, 0);
     }
 
     private void setAsProfileOwner(ComponentName admin) {
@@ -309,7 +307,8 @@
 
         // Next, add one more admin.
         // Before doing so, update the application info, now it's enabled.
-        setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+        setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                DpmMockContext.CALLER_UID);
 
         dpm.setActiveAdmin(admin2, /* replace =*/ false);
 
@@ -354,6 +353,35 @@
         mContext.callerPermissions.remove("android.permission.INTERACT_ACROSS_USERS_FULL");
     }
 
+    public void testSetActiveAdmin_multiUsers() throws Exception {
+
+        final int ANOTHER_USER_ID = 100;
+        final int ANOTHER_ADMIN_UID = UserHandle.getUid(ANOTHER_USER_ID, 20456);
+
+        mMockContext.addUser(ANOTHER_USER_ID, 0); // Add one more user.
+
+        // Set up pacakge manager for the other user.
+        setUpPackageManagerForAdmin(admin2, ANOTHER_ADMIN_UID);
+        setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
+                ANOTHER_ADMIN_UID);
+
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+        mMockContext.binder.callingUid = ANOTHER_ADMIN_UID;
+        dpm.setActiveAdmin(admin2, /* replace =*/ false);
+
+
+        mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        assertTrue(dpm.isAdminActive(admin1));
+        assertFalse(dpm.isAdminActive(admin2));
+
+        mMockContext.binder.callingUid = ANOTHER_ADMIN_UID;
+        assertFalse(dpm.isAdminActive(admin1));
+        assertTrue(dpm.isAdminActive(admin2));
+    }
+
     /**
      * Test for:
      * {@link DevicePolicyManager#setActiveAdmin}
@@ -400,9 +428,11 @@
         // having MANAGE_DEVICE_ADMINS.
         mContext.callerPermissions.clear();
 
+        // Change the caller, and call into DPMS directly with a different user-id.
+
         mContext.binder.callingUid = 1234567;
         try {
-            dpm.removeActiveAdmin(admin1);
+            dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
             fail("Didn't throw SecurityException");
         } catch (SecurityException expected) {
         }
@@ -412,7 +442,7 @@
      * Test for:
      * {@link DevicePolicyManager#removeActiveAdmin}
      */
-    public void testRemoveActiveAdmin_fromDifferentUserWithMINTERACT_ACROSS_USERS_FULL() {
+    public void testRemoveActiveAdmin_fromDifferentUserWithINTERACT_ACROSS_USERS_FULL() {
         mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
 
         // Add admin1.
@@ -424,8 +454,11 @@
 
         // Different user, but should work, because caller has proper permissions.
         mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+        // Change the caller, and call into DPMS directly with a different user-id.
         mContext.binder.callingUid = 1234567;
-        dpm.removeActiveAdmin(admin1);
+
+        dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
 
         assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
 
@@ -498,9 +531,14 @@
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
 
-        // Call from a process on the system user.
+        // In this test, change the caller user to "system".
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
 
+        // Make sure admin1 is installed on system user.
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+        setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
+                DpmMockContext.CALLER_SYSTEM_USER_UID);
+
         // DO needs to be an DA.
         dpm.setActiveAdmin(admin1, /* replace =*/ false);
 
@@ -536,8 +574,6 @@
         // Call from a process on the system user.
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
 
-        // DO needs to be an DA.
-        dpm.setActiveAdmin(admin1, /* replace =*/ false);
         try {
             dpm.setDeviceOwner("a.b.c");
             fail("Didn't throw IllegalArgumentException");
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
index 325bf9f..b80f3bf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
@@ -16,6 +16,7 @@
 package com.android.server.devicepolicy;
 
 import android.app.admin.DevicePolicyManager;
+import android.os.UserHandle;
 
 /**
  * Overrides {@link #DevicePolicyManager} for dependency injection.
@@ -31,6 +32,6 @@
 
     @Override
     public int myUserId() {
-        return DpmMockContext.CALLER_USER_HANDLE;
+        return UserHandle.getUserId(dpms.context.binder.callingUid);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 3b30a37..7b36e88 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -27,6 +27,7 @@
 import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.media.IAudioService;
 import android.os.Bundle;
 import android.os.Handler;
@@ -43,8 +44,10 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 /**
  * Context used throughout DPMS tests.
@@ -58,12 +61,12 @@
     /**
      * UID corresponding to {@link #CALLER_USER_HANDLE}.
      */
-    public static final int CALLER_UID = UserHandle.PER_USER_RANGE * CALLER_USER_HANDLE + 123;
+    public static final int CALLER_UID = UserHandle.getUid(CALLER_USER_HANDLE, 20123);
 
     /**
      * UID used when a caller is on the system user.
      */
-    public static final int CALLER_SYSTEM_USER_UID = 123;
+    public static final int CALLER_SYSTEM_USER_UID = 20321;
 
     /**
      * PID of the caller.
@@ -164,6 +167,9 @@
      */
     public final Context spiedContext;
 
+    public final File dataDir;
+    public final File systemUserDataDir;
+
     public final MockBinder binder;
     public final EnvironmentForMock environment;
     public final SystemPropertiesForMock systemProperties;
@@ -184,8 +190,14 @@
 
     public final List<String> callerPermissions = new ArrayList<>();
 
-    public DpmMockContext(Context context) {
+    private final ArrayList<UserInfo> mUserInfos = new ArrayList<>();
+
+    public DpmMockContext(Context context, File dataDir) {
         realTestContext = context;
+
+        this.dataDir = dataDir;
+        DpmTestUtils.clearDir(dataDir);
+
         binder = new MockBinder();
         environment = mock(EnvironmentForMock.class);
         systemProperties= mock(SystemPropertiesForMock.class);
@@ -205,6 +217,39 @@
         packageManager = spy(context.getPackageManager());
 
         spiedContext = mock(Context.class);
+
+        // Add the system user
+        systemUserDataDir = addUser(UserHandle.USER_SYSTEM, UserInfo.FLAG_PRIMARY);
+
+        // System user is always running.
+        when(userManager.isUserRunning(MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)))
+                .thenReturn(true);
+    }
+
+    public File addUser(int userId, int flags) {
+
+        // Set up (default) UserInfo for CALLER_USER_HANDLE.
+        final UserInfo uh = new UserInfo(userId, "user" + userId, flags);
+        when(userManager.getUserInfo(eq(userId))).thenReturn(uh);
+
+        mUserInfos.add(uh);
+        when(userManager.getUsers()).thenReturn(mUserInfos);
+
+        // Create a data directory.
+        final File dir = new File(dataDir, "user" + userId);
+        DpmTestUtils.clearDir(dir);
+
+        when(environment.getUserSystemDirectory(eq(userId))).thenReturn(dir);
+        return dir;
+    }
+
+    /**
+     * Add multiple users at once.  They'll all have flag 0.
+     */
+    public void addUsers(int... userIds) {
+        for (int userId : userIds) {
+            addUser(userId, 0);
+        }
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 6f9f6ab..63bf125 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -34,10 +34,9 @@
         super.setUp();
 
         mRealTestContext = super.getContext();
-        mMockContext = new DpmMockContext(super.getContext());
 
-        dataDir = new File(mRealTestContext.getCacheDir(), "test-data");
-        DpmTestUtils.clearDir(dataDir);
+        mMockContext = new DpmMockContext(
+                mRealTestContext, new File(mRealTestContext.getCacheDir(), "test-data"));
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
index 44a851a..7506273 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
@@ -17,6 +17,8 @@
 package com.android.server.devicepolicy;
 
 import android.os.FileUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.util.Log;
 import android.util.Printer;
 
@@ -41,6 +43,15 @@
         return list == null ? 0 : list.size();
     }
 
+    public static <T extends Parcelable> T cloneParcelable(T source) {
+        Parcel p = Parcel.obtain();
+        p.writeParcelable(source, 0);
+        p.setDataPosition(0);
+        final T clone = p.readParcelable(DpmTestUtils.class.getClassLoader());
+        p.recycle();
+        return clone;
+    }
+
     public static Printer LOG_PRINTER = new Printer() {
         @Override
         public void println(String x) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index a07d615..4a39614 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -69,22 +69,12 @@
         }
     }
 
-    private void addUsersToUserManager(int... userIds) {
-        final ArrayList<UserInfo> userInfos = new ArrayList<>();
-        for (int userId : userIds) {
-            final UserInfo ui = new UserInfo();
-            ui.id = userId;
-            userInfos.add(ui);
-        }
-        when(getContext().userManager.getUsers()).thenReturn(userInfos);
-    }
-
     public void testUpgrade01() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test01/input.xml"));
@@ -111,7 +101,7 @@
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
@@ -123,11 +113,11 @@
     }
 
     public void testUpgrade02() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test02/input.xml"));
@@ -156,7 +146,7 @@
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertTrue(owners.hasDeviceOwner());
@@ -171,11 +161,11 @@
     }
 
     public void testUpgrade03() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test03/input.xml"));
@@ -212,7 +202,7 @@
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
@@ -235,11 +225,11 @@
     }
 
     public void testUpgrade04() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test04/input.xml"));
@@ -281,7 +271,7 @@
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertTrue(owners.hasDeviceOwner());
@@ -309,11 +299,11 @@
     }
 
     public void testUpgrade05() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test05/input.xml"));
@@ -341,7 +331,7 @@
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
@@ -356,11 +346,11 @@
     }
 
     public void testUpgrade06() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test06/input.xml"));
@@ -387,7 +377,7 @@
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
@@ -401,9 +391,9 @@
     }
 
     public void testRemoveExistingFiles() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
-        final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+        final OwnersTestable owners = new OwnersTestable(getContext());
 
         // First, migrate to create new-style config files.
         createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
index b458d9b..7628c5c 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
@@ -188,7 +188,7 @@
                 // This call should be done while the rendernode's displaylist is produced.
                 // For simplicity of this test we do this before we kick off the draw.
                 mContent.getLocationInSurface(surfaceOrigin);
-                mRenderer.setContentOverdrawProtectionBounds(surfaceOrigin[0], surfaceOrigin[1],
+                mRenderer.setContentDrawBounds(surfaceOrigin[0], surfaceOrigin[1],
                         surfaceOrigin[0] + mContent.getWidth(),
                         surfaceOrigin[1] + mContent.getHeight());
                 // Determine new position for frame.
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
index 3b7bf85..f4b1f2c 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
@@ -401,7 +401,7 @@
                     if (xml.isFile()) {
                         // we need to create a pull parser around the layout XML file, and then
                         // give that to our XmlBlockParser
-                        parser = ParserFactory.create(xml);
+                        parser = ParserFactory.create(xml, true);
                     }
                 }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index 857e6d0..c7b24bc 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -178,7 +178,9 @@
         desiredStyle.mIsItalic = isItalic;
         FontInfo bestFont = null;
         int bestMatch = Integer.MAX_VALUE;
-        for (FontInfo font : mFonts) {
+        //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
+        for (int i = 0, n = mFonts.size(); i < n; i++) {
+            FontInfo font = mFonts.get(i);
             int match = computeMatch(font, desiredStyle);
             if (match < bestMatch) {
                 bestMatch = match;
@@ -415,7 +417,9 @@
         boolean isItalic = fontInfo.mIsItalic;
         // The list is usually just two fonts big. So iterating over all isn't as bad as it looks.
         // It's biggest for roboto where the size is 12.
-        for (FontInfo font : mFonts) {
+        //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
+        for (int i = 0, n = mFonts.size(); i < n; i++) {
+            FontInfo font = mFonts.get(i);
             if (font.mWeight == weight && font.mIsItalic == isItalic) {
                 return false;
             }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 65b65ec..a545283 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -480,8 +480,10 @@
             return;
         }
 
-        delegate.mTextSize = textSize;
-        delegate.updateFontObject();
+        if (delegate.mTextSize != textSize) {
+            delegate.mTextSize = textSize;
+            delegate.updateFontObject();
+        }
     }
 
     @LayoutlibDelegate
@@ -503,8 +505,10 @@
             return;
         }
 
-        delegate.mTextScaleX = scaleX;
-        delegate.updateFontObject();
+        if (delegate.mTextScaleX != scaleX) {
+            delegate.mTextScaleX = scaleX;
+            delegate.updateFontObject();
+        }
     }
 
     @LayoutlibDelegate
@@ -526,8 +530,10 @@
             return;
         }
 
-        delegate.mTextSkewX = skewX;
-        delegate.updateFontObject();
+        if (delegate.mTextSkewX != skewX) {
+            delegate.mTextSkewX = skewX;
+            delegate.updateFontObject();
+        }
     }
 
     @LayoutlibDelegate
@@ -897,9 +903,12 @@
             return 0;
         }
 
-        delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
-        delegate.mNativeTypeface = typeface;
-        delegate.updateFontObject();
+        Typeface_Delegate typefaceDelegate = Typeface_Delegate.getDelegate(typeface);
+        if (delegate.mTypeface != typefaceDelegate || delegate.mNativeTypeface != typeface) {
+            delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
+            delegate.mNativeTypeface = typeface;
+            delegate.updateFontObject();
+        }
         return typeface;
     }
 
@@ -1214,13 +1223,31 @@
         mCap = paint.mCap;
         mJoin = paint.mJoin;
         mTextAlign = paint.mTextAlign;
-        mTypeface = paint.mTypeface;
-        mNativeTypeface = paint.mNativeTypeface;
+
+        boolean needsFontUpdate = false;
+        if (mTypeface != paint.mTypeface || mNativeTypeface != paint.mNativeTypeface) {
+            mTypeface = paint.mTypeface;
+            mNativeTypeface = paint.mNativeTypeface;
+            needsFontUpdate = true;
+        }
+
+        if (mTextSize != paint.mTextSize) {
+            mTextSize = paint.mTextSize;
+            needsFontUpdate = true;
+        }
+
+        if (mTextScaleX != paint.mTextScaleX) {
+            mTextScaleX = paint.mTextScaleX;
+            needsFontUpdate = true;
+        }
+
+        if (mTextSkewX != paint.mTextSkewX) {
+            mTextSkewX = paint.mTextSkewX;
+            needsFontUpdate = true;
+        }
+
         mStrokeWidth = paint.mStrokeWidth;
         mStrokeMiter = paint.mStrokeMiter;
-        mTextSize = paint.mTextSize;
-        mTextScaleX = paint.mTextScaleX;
-        mTextSkewX = paint.mTextSkewX;
         mXfermode = paint.mXfermode;
         mColorFilter = paint.mColorFilter;
         mShader = paint.mShader;
@@ -1228,7 +1255,10 @@
         mMaskFilter = paint.mMaskFilter;
         mRasterizer = paint.mRasterizer;
         mHintingMode = paint.mHintingMode;
-        updateFontObject();
+
+        if (needsFontUpdate) {
+            updateFontObject();
+        }
     }
 
     private void reset() {
@@ -1264,10 +1294,18 @@
             // Get the fonts from the TypeFace object.
             List<Font> fonts = mTypeface.getFonts(mFontVariant);
 
+            if (fonts.isEmpty()) {
+                mFonts = Collections.emptyList();
+                return;
+            }
+
             // create new font objects as well as FontMetrics, based on the current text size
             // and skew info.
-            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
-            for (Font font : fonts) {
+            int nFonts = fonts.size();
+            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(nFonts);
+            //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
+            for (int i = 0; i < nFonts; i++) {
+                Font font = fonts.get(i);
                 if (font == null) {
                     // If the font is null, add null to infoList. When rendering the text, if this
                     // null is reached, a warning will be logged.
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index f1726eb..5db1bde 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -206,7 +206,7 @@
                 File f = new File(value.getValue());
                 if (f.isFile()) {
                     try {
-                        XmlPullParser parser = ParserFactory.create(f);
+                        XmlPullParser parser = ParserFactory.create(f, true);
 
                         BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
                                 parser, bridgeContext, value.isFramework());
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 689e359..b2dc29a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -436,7 +436,7 @@
                 // we need to create a pull parser around the layout XML file, and then
                 // give that to our XmlBlockParser
                 try {
-                    XmlPullParser parser = ParserFactory.create(xml);
+                    XmlPullParser parser = ParserFactory.create(xml, true);
 
                     // set the resource ref to have correct view cookies
                     mBridgeInflater.setResourceReference(resource);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java
new file mode 100644
index 0000000..71e7fd2
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.impl;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.Nullable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A wrapper around XmlPullParser that can peek forward to inspect if the file is a data-binding
+ * layout and some parts need to be stripped.
+ */
+public class LayoutParserWrapper implements XmlPullParser {
+
+    // Data binding constants.
+    private static final String TAG_LAYOUT = "layout";
+    private static final String TAG_DATA = "data";
+    private static final String DEFAULT = "default=";
+
+    private final XmlPullParser mDelegate;
+
+    // Storage for peeked values.
+    private boolean mPeeked;
+    private int mEventType;
+    private int mDepth;
+    private int mNext;
+    private List<Attribute> mAttributes;
+    private String mText;
+    private String mName;
+
+    // Used to end the document before the actual parser ends.
+    private int mFinalDepth = -1;
+    private boolean mEndNow;
+
+    public LayoutParserWrapper(XmlPullParser delegate) {
+        mDelegate = delegate;
+    }
+
+    public LayoutParserWrapper peekTillLayoutStart() throws IOException, XmlPullParserException {
+        final int STATE_LAYOUT_NOT_STARTED = 0;  // <layout> tag not encountered yet.
+        final int STATE_ROOT_NOT_STARTED = 1;    // the main view root not found yet.
+        final int STATE_INSIDE_DATA = 2;         // START_TAG for <data> found, but not END_TAG.
+
+        int state = STATE_LAYOUT_NOT_STARTED;
+        int dataDepth = -1;    // depth of the <data> tag. Should be two.
+        while (true) {
+            int peekNext = peekNext();
+            switch (peekNext) {
+                case START_TAG:
+                    if (state == STATE_LAYOUT_NOT_STARTED) {
+                        if (mName.equals(TAG_LAYOUT)) {
+                            state = STATE_ROOT_NOT_STARTED;
+                        } else {
+                            return this; // no layout tag in the file.
+                        }
+                    } else if (state == STATE_ROOT_NOT_STARTED) {
+                        if (mName.equals(TAG_DATA)) {
+                            state = STATE_INSIDE_DATA;
+                            dataDepth = mDepth;
+                        } else {
+                            mFinalDepth = mDepth;
+                            return this;
+                        }
+                    }
+                    break;
+                case END_TAG:
+                    if (state == STATE_INSIDE_DATA) {
+                        if (mDepth <= dataDepth) {
+                            state = STATE_ROOT_NOT_STARTED;
+                        }
+                    }
+                    break;
+                case END_DOCUMENT:
+                    // No layout start found.
+                    return this;
+            }
+            // consume the peeked tag.
+            next();
+        }
+    }
+
+    private int peekNext() throws IOException, XmlPullParserException {
+        if (mPeeked) {
+            return mNext;
+        }
+        mEventType = mDelegate.getEventType();
+        mNext = mDelegate.next();
+        if (mEventType == START_TAG) {
+            int count = mDelegate.getAttributeCount();
+            mAttributes = count > 0 ? new ArrayList<Attribute>(count) :
+                    Collections.<Attribute>emptyList();
+            for (int i = 0; i < count; i++) {
+                mAttributes.add(new Attribute(mDelegate.getAttributeNamespace(i),
+                        mDelegate.getAttributeName(i), mDelegate.getAttributeValue(i)));
+            }
+        }
+        mDepth = mDelegate.getDepth();
+        mText = mDelegate.getText();
+        mName = mDelegate.getName();
+        mPeeked = true;
+        return mNext;
+    }
+
+    private void reset() {
+        mAttributes = null;
+        mText = null;
+        mName = null;
+        mPeeked = false;
+    }
+
+    @Override
+    public int next() throws XmlPullParserException, IOException {
+        int returnValue;
+        int depth;
+        if (mPeeked) {
+            returnValue = mNext;
+            depth = mDepth;
+            reset();
+        } else if (mEndNow) {
+            return END_DOCUMENT;
+        } else {
+            returnValue = mDelegate.next();
+            depth = getDepth();
+        }
+        if (returnValue == END_TAG && depth <= mFinalDepth) {
+            mEndNow = true;
+        }
+        return returnValue;
+    }
+
+    @Override
+    public int getEventType() throws XmlPullParserException {
+        return mPeeked ? mEventType : mDelegate.getEventType();
+    }
+
+    @Override
+    public int getDepth() {
+        return mPeeked ? mDepth : mDelegate.getDepth();
+    }
+
+    @Override
+    public String getName() {
+        return mPeeked ? mName : mDelegate.getName();
+    }
+
+    @Override
+    public String getText() {
+        return mPeeked ? mText : mDelegate.getText();
+    }
+
+    @Override
+    public String getAttributeValue(@Nullable String namespace, String name) {
+        String returnValue = null;
+        if (mPeeked) {
+            if (mAttributes == null) {
+                if (mEventType != START_TAG) {
+                    throw new IndexOutOfBoundsException("getAttributeValue() called when not at START_TAG.");
+                } else {
+                    return null;
+                }
+            } else {
+                for (Attribute attribute : mAttributes) {
+                    //noinspection StringEquality for nullness check.
+                    if (attribute.name.equals(name) && (attribute.namespace == namespace ||
+                            attribute.namespace != null && attribute.namespace.equals(namespace))) {
+                        returnValue = attribute.value;
+                        break;
+                    }
+                }
+            }
+        } else {
+            returnValue = mDelegate.getAttributeValue(namespace, name);
+        }
+        // Check if the value is bound via data-binding, if yes get the default value.
+        if (returnValue != null && mFinalDepth >= 0 && returnValue.startsWith("@{")) {
+            // TODO: Improve the detection of default keyword.
+            int i = returnValue.lastIndexOf(DEFAULT);
+            return i > 0 ? returnValue.substring(i + DEFAULT.length(), returnValue.length() - 1)
+                    : null;
+        }
+        return returnValue;
+    }
+
+    private static class Attribute {
+        @Nullable
+        public final String namespace;
+        public final String name;
+        public final String value;
+
+        public Attribute(@Nullable String namespace, String name, String value) {
+            this.namespace = namespace;
+            this.name = name;
+            this.value = value;
+        }
+    }
+
+    // Not affected by peeking.
+
+    @Override
+    public void setFeature(String s, boolean b) throws XmlPullParserException {
+        mDelegate.setFeature(s, b);
+    }
+
+    @Override
+    public void setProperty(String s, Object o) throws XmlPullParserException {
+        mDelegate.setProperty(s, o);
+    }
+
+    @Override
+    public void setInput(InputStream inputStream, String s) throws XmlPullParserException {
+        mDelegate.setInput(inputStream, s);
+    }
+
+    @Override
+    public void setInput(Reader reader) throws XmlPullParserException {
+        mDelegate.setInput(reader);
+    }
+
+    @Override
+    public String getInputEncoding() {
+        return mDelegate.getInputEncoding();
+    }
+
+    @Override
+    public String getNamespace(String s) {
+        return mDelegate.getNamespace(s);
+    }
+
+    @Override
+    public String getPositionDescription() {
+        return mDelegate.getPositionDescription();
+    }
+
+    @Override
+    public int getLineNumber() {
+        return mDelegate.getLineNumber();
+    }
+
+    @Override
+    public String getNamespace() {
+        return mDelegate.getNamespace();
+    }
+
+    @Override
+    public int getColumnNumber() {
+        return mDelegate.getColumnNumber();
+    }
+
+    // -- We don't care much about the methods that follow.
+
+    @Override
+    public void require(int i, String s, String s1) throws XmlPullParserException, IOException {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public boolean getFeature(String s) {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public void defineEntityReplacementText(String s, String s1) throws XmlPullParserException {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public Object getProperty(String s) {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public int nextToken() throws XmlPullParserException, IOException {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public int getNamespaceCount(int i) throws XmlPullParserException {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public String getNamespacePrefix(int i) throws XmlPullParserException {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public String getNamespaceUri(int i) throws XmlPullParserException {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public boolean isWhitespace() throws XmlPullParserException {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public char[] getTextCharacters(int[] ints) {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public String getPrefix() {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public boolean isEmptyElementTag() throws XmlPullParserException {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public int getAttributeCount() {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public String getAttributeNamespace(int i) {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public String getAttributeName(int i) {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public String getAttributePrefix(int i) {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public String getAttributeType(int i) {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public boolean isAttributeDefault(int i) {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public String getAttributeValue(int i) {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public String nextText() throws XmlPullParserException, IOException {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+
+    @Override
+    public int nextTag() throws XmlPullParserException, IOException {
+        throw new UnsupportedOperationException("Only few parser methods are supported.");
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
index 6e67f59..e273b2c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
@@ -53,24 +53,35 @@
     @NonNull
     public static XmlPullParser create(@NonNull File f)
             throws XmlPullParserException, FileNotFoundException {
-        InputStream stream = new FileInputStream(f);
-        return create(stream, f.getName(), f.length());
+        return create(f, false);
     }
 
+    public static XmlPullParser create(@NonNull File f, boolean isLayout)
+      throws XmlPullParserException, FileNotFoundException {
+        InputStream stream = new FileInputStream(f);
+        return create(stream, f.getName(), f.length(), isLayout);
+    }
     @NonNull
     public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name)
         throws XmlPullParserException {
-        return create(stream, name, -1);
+        return create(stream, name, -1, false);
     }
 
     @NonNull
     private static XmlPullParser create(@NonNull InputStream stream, @Nullable String name,
-            long size) throws XmlPullParserException {
+            long size, boolean isLayout) throws XmlPullParserException {
         XmlPullParser parser = instantiateParser(name);
 
         stream = readAndClose(stream, name, size);
 
         parser.setInput(stream, ENCODING);
+        if (isLayout) {
+            try {
+                return new LayoutParserWrapper(parser).peekTillLayoutStart();
+            } catch (IOException e) {
+                throw new XmlPullParserException(null, parser, e);
+            }
+        }
         return parser;
     }
 
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java
new file mode 100644
index 0000000..2c33862
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.impl;
+
+import org.junit.Test;
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.StringReader;
+
+import static com.android.SdkConstants.NS_RESOURCES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+
+public class LayoutParserWrapperTest {
+    @Test
+    @SuppressWarnings("StatementWithEmptyBody")  // some for loops need to be empty statements.
+    public void testDataBindingLayout() throws Exception {
+        LayoutParserWrapper parser = getParserFromString(sDataBindingLayout);
+        parser.peekTillLayoutStart();
+        assertEquals("Expected START_TAG", START_TAG, parser.next());
+        assertEquals("RelativeLayout", parser.getName());
+        for (int next = parser.next(); next != START_TAG && next != END_DOCUMENT;
+             next = parser.next());
+        assertEquals("Expected START_TAG", START_TAG, parser.getEventType());
+        assertEquals("TextView", parser.getName());
+        assertEquals("layout_width incorrect for first text view.", "wrap_content",
+                parser.getAttributeValue(NS_RESOURCES, "layout_width"));
+        // Ensure that data-binding part is stripped.
+        assertEquals("Bound attribute android:text incorrect", "World",
+                parser.getAttributeValue(NS_RESOURCES, "text"));
+        assertEquals("resource attribute 'id' for first text view incorrect.", "@+id/first",
+                parser.getAttributeValue(NS_RESOURCES, "id"));
+        for (int next = parser.next();
+             (next != END_TAG || !"RelativeLayout".equals(parser.getName())) && next != END_DOCUMENT;
+             next = parser.next());
+        assertNotSame("Unexpected end of document", END_DOCUMENT, parser.getEventType());
+        assertEquals("Document didn't end when expected.", END_DOCUMENT, parser.next());
+    }
+
+    @Test
+    @SuppressWarnings("StatementWithEmptyBody")
+    public void testNonDataBindingLayout() throws Exception {
+        LayoutParserWrapper parser = getParserFromString(sNonDataBindingLayout);
+        parser.peekTillLayoutStart();
+        assertEquals("Expected START_TAG", START_TAG, parser.next());
+        assertEquals("RelativeLayout", parser.getName());
+        for (int next = parser.next(); next != START_TAG && next != END_DOCUMENT;
+             next = parser.next());
+        assertEquals("Expected START_TAG", START_TAG, parser.getEventType());
+        assertEquals("TextView", parser.getName());
+        assertEquals("layout_width incorrect for first text view.", "wrap_content",
+                parser.getAttributeValue(NS_RESOURCES, "layout_width"));
+        // Ensure that value isn't modified.
+        assertEquals("Bound attribute android:text incorrect", "@{user.firstName,default=World}",
+                parser.getAttributeValue(NS_RESOURCES, "text"));
+        assertEquals("resource attribute 'id' for first text view incorrect.", "@+id/first",
+                parser.getAttributeValue(NS_RESOURCES, "id"));
+        for (int next = parser.next();
+             (next != END_TAG || !"RelativeLayout".equals(parser.getName())) && next != END_DOCUMENT;
+             next = parser.next());
+        assertNotSame("Unexpected end of document", END_DOCUMENT, parser.getEventType());
+        assertEquals("Document didn't end when expected.", END_DOCUMENT, parser.next());
+    }
+
+    private static LayoutParserWrapper getParserFromString(String layoutContent) throws
+            XmlPullParserException {
+        XmlPullParser parser = new KXmlParser();
+        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+        parser.setInput(new StringReader(layoutContent));
+        return new LayoutParserWrapper(parser);
+    }
+
+    private static final String sDataBindingLayout =
+            //language=XML
+            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
+                    "<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                    "        xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n" +
+                    "        xmlns:tools=\"http://schemas.android.com/tools\"\n" +
+                    "        tools:context=\".MainActivity\"\n" +
+                    "        tools:showIn=\"@layout/activity_main\">\n" +
+                    "\n" +
+                    "    <data>\n" +
+                    "\n" +
+                    "        <variable\n" +
+                    "            name=\"user\"\n" +
+                    "            type=\"com.example.User\" />\n" +
+                    "        <variable\n" +
+                    "            name=\"activity\"\n" +
+                    "            type=\"com.example.MainActivity\" />\n" +
+                    "    </data>\n" +
+                    "\n" +
+                    "    <RelativeLayout\n" +
+                    "        android:layout_width=\"match_parent\"\n" +
+                    "        android:layout_height=\"match_parent\"\n" +
+                    "        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n" +
+                    "        android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" +
+                    "        android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" +
+                    "        android:paddingTop=\"@dimen/activity_vertical_margin\"\n" +
+                    "        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n" +
+                    "    >\n" +
+                    "\n" +
+                    "        <TextView\n" +
+                    "            android:id=\"@+id/first\"\n" +
+                    "            android:layout_width=\"wrap_content\"\n" +
+                    "            android:layout_alignParentStart=\"true\"\n" +
+                    "            android:layout_alignParentLeft=\"true\"\n" +
+                    "            android:layout_height=\"wrap_content\"\n" +
+                    "            android:text=\"@{user.firstName,default=World}\" />\n" +
+                    "\n" +
+                    "        <TextView\n" +
+                    "            android:id=\"@+id/last\"\n" +
+                    "            android:layout_width=\"wrap_content\"\n" +
+                    "            android:layout_height=\"wrap_content\"\n" +
+                    "            android:layout_toEndOf=\"@id/first\"\n" +
+                    "            android:layout_toRightOf=\"@id/first\"\n" +
+                    "            android:text=\"@{user.lastName,default=Hello}\" />\n" +
+                    "\n" +
+                    "        <Button\n" +
+                    "            android:layout_width=\"wrap_content\"\n" +
+                    "            android:layout_height=\"wrap_content\"\n" +
+                    "            android:layout_below=\"@id/last\"\n" +
+                    "            android:text=\"Submit\"\n" +
+                    "            android:onClick=\"@{activity.onClick}\"/>\n" +
+                    "    </RelativeLayout>\n" +
+                    "</layout>";
+
+    private static final String sNonDataBindingLayout =
+            //language=XML
+            "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                    "    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n" +
+                    "    android:layout_width=\"match_parent\"\n" +
+                    "    android:layout_height=\"match_parent\"\n" +
+                    "    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n" +
+                    "    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" +
+                    "    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" +
+                    "    android:paddingTop=\"@dimen/activity_vertical_margin\"\n" +
+                    "    app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n" +
+                    ">\n" +
+                    "\n" +
+                    "    <TextView\n" +
+                    "        android:id=\"@+id/first\"\n" +
+                    "        android:layout_width=\"wrap_content\"\n" +
+                    "        android:layout_alignParentStart=\"true\"\n" +
+                    "        android:layout_alignParentLeft=\"true\"\n" +
+                    "        android:layout_height=\"wrap_content\"\n" +
+                    "        android:text=\"@{user.firstName,default=World}\" />\n" +
+                    "\n" +
+                    "    <TextView\n" +
+                    "        android:id=\"@+id/last\"\n" +
+                    "        android:layout_width=\"wrap_content\"\n" +
+                    "        android:layout_height=\"wrap_content\"\n" +
+                    "        android:layout_toEndOf=\"@id/first\"\n" +
+                    "        android:layout_toRightOf=\"@id/first\"\n" +
+                    "        android:text=\"@{user.lastName,default=Hello}\" />\n" +
+                    "\n" +
+                    "    <Button\n" +
+                    "        android:layout_width=\"wrap_content\"\n" +
+                    "        android:layout_height=\"wrap_content\"\n" +
+                    "        android:layout_below=\"@id/last\"\n" +
+                    "        android:text=\"Submit\"\n" +
+                    "        android:onClick=\"@{activity.onClick}\"/>\n" +
+                    "</RelativeLayout>";
+}