Merge "Add UCE Service Down protected broadcast" into nyc-mr1-dev
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index 224823e..1dee925 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -1178,6 +1178,15 @@
         }
 
         @Override
+        public void setProperty(Property property) {
+            if (property instanceof IntProperty) {
+                mIntProperty = (IntProperty) property;
+            } else {
+                super.setProperty(property);
+            }
+        }
+
+        @Override
         public void setIntValues(int... values) {
             super.setIntValues(values);
             mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
@@ -1316,6 +1325,15 @@
         }
 
         @Override
+        public void setProperty(Property property) {
+            if (property instanceof FloatProperty) {
+                mFloatProperty = (FloatProperty) property;
+            } else {
+                super.setProperty(property);
+            }
+        }
+
+        @Override
         public void setFloatValues(float... values) {
             super.setFloatValues(values);
             mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
@@ -1516,7 +1534,7 @@
                     }
                     propertyMap.put(mPropertyName, mJniSetter);
                 }
-           }
+            }
         }
     }
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index cb17be3..ec8b288 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -57,6 +57,7 @@
 import android.media.AudioManager;
 import android.media.session.MediaController;
 import android.net.Uri;
+import android.os.BadParcelableException;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -5023,13 +5024,18 @@
     @Nullable
     public Uri getReferrer() {
         Intent intent = getIntent();
-        Uri referrer = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
-        if (referrer != null) {
-            return referrer;
-        }
-        String referrerName = intent.getStringExtra(Intent.EXTRA_REFERRER_NAME);
-        if (referrerName != null) {
-            return Uri.parse(referrerName);
+        try {
+            Uri referrer = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
+            if (referrer != null) {
+                return referrer;
+            }
+            String referrerName = intent.getStringExtra(Intent.EXTRA_REFERRER_NAME);
+            if (referrerName != null) {
+                return Uri.parse(referrerName);
+            }
+        } catch (BadParcelableException e) {
+            Log.w(TAG, "Cannot read referrer from intent;"
+                    + " intent extras contain unknown custom Parcelable objects");
         }
         if (mReferrer != null) {
             return new Uri.Builder().scheme("android-app").authority(mReferrer).build();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2c2f6c1..0728bdf 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
@@ -4578,20 +4580,37 @@
     }
 
     /**
+     * Creates a new Configuration only if override would modify base. Otherwise returns base.
+     * @param base The base configuration.
+     * @param override The update to apply to the base configuration. Can be null.
+     * @return A Configuration representing base with override applied.
+     */
+    private static Configuration createNewConfigAndUpdateIfNotNull(@NonNull Configuration base,
+            @Nullable Configuration override) {
+        if (override == null) {
+            return base;
+        }
+        Configuration newConfig = new Configuration(base);
+        newConfig.updateFrom(override);
+        return newConfig;
+    }
+
+    /**
      * Decides whether to update an Activity's configuration and whether to tell the
      * Activity/Component about it.
      * @param cb The component callback to notify of configuration change.
      * @param activityToken The Activity binder token for which this configuration change happened.
      *                      If the change is global, this is null.
      * @param newConfig The new configuration.
-     * @param overrideConfig The override config that differentiates the Activity's configuration
+     * @param amOverrideConfig The override config that differentiates the Activity's configuration
      *                       from the base global configuration.
+     *                       This is supplied by ActivityManager.
      * @param reportToActivity Notify the Activity of the change.
      */
     private void performConfigurationChanged(ComponentCallbacks2 cb,
                                              IBinder activityToken,
                                              Configuration newConfig,
-                                             Configuration overrideConfig,
+                                             Configuration amOverrideConfig,
                                              boolean reportToActivity) {
         // Only for Activity objects, check that they actually call up to their
         // superclass implementation.  ComponentCallbacks2 is an interface, so
@@ -4605,7 +4624,6 @@
         if ((activity == null) || (activity.mCurrentConfig == null)) {
             shouldChangeConfig = true;
         } else {
-
             // If the new config is the same as the config this Activity
             // is already running with then don't bother calling
             // onConfigurationChanged
@@ -4615,34 +4633,36 @@
             }
         }
 
-        if (DEBUG_CONFIGURATION) {
-            Slog.v(TAG, "Config callback " + cb + ": shouldChangeConfig=" + shouldChangeConfig);
-        }
-
         if (shouldChangeConfig) {
+            // Propagate the configuration change to the Activity and ResourcesManager.
+
+            // ContextThemeWrappers may override the configuration for that context.
+            // We must check and apply any overrides defined.
+            Configuration contextThemeWrapperOverrideConfig = null;
+            if (cb instanceof ContextThemeWrapper) {
+                final ContextThemeWrapper contextThemeWrapper = (ContextThemeWrapper) cb;
+                contextThemeWrapperOverrideConfig = contextThemeWrapper.getOverrideConfiguration();
+            }
+
+            // We only update an Activity's configuration if this is not a global
+            // configuration change. This must also be done before the callback,
+            // or else we violate the contract that the new resources are available
+            // in {@link ComponentCallbacks2#onConfigurationChanged(Configuration)}.
             if (activityToken != null) {
-                // We only update an Activity's configuration if this is not a global
-                // configuration change. This must also be done before the callback,
-                // or else we violate the contract that the new resources are available
-                // in {@link ComponentCallbacks2#onConfigurationChanged(Configuration)}.
-                mResourcesManager.updateResourcesForActivity(activityToken, overrideConfig);
+                // Apply the ContextThemeWrapper override if necessary.
+                // NOTE: Make sure the configurations are not modified, as they are treated
+                // as immutable in many places.
+                final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
+                        amOverrideConfig, contextThemeWrapperOverrideConfig);
+                mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig);
             }
 
             if (reportToActivity) {
-                Configuration configToReport = newConfig;
-
-                if (cb instanceof ContextThemeWrapper) {
-                    // ContextThemeWrappers may override the configuration for that context.
-                    // We must check and apply any overrides defined.
-                    ContextThemeWrapper contextThemeWrapper = (ContextThemeWrapper) cb;
-                    final Configuration localOverrideConfig =
-                            contextThemeWrapper.getOverrideConfiguration();
-                    if (localOverrideConfig != null) {
-                        configToReport = new Configuration(newConfig);
-                        configToReport.updateFrom(localOverrideConfig);
-                    }
-                }
-
+                // Apply the ContextThemeWrapper override if necessary.
+                // NOTE: Make sure the configurations are not modified, as they are treated
+                // as immutable in many places.
+                final Configuration configToReport = createNewConfigAndUpdateIfNotNull(
+                        newConfig, contextThemeWrapperOverrideConfig);
                 cb.onConfigurationChanged(configToReport);
             }
 
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index ab4cbcf..0164fcd 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -793,12 +793,12 @@
         return mOwningView != null && mOwningView.mAttachInfo != null;
     }
 
-    public void addAnimator(AnimatedVectorDrawable.VectorDrawableAnimatorRT animatorSet) {
+    public void registerVectorDrawableAnimator(
+            AnimatedVectorDrawable.VectorDrawableAnimatorRT animatorSet) {
         if (mOwningView == null || mOwningView.mAttachInfo == null) {
             throw new IllegalStateException("Cannot start this animator on a detached view!");
         }
-        nAddAnimator(mNativeRenderNode, animatorSet.getAnimatorNativePtr());
-        mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this);
+        mOwningView.mAttachInfo.mViewRootImpl.registerVectorDrawableAnimator(animatorSet);
     }
 
     public void endAllAnimators() {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b4131b4..415e70c 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -51,6 +51,7 @@
 
     private static native void nativeSetLayer(long nativeObject, int zorder);
     private static native void nativeSetPosition(long nativeObject, float x, float y);
+    private static native void nativeSetPositionAppliesWithResize(long nativeObject);
     private static native void nativeSetSize(long nativeObject, int w, int h);
     private static native void nativeSetTransparentRegionHint(long nativeObject, Region region);
     private static native void nativeSetAlpha(long nativeObject, float alpha);
@@ -407,6 +408,16 @@
         nativeSetPosition(mNativeObject, x, y);
     }
 
+    /**
+     * If the size changes in this transaction, position updates specified
+     * in this transaction will not complete until a buffer of the new size
+     * arrives.
+     */
+    public void setPositionAppliesWithResize() {
+        checkNotReleased();
+        nativeSetPositionAppliesWithResize(mNativeObject);
+    }
+
     public void setSize(int w, int h) {
         checkNotReleased();
         nativeSetSize(mNativeObject, w, h);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index e650d95..fcca739 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -23,6 +23,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.AnimatedVectorDrawable;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -881,6 +882,12 @@
         nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
     }
 
+    void registerVectorDrawableAnimator(
+        AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) {
+        nRegisterVectorDrawableAnimator(mRootNode.mNativeRenderNode,
+                animator.getAnimatorNativePtr());
+    }
+
     public void serializeDisplayListTree() {
         nSerializeDisplayListTree(mNativeProxy);
     }
@@ -992,6 +999,7 @@
     private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
     private static native void nDestroy(long nativeProxy, long rootRenderNode);
     private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
+    private static native void nRegisterVectorDrawableAnimator(long rootRenderNode, long animator);
 
     private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4742818..06c8e01 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -42,6 +42,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
@@ -824,6 +825,13 @@
         }
     }
 
+    public void registerVectorDrawableAnimator(
+            AnimatedVectorDrawable.VectorDrawableAnimatorRT animator) {
+        if (mAttachInfo.mHardwareRenderer != null) {
+            mAttachInfo.mHardwareRenderer.registerVectorDrawableAnimator(animator);
+        }
+    }
+
     private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
         mAttachInfo.mHardwareAccelerated = false;
         mAttachInfo.mHardwareAccelerationRequested = false;
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 28281b3c..8addffb0 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -370,6 +370,8 @@
                 systemInsets.bottom);
         final int rightInset = DecorView.getColorViewRightInset(stableInsets.right,
                 systemInsets.right);
+        final int leftInset = DecorView.getColorViewLeftInset(stableInsets.left,
+                systemInsets.left);
         if (mStatusBarColor != null) {
             mStatusBarColor.setBounds(0, 0, left + width, topInset);
             mStatusBarColor.draw(canvas);
@@ -379,9 +381,11 @@
         // don't want the navigation bar background be moving around when resizing in docked mode.
         // However, we need it for the transitions into/out of docked mode.
         if (mNavigationBarColor != null && fullscreen) {
-            final int size = DecorView.getNavBarSize(bottomInset, rightInset);
+            final int size = DecorView.getNavBarSize(bottomInset, rightInset, leftInset);
             if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) {
                 mNavigationBarColor.setBounds(width - size, 0, width, height);
+            } else if (DecorView.isNavBarToLeftEdge(bottomInset, rightInset)) {
+                mNavigationBarColor.setBounds(0, 0, size, height);
             } else {
                 mNavigationBarColor.setBounds(0, height - size, width, height);
             }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 7e38d9b..1099ef7 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -97,7 +97,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -162,13 +161,13 @@
 
     private final ColorViewState mStatusColorViewState = new ColorViewState(
             SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
-            Gravity.TOP, Gravity.LEFT,
+            Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
             Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
             com.android.internal.R.id.statusBarBackground,
             FLAG_FULLSCREEN);
     private final ColorViewState mNavigationColorViewState = new ColorViewState(
             SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
-            Gravity.BOTTOM, Gravity.RIGHT,
+            Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
             Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
             com.android.internal.R.id.navigationBarBackground,
             0 /* hideWindowFlag */);
@@ -184,9 +183,11 @@
     private int mLastTopInset = 0;
     private int mLastBottomInset = 0;
     private int mLastRightInset = 0;
+    private int mLastLeftInset = 0;
     private boolean mLastHasTopStableInset = false;
     private boolean mLastHasBottomStableInset = false;
     private boolean mLastHasRightStableInset = false;
+    private boolean mLastHasLeftStableInset = false;
     private int mLastWindowFlags = 0;
     private boolean mLastShouldAlwaysConsumeNavBar = false;
 
@@ -991,12 +992,21 @@
         return Math.min(stableRight, systemRight);
     }
 
+    static int getColorViewLeftInset(int stableLeft, int systemLeft) {
+        return Math.min(stableLeft, systemLeft);
+    }
+
     static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
         return bottomInset == 0 && rightInset > 0;
     }
 
-    static int getNavBarSize(int bottomInset, int rightInset) {
-        return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset : bottomInset;
+    static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
+        return bottomInset == 0 && leftInset > 0;
+    }
+
+    static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
+        return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
+                : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
     }
 
     WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
@@ -1016,6 +1026,8 @@
                         insets.getSystemWindowInsetBottom());
                 mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
                         insets.getSystemWindowInsetRight());
+                mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
+                        insets.getSystemWindowInsetLeft());
 
                 // Don't animate if the presence of stable insets has changed, because that
                 // indicates that the window was either just added and received them for the
@@ -1031,21 +1043,32 @@
                 boolean hasRightStableInset = insets.getStableInsetRight() != 0;
                 disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
                 mLastHasRightStableInset = hasRightStableInset;
+
+                boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
+                disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
+                mLastHasLeftStableInset = hasLeftStableInset;
+
                 mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();
             }
 
             boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
-            int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset);
+            boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
+            int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
             updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
-                    mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge,
-                    0 /* rightInset */, animate && !disallowAnimate, false /* force */);
+                    mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge || navBarToLeftEdge,
+                    navBarToLeftEdge,
+                    0 /* sideInset */, animate && !disallowAnimate, false /* force */);
 
             boolean statusBarNeedsRightInset = navBarToRightEdge
                     && mNavigationColorViewState.present;
-            int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
+            boolean statusBarNeedsLeftInset = navBarToLeftEdge
+                    && mNavigationColorViewState.present;
+            int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
+                    : statusBarNeedsLeftInset ? mLastLeftInset : 0;
             updateColorViewInt(mStatusColorViewState, sysUiVisibility,
                     calculateStatusBarColor(), mLastTopInset,
-                    false /* matchVertical */, statusBarRightInset, animate && !disallowAnimate,
+                    false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
+                    animate && !disallowAnimate,
                     mForceWindowDrawsStatusBarBackground);
         }
 
@@ -1070,15 +1093,17 @@
         int consumedTop = consumingStatusBar ? mLastTopInset : 0;
         int consumedRight = consumingNavBar ? mLastRightInset : 0;
         int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
+        int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
 
         if (mContentRoot != null
                 && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
             MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
             if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
-                    || lp.bottomMargin != consumedBottom) {
+                    || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
                 lp.topMargin = consumedTop;
                 lp.rightMargin = consumedRight;
                 lp.bottomMargin = consumedBottom;
+                lp.leftMargin = consumedLeft;
                 mContentRoot.setLayoutParams(lp);
 
                 if (insets == null) {
@@ -1089,7 +1114,7 @@
             }
             if (insets != null) {
                 insets = insets.replaceSystemWindowInsets(
-                        insets.getSystemWindowInsetLeft(),
+                        insets.getSystemWindowInsetLeft() - consumedLeft,
                         insets.getSystemWindowInsetTop() - consumedTop,
                         insets.getSystemWindowInsetRight() - consumedRight,
                         insets.getSystemWindowInsetBottom() - consumedBottom);
@@ -1126,11 +1151,12 @@
      * @param size the current size in the non-parent-matching dimension.
      * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
      *                    horizontal edge,
-     * @param rightMargin rightMargin for the color view.
+     * @param sideMargin sideMargin for the color view.
      * @param animate if true, the change will be animated.
      */
     private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
-            int size, boolean verticalBar, int rightMargin, boolean animate, boolean force) {
+            int size, boolean verticalBar, boolean seascape, int sideMargin,
+            boolean animate, boolean force) {
         state.present = (sysUiVis & state.systemUiHideFlag) == 0
                 && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
                 && ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
@@ -1145,7 +1171,9 @@
 
         int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
         int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
-        int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity;
+        int resolvedGravity = verticalBar
+                ? (seascape ? state.seascapeGravity : state.horizontalGravity)
+                : state.verticalGravity;
 
         if (view == null) {
             if (showView) {
@@ -1159,7 +1187,11 @@
 
                 LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
                         resolvedGravity);
-                lp.rightMargin = rightMargin;
+                if (seascape) {
+                    lp.leftMargin = sideMargin;
+                } else {
+                    lp.rightMargin = sideMargin;
+                }
                 addView(view, lp);
                 updateColorViewTranslations();
             }
@@ -1168,12 +1200,16 @@
             visibilityChanged = state.targetVisibility != vis;
             state.targetVisibility = vis;
             LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            int rightMargin = seascape ? 0 : sideMargin;
+            int leftMargin = seascape ? sideMargin : 0;
             if (lp.height != resolvedHeight || lp.width != resolvedWidth
-                    || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
+                    || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
+                    || lp.leftMargin != leftMargin) {
                 lp.height = resolvedHeight;
                 lp.width = resolvedWidth;
                 lp.gravity = resolvedGravity;
                 lp.rightMargin = rightMargin;
+                lp.leftMargin = leftMargin;
                 view.setLayoutParams(lp);
             }
             if (showView) {
@@ -2210,17 +2246,19 @@
         final int translucentFlag;
         final int verticalGravity;
         final int horizontalGravity;
+        final int seascapeGravity;
         final String transitionName;
         final int hideWindowFlag;
 
         ColorViewState(int systemUiHideFlag,
                 int translucentFlag, int verticalGravity, int horizontalGravity,
-                String transitionName, int id, int hideWindowFlag) {
+                int seascapeGravity, String transitionName, int id, int hideWindowFlag) {
             this.id = id;
             this.systemUiHideFlag = systemUiHideFlag;
             this.translucentFlag = translucentFlag;
             this.verticalGravity = verticalGravity;
             this.horizontalGravity = horizontalGravity;
+            this.seascapeGravity = seascapeGravity;
             this.transitionName = transitionName;
             this.hideWindowFlag = hideWindowFlag;
         }
diff --git a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
index 4b2a72d..0ba88e6 100644
--- a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
@@ -95,6 +95,12 @@
     return reinterpret_cast<jlong>(animatorSet);
 }
 
+static void setVectorDrawableTarget(JNIEnv*, jobject,jlong animatorPtr, jlong vectorDrawablePtr) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(vectorDrawablePtr);
+    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorPtr);
+    set->setVectorDrawable(tree);
+}
+
 static jlong createGroupPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId,
         jfloat startValue, jfloat endValue) {
     VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(nativePtr);
@@ -168,6 +174,7 @@
 
 static const JNINativeMethod gMethods[] = {
     {"nCreateAnimatorSet", "()J", (void*)createAnimatorSet},
+    {"nSetVectorDrawableTarget", "(JJ)V", (void*)setVectorDrawableTarget},
     {"nAddAnimator", "(JJJJJI)V", (void*)addAnimator},
     {"nCreateGroupPropertyHolder", "!(JIFF)J", (void*)createGroupPropertyHolder},
     {"nCreatePathDataPropertyHolder", "!(JJJ)J", (void*)createPathDataPropertyHolder},
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a9ed9dc..ff75677 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -248,6 +248,15 @@
     }
 }
 
+static void nativeSetPositionAppliesWithResize(JNIEnv* env, jclass clazz,
+        jlong nativeObject) {
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+    status_t err = ctrl->setPositionAppliesWithResize();
+    if (err < 0 && err != NO_INIT) {
+        doThrowIAE(env);
+    }
+}
+
 static void nativeSetSize(JNIEnv* env, jclass clazz, jlong nativeObject, jint w, jint h) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
     status_t err = ctrl->setSize(w, h);
@@ -658,6 +667,8 @@
             (void*)nativeSetLayer },
     {"nativeSetPosition", "(JFF)V",
             (void*)nativeSetPosition },
+    {"nativeSetPositionAppliesWithResize", "(J)V",
+            (void*)nativeSetPositionAppliesWithResize },
     {"nativeSetSize", "(JII)V",
             (void*)nativeSetSize },
     {"nativeSetTransparentRegionHint", "(JLandroid/graphics/Region;)V",
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 5b4bfe9..212bf57 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -32,6 +32,7 @@
 #include <utils/Looper.h>
 #include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
+#include <utils/Timers.h>
 #include <android_runtime/android_view_Surface.h>
 #include <system/window.h>
 
@@ -44,6 +45,7 @@
 #include <FrameMetricsObserver.h>
 #include <IContextFactory.h>
 #include <JankTracker.h>
+#include <PropertyValuesAnimatorSet.h>
 #include <RenderNode.h>
 #include <renderthread/CanvasContext.h>
 #include <renderthread/RenderProxy.h>
@@ -122,6 +124,31 @@
     std::vector<OnFinishedEvent> mOnFinishedEvents;
 };
 
+class FinishAndInvokeListener : public MessageHandler {
+public:
+    explicit FinishAndInvokeListener(PropertyValuesAnimatorSet* anim)
+            : mAnimator(anim) {
+        mListener = anim->getOneShotListener();
+        mRequestId = anim->getRequestId();
+    }
+
+    virtual void handleMessage(const Message& message) {
+        if (mAnimator->getRequestId() == mRequestId) {
+            // Request Id has not changed, meaning there's no animation lifecyle change since the
+            // message is posted, so go ahead and call finish to make sure the PlayState is properly
+            // updated. This is needed because before the next frame comes in from UI thread to
+            // trigger an animation update, there could be reverse/cancel etc. So we need to update
+            // the playstate in time to ensure all the subsequent events get chained properly.
+            mAnimator->end();
+        }
+        mListener->onAnimationFinished(nullptr);
+    }
+private:
+    sp<PropertyValuesAnimatorSet> mAnimator;
+    sp<AnimationListener> mListener;
+    uint32_t mRequestId;
+};
+
 class RenderingException : public MessageHandler {
 public:
     RenderingException(JavaVM* vm, const std::string& message)
@@ -160,6 +187,15 @@
 
     virtual void prepareTree(TreeInfo& info) override {
         info.errorHandler = this;
+
+        for (auto& anim : mVectorDrawableAnimators) {
+            // Assume that the property change in VD from the animators will not be consumed. Mark
+            // otherwise if the VDs are found in the display list tree. For VDs that are not in
+            // the display list tree, we stop providing animation pulses by 1) removing them from
+            // the animation list, 2) post a delayed message to end them at end time so their
+            // listeners can receive the corresponding callbacks.
+            anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false);
+        }
         // TODO: This is hacky
         info.windowInsetLeft = -stagingProperties().getLeft();
         info.windowInsetTop = -stagingProperties().getTop();
@@ -169,16 +205,46 @@
         info.windowInsetLeft = 0;
         info.windowInsetTop = 0;
         info.errorHandler = nullptr;
+
+        for (auto it = mVectorDrawableAnimators.begin(); it != mVectorDrawableAnimators.end();) {
+            if (!(*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) {
+                // Vector Drawable is not in the display list, we should remove this animator from
+                // the list and post a delayed message to end the animator.
+                detachVectorDrawableAnimator(it->get());
+                it = mVectorDrawableAnimators.erase(it);
+            } else {
+                ++it;
+            }
+        }
+        info.out.hasAnimations |= !mVectorDrawableAnimators.empty();
     }
 
     void sendMessage(const sp<MessageHandler>& handler) {
         mLooper->sendMessage(handler, 0);
     }
 
+    void sendMessageDelayed(const sp<MessageHandler>& handler, nsecs_t delayInMs) {
+        mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0);
+    }
+
     void attachAnimatingNode(RenderNode* animatingNode) {
         mPendingAnimatingRenderNodes.push_back(animatingNode);
     }
 
+    void attachPendingVectorDrawableAnimators() {
+        mVectorDrawableAnimators.insert(mPendingVectorDrawableAnimators.begin(),
+                mPendingVectorDrawableAnimators.end());
+        mPendingVectorDrawableAnimators.clear();
+    }
+
+    void detachAnimators() {
+        // Remove animators from the list and post a delayed message in future to end the animator
+        for (auto& anim : mVectorDrawableAnimators) {
+            detachVectorDrawableAnimator(anim.get());
+        }
+        mVectorDrawableAnimators.clear();
+    }
+
     void doAttachAnimatingNodes(AnimationContext* context) {
         for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) {
             RenderNode* node = mPendingAnimatingRenderNodes[i].get();
@@ -187,17 +253,57 @@
         mPendingAnimatingRenderNodes.clear();
     }
 
+    void runVectorDrawableAnimators(AnimationContext* context) {
+        for (auto it = mVectorDrawableAnimators.begin(); it != mVectorDrawableAnimators.end();) {
+            (*it)->pushStaging(*context);
+            if ((*it)->animate(*context)) {
+                it = mVectorDrawableAnimators.erase(it);
+            } else {
+                ++it;
+            }
+        }
+    }
+
     void destroy() {
         for (auto& renderNode : mPendingAnimatingRenderNodes) {
             renderNode->animators().endAllStagingAnimators();
         }
         mPendingAnimatingRenderNodes.clear();
+        mPendingVectorDrawableAnimators.clear();
+    }
+
+    void addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) {
+        mPendingVectorDrawableAnimators.insert(anim);
     }
 
 private:
     sp<Looper> mLooper;
     JavaVM* mVm;
     std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
+    std::set< sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators;
+    std::set< sp<PropertyValuesAnimatorSet> > mVectorDrawableAnimators;
+    void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) {
+        if (anim->isInfinite() || !anim->isRunning()) {
+            // Do not need to post anything if the animation is infinite (i.e. no meaningful
+            // end listener action), or if the animation has already ended.
+            return;
+        }
+        nsecs_t remainingTimeInMs = anim->getRemainingPlayTime();
+        // Post a delayed onFinished event that is scheduled to be handled when the animator ends.
+        if (anim->getOneShotListener()) {
+            // VectorDrawable's oneshot listener is updated when there are user triggered animation
+            // lifecycle changes, such as start(), end(), etc. By using checking and clearing
+            // one shot listener, we ensure the same end listener event gets posted only once.
+            // Therefore no duplicates. Another benefit of using one shot listener is that no
+            // removal is necessary: the end time of animation will not change unless triggered by
+            // user events, in which case the already posted listener's id will become stale, and
+            // the onFinished callback will then be ignored.
+            sp<FinishAndInvokeListener> message
+                    = new FinishAndInvokeListener(anim);
+            sendMessageDelayed(message, remainingTimeInMs);
+            anim->clearOneShotListener();
+        }
+    }
 };
 
 class AnimationContextBridge : public AnimationContext {
@@ -213,8 +319,16 @@
     virtual void startFrame(TreeInfo::TraversalMode mode) {
         if (mode == TreeInfo::MODE_FULL) {
             mRootNode->doAttachAnimatingNodes(this);
+            mRootNode->attachPendingVectorDrawableAnimators();
         }
         AnimationContext::startFrame(mode);
+        // Run VectorDrawable animators in the beginning of the frame instead of during prepareTree,
+        // because one VD can be in multiple render nodes' display list. So it's more simple to
+        // run them all at once before prepareTree than running them or checking whether they have
+        // already ran in each RenderNode. Note that these animators don't damage the RenderNodes.
+        // The damaging is done in prepareTree as needed after checking whether a VD has been
+        // modified.
+        mRootNode->runVectorDrawableAnimators(this);
     }
 
     // Runs any animations still left in mCurrentFrameAnimations
@@ -223,6 +337,10 @@
         postOnFinishedEvents();
     }
 
+    virtual void detachAnimators() override {
+        mRootNode->detachAnimators();
+    }
+
     virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) {
         OnFinishedEvent event(animator, listener);
         mOnFinishedEvents.push_back(event);
@@ -230,6 +348,7 @@
 
     virtual void destroy() {
         AnimationContext::destroy();
+        detachAnimators();
         postOnFinishedEvents();
     }
 
@@ -528,6 +647,13 @@
     rootRenderNode->attachAnimatingNode(animatingNode);
 }
 
+static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv* env, jobject clazz,
+        jlong rootNodePtr, jlong animatorPtr) {
+    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
+    PropertyValuesAnimatorSet* animator = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorPtr);
+    rootRenderNode->addVectorDrawableAnimator(animator);
+}
+
 static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz,
         jlong functorPtr, jboolean waitForCompletion) {
     Functor* functor = reinterpret_cast<Functor*>(functorPtr);
@@ -739,6 +865,7 @@
     { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
     { "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy },
     { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
+    { "nRegisterVectorDrawableAnimator", "(JJ)V", (void*) android_view_ThreadedRenderer_registerVectorDrawableAnimator },
     { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
     { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
     { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer },
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 6762bea..44d95d2 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -46,6 +46,7 @@
 import android.util.Log;
 import android.util.LongArray;
 import android.util.PathParser;
+import android.util.Property;
 import android.util.TimeUtils;
 import android.view.Choreographer;
 import android.view.DisplayListCanvas;
@@ -157,7 +158,7 @@
     private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
 
     /** Local, mutable animator set. */
-    private VectorDrawableAnimator mAnimatorSet = new VectorDrawableAnimatorUI(this);
+    private VectorDrawableAnimator mAnimatorSet;
 
     /**
      * The resources against which this drawable was created. Used to attempt
@@ -182,6 +183,7 @@
 
     private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res) {
         mAnimatedVectorState = new AnimatedVectorDrawableState(state, mCallback, res);
+        mAnimatorSet = new VectorDrawableAnimatorRT(this);
         mRes = res;
     }
 
@@ -390,9 +392,11 @@
                             R.styleable.AnimatedVectorDrawableTarget_animation, 0);
                     if (animResId != 0) {
                         if (theme != null) {
-                            final Animator objectAnimator = AnimatorInflater.loadAnimator(
+                            // The animator here could be ObjectAnimator or AnimatorSet.
+                            final Animator animator = AnimatorInflater.loadAnimator(
                                     res, theme, animResId, pathErrorScale);
-                            state.addTargetAnimator(target, objectAnimator);
+                            updateAnimatorProperty(animator, target, state.mVectorDrawable);
+                            state.addTargetAnimator(target, animator);
                         } else {
                             // The animation may be theme-dependent. As a
                             // workaround until Animator has full support for
@@ -414,6 +418,36 @@
         mRes = state.mPendingAnims == null ? null : res;
     }
 
+    private static void updateAnimatorProperty(Animator animator, String targetName,
+            VectorDrawable vectorDrawable) {
+        if (animator instanceof ObjectAnimator) {
+            // Change the property of the Animator from using reflection based on the property
+            // name to a Property object that wraps the setter and getter for modifying that
+            // specific property for a given object. By replacing the reflection with a direct call,
+            // we can largely reduce the time it takes for a animator to modify a VD property.
+            PropertyValuesHolder[] holders = ((ObjectAnimator) animator).getValues();
+            for (int i = 0; i < holders.length; i++) {
+                PropertyValuesHolder pvh = holders[i];
+                String propertyName = pvh.getPropertyName();
+                Object targetNameObj = vectorDrawable.getTargetByName(targetName);
+                Property property = null;
+                if (targetNameObj instanceof VectorDrawable.VObject) {
+                    property = ((VectorDrawable.VObject) targetNameObj).getProperty(propertyName);
+                } else if (targetNameObj instanceof VectorDrawable.VectorDrawableState) {
+                    property = ((VectorDrawable.VectorDrawableState) targetNameObj)
+                            .getProperty(propertyName);
+                }
+                if (property != null) {
+                    pvh.setProperty(property);
+                }
+            }
+        } else if (animator instanceof AnimatorSet) {
+            for (Animator anim : ((AnimatorSet) animator).getChildAnimations()) {
+                updateAnimatorProperty(anim, targetName, vectorDrawable);
+            }
+        }
+    }
+
     /**
      * Force to animate on UI thread.
      * @hide
@@ -616,8 +650,9 @@
 
                 for (int i = 0, count = pendingAnims.size(); i < count; i++) {
                     final PendingAnimator pendingAnimator = pendingAnims.get(i);
-                    final Animator objectAnimator = pendingAnimator.newInstance(res, t);
-                    addTargetAnimator(pendingAnimator.target, objectAnimator);
+                    final Animator animator = pendingAnimator.newInstance(res, t);
+                    updateAnimatorProperty(animator, pendingAnimator.target, mVectorDrawable);
+                    addTargetAnimator(pendingAnimator.target, animator);
                 }
             }
         }
@@ -999,7 +1034,7 @@
         private WeakReference<RenderNode> mLastSeenTarget = null;
         private int mLastListenerId = 0;
         private final IntArray mPendingAnimationActions = new IntArray();
-        private final Drawable mDrawable;
+        private final AnimatedVectorDrawable mDrawable;
 
         VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
             mDrawable = drawable;
@@ -1018,6 +1053,9 @@
             }
             mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
             parseAnimatorSet(set, 0);
+            long vectorDrawableTreePtr = mDrawable.mAnimatedVectorState.mVectorDrawable
+                    .getNativeTree();
+            nSetVectorDrawableTarget(mSetPtr, vectorDrawableTreePtr);
             mInitialized = true;
             mIsInfinite = set.getTotalDuration() == Animator.DURATION_INFINITE;
 
@@ -1254,16 +1292,19 @@
          * to the last seen RenderNode target and start right away.
          */
         protected void recordLastSeenTarget(DisplayListCanvas canvas) {
-            mLastSeenTarget = new WeakReference<RenderNode>(
-                    RenderNodeAnimatorSetHelper.getTarget(canvas));
-            if (mPendingAnimationActions.size() > 0 && useLastSeenTarget()) {
-                if (DBG_ANIMATION_VECTOR_DRAWABLE) {
-                    Log.d(LOGTAG, "Target is set in the next frame");
+            final RenderNode node = RenderNodeAnimatorSetHelper.getTarget(canvas);
+            mLastSeenTarget = new WeakReference<RenderNode>(node);
+            // Add the animator to the list of animators on every draw
+            if (mInitialized || mPendingAnimationActions.size() > 0) {
+                if (useTarget(node)) {
+                    if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+                        Log.d(LOGTAG, "Target is set in the next frame");
+                    }
+                    for (int i = 0; i < mPendingAnimationActions.size(); i++) {
+                        handlePendingAction(mPendingAnimationActions.get(i));
+                    }
+                    mPendingAnimationActions.clear();
                 }
-                for (int i = 0; i < mPendingAnimationActions.size(); i++) {
-                    handlePendingAction(mPendingAnimationActions.get(i));
-                }
-                mPendingAnimationActions.clear();
             }
         }
 
@@ -1285,10 +1326,15 @@
         private boolean useLastSeenTarget() {
             if (mLastSeenTarget != null) {
                 final RenderNode target = mLastSeenTarget.get();
-                if (target != null && target.isAttached()) {
-                    target.addAnimator(this);
-                    return true;
-                }
+                return useTarget(target);
+            }
+            return false;
+        }
+
+        private boolean useTarget(RenderNode target) {
+            if (target != null && target.isAttached()) {
+                target.registerVectorDrawableAnimator(this);
+                return true;
             }
             return false;
         }
@@ -1483,6 +1529,7 @@
     }
 
     private static native long nCreateAnimatorSet();
+    private static native void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr);
     private static native void nAddAnimator(long setPtr, long propertyValuesHolder,
              long nativeInterpolator, long startDelay, long duration, int repeatCount);
 
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index f5592fa..a8c8737 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -34,9 +34,12 @@
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.FloatProperty;
+import android.util.IntProperty;
 import android.util.LayoutDirection;
 import android.util.Log;
 import android.util.PathParser;
+import android.util.Property;
 import android.util.Xml;
 
 import com.android.internal.R;
@@ -758,6 +761,13 @@
         return mVectorState.mAutoMirrored;
     }
 
+    /**
+     * @hide
+     */
+    public long getNativeTree() {
+        return mVectorState.getNativeRenderer();
+    }
+
     static class VectorDrawableState extends ConstantState {
         // Variables below need to be copied (deep copy if applicable) for mutation.
         int[] mThemeAttrs;
@@ -790,6 +800,26 @@
         int mLastSWCachePixelCount = 0;
         int mLastHWCachePixelCount = 0;
 
+        final static Property<VectorDrawableState, Float> ALPHA =
+                new FloatProperty<VectorDrawableState>("alpha") {
+                    @Override
+                    public void setValue(VectorDrawableState state, float value) {
+                        state.setAlpha(value);
+                    }
+
+                    @Override
+                    public Float get(VectorDrawableState state) {
+                        return state.getAlpha();
+                    }
+                };
+
+        Property getProperty(String propertyName) {
+            if (ALPHA.getName().equals(propertyName)) {
+                return ALPHA;
+            }
+            return null;
+        }
+
         // This tracks the total native allocation for all the nodes.
         private int mAllocationOfAllNodes = 0;
 
@@ -967,7 +997,7 @@
     }
 
     static class VGroup extends VObject {
-        private static final int ROTATE_INDEX = 0;
+        private static final int ROTATION_INDEX = 0;
         private static final int PIVOT_X_INDEX = 1;
         private static final int PIVOT_Y_INDEX = 2;
         private static final int SCALE_X_INDEX = 3;
@@ -978,7 +1008,7 @@
 
         private static final int NATIVE_ALLOCATION_SIZE = 100;
 
-        private static final HashMap<String, Integer> sPropertyMap =
+        private static final HashMap<String, Integer> sPropertyIndexMap =
                 new HashMap<String, Integer>() {
                     {
                         put("translateX", TRANSLATE_X_INDEX);
@@ -987,19 +1017,123 @@
                         put("scaleY", SCALE_Y_INDEX);
                         put("pivotX", PIVOT_X_INDEX);
                         put("pivotY", PIVOT_Y_INDEX);
-                        put("rotation", ROTATE_INDEX);
+                        put("rotation", ROTATION_INDEX);
                     }
                 };
 
         static int getPropertyIndex(String propertyName) {
-            if (sPropertyMap.containsKey(propertyName)) {
-                return sPropertyMap.get(propertyName);
+            if (sPropertyIndexMap.containsKey(propertyName)) {
+                return sPropertyIndexMap.get(propertyName);
             } else {
                 // property not found
                 return -1;
             }
         }
 
+        // Below are the Properties that wrap the setters to avoid reflection overhead in animations
+        private static final Property<VGroup, Float> TRANSLATE_X =
+                new FloatProperty<VGroup> ("translateX") {
+                    @Override
+                    public void setValue(VGroup object, float value) {
+                        object.setTranslateX(value);
+                    }
+
+                    @Override
+                    public Float get(VGroup object) {
+                        return object.getTranslateX();
+                    }
+                };
+
+        private static final Property<VGroup, Float> TRANSLATE_Y =
+                new FloatProperty<VGroup> ("translateY") {
+                    @Override
+                    public void setValue(VGroup object, float value) {
+                        object.setTranslateY(value);
+                    }
+
+                    @Override
+                    public Float get(VGroup object) {
+                        return object.getTranslateY();
+                    }
+        };
+
+        private static final Property<VGroup, Float> SCALE_X =
+                new FloatProperty<VGroup> ("scaleX") {
+                    @Override
+                    public void setValue(VGroup object, float value) {
+                        object.setScaleX(value);
+                    }
+
+                    @Override
+                    public Float get(VGroup object) {
+                        return object.getScaleX();
+                    }
+                };
+
+        private static final Property<VGroup, Float> SCALE_Y =
+                new FloatProperty<VGroup> ("scaleY") {
+                    @Override
+                    public void setValue(VGroup object, float value) {
+                        object.setScaleY(value);
+                    }
+
+                    @Override
+                    public Float get(VGroup object) {
+                        return object.getScaleY();
+                    }
+                };
+
+        private static final Property<VGroup, Float> PIVOT_X =
+                new FloatProperty<VGroup> ("pivotX") {
+                    @Override
+                    public void setValue(VGroup object, float value) {
+                        object.setPivotX(value);
+                    }
+
+                    @Override
+                    public Float get(VGroup object) {
+                        return object.getPivotX();
+                    }
+                };
+
+        private static final Property<VGroup, Float> PIVOT_Y =
+                new FloatProperty<VGroup> ("pivotY") {
+                    @Override
+                    public void setValue(VGroup object, float value) {
+                        object.setPivotY(value);
+                    }
+
+                    @Override
+                    public Float get(VGroup object) {
+                        return object.getPivotY();
+                    }
+                };
+
+        private static final Property<VGroup, Float> ROTATION =
+                new FloatProperty<VGroup> ("rotation") {
+                    @Override
+                    public void setValue(VGroup object, float value) {
+                        object.setRotation(value);
+                    }
+
+                    @Override
+                    public Float get(VGroup object) {
+                        return object.getRotation();
+                    }
+                };
+
+        private static final HashMap<String, Property> sPropertyMap =
+                new HashMap<String, Property>() {
+                    {
+                        put("translateX", TRANSLATE_X);
+                        put("translateY", TRANSLATE_Y);
+                        put("scaleX", SCALE_X);
+                        put("scaleY", SCALE_Y);
+                        put("pivotX", PIVOT_X);
+                        put("pivotY", PIVOT_Y);
+                        put("rotation", ROTATION);
+                    }
+                };
         // Temp array to store transform values obtained from native.
         private float[] mTransform;
         /////////////////////////////////////////////////////
@@ -1055,6 +1189,15 @@
             mNativePtr = nCreateGroup();
         }
 
+        Property getProperty(String propertyName) {
+            if (sPropertyMap.containsKey(propertyName)) {
+                return sPropertyMap.get(propertyName);
+            } else {
+                // property not found
+                return null;
+            }
+        }
+
         public String getGroupName() {
             return mGroupName;
         }
@@ -1102,7 +1245,7 @@
                 throw new RuntimeException("Error: inconsistent property count");
             }
             float rotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation,
-                    mTransform[ROTATE_INDEX]);
+                    mTransform[ROTATION_INDEX]);
             float pivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX,
                     mTransform[PIVOT_X_INDEX]);
             float pivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY,
@@ -1288,6 +1431,27 @@
         String mPathName;
         @Config int mChangingConfigurations;
 
+        private static final Property<VPath, PathParser.PathData> PATH_DATA =
+                new Property<VPath, PathParser.PathData>(PathParser.PathData.class, "pathData") {
+                    @Override
+                    public void set(VPath object, PathParser.PathData data) {
+                        object.setPathData(data);
+                    }
+
+                    @Override
+                    public PathParser.PathData get(VPath object) {
+                        return object.getPathData();
+                    }
+                };
+
+        Property getProperty(String propertyName) {
+            if (PATH_DATA.getName().equals(propertyName)) {
+                return PATH_DATA;
+            }
+            // property not found
+            return null;
+        }
+
         public VPath() {
             // Empty constructor.
         }
@@ -1410,7 +1574,7 @@
 
         private static final int NATIVE_ALLOCATION_SIZE = 264;
         // Property map for animatable attributes.
-        private final static HashMap<String, Integer> sPropertyMap
+        private final static HashMap<String, Integer> sPropertyIndexMap
                 = new HashMap<String, Integer> () {
             {
                 put("strokeWidth", STROKE_WIDTH_INDEX);
@@ -1424,6 +1588,125 @@
             }
         };
 
+        // Below are the Properties that wrap the setters to avoid reflection overhead in animations
+        private static final Property<VFullPath, Float> STROKE_WIDTH =
+                new FloatProperty<VFullPath> ("strokeWidth") {
+                    @Override
+                    public void setValue(VFullPath object, float value) {
+                        object.setStrokeWidth(value);
+                    }
+
+                    @Override
+                    public Float get(VFullPath object) {
+                        return object.getStrokeWidth();
+                    }
+                };
+
+        private static final Property<VFullPath, Integer> STROKE_COLOR =
+                new IntProperty<VFullPath> ("strokeColor") {
+                    @Override
+                    public void setValue(VFullPath object, int value) {
+                        object.setStrokeColor(value);
+                    }
+
+                    @Override
+                    public Integer get(VFullPath object) {
+                        return object.getStrokeColor();
+                    }
+                };
+
+        private static final Property<VFullPath, Float> STROKE_ALPHA =
+                new FloatProperty<VFullPath> ("strokeAlpha") {
+                    @Override
+                    public void setValue(VFullPath object, float value) {
+                        object.setStrokeAlpha(value);
+                    }
+
+                    @Override
+                    public Float get(VFullPath object) {
+                        return object.getStrokeAlpha();
+                    }
+                };
+
+        private static final Property<VFullPath, Integer> FILL_COLOR =
+                new IntProperty<VFullPath>("fillColor") {
+                    @Override
+                    public void setValue(VFullPath object, int value) {
+                        object.setFillColor(value);
+                    }
+
+                    @Override
+                    public Integer get(VFullPath object) {
+                        return object.getFillColor();
+                    }
+                };
+
+        private static final Property<VFullPath, Float> FILL_ALPHA =
+                new FloatProperty<VFullPath> ("fillAlpha") {
+                    @Override
+                    public void setValue(VFullPath object, float value) {
+                        object.setFillAlpha(value);
+                    }
+
+                    @Override
+                    public Float get(VFullPath object) {
+                        return object.getFillAlpha();
+                    }
+                };
+
+        private static final Property<VFullPath, Float> TRIM_PATH_START =
+                new FloatProperty<VFullPath> ("trimPathStart") {
+                    @Override
+                    public void setValue(VFullPath object, float value) {
+                        object.setTrimPathStart(value);
+                    }
+
+                    @Override
+                    public Float get(VFullPath object) {
+                        return object.getTrimPathStart();
+                    }
+                };
+
+        private static final Property<VFullPath, Float> TRIM_PATH_END =
+                new FloatProperty<VFullPath> ("trimPathEnd") {
+                    @Override
+                    public void setValue(VFullPath object, float value) {
+                        object.setTrimPathEnd(value);
+                    }
+
+                    @Override
+                    public Float get(VFullPath object) {
+                        return object.getTrimPathEnd();
+                    }
+                };
+
+        private static final Property<VFullPath, Float> TRIM_PATH_OFFSET =
+                new FloatProperty<VFullPath> ("trimPathOffset") {
+                    @Override
+                    public void setValue(VFullPath object, float value) {
+                        object.setTrimPathOffset(value);
+                    }
+
+                    @Override
+                    public Float get(VFullPath object) {
+                        return object.getTrimPathOffset();
+                    }
+                };
+
+        private final static HashMap<String, Property> sPropertyMap
+                = new HashMap<String, Property> () {
+            {
+                put("strokeWidth", STROKE_WIDTH);
+                put("strokeColor", STROKE_COLOR);
+                put("strokeAlpha", STROKE_ALPHA);
+                put("fillColor", FILL_COLOR);
+                put("fillAlpha", FILL_ALPHA);
+                put("trimPathStart", TRIM_PATH_START);
+                put("trimPathEnd", TRIM_PATH_END);
+                put("trimPathOffset", TRIM_PATH_OFFSET);
+            }
+        };
+
         // Temp array to store property data obtained from native getter.
         private byte[] mPropertyData;
         /////////////////////////////////////////////////////
@@ -1446,11 +1729,24 @@
             mFillColors = copy.mFillColors;
         }
 
+        Property getProperty(String propertyName) {
+            Property p = super.getProperty(propertyName);
+            if (p != null) {
+                return p;
+            }
+            if (sPropertyMap.containsKey(propertyName)) {
+                return sPropertyMap.get(propertyName);
+            } else {
+                // property not found
+                return null;
+            }
+        }
+
         int getPropertyIndex(String propertyName) {
-            if (!sPropertyMap.containsKey(propertyName)) {
+            if (!sPropertyIndexMap.containsKey(propertyName)) {
                 return -1;
             } else {
-                return sPropertyMap.get(propertyName);
+                return sPropertyIndexMap.get(propertyName);
             }
         }
 
@@ -1784,6 +2080,7 @@
         abstract boolean onStateChange(int[] state);
         abstract boolean isStateful();
         abstract int getNativeSize();
+        abstract Property getProperty(String propertyName);
     }
 
     private static native long nCreateTree(long rootGroupPtr);
diff --git a/libs/hwui/AnimationContext.h b/libs/hwui/AnimationContext.h
index 909ed36..801fd87 100644
--- a/libs/hwui/AnimationContext.h
+++ b/libs/hwui/AnimationContext.h
@@ -100,6 +100,8 @@
 
     ANDROID_API virtual void destroy();
 
+    ANDROID_API virtual void detachAnimators() {}
+
 private:
     friend class AnimationHandle;
     void addAnimationHandle(AnimationHandle* handle);
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 4d65782..dc18018 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -274,6 +274,10 @@
     return playTime >= mDuration;
 }
 
+nsecs_t BaseRenderNodeAnimator::getRemainingPlayTime() {
+    return mPlayState == PlayState::Reversing ? mPlayTime : mDuration - mPlayTime;
+}
+
 void BaseRenderNodeAnimator::forceEndNow(AnimationContext& context) {
     if (mPlayState < PlayState::Finished) {
         mPlayState = PlayState::Finished;
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index fdae0f3..9476750 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -62,19 +62,23 @@
     }
     bool mayRunAsync() { return mMayRunAsync; }
     ANDROID_API void start();
-    ANDROID_API void reset();
+    ANDROID_API virtual void reset();
     ANDROID_API void reverse();
     // Terminates the animation at its current progress.
     ANDROID_API void cancel();
 
     // Terminates the animation and skip to the end of the animation.
-    ANDROID_API void end();
+    ANDROID_API virtual void end();
 
     void attach(RenderNode* target);
     virtual void onAttached() {}
     void detach() { mTarget = nullptr; }
-    void pushStaging(AnimationContext& context);
-    bool animate(AnimationContext& context);
+    ANDROID_API void pushStaging(AnimationContext& context);
+    ANDROID_API bool animate(AnimationContext& context);
+
+    // Returns the remaining time in ms for the animation. Note this should only be called during
+    // an animation on RenderThread.
+    ANDROID_API nsecs_t getRemainingPlayTime();
 
     bool isRunning() { return mPlayState == PlayState::Running
             || mPlayState == PlayState::Reversing; }
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index b572bda..28be05c 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -45,7 +45,7 @@
         , regions(stdAllocator)
         , referenceHolders(stdAllocator)
         , functors(stdAllocator)
-        , pushStagingFunctors(stdAllocator)
+        , vectorDrawables(stdAllocator)
         , hasDrawOps(false) {
 }
 
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 5b3227b..ccf71c6 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -69,6 +69,11 @@
 typedef DrawRenderNodeOp NodeOpType;
 #endif
 
+namespace VectorDrawable {
+class Tree;
+};
+typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
+
 /**
  * Holds data used in the playback a tree of DisplayLists.
  */
@@ -110,16 +115,6 @@
     LinearAllocator mReplayAllocator;
 };
 
-/**
- * Functor that can be used for objects with data in both UI thread and RT to keep the data
- * in sync. This functor, when added to DisplayList, will be call during DisplayList sync.
- */
-struct PushStagingFunctor {
-    PushStagingFunctor() {}
-    virtual ~PushStagingFunctor() {}
-    virtual void operator ()() {}
-};
-
 struct FunctorContainer {
     Functor* functor;
     GlFunctorLifecycleListener* listener;
@@ -161,7 +156,7 @@
 
     const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
     const LsaVector<FunctorContainer>& getFunctors() const { return functors; }
-    const LsaVector<PushStagingFunctor*>& getPushStagingFunctors() { return pushStagingFunctors; }
+    const LsaVector<VectorDrawableRoot*>& getVectorDrawables() { return vectorDrawables; }
 
     size_t addChild(NodeOpType* childOp);
 
@@ -203,10 +198,10 @@
     // List of functors
     LsaVector<FunctorContainer> functors;
 
-    // List of functors that need to be notified of pushStaging. Note that this list gets nothing
+    // List of VectorDrawables that need to be notified of pushStaging. Note that this list gets nothing
     // but a callback during sync DisplayList, unlike the list of functors defined above, which
     // gets special treatment exclusive for webview.
-    LsaVector<PushStagingFunctor*> pushStagingFunctors;
+    LsaVector<VectorDrawableRoot*> vectorDrawables;
 
     bool hasDrawOps; // only used if !HWUI_NEW_OPS
 
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index ca968ce..bec66295 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -417,7 +417,7 @@
 
 void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
     mDisplayList->ref(tree);
-    mDisplayList->pushStagingFunctors.push_back(tree->getFunctor());
+    mDisplayList->vectorDrawables.push_back(tree);
     addDrawOp(new (alloc()) DrawVectorDrawableOp(tree, tree->stagingProperties()->getBounds()));
 }
 
diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp
index b29f91f..e416e0c 100644
--- a/libs/hwui/PropertyValuesAnimatorSet.cpp
+++ b/libs/hwui/PropertyValuesAnimatorSet.cpp
@@ -30,6 +30,11 @@
             interpolator, startDelay, duration, repeatCount);
     mAnimators.emplace_back(animator);
     setListener(new PropertyAnimatorSetListener(this));
+
+    // Check whether any child animator is infinite after adding it them to the set.
+    if (repeatCount == -1) {
+        mIsInfinite = true;
+    }
 }
 
 PropertyValuesAnimatorSet::PropertyValuesAnimatorSet()
@@ -78,15 +83,27 @@
 void PropertyValuesAnimatorSet::start(AnimationListener* listener) {
     init();
     mOneShotListener = listener;
+    mRequestId++;
     BaseRenderNodeAnimator::start();
 }
 
 void PropertyValuesAnimatorSet::reverse(AnimationListener* listener) {
     init();
     mOneShotListener = listener;
+    mRequestId++;
     BaseRenderNodeAnimator::reverse();
 }
 
+void PropertyValuesAnimatorSet::reset() {
+    mRequestId++;
+    BaseRenderNodeAnimator::reset();
+}
+
+void PropertyValuesAnimatorSet::end() {
+    mRequestId++;
+    BaseRenderNodeAnimator::end();
+}
+
 void PropertyValuesAnimatorSet::init() {
     if (mInitialized) {
         return;
diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h
index c7ae7c0..49021bc 100644
--- a/libs/hwui/PropertyValuesAnimatorSet.h
+++ b/libs/hwui/PropertyValuesAnimatorSet.h
@@ -43,6 +43,7 @@
     float mLatestFraction = 0.0f;
 };
 
+// TODO: This class should really be named VectorDrawableAnimator
 class ANDROID_API PropertyValuesAnimatorSet : public BaseRenderNodeAnimator {
 public:
     friend class PropertyAnimatorSetListener;
@@ -50,11 +51,19 @@
 
     void start(AnimationListener* listener);
     void reverse(AnimationListener* listener);
+    virtual void reset() override;
+    virtual void end() override;
 
     void addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder,
             Interpolator* interpolators, int64_t startDelays,
             nsecs_t durations, int repeatCount);
     virtual uint32_t dirtyMask();
+    bool isInfinite() { return mIsInfinite; }
+    void setVectorDrawable(VectorDrawableRoot* vd) { mVectorDrawable = vd; }
+    VectorDrawableRoot* getVectorDrawable() const { return mVectorDrawable; }
+    AnimationListener* getOneShotListener() { return mOneShotListener.get(); }
+    void clearOneShotListener() { mOneShotListener = nullptr; }
+    uint32_t getRequestId() const { return mRequestId; }
 
 protected:
     virtual float getValue(RenderNode* target) const override;
@@ -69,6 +78,11 @@
     std::vector< std::unique_ptr<PropertyAnimator> > mAnimators;
     float mLastFraction = 0.0f;
     bool mInitialized = false;
+    VectorDrawableRoot* mVectorDrawable = nullptr;
+    bool mIsInfinite = false;
+    // This request id gets incremented (on UI thread only) when a new request to modfiy the
+    // lifecycle of an animation happens, namely when start/end/reset/reverse is called.
+    uint32_t mRequestId = 0;
 };
 
 class PropertyAnimatorSetListener : public AnimationListener {
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index b49f9b5..b35c926 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -440,8 +440,8 @@
 }
 
 void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
-    mDisplayList->pushStagingFunctors.push_back(tree->getFunctor());
     mDisplayList->ref(tree);
+    mDisplayList->vectorDrawables.push_back(tree);
     addOp(alloc().create_trivial<VectorDrawableOp>(
             tree,
             Rect(tree->stagingProperties()->getBounds()),
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 6e848fd..0f6c43e 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -419,6 +419,14 @@
     prepareSubTree(info, childFunctorsNeedLayer, mDisplayList);
     pushLayerUpdate(info);
 
+    for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) {
+        // If any vector drawable in the display list needs update, damage the node.
+        if (vectorDrawable->isDirty()) {
+            damageSelf(info);
+        }
+        vectorDrawable->setPropertyChangeWillBeConsumed(true);
+    }
+
     info.damageAccumulator->popTransform();
 }
 
@@ -477,8 +485,8 @@
         for (auto& iter : mDisplayList->getFunctors()) {
             (*iter.functor)(DrawGlInfo::kModeSync, nullptr);
         }
-        for (size_t i = 0; i < mDisplayList->getPushStagingFunctors().size(); i++) {
-            (*mDisplayList->getPushStagingFunctors()[i])();
+        for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) {
+            vectorDrawable->syncProperties();
         }
     }
 }
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index a5d1d4b..e67dfdd 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -673,21 +673,16 @@
     void onPropertyChanged(TreeProperties* prop);
     TreeProperties* mutateStagingProperties() { return &mStagingProperties; }
     const TreeProperties* stagingProperties() const { return &mStagingProperties; }
-    PushStagingFunctor* getFunctor() { return &mFunctor;}
 
     // This should only be called from animations on RT
     TreeProperties* mutateProperties() { return &mProperties; }
 
+    // This should always be called from RT.
+    bool isDirty() const { return mCache.dirty; }
+    bool getPropertyChangeWillBeConsumed() const { return mWillBeConsumed; }
+    void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; }
+
 private:
-    class VectorDrawableFunctor : public PushStagingFunctor {
-    public:
-        VectorDrawableFunctor(Tree* tree) : mTree(tree) {}
-        virtual void operator ()() {
-            mTree->syncProperties();
-        }
-    private:
-        Tree* mTree;
-    };
 
     SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
     bool allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height);
@@ -704,8 +699,6 @@
     TreeProperties mProperties = TreeProperties(this);
     TreeProperties mStagingProperties = TreeProperties(this);
 
-    VectorDrawableFunctor mFunctor = VectorDrawableFunctor(this);
-
     SkPaint mPaint;
     struct Cache {
         SkBitmap bitmap;
@@ -717,6 +710,8 @@
 
     PropertyChangedListener mPropertyChangedListener
             = PropertyChangedListener(&mCache.dirty, &mStagingCache.dirty);
+
+    mutable bool mWillBeConsumed = false;
 };
 
 } // namespace VectorDrawable
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index e6399d4..597c5c5 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -278,6 +278,7 @@
 
 void CanvasContext::stopDrawing() {
     mRenderThread.removeFrameCallback(this);
+    mAnimationContext->detachAnimators();
 }
 
 void CanvasContext::notifyFramePending() {
diff --git a/packages/PrintRecommendationService/res/values/strings.xml b/packages/PrintRecommendationService/res/values/strings.xml
index 348fcac..b6c45b7 100644
--- a/packages/PrintRecommendationService/res/values/strings.xml
+++ b/packages/PrintRecommendationService/res/values/strings.xml
@@ -26,6 +26,6 @@
     <string name="plugin_vendor_samsung">Samsung</string>
     <string name="plugin_vendor_epson">Epson</string>
     <string name="plugin_vendor_konica_minolta">Konica Minolta</string>
-    <string name="plugin_vendor_fuji">Fuji</string>
+    <string name="plugin_vendor_fuji_xerox">Fuji Xerox</string>
     <string name="plugin_vendor_morpia">Mopria</string>
 </resources>
diff --git a/packages/PrintRecommendationService/res/xml/vendorconfigs.xml b/packages/PrintRecommendationService/res/xml/vendorconfigs.xml
index 52889ce..703cf6f 100644
--- a/packages/PrintRecommendationService/res/xml/vendorconfigs.xml
+++ b/packages/PrintRecommendationService/res/xml/vendorconfigs.xml
@@ -60,7 +60,7 @@
     </vendor>
 
     <vendor>
-        <name>@string/plugin_vendor_fuji</name>
+        <name>@string/plugin_vendor_fuji_xerox</name>
         <package>jp.co.fujixerox.prt.PrintUtil.PCL</package>
         <mdns-names>
             <mdns-name>FUJI XEROX</mdns-name>
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java
index 7a2d0d89..b0da08bf 100755
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java
@@ -39,8 +39,10 @@
         String usbMfg = getString(attributes.get(ATTRIBUTE__USB_MFG));
         String usbMdl = getString(attributes.get(ATTRIBUTE__USB_MDL));
         String mfg = getString(attributes.get(ATTRIBUTE__MFG));
-        return containsVendor(product, vendorValues) || containsVendor(ty, vendorValues) || containsVendor(usbMfg, vendorValues) || containsVendor(mfg, vendorValues) && !(containsString(ty, EXCLUDE_FUJI) || containsString(product, EXCLUDE_FUJI) || containsString(usbMdl, EXCLUDE_FUJI));
-
+        return (containsVendor(product, vendorValues) || containsVendor(ty, vendorValues) ||
+                containsVendor(usbMfg, vendorValues) || containsVendor(mfg, vendorValues)) &&
+                !(containsString(ty, EXCLUDE_FUJI) || containsString(product, EXCLUDE_FUJI) ||
+                        containsString(usbMdl, EXCLUDE_FUJI));
     }
 
     public static String getVendor(NsdServiceInfo networkDevice) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index be3df54..c318275 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -614,6 +614,9 @@
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+
+        mMediaSizeComparator.onConfigurationChanged(newConfig);
+
         if (mPrintPreviewController != null) {
             mPrintPreviewController.onOrientationChanged();
         }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/MediaSizeUtils.java b/packages/PrintSpooler/src/com/android/printspooler/util/MediaSizeUtils.java
index 912ee1d..7289301 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/util/MediaSizeUtils.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/util/MediaSizeUtils.java
@@ -16,13 +16,16 @@
 
 package com.android.printspooler.util;
 
+import android.annotation.NonNull;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.print.PrintAttributes.MediaSize;
-import android.util.ArrayMap;
 
 import com.android.printspooler.R;
 
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -30,7 +33,10 @@
  */
 public final class MediaSizeUtils {
 
-    private static Map<MediaSize, String> sMediaSizeToStandardMap;
+    private static Map<MediaSize, Integer> sMediaSizeToStandardMap;
+
+    /** The media size standard for all media sizes no standard is defined for */
+    private static int sMediaSizeStandardIso;
 
     private MediaSizeUtils() {
         /* do nothing - hide constructor */
@@ -47,22 +53,32 @@
         return MediaSize.getStandardMediaSizeById(mediaSizeId);
     }
 
-    private static String getStandardForMediaSize(Context context, MediaSize mediaSize) {
+    /**
+     * Get the standard the {@link MediaSize} belongs to.
+     *
+     * @param context   The context of the caller
+     * @param mediaSize The {@link MediaSize} to be resolved
+     *
+     * @return The standard the {@link MediaSize} belongs to
+     */
+    private static int getStandardForMediaSize(Context context, MediaSize mediaSize) {
         if (sMediaSizeToStandardMap == null) {
-            sMediaSizeToStandardMap = new ArrayMap<MediaSize, String>();
+            sMediaSizeStandardIso = Integer.parseInt(context.getString(
+                    R.string.mediasize_standard_iso));
+
+            sMediaSizeToStandardMap = new HashMap<>();
             String[] mediaSizeToStandardMapValues = context.getResources()
                     .getStringArray(R.array.mediasize_to_standard_map);
             final int mediaSizeToStandardCount = mediaSizeToStandardMapValues.length;
             for (int i = 0; i < mediaSizeToStandardCount; i += 2) {
                 String mediaSizeId = mediaSizeToStandardMapValues[i];
                 MediaSize key = MediaSize.getStandardMediaSizeById(mediaSizeId);
-                String value = mediaSizeToStandardMapValues[i + 1];
+                int value = Integer.parseInt(mediaSizeToStandardMapValues[i + 1]);
                 sMediaSizeToStandardMap.put(key, value);
             }
         }
-        String standard = sMediaSizeToStandardMap.get(mediaSize);
-        return (standard != null) ? standard : context.getString(
-                R.string.mediasize_standard_iso);
+        Integer standard = sMediaSizeToStandardMap.get(mediaSize);
+        return (standard != null) ? standard : sMediaSizeStandardIso;
     }
 
     /**
@@ -73,32 +89,76 @@
     public static final class MediaSizeComparator implements Comparator<MediaSize> {
         private final Context mContext;
 
+        /** Current configuration */
+        private Configuration mCurrentConfig;
+
+        /** The standard to use for the current locale */
+        private int mCurrentStandard;
+
+        /** Mapping from media size to label */
+        private final @NonNull Map<MediaSize, String> mMediaSizeToLabel;
+
         public MediaSizeComparator(Context context) {
             mContext = context;
+            mMediaSizeToLabel = new HashMap<>();
+            mCurrentStandard = Integer.parseInt(mContext.getString(R.string.mediasize_standard));
+        }
+
+        /**
+         * Handle a configuration change by reloading all resources.
+         *
+         * @param newConfig The new configuration that will be applied.
+         */
+        public void onConfigurationChanged(@NonNull Configuration newConfig) {
+            if (mCurrentConfig == null ||
+                    (newConfig.diff(mCurrentConfig) & ActivityInfo.CONFIG_LOCALE) != 0) {
+                mCurrentStandard = Integer
+                        .parseInt(mContext.getString(R.string.mediasize_standard));
+                mMediaSizeToLabel.clear();
+
+                mCurrentConfig = newConfig;
+            }
+        }
+
+        /**
+         * Get the label for a {@link MediaSize}.
+         *
+         * @param context   The context the label should be loaded for
+         * @param mediaSize The {@link MediaSize} to resolve
+         *
+         * @return The label for the media size
+         */
+        public @NonNull String getLabel(@NonNull Context context, @NonNull MediaSize mediaSize) {
+            String label = mMediaSizeToLabel.get(mediaSize);
+
+            if (label == null) {
+                label = mediaSize.getLabel(context.getPackageManager());
+                mMediaSizeToLabel.put(mediaSize, label);
+            }
+
+            return label;
         }
 
         @Override
         public int compare(MediaSize lhs, MediaSize rhs) {
-            String currentStandard = mContext.getString(R.string.mediasize_standard);
-            String lhsStandard = getStandardForMediaSize(mContext, lhs);
-            String rhsStandard = getStandardForMediaSize(mContext, rhs);
+            int lhsStandard = getStandardForMediaSize(mContext, lhs);
+            int rhsStandard = getStandardForMediaSize(mContext, rhs);
 
             // The current standard always wins.
-            if (lhsStandard.equals(currentStandard)) {
-                if (!rhsStandard.equals(currentStandard)) {
+            if (lhsStandard == mCurrentStandard) {
+                if (rhsStandard != mCurrentStandard) {
                     return -1;
                 }
-            } else if (rhsStandard.equals(currentStandard)) {
+            } else if (rhsStandard == mCurrentStandard) {
                 return 1;
             }
 
-            if (!lhsStandard.equals(rhsStandard)) {
+            if (lhsStandard != rhsStandard) {
                 // Different standards - use the standard ordering.
-                return lhsStandard.compareTo(rhsStandard);
+                return Integer.valueOf(lhsStandard).compareTo(rhsStandard);
             } else {
                 // Same standard - sort alphabetically by label.
-                return lhs.getLabel(mContext.getPackageManager()).
-                        compareTo(rhs.getLabel(mContext.getPackageManager()));
+                return getLabel(mContext, lhs).compareTo(getLabel(mContext, rhs));
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index dba7130..bfa43fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -42,6 +42,7 @@
     private float mViewAlpha = 1.0f;
     private ValueAnimator mAlphaAnimator;
     private Rect mExcludedRect = new Rect();
+    private int mLeftInset = 0;
     private boolean mHasExcludedArea;
     private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener
             = new ValueAnimator.AnimatorUpdateListener() {
@@ -87,12 +88,12 @@
                 if (mExcludedRect.top > 0) {
                     canvas.drawRect(0, 0, getWidth(), mExcludedRect.top, mPaint);
                 }
-                if (mExcludedRect.left > 0) {
-                    canvas.drawRect(0,  mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom,
-                            mPaint);
+                if (mExcludedRect.left + mLeftInset > 0) {
+                    canvas.drawRect(0,  mExcludedRect.top, mExcludedRect.left + mLeftInset,
+                            mExcludedRect.bottom, mPaint);
                 }
-                if (mExcludedRect.right < getWidth()) {
-                    canvas.drawRect(mExcludedRect.right,
+                if (mExcludedRect.right + mLeftInset < getWidth()) {
+                    canvas.drawRect(mExcludedRect.right + mLeftInset,
                             mExcludedRect.top,
                             getWidth(),
                             mExcludedRect.bottom,
@@ -183,4 +184,14 @@
     public void setChangeRunnable(Runnable changeRunnable) {
         mChangeRunnable = changeRunnable;
     }
+
+    public void setLeftInset(int leftInset) {
+        if (mLeftInset != leftInset) {
+            mLeftInset = leftInset;
+
+            if (mHasExcludedArea) {
+                invalidate();
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 9c4480e..135c294 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -524,6 +524,10 @@
         mScrimBehind.setExcludedArea(area);
     }
 
+    public void setLeftInset(int inset) {
+        mScrimBehind.setLeftInset(inset);
+    }
+
     public int getScrimBehindColor() {
         return mScrimBehind.getScrimColorWithAlpha();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index ebfa018..7b22b88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -70,6 +70,7 @@
     private View mBrightnessMirror;
 
     private int mRightInset = 0;
+    private int mLeftInset = 0;
 
     private PhoneStatusBar mService;
     private final Paint mTransparentSrcPaint = new Paint();
@@ -93,25 +94,26 @@
     @Override
     protected boolean fitSystemWindows(Rect insets) {
         if (getFitsSystemWindows()) {
-            boolean paddingChanged = insets.left != getPaddingLeft()
-                    || insets.top != getPaddingTop()
+            boolean paddingChanged = insets.top != getPaddingTop()
                     || insets.bottom != getPaddingBottom();
 
             // Super-special right inset handling, because scrims and backdrop need to ignore it.
-            if (insets.right != mRightInset) {
+            if (insets.right != mRightInset || insets.left != mLeftInset) {
                 mRightInset = insets.right;
+                mLeftInset = insets.left;
                 applyMargins();
             }
-            // Drop top inset, apply left inset and pass through bottom inset.
+            // Drop top inset, and pass through bottom inset.
             if (paddingChanged) {
-                setPadding(insets.left, 0, 0, 0);
+                setPadding(0, 0, 0, 0);
             }
             insets.left = 0;
             insets.top = 0;
             insets.right = 0;
         } else {
-            if (mRightInset != 0) {
+            if (mRightInset != 0 || mLeftInset != 0) {
                 mRightInset = 0;
+                mLeftInset = 0;
                 applyMargins();
             }
             boolean changed = getPaddingLeft() != 0
@@ -127,13 +129,16 @@
     }
 
     private void applyMargins() {
+        mService.mScrimController.setLeftInset(mLeftInset);
         final int N = getChildCount();
         for (int i = 0; i < N; i++) {
             View child = getChildAt(i);
             if (child.getLayoutParams() instanceof LayoutParams) {
                 LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (!lp.ignoreRightInset && lp.rightMargin != mRightInset) {
+                if (!lp.ignoreRightInset
+                        && (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset)) {
                     lp.rightMargin = mRightInset;
+                    lp.leftMargin = mLeftInset;
                     child.requestLayout();
                 }
             }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d58f864..71964d1 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -222,7 +222,7 @@
                             } catch (RemoteException e) {
                                 Slog.e(TAG,"Unable to call onBrEdrDown", e);
                             } finally {
-                                mBluetoothLock.readLock().lock();
+                                mBluetoothLock.readLock().unlock();
                             }
                         } else if (st == BluetoothAdapter.STATE_ON){
                             // disable without persisting the setting
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index b89cae7..793f411 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -167,8 +167,9 @@
     private Intent mNewTaskIntent;
     private ActivityStack mSourceStack;
     private ActivityStack mTargetStack;
-    // TODO: Is the mMoveHome flag really needed?
-    private boolean mMovedHome;
+    // Indicates that we moved other task and are going to put something on top soon, so
+    // we don't want to show it redundantly or accidentally change what's shown below.
+    private boolean mMovedOtherTask;
     private boolean mMovedToFront;
     private boolean mNoAnimation;
     private boolean mKeepCurTransition;
@@ -206,7 +207,7 @@
         mSourceStack = null;
 
         mTargetStack = null;
-        mMovedHome = false;
+        mMovedOtherTask = false;
         mMovedToFront = false;
         mNoAnimation = false;
         mKeepCurTransition = false;
@@ -1039,7 +1040,6 @@
                 resumeTargetStackIfNeeded();
                 return START_RETURN_INTENT_TO_CALLER;
             }
-
             setTaskFromIntentActivity(mReusedActivity);
 
             if (!mAddingToTask && mReuseTask == null) {
@@ -1108,7 +1108,7 @@
                 Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
                 return START_RETURN_LOCK_TASK_MODE_VIOLATION;
             }
-            if (!mMovedHome) {
+            if (!mMovedOtherTask) {
                 updateTaskReturnToType(mStartActivity.task, mLaunchFlags, topStack);
             }
         } else if (mSourceRecord != null) {
@@ -1472,7 +1472,7 @@
                 if (mLaunchTaskBehind && mSourceRecord != null) {
                     intentActivity.setTaskToAffiliateWith(mSourceRecord.task);
                 }
-                mMovedHome = true;
+                mMovedOtherTask = true;
 
                 // If the launch flags carry both NEW_TASK and CLEAR_TASK, the task's activities
                 // will be cleared soon by ActivityStarter in setTaskFromIntentActivity().
@@ -1550,6 +1550,10 @@
             mReuseTask = intentActivity.task;
             mReuseTask.performClearTaskLocked();
             mReuseTask.setIntent(mStartActivity);
+            // When we clear the task - focus will be adjusted, which will bring another task
+            // to top before we launch the activity we need. This will temporary swap their
+            // mTaskToReturnTo values and we don't want to overwrite them accidentally.
+            mMovedOtherTask = true;
         } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                 || mLaunchSingleInstance || mLaunchSingleTask) {
             ActivityRecord top = intentActivity.task.performClearTaskLocked(mStartActivity,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e0cfe4e..fae743c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -262,6 +262,10 @@
     private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
             "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
 
+    private static final int NAV_BAR_BOTTOM = 0;
+    private static final int NAV_BAR_RIGHT = 1;
+    private static final int NAV_BAR_LEFT = 2;
+
     /**
      * Keyguard stuff
      */
@@ -354,9 +358,8 @@
     int mStatusBarHeight;
     WindowState mNavigationBar = null;
     boolean mHasNavigationBar = false;
-    boolean mCanHideNavigationBar = false;
     boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
-    boolean mNavigationBarOnBottom = true; // is the navigation bar on the bottom *right now*?
+    int mNavigationBarPosition = NAV_BAR_BOTTOM;
     int[] mNavigationBarHeightForRotationDefault = new int[4];
     int[] mNavigationBarWidthForRotationDefault = new int[4];
     int[] mNavigationBarHeightForRotationInCarMode = new int[4];
@@ -1683,13 +1686,19 @@
                     }
                     @Override
                     public void onSwipeFromBottom() {
-                        if (mNavigationBar != null && mNavigationBarOnBottom) {
+                        if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) {
                             requestTransientBars(mNavigationBar);
                         }
                     }
                     @Override
                     public void onSwipeFromRight() {
-                        if (mNavigationBar != null && !mNavigationBarOnBottom) {
+                        if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) {
+                            requestTransientBars(mNavigationBar);
+                        }
+                    }
+                    @Override
+                    public void onSwipeFromLeft() {
+                        if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) {
                             requestTransientBars(mNavigationBar);
                         }
                     }
@@ -2778,8 +2787,8 @@
             if (win.getAttrs().windowAnimations != 0) {
                 return 0;
             }
-            // This can be on either the bottom or the right.
-            if (mNavigationBarOnBottom) {
+            // This can be on either the bottom or the right or the left.
+            if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
                 if (transit == TRANSIT_EXIT
                         || transit == TRANSIT_HIDE) {
                     return R.anim.dock_bottom_exit;
@@ -2787,7 +2796,7 @@
                         || transit == TRANSIT_SHOW) {
                     return R.anim.dock_bottom_enter;
                 }
-            } else {
+            } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
                 if (transit == TRANSIT_EXIT
                         || transit == TRANSIT_HIDE) {
                     return R.anim.dock_right_exit;
@@ -2795,6 +2804,14 @@
                         || transit == TRANSIT_SHOW) {
                     return R.anim.dock_right_enter;
                 }
+            } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+                if (transit == TRANSIT_EXIT
+                        || transit == TRANSIT_HIDE) {
+                    return R.anim.dock_left_exit;
+                } else if (transit == TRANSIT_ENTER
+                        || transit == TRANSIT_SHOW) {
+                    return R.anim.dock_left_enter;
+                }
             }
         } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
             return selectDockedDividerAnimationLw(win, transit);
@@ -2823,10 +2840,12 @@
         // If the divider is behind the navigation bar, don't animate.
         final Rect frame = win.getFrameLw();
         final boolean behindNavBar = mNavigationBar != null
-                && ((mNavigationBarOnBottom
+                && ((mNavigationBarPosition == NAV_BAR_BOTTOM
                         && frame.top + insets >= mNavigationBar.getFrameLw().top)
-                || (!mNavigationBarOnBottom
-                        && frame.left + insets >= mNavigationBar.getFrameLw().left));
+                || (mNavigationBarPosition == NAV_BAR_RIGHT
+                        && frame.left + insets >= mNavigationBar.getFrameLw().left)
+                || (mNavigationBarPosition == NAV_BAR_LEFT
+                        && frame.right - insets <= mNavigationBar.getFrameLw().right));
         final boolean landscape = frame.height() > frame.width();
         final boolean offscreenLandscape = landscape && (frame.right - insets <= 0
                 || frame.left + insets >= win.getDisplayFrameLw().right);
@@ -4019,7 +4038,7 @@
             navVisible |= !canHideNavigationBar();
 
             boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
-                    displayRotation, uiMode, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
+                    displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
                     navAllowedHidden, statusBarExpandedNotKeyguard);
             if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
                     mDockLeft, mDockTop, mDockRight, mDockBottom));
@@ -4098,8 +4117,8 @@
     }
 
     private boolean layoutNavigationBar(int displayWidth, int displayHeight, int displayRotation,
-            int uiMode, int overscanRight, int overscanBottom, Rect dcf, boolean navVisible,
-            boolean navTranslucent, boolean navAllowedHidden,
+            int uiMode, int overscanLeft, int overscanRight, int overscanBottom, Rect dcf,
+            boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
             boolean statusBarExpandedNotKeyguard) {
         if (mNavigationBar != null) {
             boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
@@ -4107,8 +4126,9 @@
             // size.  We need to do this directly, instead of relying on
             // it to bubble up from the nav bar, because this needs to
             // change atomically with screen rotations.
-            mNavigationBarOnBottom = isNavigationBarOnBottom(displayWidth, displayHeight);
-            if (mNavigationBarOnBottom) {
+            mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight,
+                    displayRotation);
+            if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
                 // It's a system nav bar or a portrait screen; nav bar goes on bottom.
                 int top = displayHeight - overscanBottom
                         - getNavigationBarHeight(displayRotation, uiMode);
@@ -4134,7 +4154,7 @@
                     // we can tell the app that it is covered by it.
                     mSystemBottom = mTmpNavigationFrame.top;
                 }
-            } else {
+            } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
                 // Landscape screen; nav bar goes to the right.
                 int left = displayWidth - overscanRight
                         - getNavigationBarWidth(displayRotation, uiMode);
@@ -4160,6 +4180,33 @@
                     // we can tell the app that it is covered by it.
                     mSystemRight = mTmpNavigationFrame.left;
                 }
+            } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+                // Seascape screen; nav bar goes to the left.
+                int right = overscanLeft + getNavigationBarWidth(displayRotation, uiMode);
+                mTmpNavigationFrame.set(overscanLeft, 0, right, displayHeight);
+                mStableLeft = mStableFullscreenLeft = mTmpNavigationFrame.right;
+                if (transientNavBarShowing) {
+                    mNavigationBarController.setBarShowingLw(true);
+                } else if (navVisible) {
+                    mNavigationBarController.setBarShowingLw(true);
+                    mDockLeft = mTmpNavigationFrame.right;
+                    // TODO: not so sure about those:
+                    mRestrictedScreenLeft = mRestrictedOverscanScreenLeft = mDockLeft;
+                    mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
+                    mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
+                } else {
+                    // We currently want to hide the navigation UI - unless we expanded the status
+                    // bar.
+                    mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
+                }
+                if (navVisible && !navTranslucent && !navAllowedHidden
+                        && !mNavigationBar.isAnimatingLw()
+                        && !mNavigationBarController.wasRecentlyTranslucent()) {
+                    // If the nav bar is currently requested to be visible,
+                    // and not in the process of animating on or off, then
+                    // we can tell the app that it is covered by it.
+                    mSystemLeft = mTmpNavigationFrame.right;
+                }
             }
             // Make sure the content and current rectangles are updated to
             // account for the restrictions from the navigation bar.
@@ -4180,8 +4227,15 @@
         return false;
     }
 
-    private boolean isNavigationBarOnBottom(int displayWidth, int displayHeight) {
-        return !mNavigationBarCanMove || displayWidth < displayHeight;
+    private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
+        if (mNavigationBarCanMove && displayWidth > displayHeight) {
+            if (displayRotation == Surface.ROTATION_270) {
+                return NAV_BAR_LEFT;
+            } else {
+                return NAV_BAR_RIGHT;
+            }
+        }
+        return NAV_BAR_BOTTOM;
     }
 
     /** {@inheritDoc} */
@@ -4357,7 +4411,11 @@
             if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
                 // The status bar forces the navigation bar while it's visible. Make sure the IME
                 // avoids the navigation bar in that case.
-                pf.right = df.right = of.right = cf.right = vf.right = mStableRight;
+                if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+                    pf.right = df.right = of.right = cf.right = vf.right = mStableRight;
+                } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+                    pf.left = df.left = of.left = cf.left = vf.left = mStableLeft;
+                }
             }
             // IM dock windows always go to the bottom of the screen.
             attrs.gravity = Gravity.BOTTOM;
@@ -6463,10 +6521,13 @@
 
         // Only navigation bar
         if (mNavigationBar != null) {
-            if (isNavigationBarOnBottom(displayWidth, displayHeight)) {
+            int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
+            if (position == NAV_BAR_BOTTOM) {
                 outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
-            } else {
+            } else if (position == NAV_BAR_RIGHT) {
                 outInsets.right = getNavigationBarWidth(displayRotation, mUiMode);
+            } else if (position == NAV_BAR_LEFT) {
+                outInsets.left = getNavigationBarWidth(displayRotation, mUiMode);
             }
         }
     }
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
index 80e4341..598c58e 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
@@ -43,6 +43,7 @@
     private static final int SWIPE_FROM_TOP = 1;
     private static final int SWIPE_FROM_BOTTOM = 2;
     private static final int SWIPE_FROM_RIGHT = 3;
+    private static final int SWIPE_FROM_LEFT = 4;
 
     private final Context mContext;
     private final int mSwipeStartThreshold;
@@ -127,6 +128,9 @@
                     } else if (swipe == SWIPE_FROM_RIGHT) {
                         if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
                         mCallbacks.onSwipeFromRight();
+                    } else if (swipe == SWIPE_FROM_LEFT) {
+                        if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft");
+                        mCallbacks.onSwipeFromLeft();
                     }
                 }
                 break;
@@ -229,6 +233,11 @@
                 && elapsed < SWIPE_TIMEOUT_MS) {
             return SWIPE_FROM_RIGHT;
         }
+        if (fromX <= mSwipeStartThreshold
+                && x > fromX + mSwipeDistanceThreshold
+                && elapsed < SWIPE_TIMEOUT_MS) {
+            return SWIPE_FROM_LEFT;
+        }
         return SWIPE_NONE;
     }
 
@@ -265,6 +274,7 @@
         void onSwipeFromTop();
         void onSwipeFromBottom();
         void onSwipeFromRight();
+        void onSwipeFromLeft();
         void onFling(int durationMs);
         void onDown();
         void onUpOrCancel();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e46ed8d..881fc10 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1314,7 +1314,6 @@
     }
 
     private int resolveStackClip() {
-
         // App animation overrides window animation stack clip mode.
         if (mAppAnimator != null && mAppAnimator.animation != null) {
             return mAppAnimator.getStackClip();
@@ -1420,6 +1419,9 @@
         // aren't observing known issues here outside of PiP resizing. (Typically
         // the other windows that use -1 are PopupWindows which aren't likely
         // to be rendering while we resize).
+
+        boolean wasForceScaled = mForceScaleUntilResize;
+
         if (!w.inPinnedWorkspace() || (!w.mRelayoutCalled || w.mInRelayout)) {
             mSurfaceResized = mSurfaceController.setSizeInTransaction(
                     mTmpSize.width(), mTmpSize.height(), recoveringMemory);
@@ -1428,13 +1430,17 @@
         }
         mForceScaleUntilResize = mForceScaleUntilResize && !mSurfaceResized;
 
-
         calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
+
+        float surfaceWidth = mSurfaceController.getWidth();
+        float surfaceHeight = mSurfaceController.getHeight();
+
         if ((task != null && task.mStack.getForceScaleToCrop()) || mForceScaleUntilResize) {
             int hInsets = w.getAttrs().surfaceInsets.left + w.getAttrs().surfaceInsets.right;
             int vInsets = w.getAttrs().surfaceInsets.top + w.getAttrs().surfaceInsets.bottom;
-            float surfaceWidth = mSurfaceController.getWidth();
-            float surfaceHeight = mSurfaceController.getHeight();
+            if (!mForceScaleUntilResize) {
+                mSurfaceController.forceScaleableInTransaction(true);
+            }
             // We want to calculate the scaling based on the content area, not based on
             // the entire surface, so that we scale in sync with windows that don't have insets.
             mExtraHScale = (mTmpClipRect.width() - hInsets) / (float)(surfaceWidth - hInsets);
@@ -1455,7 +1461,8 @@
             posX += w.getAttrs().surfaceInsets.left * (1 - mExtraHScale);
             posY += w.getAttrs().surfaceInsets.top * (1 - mExtraVScale);
 
-            mSurfaceController.setPositionInTransaction(posX, posY, recoveringMemory);
+            mSurfaceController.setPositionInTransaction((float)Math.floor(posX),
+                    (float)Math.floor(posY), recoveringMemory);
 
             // Since we are scaled to fit in our previously desired crop, we can now
             // expose the whole window in buffer space, and not risk extending
@@ -1467,7 +1474,7 @@
             // We need to ensure for each surface, that we disable transformation matrix
             // scaling in the same transaction which we resize the surface in.
             // As we are in SCALING_MODE_SCALE_TO_WINDOW, SurfaceFlinger will
-            // then take over the scaling until the new buffer arrives, and things 
+            // then take over the scaling until the new buffer arrives, and things
             // will be seamless.
             mForceScaleUntilResize = true;
         } else {
@@ -1475,6 +1482,22 @@
                     recoveringMemory);
         }
 
+        // If we are ending the scaling mode. We switch to SCALING_MODE_FREEZE
+        // to prevent further updates until buffer latch. Normally position
+        // would continue to apply immediately. But we need a different position
+        // before and after resize (since we have scaled the shadows, as discussed
+        // above).
+        if (wasForceScaled && !mForceScaleUntilResize) {
+            mSurfaceController.setPositionAppliesWithResizeInTransaction(true);
+            mSurfaceController.forceScaleableInTransaction(false);
+        }
+        if (w.inPinnedWorkspace()) {
+            mTmpClipRect.set(0, 0, -1, -1);
+            task.mStack.getDimBounds(mTmpFinalClipRect);
+            mTmpFinalClipRect.inset(-w.mAttrs.surfaceInsets.left, -w.mAttrs.surfaceInsets.top,
+                    -w.mAttrs.surfaceInsets.right, -w.mAttrs.surfaceInsets.bottom);
+        }
+
         updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
 
         mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 9646a49..c30da14 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -176,7 +176,7 @@
         if (SHOW_TRANSACTIONS) logSurface(
                 "CROP " + clipRect.toShortString(), null);
         try {
-            if (clipRect.width() > 0 && clipRect.height() > 0) {
+            if (clipRect.width() != 0 && clipRect.height() != 0) {
                 mSurfaceControl.setWindowCrop(clipRect);
                 mHiddenForCrop = false;
                 updateVisibility();
@@ -236,6 +236,10 @@
         }
     }
 
+    void setPositionAppliesWithResizeInTransaction(boolean recoveringMemory) {
+        mSurfaceControl.setPositionAppliesWithResize();
+    }
+
     void setMatrixInTransaction(float dsdx, float dtdx, float dsdy, float dtdy,
             boolean recoveringMemory) {
         try {
@@ -554,6 +558,13 @@
         }
 
         @Override
+        public void setPositionAppliesWithResize() {
+            if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setPositionAppliesWithResize(): OLD: "
+                    + this + ". Called by" + Debug.getCallers(9));
+            super.setPositionAppliesWithResize();
+        }
+
+        @Override
         public void setSize(int w, int h) {
             if (w != mSize.x || h != mSize.y) {
                 if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setSize(" + w + "," + h + "): OLD:"