OSDN Git Service

Account for scaling of surfaceInset area in magnification.
[android-x86/frameworks-base.git] / services / core / java / com / android / server / wm / WindowStateAnimator.java
index 42042b9..c2ec8c1 100644 (file)
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.StackId;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerService.DEBUG_LAYERS;
-import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerService.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerService.DEBUG_SURFACE_TRACE;
-import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static com.android.server.wm.AppWindowAnimator.sDummyAnimation;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_CROP;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.localLOGV;
-import static com.android.server.wm.WindowManagerService.LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.WindowManagerService.LayoutFields.SET_TURN_ON_SCREEN;
+import static com.android.server.wm.WindowManagerService.logWithStack;
+import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
+import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
 
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.Debug;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.util.Slog;
-import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.MagnificationSpec;
+import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.view.View;
 import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
 import android.view.WindowManager.LayoutParams;
+import android.view.WindowManagerPolicy;
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
 import android.view.animation.AnimationUtils;
@@ -59,13 +73,32 @@ import android.view.animation.Transformation;
 import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 
 /**
  * Keep track of animations and surface operations for a single WindowState.
  **/
 class WindowStateAnimator {
-    static final String TAG = "WindowStateAnimator";
+    static final String TAG = TAG_WITH_CLASS_NAME ? "WindowStateAnimator" : TAG_WM;
+    static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
+
+    /**
+     * Mode how the window gets clipped by the stack bounds during an animation: The clipping should
+     * be applied after applying the animation transformation, i.e. the stack bounds don't move
+     * during the animation.
+     */
+    static final int STACK_CLIP_AFTER_ANIM = 0;
+
+    /**
+     * Mode how the window gets clipped by the stack bounds: The clipping should be applied before
+     * applying the animation transformation, i.e. the stack bounds move with the window.
+     */
+    static final int STACK_CLIP_BEFORE_ANIM = 1;
+
+    /**
+     * Mode how window gets clipped by the stack bounds during an animation: Don't clip the window
+     * by the stack bounds.
+     */
+    static final int STACK_CLIP_NONE = 2;
 
     // Unchanging local convenience fields.
     final WindowManagerService mService;
@@ -77,6 +110,7 @@ class WindowStateAnimator {
     final WindowManagerPolicy mPolicy;
     final Context mContext;
     final boolean mIsWallpaper;
+    final WallpaperController mWallpaperControllerLocked;
 
     // Currently running animation.
     boolean mAnimating;
@@ -91,15 +125,20 @@ class WindowStateAnimator {
     int mLastLayer;
     long mAnimationStartTime;
     long mLastAnimationTime;
-
-    SurfaceControl mSurfaceControl;
-    SurfaceControl mPendingDestroySurface;
+    int mStackClip = STACK_CLIP_BEFORE_ANIM;
 
     /**
      * Set when we have changed the size of the surface, to know that
      * we must tell them application to resize (and thus redraw itself).
      */
     boolean mSurfaceResized;
+    /**
+     * Whether we should inform the client on next relayoutWindow that
+     * the surface has been resized since last time.
+     */
+    boolean mReportSurfaceResized;
+    WindowSurfaceController mSurfaceController;
+    private WindowSurfaceController mPendingDestroySurface;
 
     /**
      * Set if the client has asked that the destroy of its surface be delayed
@@ -107,6 +146,7 @@ class WindowStateAnimator {
      */
     boolean mSurfaceDestroyDeferred;
 
+    private boolean mDestroyPreservedSurfaceUponRedraw;
     float mShownAlpha = 0;
     float mAlpha = 0;
     float mLastAlpha = 0;
@@ -114,31 +154,30 @@ class WindowStateAnimator {
     boolean mHasClipRect;
     Rect mClipRect = new Rect();
     Rect mTmpClipRect = new Rect();
+    Rect mTmpFinalClipRect = new Rect();
     Rect mLastClipRect = new Rect();
+    Rect mLastFinalClipRect = new Rect();
+    Rect mTmpStackBounds = new Rect();
 
-    // Used to save animation distances between the time they are calculated and when they are
-    // used.
-    int mAnimDw;
-    int mAnimDh;
+    /**
+     * This is rectangle of the window's surface that is not covered by
+     * system decorations.
+     */
+    private final Rect mSystemDecorRect = new Rect();
+    private final Rect mLastSystemDecorRect = new Rect();
 
-    /** Is the next animation to be started a window move animation? */
-    boolean mAnimateMove = false;
+    // Used to save animation distances between the time they are calculated and when they are used.
+    private int mAnimDx;
+    private int mAnimDy;
 
-    /** Are we currently running a window move animation? */
-    boolean mAnimatingMove = false;
+    /** Is the next animation to be started a window move animation? */
+    private boolean mAnimateMove = false;
 
     float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
     float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
 
     boolean mHaveMatrix;
 
-    // For debugging, this is the last information given to the surface flinger.
-    boolean mSurfaceShown;
-    float mSurfaceX, mSurfaceY;
-    float mSurfaceW, mSurfaceH;
-    int mSurfaceLayer;
-    float mSurfaceAlpha;
-
     // Set to true if, when the window gets displayed, it should perform
     // an enter animation.
     boolean mEnterAnimationPending;
@@ -148,7 +187,10 @@ class WindowStateAnimator {
      * window is first added or shown, cleared when the callback has been made. */
     boolean mEnteringAnimation;
 
+    private boolean mAnimationStartDelayed;
+
     boolean mKeyguardGoingAwayAnimation;
+    boolean mKeyguardGoingAwayWithWallpaper;
 
     /** The pixel format of the underlying SurfaceControl */
     int mSurfaceFormat;
@@ -168,9 +210,6 @@ class WindowStateAnimator {
     /** Set when the window has been shown in the screen the first time. */
     static final int HAS_DRAWN = 4;
 
-    private static final int SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN =
-            View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-
     String drawStateToString() {
         switch (mDrawState) {
             case NO_SURFACE: return "NO_SURFACE";
@@ -188,6 +227,22 @@ class WindowStateAnimator {
 
     int mAttrType;
 
+    static final long PENDING_TRANSACTION_FINISH_WAIT_TIME = 100;
+    long mDeferTransactionUntilFrame = -1;
+    long mDeferTransactionTime = -1;
+
+    boolean mForceScaleUntilResize;
+
+    // WindowState.mHScale and WindowState.mVScale contain the
+    // scale according to client specified layout parameters (e.g.
+    // one layout size, with another surface size, creates such scaling).
+    // Here we track an additional scaling factor used to follow stack
+    // scaling (as in the case of the Pinned stack animation).
+    float mExtraHScale = (float) 1.0;
+    float mExtraVScale = (float) 1.0;
+
+    private final Rect mTmpSize = new Rect();
+
     WindowStateAnimator(final WindowState win) {
         final WindowManagerService service = win.mService;
 
@@ -198,8 +253,8 @@ class WindowStateAnimator {
         final DisplayContent displayContent = win.getDisplayContent();
         if (displayContent != null) {
             final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-            mAnimDw = displayInfo.appWidth;
-            mAnimDh = displayInfo.appHeight;
+            mAnimDx = displayInfo.appWidth;
+            mAnimDy = displayInfo.appHeight;
         } else {
             Slog.w(TAG, "WindowStateAnimator ctor: Display has been removed");
             // This is checked on return and dealt with.
@@ -212,9 +267,10 @@ class WindowStateAnimator {
         mSession = win.mSession;
         mAttrType = win.mAttrs.type;
         mIsWallpaper = win.mIsWallpaper;
+        mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
     }
 
-    public void setAnimation(Animation anim, long startTime) {
+    public void setAnimation(Animation anim, long startTime, int stackClip) {
         if (localLOGV) Slog.v(TAG, "Setting animation in " + this + ": " + anim);
         mAnimating = false;
         mLocalAnimating = false;
@@ -226,10 +282,15 @@ class WindowStateAnimator {
         mTransformation.setAlpha(mLastHidden ? 0 : 1);
         mHasLocalTransformation = true;
         mAnimationStartTime = startTime;
+        mStackClip = stackClip;
+    }
+
+    public void setAnimation(Animation anim, int stackClip) {
+        setAnimation(anim, -1, stackClip);
     }
 
     public void setAnimation(Animation anim) {
-        setAnimation(anim, -1);
+        setAnimation(anim, -1, STACK_CLIP_AFTER_ANIM);
     }
 
     public void clearAnimation() {
@@ -239,33 +300,58 @@ class WindowStateAnimator {
             mAnimation.cancel();
             mAnimation = null;
             mKeyguardGoingAwayAnimation = false;
+            mKeyguardGoingAwayWithWallpaper = false;
+            mStackClip = STACK_CLIP_BEFORE_ANIM;
         }
     }
 
-    /** Is the window or its container currently animating? */
-    boolean isAnimating() {
+    /**
+     * Is the window or its container currently set to animate or currently animating?
+     */
+    boolean isAnimationSet() {
         return mAnimation != null
                 || (mAttachedWinAnimator != null && mAttachedWinAnimator.mAnimation != null)
                 || (mAppAnimator != null && mAppAnimator.isAnimating());
     }
 
+    /**
+     * @return whether an animation is about to start, i.e. the animation is set already but we
+     *         haven't processed the first frame yet.
+     */
+    boolean isAnimationStarting() {
+        return isAnimationSet() && !mAnimating;
+    }
+
     /** Is the window animating the DummyAnimation? */
     boolean isDummyAnimation() {
         return mAppAnimator != null
-                && mAppAnimator.animation == AppWindowAnimator.sDummyAnimation;
+                && mAppAnimator.animation == sDummyAnimation;
     }
 
-    /** Is this window currently set to animate or currently animating? */
-    boolean isWindowAnimating() {
+    /**
+     * Is this window currently set to animate or currently animating?
+     */
+    boolean isWindowAnimationSet() {
         return mAnimation != null;
     }
 
+    /**
+     * Is this window currently waiting to run an opening animation?
+     */
+    boolean isWaitingForOpening() {
+        return mService.mAppTransition.isTransitionSet() && isDummyAnimation()
+                && mService.mOpeningApps.contains(mWin.mAppToken);
+    }
+
     void cancelExitAnimationForNextAnimationLocked() {
+        if (DEBUG_ANIM) Slog.d(TAG,
+                "cancelExitAnimationForNextAnimationLocked: " + mWin);
+
         if (mAnimation != null) {
             mAnimation.cancel();
             mAnimation = null;
             mLocalAnimating = false;
-            destroySurfaceLocked();
+            mWin.destroyOrSaveSurface();
         }
     }
 
@@ -273,17 +359,23 @@ class WindowStateAnimator {
         if ((mAnimation == null) || !mLocalAnimating) {
             return false;
         }
+        currentTime = getAnimationFrameTime(mAnimation, currentTime);
         mTransformation.clear();
         final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
-        if (false && DEBUG_ANIM) Slog.v(
-            TAG, "Stepped animation in " + this +
-            ": more=" + more + ", xform=" + mTransformation);
+        if (mAnimationStartDelayed && mAnimationIsEntrance) {
+            mTransformation.setAlpha(0f);
+        }
+        if (false && DEBUG_ANIM) Slog.v(TAG, "Stepped animation in " + this + ": more=" + more
+                + ", xform=" + mTransformation);
         return more;
     }
 
     // This must be called while inside a transaction.  Returns true if
     // there is more animation to run.
     boolean stepAnimationLocked(long currentTime) {
+        // Save the animation state as it was before this step so WindowManagerService can tell if
+        // we just started or just stopped animating by comparing mWasAnimating with isAnimationSet().
+        mWasAnimating = mAnimating;
         final DisplayContent displayContent = mWin.getDisplayContent();
         if (displayContent != null && mService.okToDisplay()) {
             // We will run animations as long as the display isn't frozen.
@@ -296,19 +388,19 @@ class WindowStateAnimator {
                         TAG, "Starting animation in " + this +
                         " @ " + currentTime + ": ww=" + mWin.mFrame.width() +
                         " wh=" + mWin.mFrame.height() +
-                        " dw=" + mAnimDw + " dh=" + mAnimDh +
+                        " dx=" + mAnimDx + " dy=" + mAnimDy +
                         " scale=" + mService.getWindowAnimationScaleLocked());
                     final DisplayInfo displayInfo = displayContent.getDisplayInfo();
                     if (mAnimateMove) {
                         mAnimateMove = false;
                         mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
-                                mAnimDw, mAnimDh);
+                                mAnimDx, mAnimDy);
                     } else {
                         mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
                                 displayInfo.appWidth, displayInfo.appHeight);
                     }
-                    mAnimDw = displayInfo.appWidth;
-                    mAnimDh = displayInfo.appHeight;
+                    mAnimDx = displayInfo.appWidth;
+                    mAnimDy = displayInfo.appHeight;
                     mAnimation.setStartTime(mAnimationStartTime != -1
                             ? mAnimationStartTime
                             : currentTime);
@@ -344,7 +436,7 @@ class WindowStateAnimator {
                 // Little trick to get through the path below to act like
                 // we have finished an animation.
                 mAnimating = true;
-            } else if (isAnimating()) {
+            } else if (isAnimationSet()) {
                 mAnimating = true;
             }
         } else if (mAnimation != null) {
@@ -359,13 +451,13 @@ class WindowStateAnimator {
 
         // Done animating, clean up.
         if (DEBUG_ANIM) Slog.v(
-            TAG, "Animation done in " + this + ": exiting=" + mWin.mExiting
+            TAG, "Animation done in " + this + ": exiting=" + mWin.mAnimatingExit
             + ", reportedVisible="
             + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
 
         mAnimating = false;
         mKeyguardGoingAwayAnimation = false;
-        mAnimatingMove = false;
+        mKeyguardGoingAwayWithWallpaper = false;
         mLocalAnimating = false;
         if (mAnimation != null) {
             mAnimation.cancel();
@@ -374,37 +466,13 @@ class WindowStateAnimator {
         if (mAnimator.mWindowDetachedWallpaper == mWin) {
             mAnimator.mWindowDetachedWallpaper = null;
         }
-        mAnimLayer = mWin.mLayer;
-        if (mWin.mIsImWindow) {
-            mAnimLayer += mService.mInputMethodAnimLayerAdjustment;
-        } else if (mIsWallpaper) {
-            mAnimLayer += mService.mWallpaperAnimLayerAdjustment;
-        }
-        if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this
-                + " anim layer: " + mAnimLayer);
+        mAnimLayer = mWin.mLayer
+                + mService.mLayersController.getSpecialWindowAnimLayerAdjustment(mWin);
+        if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer);
         mHasTransformation = false;
         mHasLocalTransformation = false;
-        if (mWin.mPolicyVisibility != mWin.mPolicyVisibilityAfterAnim) {
-            if (DEBUG_VISIBILITY) {
-                Slog.v(TAG, "Policy visibility changing after anim in " + this + ": "
-                        + mWin.mPolicyVisibilityAfterAnim);
-            }
-            mWin.mPolicyVisibility = mWin.mPolicyVisibilityAfterAnim;
-            if (displayContent != null) {
-                displayContent.layoutNeeded = true;
-            }
-            if (!mWin.mPolicyVisibility) {
-                if (mService.mCurrentFocus == mWin) {
-                    if (WindowManagerService.DEBUG_FOCUS_LIGHT) Slog.i(TAG,
-                            "setAnimationLocked: setting mFocusMayChange true");
-                    mService.mFocusMayChange = true;
-                }
-                // Window is no longer visible -- make sure if we were waiting
-                // for it to be displayed before enabling the display, that
-                // we allow the display to be enabled now.
-                mService.enableScreenIfNeededLocked();
-            }
-        }
+        mStackClip = STACK_CLIP_BEFORE_ANIM;
+        mWin.checkPolicyVisibilityChange();
         mTransformation.clear();
         if (mDrawState == HAS_DRAWN
                 && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
@@ -426,8 +494,9 @@ class WindowStateAnimator {
         finishExit();
         final int displayId = mWin.getDisplayId();
         mAnimator.setPendingLayoutChanges(displayId, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
-        if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats(
-                "WindowStateAnimator", mAnimator.getPendingLayoutChanges(displayId));
+        if (DEBUG_LAYOUT_REPEATS)
+            mService.mWindowPlacerLocked.debugLayoutRepeats(
+                    "WindowStateAnimator", mAnimator.getPendingLayoutChanges(displayId));
 
         if (mWin.mAppToken != null) {
             mWin.mAppToken.updateReportedVisibilityLocked();
@@ -437,72 +506,85 @@ class WindowStateAnimator {
     }
 
     void finishExit() {
-        if (WindowManagerService.DEBUG_ANIM) Slog.v(
+        if (DEBUG_ANIM) Slog.v(
                 TAG, "finishExit in " + this
-                + ": exiting=" + mWin.mExiting
+                + ": exiting=" + mWin.mAnimatingExit
                 + " remove=" + mWin.mRemoveOnExit
-                + " windowAnimating=" + isWindowAnimating());
+                + " windowAnimating=" + isWindowAnimationSet());
 
-        final int N = mWin.mChildWindows.size();
-        for (int i=0; i<N; i++) {
-            mWin.mChildWindows.get(i).mWinAnimator.finishExit();
+        if (!mWin.mChildWindows.isEmpty()) {
+            // Copying to a different list as multiple children can be removed.
+            final WindowList childWindows = new WindowList(mWin.mChildWindows);
+            for (int i = childWindows.size() - 1; i >= 0; i--) {
+                childWindows.get(i).mWinAnimator.finishExit();
+            }
         }
 
-        if (mEnteringAnimation && mWin.mAppToken == null) {
-            try {
-                mEnteringAnimation = false;
-                mWin.mClient.dispatchWindowShown();
-            } catch (RemoteException e) {
+        if (mEnteringAnimation) {
+            mEnteringAnimation = false;
+            mService.requestTraversal();
+            // System windows don't have an activity and an app token as a result, but need a way
+            // to be informed about their entrance animation end.
+            if (mWin.mAppToken == null) {
+                try {
+                    mWin.mClient.dispatchWindowShown();
+                } catch (RemoteException e) {
+                }
             }
         }
 
-        if (!isWindowAnimating()) {
+        if (!isWindowAnimationSet()) {
             //TODO (multidisplay): Accessibility is supported only for the default display.
             if (mService.mAccessibilityController != null
-                    && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                    && mWin.getDisplayId() == DEFAULT_DISPLAY) {
                 mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
             }
         }
 
-        if (!mWin.mExiting) {
+        if (!mWin.mAnimatingExit) {
             return;
         }
 
-        if (isWindowAnimating()) {
+        if (isWindowAnimationSet()) {
             return;
         }
 
-        if (WindowManagerService.localLOGV) Slog.v(
-                TAG, "Exit animation finished in " + this
-                + ": remove=" + mWin.mRemoveOnExit);
-        if (mSurfaceControl != null) {
-            mService.mDestroySurface.add(mWin);
-            mWin.mDestroying = true;
-            if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(
-                mWin, "HIDE (finishExit)", null);
-            hide();
-        }
-        mWin.mExiting = false;
-        if (mWin.mRemoveOnExit) {
-            mService.mPendingRemove.add(mWin);
-            mWin.mRemoveOnExit = false;
-        }
-        mService.hideWallpapersLocked(mWin);
+        if (WindowManagerService.localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG,
+                "Exit animation finished in " + this + ": remove=" + mWin.mRemoveOnExit);
+
+
+        mWin.mDestroying = true;
+
+        final boolean hasSurface = hasSurface();
+        if (hasSurface) {
+            hide("finishExit");
+        }
+
+        // If we have an app token, we ask it to destroy the surface for us,
+        // so that it can take care to ensure the activity has actually stopped
+        // and the surface is not still in use. Otherwise we add the service to
+        // mDestroySurface and allow it to be processed in our next transaction.
+        if (mWin.mAppToken != null) {
+            mWin.mAppToken.destroySurfaces();
+        } else {
+            if (hasSurface) {
+                mService.mDestroySurface.add(mWin);
+            }
+            if (mWin.mRemoveOnExit) {
+                mService.mPendingRemove.add(mWin);
+                mWin.mRemoveOnExit = false;
+            }
+        }
+        mWin.mAnimatingExit = false;
+        mWallpaperControllerLocked.hideWallpapers(mWin);
     }
 
-    void hide() {
+    void hide(String reason) {
         if (!mLastHidden) {
             //dump();
             mLastHidden = true;
-            if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
-                    "HIDE (performLayout)", null);
-            if (mSurfaceControl != null) {
-                mSurfaceShown = false;
-                try {
-                    mSurfaceControl.hide();
-                } catch (RuntimeException e) {
-                    Slog.w(TAG, "Exception hiding surface in " + mWin);
-                }
+            if (mSurfaceController != null) {
+                mSurfaceController.hideInTransaction(reason);
             }
         }
     }
@@ -514,17 +596,21 @@ class WindowStateAnimator {
             Slog.v(TAG, "Finishing drawing window " + mWin + ": mDrawState="
                     + drawStateToString());
         }
+
+        boolean layoutNeeded = mWin.clearAnimatingWithSavedSurface();
+
         if (mDrawState == DRAW_PENDING) {
             if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
-                Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + this + " in "
-                        + mSurfaceControl);
+                Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + mWin + " in "
+                        + mSurfaceController);
             if (DEBUG_STARTING_WINDOW && startingWindow) {
                 Slog.v(TAG, "Draw state now committed in " + mWin);
             }
             mDrawState = COMMIT_DRAW_PENDING;
-            return true;
+            layoutNeeded = true;
         }
-        return false;
+
+        return layoutNeeded;
     }
 
     // This must be called while inside a transaction.
@@ -538,484 +624,298 @@ class WindowStateAnimator {
             return false;
         }
         if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) {
-            Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceControl);
+            Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceController);
         }
         mDrawState = READY_TO_SHOW;
+        boolean result = false;
         final AppWindowToken atoken = mWin.mAppToken;
         if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
-            return performShowLocked();
+            result = performShowLocked();
         }
-        return false;
+        return result;
     }
 
-    static class SurfaceTrace extends SurfaceControl {
-        private final static String SURFACE_TAG = "SurfaceTrace";
-        private final static boolean logSurfaceTrace = DEBUG_SURFACE_TRACE;
-        final static ArrayList<SurfaceTrace> sSurfaces = new ArrayList<SurfaceTrace>();
-
-        private float mSurfaceTraceAlpha = 0;
-        private int mLayer;
-        private final PointF mPosition = new PointF();
-        private final Point mSize = new Point();
-        private final Rect mWindowCrop = new Rect();
-        private boolean mShown = false;
-        private int mLayerStack;
-        private boolean mIsOpaque;
-        private float mDsdx, mDtdx, mDsdy, mDtdy;
-        private final String mName;
-
-        public SurfaceTrace(SurfaceSession s,
-                       String name, int w, int h, int format, int flags)
-                   throws OutOfResourcesException {
-            super(s, name, w, h, format, flags);
-            mName = name != null ? name : "Not named";
-            mSize.set(w, h);
-            if (logSurfaceTrace) Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
-                    + Debug.getCallers(3));
-            synchronized (sSurfaces) {
-                sSurfaces.add(0, this);
-            }
+    void preserveSurfaceLocked() {
+        if (mDestroyPreservedSurfaceUponRedraw) {
+            // This could happen when switching the surface mode very fast. For example,
+            // we preserved a surface when dragResizing changed to true. Then before the
+            // preserved surface is removed, dragResizing changed to false again.
+            // In this case, we need to leave the preserved surface alone, and destroy
+            // the actual surface, so that the createSurface call could create a surface
+            // of the proper size. The preserved surface will still be removed when client
+            // finishes drawing to the new surface.
+            mSurfaceDestroyDeferred = false;
+            destroySurfaceLocked();
+            mSurfaceDestroyDeferred = true;
+            return;
         }
-
-        @Override
-        public void setAlpha(float alpha) {
-            if (mSurfaceTraceAlpha != alpha) {
-                if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setAlpha(" + alpha + "): OLD:" + this +
-                        ". Called by " + Debug.getCallers(3));
-                mSurfaceTraceAlpha = alpha;
-            }
-            super.setAlpha(alpha);
+        if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", false);
+        if (mSurfaceController != null) {
+            mSurfaceController.setLayer(mAnimLayer + 1);
         }
+        mDestroyPreservedSurfaceUponRedraw = true;
+        mSurfaceDestroyDeferred = true;
+        destroySurfaceLocked();
+    }
 
-        @Override
-        public void setLayer(int zorder) {
-            if (zorder != mLayer) {
-                if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setLayer(" + zorder + "): OLD:" + this
-                        + ". Called by " + Debug.getCallers(3));
-                mLayer = zorder;
-            }
-            super.setLayer(zorder);
-
-            synchronized (sSurfaces) {
-                sSurfaces.remove(this);
-                int i;
-                for (i = sSurfaces.size() - 1; i >= 0; i--) {
-                    SurfaceTrace s = sSurfaces.get(i);
-                    if (s.mLayer < zorder) {
-                        break;
-                    }
-                }
-                sSurfaces.add(i + 1, this);
-            }
+    void destroyPreservedSurfaceLocked() {
+        if (!mDestroyPreservedSurfaceUponRedraw) {
+            return;
         }
+        destroyDeferredSurfaceLocked();
+        mDestroyPreservedSurfaceUponRedraw = false;
+    }
 
-        @Override
-        public void setPosition(float x, float y) {
-            if (x != mPosition.x || y != mPosition.y) {
-                if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setPosition(" + x + "," + y + "): OLD:"
-                        + this + ". Called by " + Debug.getCallers(3));
-                mPosition.set(x, y);
-            }
-            super.setPosition(x, y);
+    void markPreservedSurfaceForDestroy() {
+        if (mDestroyPreservedSurfaceUponRedraw
+                && !mService.mDestroyPreservedSurface.contains(mWin)) {
+            mService.mDestroyPreservedSurface.add(mWin);
         }
+    }
 
-        @Override
-        public void setSize(int w, int h) {
-            if (w != mSize.x || h != mSize.y) {
-                if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setSize(" + w + "," + h + "): OLD:"
-                        + this + ". Called by " + Debug.getCallers(3));
-                mSize.set(w, h);
-            }
-            super.setSize(w, h);
+    WindowSurfaceController createSurfaceLocked() {
+        final WindowState w = mWin;
+        if (w.hasSavedSurface()) {
+            if (DEBUG_ANIM) Slog.i(TAG,
+                    "createSurface: " + this + ": called when we had a saved surface");
+            w.restoreSavedSurface();
+            return mSurfaceController;
         }
 
-        @Override
-        public void setWindowCrop(Rect crop) {
-            if (crop != null) {
-                if (!crop.equals(mWindowCrop)) {
-                    if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setWindowCrop("
-                            + crop.toShortString() + "): OLD:" + this + ". Called by "
-                            + Debug.getCallers(3));
-                    mWindowCrop.set(crop);
-                }
-            }
-            super.setWindowCrop(crop);
+        if (mSurfaceController != null) {
+            return mSurfaceController;
         }
 
-        @Override
-        public void setLayerStack(int layerStack) {
-            if (layerStack != mLayerStack) {
-                if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setLayerStack(" + layerStack + "): OLD:"
-                        + this + ". Called by " + Debug.getCallers(3));
-                mLayerStack = layerStack;
-            }
-            super.setLayerStack(layerStack);
-        }
+        w.setHasSurface(false);
+
+        if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG,
+                "createSurface " + this + ": mDrawState=DRAW_PENDING");
 
-        @Override
-        public void setOpaque(boolean isOpaque) {
-            if (isOpaque != mIsOpaque) {
-                if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setOpaque(" + isOpaque + "): OLD:"
-                        + this + ". Called by " + Debug.getCallers(3));
-                mIsOpaque = isOpaque;
+        mDrawState = DRAW_PENDING;
+        if (w.mAppToken != null) {
+            if (w.mAppToken.mAppAnimator.animation == null) {
+                w.mAppToken.clearAllDrawn();
+            } else {
+                // Currently animating, persist current state of allDrawn until animation
+                // is complete.
+                w.mAppToken.deferClearAllDrawn = true;
             }
-            super.setOpaque(isOpaque);
         }
 
-        @Override
-        public void setSecure(boolean isSecure) {
-            super.setSecure(isSecure);
-        }
+        mService.makeWindowFreezingScreenIfNeededLocked(w);
 
-        @Override
-        public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
-            if (dsdx != mDsdx || dtdx != mDtdx || dsdy != mDsdy || dtdy != mDtdy) {
-                if (logSurfaceTrace) Slog.v(SURFACE_TAG, "setMatrix(" + dsdx + "," + dtdx + ","
-                        + dsdy + "," + dtdy + "): OLD:" + this + ". Called by "
-                        + Debug.getCallers(3));
-                mDsdx = dsdx;
-                mDtdx = dtdx;
-                mDsdy = dsdy;
-                mDtdy = dtdy;
-            }
-            super.setMatrix(dsdx, dtdx, dsdy, dtdy);
-        }
+        int flags = SurfaceControl.HIDDEN;
+        final WindowManager.LayoutParams attrs = w.mAttrs;
 
-        @Override
-        public void hide() {
-            if (mShown) {
-                if (logSurfaceTrace) Slog.v(SURFACE_TAG, "hide: OLD:" + this + ". Called by "
-                        + Debug.getCallers(3));
-                mShown = false;
-            }
-            super.hide();
+        if (mService.isSecureLocked(w)) {
+            flags |= SurfaceControl.SECURE;
         }
 
-        @Override
-        public void show() {
-            if (!mShown) {
-                if (logSurfaceTrace) Slog.v(SURFACE_TAG, "show: OLD:" + this + ". Called by "
-                        + Debug.getCallers(3));
-                mShown = true;
-            }
-            super.show();
-        }
+        mTmpSize.set(w.mFrame.left + w.mXOffset, w.mFrame.top + w.mYOffset, 0, 0);
+        calculateSurfaceBounds(w, attrs);
+        final int width = mTmpSize.width();
+        final int height = mTmpSize.height();
 
-        @Override
-        public void destroy() {
-            super.destroy();
-            if (logSurfaceTrace) Slog.v(SURFACE_TAG, "destroy: " + this + ". Called by "
-                    + Debug.getCallers(3));
-            synchronized (sSurfaces) {
-                sSurfaces.remove(this);
-            }
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG, "Creating surface in session "
+                    + mSession.mSurfaceSession + " window " + this
+                    + " w=" + width + " h=" + height
+                    + " x=" + mTmpSize.left + " y=" + mTmpSize.top
+                    + " format=" + attrs.format + " flags=" + flags);
         }
 
-        @Override
-        public void release() {
-            super.release();
-            if (logSurfaceTrace) Slog.v(SURFACE_TAG, "release: " + this + ". Called by "
-                    + Debug.getCallers(3));
-            synchronized (sSurfaces) {
-                sSurfaces.remove(this);
-            }
-        }
+        // We may abort, so initialize to defaults.
+        mLastSystemDecorRect.set(0, 0, 0, 0);
+        mHasClipRect = false;
+        mClipRect.set(0, 0, 0, 0);
+        mLastClipRect.set(0, 0, 0, 0);
 
-        static void dumpAllSurfaces(PrintWriter pw, String header) {
-            synchronized (sSurfaces) {
-                final int N = sSurfaces.size();
-                if (N <= 0) {
-                    return;
-                }
-                if (header != null) {
-                    pw.println(header);
-                }
-                pw.println("WINDOW MANAGER SURFACES (dumpsys window surfaces)");
-                for (int i = 0; i < N; i++) {
-                    SurfaceTrace s = sSurfaces.get(i);
-                    pw.print("  Surface #"); pw.print(i); pw.print(": #");
-                            pw.print(Integer.toHexString(System.identityHashCode(s)));
-                            pw.print(" "); pw.println(s.mName);
-                    pw.print("    mLayerStack="); pw.print(s.mLayerStack);
-                            pw.print(" mLayer="); pw.println(s.mLayer);
-                    pw.print("    mShown="); pw.print(s.mShown); pw.print(" mAlpha=");
-                            pw.print(s.mSurfaceTraceAlpha); pw.print(" mIsOpaque=");
-                            pw.println(s.mIsOpaque);
-                    pw.print("    mPosition="); pw.print(s.mPosition.x); pw.print(",");
-                            pw.print(s.mPosition.y);
-                            pw.print(" mSize="); pw.print(s.mSize.x); pw.print("x");
-                            pw.println(s.mSize.y);
-                    pw.print("    mCrop="); s.mWindowCrop.printShortString(pw); pw.println();
-                    pw.print("    Transform: ("); pw.print(s.mDsdx); pw.print(", ");
-                            pw.print(s.mDtdx); pw.print(", "); pw.print(s.mDsdy);
-                            pw.print(", "); pw.print(s.mDtdy); pw.println(")");
-                }
-            }
-        }
+        // Set up surface control with initial size.
+        try {
 
-        @Override
-        public String toString() {
-            return "Surface " + Integer.toHexString(System.identityHashCode(this)) + " "
-                    + mName + " (" + mLayerStack + "): shown=" + mShown + " layer=" + mLayer
-                    + " alpha=" + mSurfaceTraceAlpha + " " + mPosition.x + "," + mPosition.y
-                    + " " + mSize.x + "x" + mSize.y
-                    + " crop=" + mWindowCrop.toShortString()
-                    + " opaque=" + mIsOpaque
-                    + " (" + mDsdx + "," + mDtdx + "," + mDsdy + "," + mDtdy + ")";
+            final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0;
+            final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
+            if (!PixelFormat.formatHasAlpha(attrs.format)
+                    // Don't make surface with surfaceInsets opaque as they display a
+                    // translucent shadow.
+                    && attrs.surfaceInsets.left == 0
+                    && attrs.surfaceInsets.top == 0
+                    && attrs.surfaceInsets.right == 0
+                    && attrs.surfaceInsets.bottom == 0
+                    // Don't make surface opaque when resizing to reduce the amount of
+                    // artifacts shown in areas the app isn't drawing content to.
+                    && !w.isDragResizing()) {
+                flags |= SurfaceControl.OPAQUE;
+            }
+
+            mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
+                    attrs.getTitle().toString(),
+                    width, height, format, flags, this);
+
+            w.setHasSurface(true);
+
+            if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+                Slog.i(TAG, "  CREATE SURFACE "
+                        + mSurfaceController + " IN SESSION "
+                        + mSession.mSurfaceSession
+                        + ": pid=" + mSession.mPid + " format="
+                        + attrs.format + " flags=0x"
+                        + Integer.toHexString(flags)
+                        + " / " + this);
+            }
+        } catch (OutOfResourcesException e) {
+            Slog.w(TAG, "OutOfResourcesException creating surface");
+            mService.reclaimSomeSurfaceMemoryLocked(this, "create", true);
+            mDrawState = NO_SURFACE;
+            return null;
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception creating surface", e);
+            mDrawState = NO_SURFACE;
+            return null;
         }
-    }
 
-    SurfaceControl createSurfaceLocked() {
-        final WindowState w = mWin;
-        if (mSurfaceControl == null) {
-            if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG,
-                    "createSurface " + this + ": mDrawState=DRAW_PENDING");
-            mDrawState = DRAW_PENDING;
-            if (w.mAppToken != null) {
-                if (w.mAppToken.mAppAnimator.animation == null) {
-                    w.mAppToken.allDrawn = false;
-                    w.mAppToken.deferClearAllDrawn = false;
-                } else {
-                    // Currently animating, persist current state of allDrawn until animation
-                    // is complete.
-                    w.mAppToken.deferClearAllDrawn = true;
-                }
-            }
+        if (WindowManagerService.localLOGV) Slog.v(TAG, "Got surface: " + mSurfaceController
+                + ", set left=" + w.mFrame.left + " top=" + w.mFrame.top
+                + ", animLayer=" + mAnimLayer);
 
-            mService.makeWindowFreezingScreenIfNeededLocked(w);
+        if (SHOW_LIGHT_TRANSACTIONS) {
+            Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
+            WindowManagerService.logSurface(w, "CREATE pos=("
+                    + w.mFrame.left + "," + w.mFrame.top + ") ("
+                    + width + "x" + height + "), layer=" + mAnimLayer + " HIDE", false);
+        }
 
-            int flags = SurfaceControl.HIDDEN;
-            final WindowManager.LayoutParams attrs = w.mAttrs;
+        // Start a new transaction and apply position & offset.
+        final int layerStack = w.getDisplayContent().getDisplay().getLayerStack();
+        mSurfaceController.setPositionAndLayer(mTmpSize.left, mTmpSize.top, layerStack, mAnimLayer);
+        mLastHidden = true;
 
-            if (mService.isSecureLocked(w)) {
-                flags |= SurfaceControl.SECURE;
-            }
+        if (WindowManagerService.localLOGV) Slog.v(TAG, "Created surface " + this);
+        return mSurfaceController;
+    }
 
-            int width;
-            int height;
-            if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
-                // for a scaled surface, we always want the requested
-                // size.
-                width = w.mRequestedWidth;
-                height = w.mRequestedHeight;
+    private void calculateSurfaceBounds(WindowState w, LayoutParams attrs) {
+        if ((attrs.flags & FLAG_SCALED) != 0) {
+            // For a scaled surface, we always want the requested size.
+            mTmpSize.right = mTmpSize.left + w.mRequestedWidth;
+            mTmpSize.bottom = mTmpSize.top + w.mRequestedHeight;
+        } else {
+            // When we're doing a drag-resizing, request a surface that's fullscreen size,
+            // so that we don't need to reallocate during the process. This also prevents
+            // buffer drops due to size mismatch.
+            if (w.isDragResizing()) {
+                if (w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM) {
+                    mTmpSize.left = 0;
+                    mTmpSize.top = 0;
+                }
+                final DisplayInfo displayInfo = w.getDisplayInfo();
+                mTmpSize.right = mTmpSize.left + displayInfo.logicalWidth;
+                mTmpSize.bottom = mTmpSize.top + displayInfo.logicalHeight;
             } else {
-                width = w.mCompatFrame.width();
-                height = w.mCompatFrame.height();
-            }
-
-            // Something is wrong and SurfaceFlinger will not like this,
-            // try to revert to sane values
-            if (width <= 0) {
-                width = 1;
+                mTmpSize.right = mTmpSize.left + w.mCompatFrame.width();
+                mTmpSize.bottom = mTmpSize.top + w.mCompatFrame.height();
             }
-            if (height <= 0) {
-                height = 1;
-            }
-
-            float left = w.mFrame.left + w.mXOffset;
-            float top = w.mFrame.top + w.mYOffset;
-
-            // Adjust for surface insets.
-            width += attrs.surfaceInsets.left + attrs.surfaceInsets.right;
-            height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom;
-            left -= attrs.surfaceInsets.left;
-            top -= attrs.surfaceInsets.top;
-
-            if (DEBUG_VISIBILITY) {
-                Slog.v(TAG, "Creating surface in session "
-                        + mSession.mSurfaceSession + " window " + this
-                        + " w=" + width + " h=" + height
-                        + " x=" + left + " y=" + top
-                        + " format=" + attrs.format + " flags=" + flags);
-            }
-
-            // We may abort, so initialize to defaults.
-            mSurfaceShown = false;
-            mSurfaceLayer = 0;
-            mSurfaceAlpha = 0;
-            mSurfaceX = 0;
-            mSurfaceY = 0;
-            w.mLastSystemDecorRect.set(0, 0, 0, 0);
-            mHasClipRect = false;
-            mClipRect.set(0, 0, 0, 0);
-            mLastClipRect.set(0, 0, 0, 0);
-
-            // Set up surface control with initial size.
-            try {
-                mSurfaceW = width;
-                mSurfaceH = height;
-
-                final boolean isHwAccelerated = (attrs.flags &
-                        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
-                final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
-                if (!PixelFormat.formatHasAlpha(attrs.format)
-                        && attrs.surfaceInsets.left == 0
-                        && attrs.surfaceInsets.top == 0
-                        && attrs.surfaceInsets.right == 0
-                        && attrs.surfaceInsets.bottom  == 0) {
-                    flags |= SurfaceControl.OPAQUE;
-                }
-
-                mSurfaceFormat = format;
-                if (DEBUG_SURFACE_TRACE) {
-                    mSurfaceControl = new SurfaceTrace(
-                            mSession.mSurfaceSession,
-                            attrs.getTitle().toString(),
-                            width, height, format, flags);
-                } else {
-                    mSurfaceControl = new SurfaceControl(
-                        mSession.mSurfaceSession,
-                        attrs.getTitle().toString(),
-                        width, height, format, flags);
-                }
+        }
 
-                w.mHasSurface = true;
+        // Something is wrong and SurfaceFlinger will not like this, try to revert to sane values.
+        // This doesn't necessarily mean that there is an error in the system. The sizes might be
+        // incorrect, because it is before the first layout or draw.
+        if (mTmpSize.width() < 1) {
+            mTmpSize.right = mTmpSize.left + 1;
+        }
+        if (mTmpSize.height() < 1) {
+            mTmpSize.bottom = mTmpSize.top + 1;
+        }
 
-                if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
-                    Slog.i(TAG, "  CREATE SURFACE "
-                            + mSurfaceControl + " IN SESSION "
-                            + mSession.mSurfaceSession
-                            + ": pid=" + mSession.mPid + " format="
-                            + attrs.format + " flags=0x"
-                            + Integer.toHexString(flags)
-                            + " / " + this);
-                }
-            } catch (OutOfResourcesException e) {
-                w.mHasSurface = false;
-                Slog.w(TAG, "OutOfResourcesException creating surface");
-                mService.reclaimSomeSurfaceMemoryLocked(this, "create", true);
-                mDrawState = NO_SURFACE;
-                return null;
-            } catch (Exception e) {
-                w.mHasSurface = false;
-                Slog.e(TAG, "Exception creating surface", e);
-                mDrawState = NO_SURFACE;
-                return null;
-            }
+        // Adjust for surface insets.
+        mTmpSize.left -= attrs.surfaceInsets.left;
+        mTmpSize.top -= attrs.surfaceInsets.top;
+        mTmpSize.right += attrs.surfaceInsets.right;
+        mTmpSize.bottom += attrs.surfaceInsets.bottom;
+    }
 
-            if (WindowManagerService.localLOGV) {
-                Slog.v(TAG, "Got surface: " + mSurfaceControl
-                        + ", set left=" + w.mFrame.left + " top=" + w.mFrame.top
-                        + ", animLayer=" + mAnimLayer);
-            }
+    boolean hasSurface() {
+        return !mWin.hasSavedSurface()
+                && mSurfaceController != null && mSurfaceController.hasSurface();
+    }
 
-            if (SHOW_LIGHT_TRANSACTIONS) {
-                Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
-                WindowManagerService.logSurface(w, "CREATE pos=("
-                        + w.mFrame.left + "," + w.mFrame.top + ") ("
-                        + w.mCompatFrame.width() + "x" + w.mCompatFrame.height()
-                        + "), layer=" + mAnimLayer + " HIDE", null);
+    void destroySurfaceLocked() {
+        final AppWindowToken wtoken = mWin.mAppToken;
+        if (wtoken != null) {
+            if (mWin == wtoken.startingWindow) {
+                wtoken.startingDisplayed = false;
             }
+        }
 
-            // Start a new transaction and apply position & offset.
-            SurfaceControl.openTransaction();
-            try {
-                mSurfaceX = left;
-                mSurfaceY = top;
+        mWin.clearHasSavedSurface();
 
-                try {
-                    mSurfaceControl.setPosition(left, top);
-                    mSurfaceLayer = mAnimLayer;
-                    final DisplayContent displayContent = w.getDisplayContent();
-                    if (displayContent != null) {
-                        mSurfaceControl.setLayerStack(displayContent.getDisplay().getLayerStack());
-                    }
-                    mSurfaceControl.setLayer(mAnimLayer);
-                    mSurfaceControl.setAlpha(0);
-                    mSurfaceShown = false;
-                } catch (RuntimeException e) {
-                    Slog.w(TAG, "Error creating surface in " + w, e);
-                    mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true);
-                }
-                mLastHidden = true;
-            } finally {
-                SurfaceControl.closeTransaction();
-                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
-                        "<<< CLOSE TRANSACTION createSurfaceLocked");
-            }
-            if (WindowManagerService.localLOGV) Slog.v(
-                    TAG, "Created surface " + this);
+        if (mSurfaceController == null) {
+            return;
         }
-        return mSurfaceControl;
-    }
 
-    void destroySurfaceLocked() {
-        if (mWin.mAppToken != null && mWin == mWin.mAppToken.startingWindow) {
-            mWin.mAppToken.startingDisplayed = false;
+        int i = mWin.mChildWindows.size();
+        // When destroying a surface we want to make sure child windows are hidden. If we are
+        // preserving the surface until redraw though we intend to swap it out with another surface
+        // for resizing. In this case the window always remains visible to the user and the child
+        // windows should likewise remain visible.
+        while (!mDestroyPreservedSurfaceUponRedraw && i > 0) {
+            i--;
+            WindowState c = mWin.mChildWindows.get(i);
+            c.mAttachedHidden = true;
         }
 
-        if (mSurfaceControl != null) {
-
-            int i = mWin.mChildWindows.size();
-            while (i > 0) {
-                i--;
-                WindowState c = mWin.mChildWindows.get(i);
-                c.mAttachedHidden = true;
-            }
-
-            try {
-                if (DEBUG_VISIBILITY) {
-                    RuntimeException e = null;
-                    if (!WindowManagerService.HIDE_STACK_CRAWLS) {
-                        e = new RuntimeException();
-                        e.fillInStackTrace();
-                    }
-                    Slog.w(TAG, "Window " + this + " destroying surface "
-                            + mSurfaceControl + ", session " + mSession, e);
-                }
-                if (mSurfaceDestroyDeferred) {
-                    if (mSurfaceControl != null && mPendingDestroySurface != mSurfaceControl) {
-                        if (mPendingDestroySurface != null) {
-                            if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
-                                RuntimeException e = null;
-                                if (!WindowManagerService.HIDE_STACK_CRAWLS) {
-                                    e = new RuntimeException();
-                                    e.fillInStackTrace();
-                                }
-                                WindowManagerService.logSurface(mWin, "DESTROY PENDING", e);
-                            }
-                            mPendingDestroySurface.destroy();
-                        }
-                        mPendingDestroySurface = mSurfaceControl;
-                    }
-                } else {
-                    if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
-                        RuntimeException e = null;
-                        if (!WindowManagerService.HIDE_STACK_CRAWLS) {
-                            e = new RuntimeException();
-                            e.fillInStackTrace();
+        try {
+            if (DEBUG_VISIBILITY) logWithStack(TAG, "Window " + this + " destroying surface "
+                    + mSurfaceController + ", session " + mSession);
+            if (mSurfaceDestroyDeferred) {
+                if (mSurfaceController != null && mPendingDestroySurface != mSurfaceController) {
+                    if (mPendingDestroySurface != null) {
+                        if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+                            WindowManagerService.logSurface(mWin, "DESTROY PENDING", true);
                         }
-                        WindowManagerService.logSurface(mWin, "DESTROY", e);
+                        mPendingDestroySurface.destroyInTransaction();
                     }
-                    mSurfaceControl.destroy();
+                    mPendingDestroySurface = mSurfaceController;
+                }
+            } else {
+                if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+                    WindowManagerService.logSurface(mWin, "DESTROY", true);
                 }
-                mService.hideWallpapersLocked(mWin);
-            } catch (RuntimeException e) {
-                Slog.w(TAG, "Exception thrown when destroying Window " + this
-                    + " surface " + mSurfaceControl + " session " + mSession
-                    + ": " + e.toString());
+                destroySurface();
+            }
+            // Don't hide wallpaper if we're deferring the surface destroy
+            // because of a surface change.
+            if (!mDestroyPreservedSurfaceUponRedraw) {
+                mWallpaperControllerLocked.hideWallpapers(mWin);
             }
+        } catch (RuntimeException e) {
+            Slog.w(TAG, "Exception thrown when destroying Window " + this
+                + " surface " + mSurfaceController + " session " + mSession + ": " + e.toString());
+        }
 
-            mSurfaceShown = false;
-            mSurfaceControl = null;
-            mWin.mHasSurface = false;
-            mDrawState = NO_SURFACE;
+        // Whether the surface was preserved (and copied to mPendingDestroySurface) or not, it
+        // needs to be cleared to match the WindowState.mHasSurface state. It is also necessary
+        // so it can be recreated successfully in mPendingDestroySurface case.
+        mWin.setHasSurface(false);
+        if (mSurfaceController != null) {
+            mSurfaceController.setShown(false);
         }
+        mSurfaceController = null;
+        mDrawState = NO_SURFACE;
     }
 
     void destroyDeferredSurfaceLocked() {
         try {
             if (mPendingDestroySurface != null) {
                 if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
-                    RuntimeException e = null;
-                    if (!WindowManagerService.HIDE_STACK_CRAWLS) {
-                        e = new RuntimeException();
-                        e.fillInStackTrace();
-                    }
-                    WindowManagerService.logSurface(mWin, "DESTROY PENDING", e);
+                    WindowManagerService.logSurface(mWin, "DESTROY PENDING", true);
+                }
+                mPendingDestroySurface.destroyInTransaction();
+                // Don't hide wallpaper if we're destroying a deferred surface
+                // after a surface mode change.
+                if (!mDestroyPreservedSurfaceUponRedraw) {
+                    mWallpaperControllerLocked.hideWallpapers(mWin);
                 }
-                mPendingDestroySurface.destroy();
-                mService.hideWallpapersLocked(mWin);
             }
         } catch (RuntimeException e) {
             Slog.w(TAG, "Exception thrown when destroying Window "
@@ -1026,6 +926,22 @@ class WindowStateAnimator {
         mPendingDestroySurface = null;
     }
 
+    void applyMagnificationSpec(MagnificationSpec spec, Matrix transform) {
+        final int surfaceInsetLeft = mWin.mAttrs.surfaceInsets.left;
+        final int surfaceInsetTop = mWin.mAttrs.surfaceInsets.top;
+
+        if (spec != null && !spec.isNop()) {
+            float scale = spec.scale;
+            transform.postScale(scale, scale);
+            transform.postTranslate(spec.offsetX, spec.offsetY);
+
+            // As we are scaling the whole surface, to keep the content
+            // in the same position we will also have to scale the surfaceInsets.
+            transform.postTranslate(-(surfaceInsetLeft*scale - surfaceInsetLeft),
+                    -(surfaceInsetTop*scale - surfaceInsetTop));
+        }
+    }
+
     void computeShownFrameLocked() {
         final boolean selfTransformation = mHasLocalTransformation;
         Transformation attachedTransformation =
@@ -1036,14 +952,14 @@ class WindowStateAnimator {
 
         // Wallpapers are animated based on the "real" window they
         // are currently targeting.
-        final WindowState wallpaperTarget = mService.mWallpaperTarget;
+        final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
         if (mIsWallpaper && wallpaperTarget != null && mService.mAnimateWallpaperWithTarget) {
             final WindowStateAnimator wallpaperAnimator = wallpaperTarget.mWinAnimator;
             if (wallpaperAnimator.mHasLocalTransformation &&
                     wallpaperAnimator.mAnimation != null &&
                     !wallpaperAnimator.mAnimation.getDetachWallpaper()) {
                 attachedTransformation = wallpaperAnimator.mTransformation;
-                if (WindowManagerService.DEBUG_WALLPAPER && attachedTransformation != null) {
+                if (DEBUG_WALLPAPER && attachedTransformation != null) {
                     Slog.v(TAG, "WP target attached xform: " + attachedTransformation);
                 }
             }
@@ -1053,7 +969,7 @@ class WindowStateAnimator {
                     && wpAppAnimator.animation != null
                     && !wpAppAnimator.animation.getDetachWallpaper()) {
                 appTransformation = wpAppAnimator.transformation;
-                if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) {
+                if (DEBUG_WALLPAPER && appTransformation != null) {
                     Slog.v(TAG, "WP target app xform: " + appTransformation);
                 }
             }
@@ -1096,32 +1012,34 @@ class WindowStateAnimator {
             if (selfTransformation) {
                 tmpMatrix.postConcat(mTransformation.getMatrix());
             }
-            tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
             if (attachedTransformation != null) {
                 tmpMatrix.postConcat(attachedTransformation.getMatrix());
             }
             if (appTransformation != null) {
                 tmpMatrix.postConcat(appTransformation.getMatrix());
             }
+
+            // The translation that applies the position of the window needs to be applied at the
+            // end in case that other translations include scaling. Otherwise the scaling will
+            // affect this translation. But it needs to be set before the screen rotation animation
+            // so the pivot point is at the center of the screen for all windows.
+            tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
             if (screenAnimation) {
                 tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
             }
 
             //TODO (multidisplay): Magnification is supported only for the default display.
-            if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
+            if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
                 MagnificationSpec spec = mService.mAccessibilityController
                         .getMagnificationSpecForWindowLocked(mWin);
-                if (spec != null && !spec.isNop()) {
-                    tmpMatrix.postScale(spec.scale, spec.scale);
-                    tmpMatrix.postTranslate(spec.offsetX, spec.offsetY);
-                }
+                applyMagnificationSpec(spec, tmpMatrix);
             }
 
             // "convert" it into SurfaceFlinger's format
             // (a 2x2 matrix + an offset)
             // Here we must not transform the position of the surface
             // since it is already included in the transformation.
-            //Slog.i(TAG, "Transform: " + matrix);
+            //Slog.i(TAG_WM, "Transform: " + matrix);
 
             mHaveMatrix = true;
             tmpMatrix.getValues(tmpFloats);
@@ -1131,9 +1049,7 @@ class WindowStateAnimator {
             mDtDy = tmpFloats[Matrix.MSCALE_Y];
             float x = tmpFloats[Matrix.MTRANS_X];
             float y = tmpFloats[Matrix.MTRANS_Y];
-            int w = frame.width();
-            int h = frame.height();
-            mWin.mShownFrame.set(x, y, x+w, y+h);
+            mWin.mShownPosition.set((int) x, (int) y);
 
             // Now set the alpha...  but because our current hardware
             // can't do alpha transformation on a non-opaque surface,
@@ -1145,7 +1061,7 @@ class WindowStateAnimator {
                     || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
                     || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
                             && x == frame.left && y == frame.top))) {
-                //Slog.i(TAG, "Applying alpha transform");
+                //Slog.i(TAG_WM, "Applying alpha transform");
                 if (selfTransformation) {
                     mShownAlpha *= mTransformation.getAlpha();
                 }
@@ -1156,22 +1072,25 @@ class WindowStateAnimator {
                     mShownAlpha *= appTransformation.getAlpha();
                     if (appTransformation.hasClipRect()) {
                         mClipRect.set(appTransformation.getClipRect());
-                        if (mWin.mHScale > 0) {
-                            mClipRect.left /= mWin.mHScale;
-                            mClipRect.right /= mWin.mHScale;
-                        }
-                        if (mWin.mVScale > 0) {
-                            mClipRect.top /= mWin.mVScale;
-                            mClipRect.bottom /= mWin.mVScale;
-                        }
                         mHasClipRect = true;
+                        // The app transformation clip will be in the coordinate space of the main
+                        // activity window, which the animation correctly assumes will be placed at
+                        // (0,0)+(insets) relative to the containing frame. This isn't necessarily
+                        // true for child windows though which can have an arbitrary frame position
+                        // relative to their containing frame. We need to offset the difference
+                        // between the containing frame as used to calculate the crop and our
+                        // bounds to compensate for this.
+                        if (mWin.layoutInParentFrame()) {
+                            mClipRect.offset( (mWin.mContainingFrame.left - mWin.mFrame.left),
+                                    mWin.mContainingFrame.top - mWin.mFrame.top );
+                        }
                     }
                 }
                 if (screenAnimation) {
                     mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha();
                 }
             } else {
-                //Slog.i(TAG, "Not applying alpha transform");
+                //Slog.i(TAG_WM, "Not applying alpha transform");
             }
 
             if ((DEBUG_SURFACE_TRACE || WindowManagerService.localLOGV)
@@ -1184,7 +1103,14 @@ class WindowStateAnimator {
                     + " screen=" + (screenAnimation ?
                             screenRotationAnimation.getEnterTransformation().getAlpha() : "null"));
             return;
-        } else if (mIsWallpaper && mService.mInnerFields.mWallpaperActionPending) {
+        } else if (mIsWallpaper && mService.mWindowPlacerLocked.mWallpaperActionPending) {
+            return;
+        } else if (mWin.isDragResizeChanged()) {
+            // This window is awaiting a relayout because user just started (or ended)
+            // drag-resizing. The shown frame (which affects surface size and pos)
+            // should not be updated until we get next finished draw with the new surface.
+            // Otherwise one or two frames rendered with old settings would be displayed
+            // with new geometry.
             return;
         }
 
@@ -1194,7 +1120,7 @@ class WindowStateAnimator {
 
         MagnificationSpec spec = null;
         //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
+        if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
             spec = mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
         }
         if (spec != null) {
@@ -1205,10 +1131,7 @@ class WindowStateAnimator {
             tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
             tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
 
-            if (spec != null && !spec.isNop()) {
-                tmpMatrix.postScale(spec.scale, spec.scale);
-                tmpMatrix.postTranslate(spec.offsetX, spec.offsetY);
-            }
+            applyMagnificationSpec(spec, tmpMatrix);
 
             tmpMatrix.getValues(tmpFloats);
 
@@ -1219,15 +1142,13 @@ class WindowStateAnimator {
             mDtDy = tmpFloats[Matrix.MSCALE_Y];
             float x = tmpFloats[Matrix.MTRANS_X];
             float y = tmpFloats[Matrix.MTRANS_Y];
-            int w = frame.width();
-            int h = frame.height();
-            mWin.mShownFrame.set(x, y, x + w, y + h);
+            mWin.mShownPosition.set((int) x, (int) y);
 
             mShownAlpha = mAlpha;
         } else {
-            mWin.mShownFrame.set(mWin.mFrame);
+            mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
             if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
-                mWin.mShownFrame.offset(mWin.mXOffset, mWin.mYOffset);
+                mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
             }
             mShownAlpha = mAlpha;
             mHaveMatrix = false;
@@ -1238,8 +1159,9 @@ class WindowStateAnimator {
         }
     }
 
-    private void applyDecorRect(final Rect decorRect) {
+    private void calculateSystemDecorRect() {
         final WindowState w = mWin;
+        final Rect decorRect = w.mDecorFrame;
         final int width = w.mFrame.width();
         final int height = w.mFrame.height();
 
@@ -1248,11 +1170,34 @@ class WindowStateAnimator {
         final int top = w.mYOffset + w.mFrame.top;
 
         // Initialize the decor rect to the entire frame.
-        w.mSystemDecorRect.set(0, 0, width, height);
+        if (w.isDockedResizing() ||
+                (w.isChildWindow() && w.mAttachedWindow.isDockedResizing())) {
+
+            // If we are resizing with the divider, the task bounds might be smaller than the
+            // stack bounds. The system decor is used to clip to the task bounds, which we don't
+            // want in this case in order to avoid holes.
+            //
+            // We take care to not shrink the width, for surfaces which are larger than
+            // the display region. Of course this area will not eventually be visible
+            // but if we truncate the width now, we will calculate incorrectly
+            // when adjusting to the stack bounds.
+            final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
+            mSystemDecorRect.set(0, 0,
+                    Math.max(width, displayInfo.logicalWidth),
+                    Math.max(height, displayInfo.logicalHeight));
+        } else {
+            mSystemDecorRect.set(0, 0, width, height);
+        }
 
-        // Intersect with the decor rect, offsetted by window position.
-        w.mSystemDecorRect.intersect(decorRect.left - left, decorRect.top - top,
-                decorRect.right - left, decorRect.bottom - top);
+        // If a freeform window is animating from a position where it would be cutoff, it would be
+        // cutoff during the animation. We don't want that, so for the duration of the animation
+        // we ignore the decor cropping and depend on layering to position windows correctly.
+        final boolean cropToDecor = !(w.inFreeformWorkspace() && w.isAnimatingLw());
+        if (cropToDecor) {
+            // Intersect with the decor rect, offsetted by window position.
+            mSystemDecorRect.intersect(decorRect.left - left, decorRect.top - top,
+                    decorRect.right - left, decorRect.bottom - top);
+        }
 
         // If size compatibility is being applied to the window, the
         // surface is scaled relative to the screen.  Also apply this
@@ -1262,55 +1207,67 @@ class WindowStateAnimator {
         // much and hide part of the window that should be seen.
         if (w.mEnforceSizeCompat && w.mInvGlobalScale != 1.0f) {
             final float scale = w.mInvGlobalScale;
-            w.mSystemDecorRect.left = (int) (w.mSystemDecorRect.left * scale - 0.5f);
-            w.mSystemDecorRect.top = (int) (w.mSystemDecorRect.top * scale - 0.5f);
-            w.mSystemDecorRect.right = (int) ((w.mSystemDecorRect.right+1) * scale - 0.5f);
-            w.mSystemDecorRect.bottom = (int) ((w.mSystemDecorRect.bottom+1) * scale - 0.5f);
+            mSystemDecorRect.left = (int) (mSystemDecorRect.left * scale - 0.5f);
+            mSystemDecorRect.top = (int) (mSystemDecorRect.top * scale - 0.5f);
+            mSystemDecorRect.right = (int) ((mSystemDecorRect.right + 1) * scale - 0.5f);
+            mSystemDecorRect.bottom = (int) ((mSystemDecorRect.bottom + 1) * scale - 0.5f);
         }
     }
 
-    void updateSurfaceWindowCrop(final boolean recoveringMemory) {
+    void calculateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect) {
         final WindowState w = mWin;
         final DisplayContent displayContent = w.getDisplayContent();
         if (displayContent == null) {
+            clipRect.setEmpty();
+            finalClipRect.setEmpty();
             return;
         }
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        if (DEBUG_WINDOW_CROP) Slog.d(TAG,
+                "Updating crop win=" + w + " mLastCrop=" + mLastClipRect);
 
         // Need to recompute a new system decor rect each time.
-        if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
-            // Currently can't do this cropping for scaled windows.  We'll
-            // just keep the crop rect the same as the source surface.
-            w.mSystemDecorRect.set(0, 0, w.mRequestedWidth, w.mRequestedHeight);
-        } else if (!w.isDefaultDisplay()) {
+        if (!w.isDefaultDisplay()) {
             // On a different display there is no system decor.  Crop the window
             // by the screen boundaries.
-            w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
-            w.mSystemDecorRect.intersect(-w.mCompatFrame.left, -w.mCompatFrame.top,
+            mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
+            mSystemDecorRect.intersect(-w.mCompatFrame.left, -w.mCompatFrame.top,
                     displayInfo.logicalWidth - w.mCompatFrame.left,
                     displayInfo.logicalHeight - w.mCompatFrame.top);
         } else if (w.mLayer >= mService.mSystemDecorLayer) {
             // Above the decor layer is easy, just use the entire window.
-            w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
+            mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
         } else if (w.mDecorFrame.isEmpty()) {
             // Windows without policy decor aren't cropped.
-            w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
-        } else if (w.mAttrs.type == LayoutParams.TYPE_WALLPAPER && mAnimator.mAnimating) {
+            mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
+        } else if (w.mAttrs.type == LayoutParams.TYPE_WALLPAPER && mAnimator.isAnimating()) {
             // If we're animating, the wallpaper crop should only be updated at the end of the
             // animation.
-            mTmpClipRect.set(w.mSystemDecorRect);
-            applyDecorRect(w.mDecorFrame);
-            w.mSystemDecorRect.union(mTmpClipRect);
+            mTmpClipRect.set(mSystemDecorRect);
+            calculateSystemDecorRect();
+            mSystemDecorRect.union(mTmpClipRect);
         } else {
             // Crop to the system decor specified by policy.
-            applyDecorRect(w.mDecorFrame);
+            calculateSystemDecorRect();
+            if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop win=" + w + " mDecorFrame="
+                    + w.mDecorFrame + " mSystemDecorRect=" + mSystemDecorRect);
         }
 
-        final boolean fullscreen = w.isFullscreen(displayInfo.appWidth, displayInfo.appHeight);
-        final Rect clipRect = mTmpClipRect;
+        final boolean fullscreen = w.isFrameFullscreen(displayInfo);
+        final boolean isFreeformResizing =
+                w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
+
         // We use the clip rect as provided by the tranformation for non-fullscreen windows to
         // avoid premature clipping with the system decor rect.
-        clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : w.mSystemDecorRect);
+        clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : mSystemDecorRect);
+        if (DEBUG_WINDOW_CROP) Slog.d(TAG, "win=" + w + " Initial clip rect: " + clipRect
+                + " mHasClipRect=" + mHasClipRect + " fullscreen=" + fullscreen);
+
+        if (isFreeformResizing && !w.isChildWindow()) {
+            // For freeform resizing non child windows, we are using the big surface positioned
+            // at 0,0. Thus we must express the crop in that coordinate space.
+            clipRect.offset(w.mShownPosition.x, w.mShownPosition.y);
+        }
 
         // Expand the clip rect for surface insets.
         final WindowManager.LayoutParams attrs = w.mAttrs;
@@ -1325,129 +1282,252 @@ class WindowStateAnimator {
             // clip rect extends outside the system decor rect.
             clipRect.intersect(mClipRect);
         }
-
         // The clip rect was generated assuming (0,0) as the window origin,
         // so we need to translate to match the actual surface coordinates.
         clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
 
-        if (!clipRect.equals(mLastClipRect)) {
-            mLastClipRect.set(clipRect);
-            try {
-                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                        "CROP " + clipRect.toShortString(), null);
-                mSurfaceControl.setWindowCrop(clipRect);
-            } catch (RuntimeException e) {
-                Slog.w(TAG, "Error setting crop surface of " + w
-                        + " crop=" + clipRect.toShortString(), e);
-                if (!recoveringMemory) {
-                    mService.reclaimSomeSurfaceMemoryLocked(this, "crop", true);
-                }
-            }
+        finalClipRect.setEmpty();
+        adjustCropToStackBounds(w, clipRect, finalClipRect, isFreeformResizing);
+        if (DEBUG_WINDOW_CROP) Slog.d(TAG,
+                "win=" + w + " Clip rect after stack adjustment=" + clipRect);
+
+        w.transformClipRectFromScreenToSurfaceSpace(clipRect);
+
+        // See {@link WindowState#notifyMovedInStack} for why this is necessary.
+        if (w.hasJustMovedInStack() && mLastClipRect.isEmpty() && !clipRect.isEmpty()) {
+            clipRect.setEmpty();
         }
     }
 
-    void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
-        final WindowState w = mWin;
+    void updateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect, boolean recoveringMemory) {
+        if (DEBUG_WINDOW_CROP) Slog.d(TAG, "updateSurfaceWindowCrop: win=" + mWin
+                + " clipRect=" + clipRect + " finalClipRect=" + finalClipRect);
+        if (clipRect != null) {
+            if (!clipRect.equals(mLastClipRect)) {
+                mLastClipRect.set(clipRect);
+                mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
+            }
+        } else {
+            mSurfaceController.clearCropInTransaction(recoveringMemory);
+        }
+        if (!finalClipRect.equals(mLastFinalClipRect)) {
+            mLastFinalClipRect.set(finalClipRect);
+            mSurfaceController.setFinalCropInTransaction(finalClipRect);
+            if (mDestroyPreservedSurfaceUponRedraw && mPendingDestroySurface != null) {
+                mPendingDestroySurface.setFinalCropInTransaction(finalClipRect);
+            }
+        }
+    }
 
-        int width;
-        int height;
-        if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
-            // for a scaled surface, we always want the requested
-            // size.
-            width  = w.mRequestedWidth;
-            height = w.mRequestedHeight;
+    private int resolveStackClip() {
+        // App animation overrides window animation stack clip mode.
+        if (mAppAnimator != null && mAppAnimator.animation != null) {
+            return mAppAnimator.getStackClip();
         } else {
-            width = w.mCompatFrame.width();
-            height = w.mCompatFrame.height();
+            return mStackClip;
         }
+    }
+    private void adjustCropToStackBounds(WindowState w, Rect clipRect, Rect finalClipRect,
+            boolean isFreeformResizing) {
 
-        // Something is wrong and SurfaceFlinger will not like this,
-        // try to revert to sane values
-        if (width < 1) {
-            width = 1;
+        final DisplayContent displayContent = w.getDisplayContent();
+        if (displayContent != null && !displayContent.isDefaultDisplay) {
+            // There are some windows that live on other displays while their app and main window
+            // live on the default display (e.g. casting...). We don't want to crop this windows
+            // to the stack bounds which is only currently supported on the default display.
+            // TODO(multi-display): Need to support cropping to stack bounds on other displays
+            // when we have stacks on other displays.
+            return;
         }
-        if (height < 1) {
-            height = 1;
+
+        final Task task = w.getTask();
+        if (task == null || !task.cropWindowsToStackBounds()) {
+            return;
         }
 
-        float left = w.mShownFrame.left;
-        float top = w.mShownFrame.top;
+        final int stackClip = resolveStackClip();
 
-        // Adjust for surface insets.
-        final LayoutParams attrs = w.getAttrs();
-        final int displayId = w.getDisplayId();
-        float scale = 1.0f;
-        // Magnification is supported only for the default display.
-        if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
-            MagnificationSpec spec =
-                    mService.mAccessibilityController.getMagnificationSpecForWindowLocked(w);
-            if (spec != null && !spec.isNop()) {
-                scale = spec.scale;
-            }
+        // It's animating and we don't want to clip it to stack bounds during animation - abort.
+        if (isAnimationSet() && stackClip == STACK_CLIP_NONE) {
+            return;
         }
 
-        width += scale * (attrs.surfaceInsets.left + attrs.surfaceInsets.right);
-        height += scale * (attrs.surfaceInsets.top + attrs.surfaceInsets.bottom);
-        left -= scale * attrs.surfaceInsets.left;
-        top -= scale * attrs.surfaceInsets.top;
-
-        final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top;
-        if (surfaceMoved) {
-            mSurfaceX = left;
-            mSurfaceY = top;
-
-            try {
-                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                        "POS " + left + ", " + top, null);
-                mSurfaceControl.setPosition(left, top);
-            } catch (RuntimeException e) {
-                Slog.w(TAG, "Error positioning surface of " + w
-                        + " pos=(" + left + "," + top + ")", e);
-                if (!recoveringMemory) {
-                    mService.reclaimSomeSurfaceMemoryLocked(this, "position", true);
-                }
-            }
+        final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
+        if (w == winShowWhenLocked && mPolicy.isKeyguardShowingOrOccluded()) {
+            return;
         }
 
-        final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
-        if (surfaceResized) {
-            mSurfaceW = width;
-            mSurfaceH = height;
-            mSurfaceResized = true;
-
-            try {
-                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                        "SIZE " + width + "x" + height, null);
-                mSurfaceControl.setSize(width, height);
-                mSurfaceControl.setMatrix(
-                        mDsDx * w.mHScale, mDtDx * w.mVScale,
-                        mDsDy * w.mHScale, mDtDy * w.mVScale);
-                mAnimator.setPendingLayoutChanges(w.getDisplayId(),
-                        WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
-                if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
-                    final TaskStack stack = w.getStack();
-                    if (stack != null) {
-                        stack.startDimmingIfNeeded(this);
-                    }
-                }
-            } catch (RuntimeException e) {
-                // If something goes wrong with the surface (such
-                // as running out of memory), don't take down the
-                // entire system.
-                Slog.e(TAG, "Error resizing surface of " + w
-                        + " size=(" + width + "x" + height + ")", e);
-                if (!recoveringMemory) {
-                    mService.reclaimSomeSurfaceMemoryLocked(this, "size", true);
-                }
+        final TaskStack stack = task.mStack;
+        stack.getDimBounds(mTmpStackBounds);
+        final Rect surfaceInsets = w.getAttrs().surfaceInsets;
+        // When we resize we use the big surface approach, which means we can't trust the
+        // window frame bounds anymore. Instead, the window will be placed at 0, 0, but to avoid
+        // hardcoding it, we use surface coordinates.
+        final int frameX = isFreeformResizing ? (int) mSurfaceController.getX() :
+                w.mFrame.left + mWin.mXOffset - surfaceInsets.left;
+        final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() :
+                w.mFrame.top + mWin.mYOffset - surfaceInsets.top;
+
+        // If we are animating, we either apply the clip before applying all the animation
+        // transformation or after all the transformation.
+        final boolean useFinalClipRect = isAnimationSet() && stackClip == STACK_CLIP_AFTER_ANIM
+                || mDestroyPreservedSurfaceUponRedraw;
+
+        // We need to do some acrobatics with surface position, because their clip region is
+        // relative to the inside of the surface, but the stack bounds aren't.
+        if (useFinalClipRect) {
+            finalClipRect.set(mTmpStackBounds);
+        } else {
+            if (StackId.hasWindowShadow(stack.mStackId)
+                    && !StackId.isTaskResizeAllowed(stack.mStackId)) {
+                // The windows in this stack display drop shadows and the fill the entire stack
+                // area. Adjust the stack bounds we will use to cropping take into account the
+                // offsets we use to display the drop shadow so it doesn't get cropped.
+                mTmpStackBounds.inset(-surfaceInsets.left, -surfaceInsets.top,
+                        -surfaceInsets.right, -surfaceInsets.bottom);
+            }
+
+            clipRect.left = Math.max(0,
+                    Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
+            clipRect.top = Math.max(0,
+                    Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY);
+            clipRect.right = Math.max(0,
+                    Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX);
+            clipRect.bottom = Math.max(0,
+                    Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
+        }
+    }
+
+    void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
+        final WindowState w = mWin;
+        final Task task = w.getTask();
+
+        // We got resized, so block all updates until we got the new surface.
+        if (w.isResizedWhileNotDragResizing() && !w.isGoneForLayoutLw()) {
+            return;
+        }
+
+        mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0);
+        calculateSurfaceBounds(w, w.getAttrs());
+
+        mExtraHScale = (float) 1.0;
+        mExtraVScale = (float) 1.0;
+
+        boolean wasForceScaled = mForceScaleUntilResize;
+        boolean wasSeamlesslyRotated = w.mSeamlesslyRotated;
+
+        // Once relayout has been called at least once, we need to make sure
+        // we only resize the client surface during calls to relayout. For
+        // clients which use indeterminate measure specs (MATCH_PARENT),
+        // we may try and change their window size without a call to relayout.
+        // However, this would be unsafe, as the client may be in the middle
+        // of producing a frame at the old size, having just completed layout
+        // to find the surface size changed underneath it.
+        if (!w.mRelayoutCalled || w.mInRelayout) {
+            mSurfaceResized = mSurfaceController.setSizeInTransaction(
+                    mTmpSize.width(), mTmpSize.height(), recoveringMemory);
+        } else {
+            mSurfaceResized = false;
+        }
+        mForceScaleUntilResize = mForceScaleUntilResize && !mSurfaceResized;
+        // If we are undergoing seamless rotation, the surface has already
+        // been set up to persist at it's old location. We need to freeze
+        // updates until a resize occurs.
+        w.mSeamlesslyRotated = w.mSeamlesslyRotated && !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;
+            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);
+            mExtraVScale = (mTmpClipRect.height() - vInsets) / (float)(surfaceHeight - vInsets);
+
+            // In the case of ForceScaleToCrop we scale entire tasks together,
+            // and so we need to scale our offsets relative to the task bounds
+            // or parent and child windows would fall out of alignment.
+            int posX = (int) (mTmpSize.left - w.mAttrs.x * (1 - mExtraHScale));
+            int posY = (int) (mTmpSize.top - w.mAttrs.y * (1 - mExtraVScale));
+            // Imagine we are scaling down. As we scale the buffer down, we decrease the
+            // distance between the surface top left, and the start of the surface contents
+            // (previously it was surfaceInsets.left pixels in screen space but now it
+            // will be surfaceInsets.left*mExtraHScale). This means in order to keep the
+            // non inset content at the same position, we have to shift the whole window
+            // forward. Likewise for scaling up, we've increased this distance, and we need
+            // to shift by a negative number to compensate.
+            posX += w.getAttrs().surfaceInsets.left * (1 - mExtraHScale);
+            posY += w.getAttrs().surfaceInsets.top * (1 - mExtraVScale);
+
+            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
+            // past where the system would have cropped us
+            mTmpClipRect.set(0, 0, (int)surfaceWidth, (int)surfaceHeight);
+            mTmpFinalClipRect.setEmpty();
+
+            // Various surfaces in the scaled stack may resize at different times.
+            // 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
+            // will be seamless.
+            mForceScaleUntilResize = true;
+        } else {
+            if (!w.mSeamlesslyRotated) {
+                mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top,
+                        recoveringMemory);
             }
         }
 
-        updateSurfaceWindowCrop(recoveringMemory);
+        // If we are ending the scaling mode. We switch to SCALING_MODE_FREEZE
+        // to prevent further updates until buffer latch.
+        // When ending both force scaling, and seamless rotation, we need to freeze
+        // the Surface geometry until a buffer comes in at the new size (normally position and crop
+        // are unfrozen). setGeometryAppliesWithResizeInTransaction accomplishes this for us.
+        if ((wasForceScaled && !mForceScaleUntilResize) ||
+                (wasSeamlesslyRotated && !w.mSeamlesslyRotated)) {
+            mSurfaceController.setGeometryAppliesWithResizeInTransaction(true);
+            mSurfaceController.forceScaleableInTransaction(false);
+        }
+
+        Rect clipRect = mTmpClipRect;
+        if (w.inPinnedWorkspace()) {
+            clipRect = null;
+            task.mStack.getDimBounds(mTmpFinalClipRect);
+            mTmpFinalClipRect.inset(-w.mAttrs.surfaceInsets.left, -w.mAttrs.surfaceInsets.top,
+                    -w.mAttrs.surfaceInsets.right, -w.mAttrs.surfaceInsets.bottom);
+        }
+
+        if (!w.mSeamlesslyRotated) {
+            updateSurfaceWindowCrop(clipRect, mTmpFinalClipRect, recoveringMemory);
+            mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
+                    mDtDx * w.mVScale * mExtraVScale,
+                    mDsDy * w.mHScale * mExtraHScale,
+                    mDtDy * w.mVScale * mExtraVScale, recoveringMemory);
+        }
+
+        if (mSurfaceResized) {
+            mReportSurfaceResized = true;
+            mAnimator.setPendingLayoutChanges(w.getDisplayId(),
+                    WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
+            w.applyDimLayerIfNeeded();
+        }
+
     }
 
-    public void prepareSurfaceLocked(final boolean recoveringMemory) {
+    void prepareSurfaceLocked(final boolean recoveringMemory) {
         final WindowState w = mWin;
-        if (mSurfaceControl == null) {
+        if (!hasSurface()) {
             if (w.mOrientationChanging) {
                 if (DEBUG_ORIENTATION) {
                     Slog.v(TAG, "Orientation change skips hidden " + w);
@@ -1457,6 +1537,17 @@ class WindowStateAnimator {
             return;
         }
 
+        // Do not change surface properties of opening apps if we are waiting for the
+        // transition to be ready. transitionGoodToGo could be not ready even after all
+        // opening apps are drawn. It's only waiting on isFetchingAppTransitionsSpecs()
+        // to get the animation spec. (For example, go into Recents and immediately open
+        // the same app again before the app's surface is destroyed or saved, the surface
+        // is always ready in the whole process.) If we go ahead here, the opening app
+        // will be shown with the full size before the correct animation spec arrives.
+        if (isWaitingForOpening()) {
+            return;
+        }
+
         boolean displayed = false;
 
         computeShownFrameLocked();
@@ -1465,10 +1556,10 @@ class WindowStateAnimator {
 
         if (mIsWallpaper && !mWin.mWallpaperVisible) {
             // Wallpaper is no longer visible and there is no wp target => hide it.
-            hide();
+            hide("prepareSurfaceLocked");
         } else if (w.mAttachedHidden || !w.isOnScreen()) {
-            hide();
-            mService.hideWallpapersLocked(w);
+            hide("prepareSurfaceLocked");
+            mWallpaperControllerLocked.hideWallpapers(w);
 
             // If we are waiting for this window to handle an
             // orientation change, well, it is hidden, so
@@ -1499,53 +1590,44 @@ class WindowStateAnimator {
             mLastDtDy = mDtDy;
             w.mLastHScale = w.mHScale;
             w.mLastVScale = w.mVScale;
-            if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+            if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+                    "controller=" + mSurfaceController +
                     "alpha=" + mShownAlpha + " layer=" + mAnimLayer
                     + " matrix=[" + mDsDx + "*" + w.mHScale
                     + "," + mDtDx + "*" + w.mVScale
                     + "][" + mDsDy + "*" + w.mHScale
-                    + "," + mDtDy + "*" + w.mVScale + "]", null);
-            if (mSurfaceControl != null) {
-                try {
-                    mSurfaceAlpha = mShownAlpha;
-                    mSurfaceControl.setAlpha(mShownAlpha);
-                    mSurfaceLayer = mAnimLayer;
-                    mSurfaceControl.setLayer(mAnimLayer);
-                    mSurfaceControl.setMatrix(
-                            mDsDx * w.mHScale, mDtDx * w.mVScale,
-                            mDsDy * w.mHScale, mDtDy * w.mVScale);
-
-                    if (mLastHidden && mDrawState == HAS_DRAWN) {
-                        if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                                "SHOW (performLayout)", null);
-                        if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
-                                + " during relayout");
-                        if (showSurfaceRobustlyLocked()) {
-                            mLastHidden = false;
-                            if (mIsWallpaper) {
-                                mService.dispatchWallpaperVisibility(w, true);
-                            }
-                            // This draw means the difference between unique content and mirroring.
-                            // Run another pass through performLayout to set mHasContent in the
-                            // LogicalDisplay.
-                            mAnimator.setPendingLayoutChanges(w.getDisplayId(),
-                                    WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
-                        } else {
-                            w.mOrientationChanging = false;
-                        }
-                    }
-                    if (mSurfaceControl != null) {
-                        w.mToken.hasVisible = true;
-                    }
-                } catch (RuntimeException e) {
-                    Slog.w(TAG, "Error updating surface in " + w, e);
-                    if (!recoveringMemory) {
-                        mService.reclaimSomeSurfaceMemoryLocked(this, "update", true);
+                    + "," + mDtDy + "*" + w.mVScale + "]", false);
+
+            boolean prepared =
+                mSurfaceController.prepareToShowInTransaction(mShownAlpha, mAnimLayer,
+                        mDsDx * w.mHScale * mExtraHScale,
+                        mDtDx * w.mVScale * mExtraVScale,
+                        mDsDy * w.mHScale * mExtraHScale,
+                        mDtDy * w.mVScale * mExtraVScale,
+                        recoveringMemory);
+
+            if (prepared && mLastHidden && mDrawState == HAS_DRAWN) {
+                if (showSurfaceRobustlyLocked()) {
+                    markPreservedSurfaceForDestroy();
+                    mAnimator.requestRemovalOfReplacedWindows(w);
+                    mLastHidden = false;
+                    if (mIsWallpaper) {
+                        mWallpaperControllerLocked.dispatchWallpaperVisibility(w, true);
                     }
+                    // This draw means the difference between unique content and mirroring.
+                    // Run another pass through performLayout to set mHasContent in the
+                    // LogicalDisplay.
+                    mAnimator.setPendingLayoutChanges(w.getDisplayId(),
+                            WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
+                } else {
+                    w.mOrientationChanging = false;
                 }
             }
+            if (hasSurface()) {
+                w.mToken.hasVisible = true;
+            }
         } else {
-            if (DEBUG_ANIM && isAnimating()) {
+            if (DEBUG_ANIM && isAnimationSet()) {
                 Slog.v(TAG, "prepareSurface: No changes in animation for " + this);
             }
             displayed = true;
@@ -1568,52 +1650,32 @@ class WindowStateAnimator {
     }
 
     void setTransparentRegionHintLocked(final Region region) {
-        if (mSurfaceControl == null) {
+        if (mSurfaceController == null) {
             Slog.w(TAG, "setTransparentRegionHint: null mSurface after mHasSurface true");
             return;
         }
-        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setTransparentRegion");
-        SurfaceControl.openTransaction();
-        try {
-            if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
-                    "transparentRegionHint=" + region, null);
-            mSurfaceControl.setTransparentRegionHint(region);
-        } finally {
-            SurfaceControl.closeTransaction();
-            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
-                    "<<< CLOSE TRANSACTION setTransparentRegion");
-        }
+        mSurfaceController.setTransparentRegionHint(region);
     }
 
-    void setWallpaperOffset(RectF shownFrame) {
+    void setWallpaperOffset(Point shownPosition) {
         final LayoutParams attrs = mWin.getAttrs();
-        final int left = ((int) shownFrame.left) - attrs.surfaceInsets.left;
-        final int top = ((int) shownFrame.top) - attrs.surfaceInsets.top;
-        if (mSurfaceX != left || mSurfaceY != top) {
-            if (mAnimating) {
-                // If this window (or its app token) is animating, then the position
-                // of the surface will be re-computed on the next animation frame.
-                // We can't poke it directly here because it depends on whatever
-                // transformation is being applied by the animation.
-                return;
-            }
-            mSurfaceX = left;
-            mSurfaceY = top;
+        final int left = shownPosition.x - attrs.surfaceInsets.left;
+        final int top = shownPosition.y - attrs.surfaceInsets.top;
+
+        try {
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
             SurfaceControl.openTransaction();
-            try {
-                if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
-                        "POS " + left + ", " + top, null);
-                mSurfaceControl.setPosition(mWin.mFrame.left + left, mWin.mFrame.top + top);
-                updateSurfaceWindowCrop(false);
-            } catch (RuntimeException e) {
-                Slog.w(TAG, "Error positioning surface of " + mWin
-                        + " pos=(" + left + "," + top + ")", e);
-            } finally {
-                SurfaceControl.closeTransaction();
-                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
-                        "<<< CLOSE TRANSACTION setWallpaperOffset");
-            }
+            mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left,
+                    mWin.mFrame.top + top, false);
+            calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
+            updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, false);
+        } catch (RuntimeException e) {
+            Slog.w(TAG, "Error positioning surface of " + mWin
+                    + " pos=(" + left + "," + top + ")", e);
+        } finally {
+            SurfaceControl.closeTransaction();
+            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+                    "<<< CLOSE TRANSACTION setWallpaperOffset");
         }
     }
 
@@ -1625,12 +1687,11 @@ class WindowStateAnimator {
      * @return True if format was succesfully changed, false otherwise
      */
     boolean tryChangeFormatInPlaceLocked() {
-        if (mSurfaceControl == null) {
+        if (mSurfaceController == null) {
             return false;
         }
         final LayoutParams attrs = mWin.getAttrs();
-        final boolean isHwAccelerated = (attrs.flags &
-                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
+        final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0;
         final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
         if (format == mSurfaceFormat) {
             setOpaqueLocked(!PixelFormat.formatHasAlpha(attrs.format));
@@ -1640,35 +1701,17 @@ class WindowStateAnimator {
     }
 
     void setOpaqueLocked(boolean isOpaque) {
-        if (mSurfaceControl == null) {
+        if (mSurfaceController == null) {
             return;
         }
-        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setOpaqueLocked");
-        SurfaceControl.openTransaction();
-        try {
-            if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "isOpaque=" + isOpaque,
-                    null);
-            mSurfaceControl.setOpaque(isOpaque);
-        } finally {
-            SurfaceControl.closeTransaction();
-            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setOpaqueLocked");
-        }
+        mSurfaceController.setOpaque(isOpaque);
     }
 
     void setSecureLocked(boolean isSecure) {
-        if (mSurfaceControl == null) {
+        if (mSurfaceController == null) {
             return;
         }
-        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setSecureLocked");
-        SurfaceControl.openTransaction();
-        try {
-            if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "isSecure=" + isSecure,
-                    null);
-            mSurfaceControl.setSecure(isSecure);
-        } finally {
-            SurfaceControl.closeTransaction();
-            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setSecureLocked");
-        }
+        mSurfaceController.setSecure(isSecure);
     }
 
     // This must be called while inside a transaction.
@@ -1696,8 +1739,6 @@ class WindowStateAnimator {
                     + Debug.getCallers(3));
         }
         if (mDrawState == READY_TO_SHOW && mWin.isReadyForDisplayIgnoringKeyguard()) {
-            if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
-                WindowManagerService.logSurface(mWin, "SHOW (performShowLocked)", null);
             if (DEBUG_VISIBILITY || (DEBUG_STARTING_WINDOW &&
                     mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING)) {
                 Slog.v(TAG, "Showing " + this
@@ -1719,7 +1760,7 @@ class WindowStateAnimator {
             // Force the show in the next prepareSurfaceLocked() call.
             mLastAlpha = -1;
             if (DEBUG_SURFACE_TRACE || DEBUG_ANIM)
-                Slog.v(TAG, "performShowLocked: mDrawState=HAS_DRAWN in " + this);
+                Slog.v(TAG, "performShowLocked: mDrawState=HAS_DRAWN in " + mWin);
             mDrawState = HAS_DRAWN;
             mService.scheduleAnimationLocked();
 
@@ -1729,7 +1770,7 @@ class WindowStateAnimator {
                 WindowState c = mWin.mChildWindows.get(i);
                 if (c.mAttachedHidden) {
                     c.mAttachedHidden = false;
-                    if (c.mWinAnimator.mSurfaceControl != null) {
+                    if (c.mWinAnimator.mSurfaceController != null) {
                         c.mWinAnimator.performShowLocked();
                         // It hadn't been shown, which means layout not
                         // performed on it, so now we want to make sure to
@@ -1744,29 +1785,16 @@ class WindowStateAnimator {
                 }
             }
 
-            if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING
-                    && mWin.mAppToken != null) {
-                mWin.mAppToken.firstWindowDrawn = true;
-
-                if (mWin.mAppToken.startingData != null) {
-                    if (WindowManagerService.DEBUG_STARTING_WINDOW ||
-                            WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
-                            "Finish starting " + mWin.mToken
-                            + ": first real window is shown, no animation");
-                    // If this initial window is animating, stop it -- we
-                    // will do an animation to reveal it from behind the
-                    // starting window, so there is no need for it to also
-                    // be doing its own stuff.
-                    clearAnimation();
-                    mService.mFinishedStarting.add(mWin.mAppToken);
-                    mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
-                }
-                mWin.mAppToken.updateReportedVisibilityLocked();
+            if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING && mWin.mAppToken != null) {
+                mWin.mAppToken.onFirstWindowDrawn(mWin, this);
+            }
+
+            if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+                mWin.mDisplayContent.mDividerControllerLocked.resetImeHideRequested();
             }
 
             return true;
         }
-
         return false;
     }
 
@@ -1778,29 +1806,31 @@ class WindowStateAnimator {
      *
      * @return Returns true if the surface was successfully shown.
      */
-    boolean showSurfaceRobustlyLocked() {
-        try {
-            if (mSurfaceControl != null) {
-                mSurfaceShown = true;
-                mSurfaceControl.show();
-                if (mWin.mTurnOnScreen) {
-                    if (DEBUG_VISIBILITY) Slog.v(TAG,
-                            "Show surface turning screen on: " + mWin);
-                    mWin.mTurnOnScreen = false;
-                    mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
-                }
-            }
-            return true;
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failure showing surface " + mSurfaceControl + " in " + mWin, e);
+    private boolean showSurfaceRobustlyLocked() {
+        final Task task = mWin.getTask();
+        if (task != null && StackId.windowsAreScaleable(task.mStack.mStackId)) {
+            mSurfaceController.forceScaleableInTransaction(true);
         }
 
-        mService.reclaimSomeSurfaceMemoryLocked(this, "show", true);
+        boolean shown = mSurfaceController.showRobustlyInTransaction();
+        if (!shown)
+            return false;
 
-        return false;
+        if (mWin.mTurnOnScreen) {
+            if (DEBUG_VISIBILITY) Slog.v(TAG, "Show surface turning screen on: " + mWin);
+            mWin.mTurnOnScreen = false;
+            mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
+        }
+        return true;
     }
 
     void applyEnterAnimationLocked() {
+        // If we are the new part of a window replacement transition and we have requested
+        // not to animate, we instead want to make it seamless, so we don't want to apply
+        // an enter transition.
+        if (mWin.mSkipEnterAnimationForSeamlessReplacement) {
+            return;
+        }
         final int transit;
         if (mEnterAnimationPending) {
             mEnterAnimationPending = false;
@@ -1811,7 +1841,7 @@ class WindowStateAnimator {
         applyAnimationLocked(transit, true);
         //TODO (multidisplay): Magnification is supported only for the default display.
         if (mService.mAccessibilityController != null
-                && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
+                && mWin.getDisplayId() == DEFAULT_DISPLAY) {
             mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
         }
     }
@@ -1844,6 +1874,7 @@ class WindowStateAnimator {
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
         // is running.
+        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked");
         if (mService.okToDisplay()) {
             int anim = mPolicy.selectAnimationLw(mWin, transit);
             int attr = -1;
@@ -1869,28 +1900,29 @@ class WindowStateAnimator {
                     a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr);
                 }
             }
-            if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
+            if (DEBUG_ANIM) Slog.v(TAG,
                     "applyAnimation: win=" + this
                     + " anim=" + anim + " attr=0x" + Integer.toHexString(attr)
                     + " a=" + a
                     + " transit=" + transit
                     + " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
             if (a != null) {
-                if (WindowManagerService.DEBUG_ANIM) {
-                    RuntimeException e = null;
-                    if (!WindowManagerService.HIDE_STACK_CRAWLS) {
-                        e = new RuntimeException();
-                        e.fillInStackTrace();
-                    }
-                    Slog.v(TAG, "Loaded animation " + a + " for " + this, e);
-                }
+                if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
                 setAnimation(a);
                 mAnimationIsEntrance = isEntrance;
             }
         } else {
             clearAnimation();
         }
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
 
+        if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+            mService.adjustForImeIfNeeded(mWin.mDisplayContent);
+            if (isEntrance) {
+                mWin.setDisplayLayoutNeeded();
+                mService.mWindowPlacerLocked.requestTraversal();
+            }
+        }
         return mAnimation != null;
     }
 
@@ -1912,7 +1944,7 @@ class WindowStateAnimator {
         fadeOut.setDuration(fadeDuration);
         fadeOut.setStartOffset(elapsed);
         newAnimation.addAnimation(fadeOut);
-        newAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mAnimDw, mAnimDh);
+        newAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mAnimDx, mAnimDy);
         mAnimation = newAnimation;
     }
 
@@ -1922,7 +1954,8 @@ class WindowStateAnimator {
             pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating);
                     pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
                     pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
-                    pw.print(" mAnimation="); pw.println(mAnimation);
+                    pw.print(" mAnimation="); pw.print(mAnimation);
+                    pw.print(" mStackClip="); pw.println(mStackClip);
         }
         if (mHasTransformation || mHasLocalTransformation) {
             pw.print(prefix); pw.print("XForm: has=");
@@ -1931,21 +1964,23 @@ class WindowStateAnimator {
                     pw.print(" "); mTransformation.printShortString(pw);
                     pw.println();
         }
-        if (mSurfaceControl != null) {
-            if (dumpAll) {
-                pw.print(prefix); pw.print("mSurface="); pw.println(mSurfaceControl);
-                pw.print(prefix); pw.print("mDrawState=");
-                pw.print(drawStateToString());
-                pw.print(" mLastHidden="); pw.println(mLastHidden);
+        if (mSurfaceController != null) {
+            mSurfaceController.dump(pw, prefix, dumpAll);
+        }
+        if (dumpAll) {
+            pw.print(prefix); pw.print("mDrawState="); pw.print(drawStateToString());
+            pw.print(prefix); pw.print(" mLastHidden="); pw.println(mLastHidden);
+            pw.print(prefix); pw.print("mSystemDecorRect="); mSystemDecorRect.printShortString(pw);
+            pw.print(" last="); mLastSystemDecorRect.printShortString(pw);
+            pw.print(" mHasClipRect="); pw.print(mHasClipRect);
+            pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw);
+
+            if (!mLastFinalClipRect.isEmpty()) {
+                pw.print(" mLastFinalClipRect="); mLastFinalClipRect.printShortString(pw);
             }
-            pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
-                    pw.print(" layer="); pw.print(mSurfaceLayer);
-                    pw.print(" alpha="); pw.print(mSurfaceAlpha);
-                    pw.print(" rect=("); pw.print(mSurfaceX);
-                    pw.print(","); pw.print(mSurfaceY);
-                    pw.print(") "); pw.print(mSurfaceW);
-                    pw.print(" x "); pw.println(mSurfaceH);
+            pw.println();
         }
+
         if (mPendingDestroySurface != null) {
             pw.print(prefix); pw.print("mPendingDestroySurface=");
                     pw.println(mPendingDestroySurface);
@@ -1966,6 +2001,9 @@ class WindowStateAnimator {
                     pw.print(" mDsDy="); pw.print(mDsDy);
                     pw.print(" mDtDy="); pw.println(mDtDy);
         }
+        if (mAnimationStartDelayed) {
+            pw.print(prefix); pw.print("mAnimationStartDelayed="); pw.print(mAnimationStartDelayed);
+        }
     }
 
     @Override
@@ -1977,4 +2015,186 @@ class WindowStateAnimator {
         sb.append('}');
         return sb.toString();
     }
+
+    void reclaimSomeSurfaceMemory(String operation, boolean secure) {
+        mService.reclaimSomeSurfaceMemoryLocked(this, operation, secure);
+    }
+
+    boolean getShown() {
+        if (mSurfaceController != null) {
+            return mSurfaceController.getShown();
+        }
+        return false;
+    }
+
+    void destroySurface() {
+        try {
+            if (mSurfaceController != null) {
+                mSurfaceController.destroyInTransaction();
+            }
+        } catch (RuntimeException e) {
+            Slog.w(TAG, "Exception thrown when destroying surface " + this
+                    + " surface " + mSurfaceController + " session " + mSession + ": " + e);
+        } finally {
+            mWin.setHasSurface(false);
+            mSurfaceController = null;
+            mDrawState = NO_SURFACE;
+        }
+    }
+
+    void setMoveAnimation(int left, int top) {
+        final Animation a = AnimationUtils.loadAnimation(mContext,
+                com.android.internal.R.anim.window_move_from_decor);
+        setAnimation(a);
+        mAnimDx = mWin.mLastFrame.left - left;
+        mAnimDy = mWin.mLastFrame.top - top;
+        mAnimateMove = true;
+    }
+
+    void deferTransactionUntilParentFrame(long frameNumber) {
+        if (!mWin.isChildWindow()) {
+            return;
+        }
+        mDeferTransactionUntilFrame = frameNumber;
+        mDeferTransactionTime = System.currentTimeMillis();
+        mSurfaceController.deferTransactionUntil(
+                mWin.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
+                frameNumber);
+    }
+
+    // Defer the current transaction to the frame number of the last saved transaction.
+    // We do this to avoid shooting through an unsynchronized transaction while something is
+    // pending. This is generally fine, as either we will get in on the synchronization,
+    // or SurfaceFlinger will see that the frame has already occured. The only
+    // potential problem is in frame number resets so we reset things with a timeout
+    // every so often to be careful.
+    void deferToPendingTransaction() {
+        if (mDeferTransactionUntilFrame < 0) {
+            return;
+        }
+        long time = System.currentTimeMillis();
+        if (time > mDeferTransactionTime + PENDING_TRANSACTION_FINISH_WAIT_TIME) {
+            mDeferTransactionTime = -1;
+            mDeferTransactionUntilFrame = -1;
+        } else {
+            mSurfaceController.deferTransactionUntil(
+                    mWin.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
+                    mDeferTransactionUntilFrame);
+        }
+    }
+
+    /**
+     * Sometimes we need to synchronize the first frame of animation with some external event.
+     * To achieve this, we prolong the start of the animation and keep producing the first frame of
+     * the animation.
+     */
+    private long getAnimationFrameTime(Animation animation, long currentTime) {
+        if (mAnimationStartDelayed) {
+            animation.setStartTime(currentTime);
+            return currentTime + 1;
+        }
+        return currentTime;
+    }
+
+    void startDelayingAnimationStart() {
+        mAnimationStartDelayed = true;
+    }
+
+    void endDelayingAnimationStart() {
+        mAnimationStartDelayed = false;
+    }
+
+    void seamlesslyRotateWindow(int oldRotation, int newRotation) {
+        final WindowState w = mWin;
+        if (!w.isVisibleNow() || w.mIsWallpaper) {
+            return;
+        }
+
+        final Rect cropRect = mService.mTmpRect;
+        final Rect displayRect = mService.mTmpRect2;
+        final RectF frameRect = mService.mTmpRectF;
+        final Matrix transform = mService.mTmpTransform;
+
+        final float x = w.mFrame.left;
+        final float y = w.mFrame.top;
+        final float width = w.mFrame.width();
+        final float height = w.mFrame.height();
+
+        mService.getDefaultDisplayContentLocked().getLogicalDisplayRect(displayRect);
+        final float displayWidth = displayRect.width();
+        final float displayHeight = displayRect.height();
+
+        // Compute a transform matrix to undo the coordinate space transformation,
+        // and present the window at the same physical position it previously occupied.
+        final int deltaRotation = DisplayContent.deltaRotation(newRotation, oldRotation);
+        switch (deltaRotation) {
+        case Surface.ROTATION_0:
+            transform.reset();
+            break;
+        case Surface.ROTATION_270:
+            transform.setRotate(270, 0, 0);
+            transform.postTranslate(0, displayHeight);
+            transform.postTranslate(y, 0);
+            break;
+        case Surface.ROTATION_180:
+            transform.reset();
+            break;
+        case Surface.ROTATION_90:
+            transform.setRotate(90, 0, 0);
+            transform.postTranslate(displayWidth, 0);
+            transform.postTranslate(-y, x);
+            break;
+        }
+
+        // We have two cases:
+        //  1. Windows with NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY:
+        //     These windows never change buffer size when rotating. Rather the window manager
+        //     just updates the scaling factors to fit in the new coordinate system,
+        //     and SurfaceFlinger takes care of updating the buffer contents. So in this case
+        //     we just need we just need to update the scaling factors and things are seamless
+        //     already.
+        //  2. Other windows:
+        //     In this case, we need to apply a rotation matrix to the window. For example
+        //     if we have a portrait window and rotate to landscape, the window is still portrait
+        //     and now extends off the bottom of the screen (and only halfway across). Essentially we
+        //     apply a transform to display the current buffer at it's old position
+        //     (in the new coordinate space). We then freeze layer updates until the resize
+        //     occurs, at which point we undo, them.
+        if (w.isChildWindow() && mSurfaceController.getTransformToDisplayInverse()) {
+            frameRect.set(x, y, x+width, y+height);
+            transform.mapRect(frameRect);
+
+            w.mAttrs.x = (int) frameRect.left - w.mAttachedWindow.mFrame.left;
+            w.mAttrs.y = (int) frameRect.top - w.mAttachedWindow.mFrame.top;
+            w.mAttrs.width = (int) Math.ceil(frameRect.width());
+            w.mAttrs.height = (int) Math.ceil(frameRect.height());
+
+            w.setWindowScale(w.mRequestedWidth, w.mRequestedHeight);
+
+            w.applyGravityAndUpdateFrame(w.mContainingFrame, w.mDisplayFrame);
+            computeShownFrameLocked();
+            setSurfaceBoundariesLocked(false);
+
+            // The stack bounds will not yet be rotated at this point so setSurfaceBoundaries locked
+            // will crop us incorrectly. Overwrite the crop, exposing the full surface. By the next
+            // transaction this will be corrected.
+            cropRect.set(0, 0, w.mRequestedWidth, w.mRequestedWidth + w.mRequestedHeight);
+            mSurfaceController.setCropInTransaction(cropRect, false);
+        } else {
+            w.mSeamlesslyRotated = true;
+            transform.getValues(mService.mTmpFloats);
+
+            float DsDx = mService.mTmpFloats[Matrix.MSCALE_X];
+            float DtDx = mService.mTmpFloats[Matrix.MSKEW_Y];
+            float DsDy = mService.mTmpFloats[Matrix.MSKEW_X];
+            float DtDy = mService.mTmpFloats[Matrix.MSCALE_Y];
+            float nx = mService.mTmpFloats[Matrix.MTRANS_X];
+            float ny = mService.mTmpFloats[Matrix.MTRANS_Y];
+            mSurfaceController.setPositionInTransaction(nx, ny, false);
+            mSurfaceController.setMatrixInTransaction(DsDx * w.mHScale,
+                    DtDx * w.mVScale,
+                    DsDy * w.mHScale,
+                    DtDy * w.mVScale, false);
+        }
+    }
 }