Lock free animations (2/2)

Second CL that migrates WSA to use SurfaceAnimator

We start our synchronized app transition journey by showing that
the concept works by using WindowState animations as proof of
concept.

The main class in this CL are SurfaceAnimator and
SurfaceAnimatorRunner. When we start an animation on a Window, we
create a new bufferless surface, called "The Leash", in the
hierarchy and attach the surface of WindowState onto it, while
attaching the leash onto the old surface parent which is still
responsible for z-layering.

Then, we pass off the Leash into SurfaceAnimationRunner, which then
changes the surface properties of Leash in every animation frame,
without holding the WM lock. While it's doing that we can still
update the z-layering of the window, or even relayout the window
of needed - the important surfaces for this are still under WM's
control.

In case the animation is finished the window surface gets
reparented to its original parent, and the leash is abandoned.
Note that the reparenting is done in the same transaction as
processing the animation finish, such that we don't end up with
a flicker in case of a disappearing animation, where the window
surface gets destroyed.

In case the animation needs to be cancelled, WM can revoke control
of the leash by reparenting the window surface. Even if the
cancellation signal is heavily delayed, WM immediately regains
control over the surface by reparenting it within a transaction.

We also introduce the concept of animating a WindowContainer. We
clean up isAnimating:
- isLocalAnimating: is the container itself animating
- isAnimating: is the container or one of its parents animating
- isSelfOrChildAnimating: is local animating or any child
animating.

SurfaceAnimationRunner also needs it's own thread so it's not getting
bogged down by any WM lock contention by processing regular
animation frames. We call that thread android.anim.lf (lockfree).

Now, imagine that SurfaceAnimationAnimator would sit behind an IPC in
another process and instead of animating WindowState, we'd animate
AppWindowToken. Then, synchronized app transitions would be done.

Test: go/wm-smoke
Test: SurfaceAnimatorTest
Test: SurfaceAnimationRunnerTest
Test: WindowContainerTests
Bug: 64674361

Change-Id: Idf59daa90361af57fce1128d19a0c0dbf5971d18
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index c39ce98..94a0cb7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -66,6 +66,7 @@
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.animation.Transformation;
 
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.input.InputApplicationHandle;
@@ -75,6 +76,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.LinkedList;
 
 class AppTokenList extends ArrayList<AppWindowToken> {
 }
@@ -231,7 +233,7 @@
             // 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.
-            winAnimator.clearAnimation();
+            win.cancelAnimation();
             if (getController() != null) {
                 getController().removeStartingWindow();
             }
@@ -389,7 +391,7 @@
         }
 
         for (int i = mChildren.size() - 1; i >= 0 && !delayed; i--) {
-            if ((mChildren.get(i)).isWindowAnimationSet()) {
+            if ((mChildren.get(i)).isSelfOrChildAnimating()) {
                 delayed = true;
             }
         }
@@ -610,8 +612,12 @@
      */
     private void destroySurfaces(boolean cleanupOnResume) {
         boolean destroyedSomething = false;
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState win = mChildren.get(i);
+
+        // Copying to a different list as multiple children can be removed.
+        // TODO: Not sure why this is needed.
+        final LinkedList<WindowState> children = new LinkedList<>(mChildren);
+        for (int i = children.size() - 1; i >= 0; i--) {
+            final WindowState win = children.get(i);
             destroyedSomething |= win.destroySurface(cleanupOnResume, mAppStopped);
         }
         if (destroyedSomething) {
@@ -1320,7 +1326,7 @@
                             + " pv=" + w.mPolicyVisibility
                             + " mDrawState=" + winAnimator.drawStateToString()
                             + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
-                            + " a=" + winAnimator.mAnimating);
+                            + " a=" + winAnimator.isAnimationSet());
                 }
             }
 
@@ -1520,6 +1526,11 @@
     }
 
     @Override
+    boolean isSelfAnimating() {
+        return mAppAnimator.isAnimating();
+    }
+
+    @Override
     void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);
         if (appToken != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index eda8fec..f05cf2a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -146,6 +146,7 @@
 import android.view.MagnificationSpec;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -339,6 +340,7 @@
             new ApplySurfaceChangesTransactionState();
     private final ScreenshotApplicationState mScreenshotApplicationState =
             new ScreenshotApplicationState();
+    private final Transaction mTmpTransaction = new Transaction();
 
     // True if this display is in the process of being removed. Used to determine if the removal of
     // the display's direct children should be allowed.
@@ -381,27 +383,6 @@
 
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
-        if (winAnimator.hasSurface()) {
-            final boolean wasAnimating = winAnimator.mWasAnimating;
-            final boolean nowAnimating = winAnimator.stepAnimationLocked(
-                    mTmpWindowAnimator.mCurrentTime);
-            winAnimator.mWasAnimating = nowAnimating;
-            mTmpWindowAnimator.orAnimating(nowAnimating);
-
-            if (DEBUG_WALLPAPER) Slog.v(TAG,
-                    w + ": wasAnimating=" + wasAnimating + ", nowAnimating=" + nowAnimating);
-
-            if (wasAnimating && !winAnimator.mAnimating
-                    && mWallpaperController.isWallpaperTarget(w)) {
-                mTmpWindowAnimator.mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
-                pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-                if (DEBUG_LAYOUT_REPEATS) {
-                    mService.mWindowPlacerLocked.debugLayoutRepeats(
-                            "updateWindowsAndWallpaperLocked 2", pendingLayoutChanges);
-                }
-            }
-        }
-
         final AppWindowToken atoken = w.mAppToken;
         if (winAnimator.mDrawState == READY_TO_SHOW) {
             if (atoken == null || atoken.allDrawn) {
@@ -434,13 +415,13 @@
 
         // If this window is animating, make a note that we have an animating window and take
         // care of a request to run a detached wallpaper animation.
-        if (winAnimator.mAnimating) {
-            if (winAnimator.mAnimation != null) {
-                if ((flags & FLAG_SHOW_WALLPAPER) != 0
-                        && winAnimator.mAnimation.getDetachWallpaper()) {
+        if (winAnimator.isAnimationSet()) {
+            final AnimationAdapter anim = w.getAnimation();
+            if (anim != null) {
+                if ((flags & FLAG_SHOW_WALLPAPER) != 0 && anim.getDetachWallpaper()) {
                     mTmpWindow = w;
                 }
-                final int color = winAnimator.mAnimation.getBackgroundColor();
+                final int color = anim.getBackgroundColor();
                 if (color != 0) {
                     final TaskStack stack = w.getStack();
                     if (stack != null) {
@@ -448,7 +429,6 @@
                     }
                 }
             }
-            mTmpWindowAnimator.setAnimating(true);
         }
 
         // If this window's app token is running a detached wallpaper animation, make a note so
@@ -684,7 +664,10 @@
             mWallpaperController.updateWallpaperVisibility();
         }
 
-        w.handleWindowMovedIfNeeded();
+        // Use mTmpTransaction instead of mPendingTransaction because we don't want to commit
+        // other changes in mPendingTransaction at this point.
+        w.handleWindowMovedIfNeeded(mTmpTransaction);
+        SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
 
         final WindowStateAnimator winAnimator = w.mWinAnimator;
 
@@ -720,7 +703,7 @@
                 }
             }
             final TaskStack stack = w.getStack();
-            if ((!winAnimator.isAnimationStarting() && !winAnimator.isWaitingForOpening())
+            if ((!winAnimator.isWaitingForOpening())
                     || (stack != null && stack.isAnimatingBounds())) {
                 // Updates the shown frame before we set up the surface. This is needed
                 // because the resizing could change the top-left position (in addition to
@@ -736,6 +719,12 @@
                 winAnimator.computeShownFrameLocked();
             }
             winAnimator.setSurfaceBoundariesLocked(mTmpRecoveringMemory /* recoveringMemory */);
+
+            // Since setSurfaceBoundariesLocked applies the clipping, we need to apply the position
+            // to the surface of the window container as well. Use mTmpTransaction instead of
+            // mPendingTransaction to avoid committing any existing changes in there.
+            w.updateSurfacePosition(mTmpTransaction);
+            SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
         }
 
         final AppWindowToken atoken = w.mAppToken;
@@ -2617,8 +2606,7 @@
         forAllWindows(w -> {
             if (w.mAppToken == null && policy.canBeHiddenByKeyguardLw(w)
                     && w.wouldBeVisibleIfPolicyIgnored() && !w.isVisible()) {
-                w.mWinAnimator.setAnimation(
-                        policy.createHiddenByKeyguardExit(onWallpaper, goingToShade));
+                w.startAnimation(policy.createHiddenByKeyguardExit(onWallpaper, goingToShade));
             }
         }, true /* traverseTopToBottom */);
     }
@@ -3775,13 +3763,13 @@
     }
 
     @Override
-    void destroyAfterPendingTransaction(SurfaceControl surface) {
+    public void destroyAfterPendingTransaction(SurfaceControl surface) {
         mPendingDestroyingSurfaces.add(surface);
     }
 
     /**
      * Destroys any surfaces that have been put into the pending list with
-     * {@link #destroyAfterTransaction}.
+     * {@link #destroyAfterPendingTransaction}.
      */
     void onPendingTransactionApplied() {
         for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index a37598e..03c0768 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -751,10 +751,10 @@
 
                 // There might be an old window delaying the animation start - clear it.
                 if (mDelayedImeWin != null) {
-                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
+                    mDelayedImeWin.endDelayingAnimationStart();
                 }
                 mDelayedImeWin = imeWin;
-                imeWin.mWinAnimator.startDelayingAnimationStart();
+                imeWin.startDelayingAnimationStart();
             }
 
             // If we are already waiting for something to be drawn, clear out the old one so it
@@ -765,25 +765,27 @@
                 mService.mWaitingForDrawnCallback.run();
             }
             mService.mWaitingForDrawnCallback = () -> {
-                mAnimationStartDelayed = false;
-                if (mDelayedImeWin != null) {
-                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
+                synchronized (mService.mWindowMap) {
+                    mAnimationStartDelayed = false;
+                    if (mDelayedImeWin != null) {
+                        mDelayedImeWin.endDelayingAnimationStart();
+                    }
+                    // If the adjust status changed since this was posted, only notify
+                    // the new states and don't animate.
+                    long duration = 0;
+                    if (mAdjustedForIme == adjustedForIme
+                            && mAdjustedForDivider == adjustedForDivider) {
+                        duration = IME_ADJUST_ANIM_DURATION;
+                    } else {
+                        Slog.w(TAG, "IME adjust changed while waiting for drawn:"
+                                + " adjustedForIme=" + adjustedForIme
+                                + " adjustedForDivider=" + adjustedForDivider
+                                + " mAdjustedForIme=" + mAdjustedForIme
+                                + " mAdjustedForDivider=" + mAdjustedForDivider);
+                    }
+                    notifyAdjustedForImeChanged(
+                            mAdjustedForIme || mAdjustedForDivider, duration);
                 }
-                // If the adjust status changed since this was posted, only notify
-                // the new states and don't animate.
-                long duration = 0;
-                if (mAdjustedForIme == adjustedForIme
-                        && mAdjustedForDivider == adjustedForDivider) {
-                    duration = IME_ADJUST_ANIM_DURATION;
-                } else {
-                    Slog.w(TAG, "IME adjust changed while waiting for drawn:"
-                            + " adjustedForIme=" + adjustedForIme
-                            + " adjustedForDivider=" + adjustedForDivider
-                            + " mAdjustedForIme=" + mAdjustedForIme
-                            + " mAdjustedForDivider=" + mAdjustedForDivider);
-                }
-                notifyAdjustedForImeChanged(
-                        mAdjustedForIme || mAdjustedForDivider, duration);
             };
         } else {
             notifyAdjustedForImeChanged(
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 5bc2722..b64ce1a 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -29,7 +29,6 @@
 import android.view.Choreographer;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
-import android.view.animation.Transformation;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -94,9 +93,6 @@
         synchronized (mLock) {
             if (mPendingAnimations.containsKey(leash)) {
                 mPendingAnimations.remove(leash);
-                // TODO: Releasing the leash is problematic if reparenting hasn't happened yet.
-                // Fix with transaction
-                //leash.release();
                 return;
             }
             final ValueAnimator anim = mRunningAnimations.get(leash);
@@ -105,7 +101,6 @@
                 SurfaceAnimationThread.getHandler().post(() -> {
                     anim.cancel();
                     applyTransaction();
-                    //leash.release();
                 });
             }
         }
@@ -123,7 +118,7 @@
 
         // Animation length is already expected to be scaled.
         result.overrideDurationScale(1.0f);
-        result.setDuration(a.animSpec.getDuration());
+        result.setDuration(a.mAnimSpec.getDuration());
         result.addUpdateListener(animation -> {
             applyTransformation(a, mFrameTransaction, result.getCurrentPlayTime());
 
@@ -136,7 +131,7 @@
 
             @Override
             public void onAnimationStart(Animator animation) {
-                mFrameTransaction.show(a.leash);
+                mFrameTransaction.show(a.mLeash);
             }
 
             @Override
@@ -147,26 +142,20 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 synchronized (mLock) {
-                    mRunningAnimations.remove(a.leash);
+                    mRunningAnimations.remove(a.mLeash);
                 }
                 if (!mCancelled) {
                     // Post on other thread that we can push final state without jank.
-                    AnimationThread.getHandler().post(() -> {
-                        a.finishCallback.run();
-
-                        // Make sure to release the leash after finishCallback has been invoked such
-                        // that reparenting is done already when releasing the leash.
-                        a.leash.release();
-                    });
+                    AnimationThread.getHandler().post(a.mFinishCallback);
                 }
             }
         });
         result.start();
-        mRunningAnimations.put(a.leash, result);
+        mRunningAnimations.put(a.mLeash, result);
     }
 
     private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
-        a.animSpec.apply(t, a.leash, currentPlayTime);
+        a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
     }
 
     private void stepAnimation(long frameTimeNanos) {
@@ -189,14 +178,14 @@
     }
 
     private static final class RunningAnimation {
-        final AnimationSpec animSpec;
-        final SurfaceControl leash;
-        final Runnable finishCallback;
+        final AnimationSpec mAnimSpec;
+        final SurfaceControl mLeash;
+        final Runnable mFinishCallback;
 
         RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
-            this.animSpec = animSpec;
-            this.leash = leash;
-            this.finishCallback = finishCallback;
+            mAnimSpec = animSpec;
+            mLeash = leash;
+            mFinishCallback = finishCallback;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 713d58b..e165211 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -97,7 +97,7 @@
     void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
         cancelAnimation(t, true /* restarting */);
         mAnimation = anim;
-        final SurfaceControl surface = mAnimatable.getSurface();
+        final SurfaceControl surface = mAnimatable.getSurfaceControl();
         if (surface == null) {
             Slog.w(TAG, "Unable to start animation, surface is null or no children.");
             cancelAnimation();
@@ -105,7 +105,7 @@
         }
         mLeash = createAnimationLeash(surface, t,
                 mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
-        mAnimatable.onLeashCreated(t, mLeash);
+        mAnimatable.onAnimationLeashCreated(t, mLeash);
         if (mAnimationStartDelayed) {
             if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
             return;
@@ -169,7 +169,16 @@
      * surface is reparented to the leash. This method takes care of that.
      */
     void setLayer(Transaction t, int layer) {
-        t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurface(), layer);
+        t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), layer);
+    }
+
+    /**
+     * Sets the surface to be relatively layered.
+     *
+     * @see #setLayer
+     */
+    void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
+        t.setRelativeLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), relativeTo, layer);
     }
 
     /**
@@ -178,7 +187,7 @@
      * @see #setLayer
      */
     void reparent(Transaction t, SurfaceControl newParent) {
-        t.reparent(mLeash != null ? mLeash : mAnimatable.getSurface(), newParent.getHandle());
+        t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent.getHandle());
     }
 
     /**
@@ -207,8 +216,8 @@
     }
 
     private void reset(Transaction t) {
-        final SurfaceControl surface = mAnimatable.getSurface();
-        final SurfaceControl parent = mAnimatable.getParentSurface();
+        final SurfaceControl surface = mAnimatable.getSurfaceControl();
+        final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
 
         // If the surface was destroyed, we don't care to reparent it back.
         final boolean destroy = mLeash != null && surface != null && parent != null;
@@ -216,19 +225,22 @@
             if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
             t.reparent(surface, parent.getHandle());
         }
+        if (mLeash != null) {
+            mAnimatable.destroyAfterPendingTransaction(mLeash);
+        }
         mLeash = null;
         mAnimation = null;
 
         // Make sure to inform the animatable after the leash was destroyed.
         if (destroy) {
-            mAnimatable.onLeashDestroyed(t);
+            mAnimatable.onAnimationLeashDestroyed(t);
         }
     }
 
     private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
             int height, boolean hidden) {
         if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
-        final SurfaceControl.Builder builder = mAnimatable.makeLeash()
+        final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
                 .setName(surface + " - animation-leash")
                 .setSize(width, height);
         final SurfaceControl leash = builder.build();
@@ -273,7 +285,7 @@
          * @param t The transaction to use to apply any necessary changes.
          * @param leash The leash that was created.
          */
-        void onLeashCreated(Transaction t, SurfaceControl leash);
+        void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
 
         /**
          * Called when the leash is being destroyed, and the surface was reparented back to the
@@ -281,22 +293,30 @@
          *
          * @param t The transaction to use to apply any necessary changes.
          */
-        void onLeashDestroyed(Transaction t);
+        void onAnimationLeashDestroyed(Transaction t);
 
         /**
-         * @return A new child surface.
+         * Destroy a given surface after executing {@link #getPendingTransaction}.
+         *
+         * @see WindowContainer#destroyAfterPendingTransaction
          */
-        SurfaceControl.Builder makeLeash();
+        void destroyAfterPendingTransaction(SurfaceControl surface);
+
+        /**
+         * @return A new surface to be used for the animation leash, inserted at the correct
+         *         position in the hierarchy.
+         */
+        SurfaceControl.Builder makeAnimationLeash();
 
         /**
          * @return The surface of the object to be animated.
          */
-        @Nullable SurfaceControl getSurface();
+        @Nullable SurfaceControl getSurfaceControl();
 
         /**
          * @return The parent of the surface object to be animated.
          */
-        @Nullable SurfaceControl getParentSurface();
+        @Nullable SurfaceControl getParentSurfaceControl();
 
         /**
          * @return The width of the surface to be animated.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6ea8a47..244eb66 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -22,7 +22,6 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.res.Configuration.EMPTY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 
 import static com.android.server.EventLogTags.WM_TASK_REMOVED;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
@@ -43,7 +42,6 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
-import android.view.DisplayInfo;
 import android.view.Surface;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -162,7 +160,7 @@
     boolean shouldDeferRemoval() {
         // TODO: This should probably return false if mChildren.isEmpty() regardless if the stack
         // is animating...
-        return hasWindowsAlive() && mStack.isAnimating();
+        return hasWindowsAlive() && mStack.isSelfOrChildAnimating();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 28b1390..9946c6a 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -935,7 +935,7 @@
 
     @Override
     void removeIfPossible() {
-        if (isAnimating()) {
+        if (isSelfOrChildAnimating()) {
             mDeferRemoval = true;
             return;
         }
@@ -1643,7 +1643,7 @@
 
     /** Returns true if a removal action is still being deferred. */
     boolean checkCompleteDeferredRemoval() {
-        if (isAnimating()) {
+        if (isSelfOrChildAnimating()) {
             return true;
         }
         if (mDeferRemoval) {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index a12c0e5..3389f71 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -115,7 +115,7 @@
     void startAnimation(Animation anim) {
         for (int ndx = mChildren.size() - 1; ndx >= 0; ndx--) {
             final WindowState windowState = mChildren.get(ndx);
-            windowState.mWinAnimator.setAnimation(anim);
+            windowState.startAnimation(anim);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 56175c7..bb25297 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -31,7 +31,7 @@
 
     private Animation mAnimation;
     private final Point mPosition = new Point();
-    private final ThreadLocal<Tmp> mThreadLocalTmps = ThreadLocal.withInitial(Tmp::new);
+    private final ThreadLocal<TmpValues> mThreadLocalTmps = ThreadLocal.withInitial(TmpValues::new);
 
     public WindowAnimationSpec(Animation animation, Point position)  {
         mAnimation = animation;
@@ -55,7 +55,7 @@
 
     @Override
     public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
-        final Tmp tmp = mThreadLocalTmps.get();
+        final TmpValues tmp = mThreadLocalTmps.get();
         tmp.transformation.clear();
         mAnimation.getTransformation(currentPlayTime, tmp.transformation);
         tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
@@ -63,7 +63,7 @@
         t.setAlpha(leash, tmp.transformation.getAlpha());
     }
 
-    private static class Tmp {
+    private static class TmpValues {
         final Transformation transformation = new Transformation();
         final float[] floats = new float[9];
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d6329bf..b2b6119 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -19,6 +19,9 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+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.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
 import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
 import static com.android.server.wm.proto.WindowContainerProto.VISIBLE;
@@ -26,14 +29,20 @@
 
 import android.annotation.CallSuper;
 import android.content.res.Configuration;
+import android.graphics.PixelFormat.Opacity;
+import android.util.Slog;
 import android.view.MagnificationSpec;
 import android.view.SurfaceControl;
+import android.view.SurfaceControl.Builder;
 import android.view.SurfaceSession;
 import android.util.Pools;
 
 import android.util.proto.ProtoOutputStream;
-import com.android.internal.util.ToBooleanFunction;
 
+import com.android.internal.util.ToBooleanFunction;
+import com.android.server.wm.SurfaceAnimator.Animatable;
+
+import java.io.PrintWriter;
 import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.function.Consumer;
@@ -46,7 +55,9 @@
  * changes are made to this class.
  */
 class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
-        implements Comparable<WindowContainer> {
+        implements Comparable<WindowContainer>, Animatable {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
 
     static final int POSITION_TOP = Integer.MAX_VALUE;
     static final int POSITION_BOTTOM = Integer.MIN_VALUE;
@@ -56,7 +67,7 @@
      * For removing or setting new parent {@link #setParent} should be used, because it also
      * performs configuration updates based on new parent's settings.
      */
-    private WindowContainer mParent = null;
+    private WindowContainer<WindowContainer> mParent = null;
 
     // List of children for this window container. List is in z-order as the children appear on
     // screen with the top-most window container at the tail of the list.
@@ -69,7 +80,7 @@
             new Pools.SynchronizedPool<>(3);
 
     // The owner/creator for this container. No controller if null.
-     WindowContainerController mController;
+    WindowContainerController mController;
 
     protected SurfaceControl mSurfaceControl;
     private int mLastLayer = 0;
@@ -78,12 +89,14 @@
     /**
      * Applied as part of the animation pass in "prepareSurfaces".
      */
-    private final Transaction mPendingTransaction;
+    protected final Transaction mPendingTransaction;
+    protected final SurfaceAnimator mSurfaceAnimator;
     protected final WindowManagerService mService;
 
     WindowContainer(WindowManagerService service) {
         mService = service;
         mPendingTransaction = service.mTransactionFactory.make();
+        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, service);
     }
 
     @Override
@@ -101,7 +114,7 @@
         return mChildren.get(index);
     }
 
-    final protected void setParent(WindowContainer parent) {
+    final protected void setParent(WindowContainer<WindowContainer> parent) {
         mParent = parent;
         // Removing parent usually means that we've detached this entity to destroy it or to attach
         // to another parent. In both cases we don't need to update the configuration now.
@@ -123,14 +136,18 @@
         if (mParent == null) {
             return;
         }
+
         if (mSurfaceControl == null) {
             // If we don't yet have a surface, but we now have a parent, we should
             // build a surface.
             mSurfaceControl = makeSurface().build();
             getPendingTransaction().show(mSurfaceControl);
         } else {
-            // If we have a surface but a new parent, we just need to perform a reparent.
-            getPendingTransaction().reparent(mSurfaceControl, mParent.mSurfaceControl.getHandle());
+            // If we have a surface but a new parent, we just need to perform a reparent. Go through
+            // surface animator such that hierarchy is preserved when animating, i.e.
+            // mSurfaceControl stays attached to the leash and we just reparent the leash to the
+            // new parent.
+            mSurfaceAnimator.reparent(getPendingTransaction(), mParent.mSurfaceControl);
         }
 
         // Either way we need to ask the parent to assign us a Z-order.
@@ -139,8 +156,8 @@
     }
 
     // Temp. holders for a chain of containers we are currently processing.
-    private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList();
-    private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList();
+    private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList<>();
+    private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList<>();
 
     /**
      * Adds the input window container has a child of this container in order based on the input
@@ -214,7 +231,7 @@
     @CallSuper
     void removeImmediately() {
         while (!mChildren.isEmpty()) {
-            final WindowContainer child = mChildren.peekLast();
+            final E child = mChildren.peekLast();
             child.removeImmediately();
             // Need to do this after calling remove on the child because the child might try to
             // remove/detach itself from its parent which will cause an exception if we remove
@@ -401,16 +418,40 @@
         }
     }
 
-    boolean isAnimating() {
+    /**
+     * @return Whether our own container is running an animation or any child, no matter how deep in
+     *         the hierarchy, is animating.
+     */
+    boolean isSelfOrChildAnimating() {
+        if (isSelfAnimating()) {
+            return true;
+        }
         for (int j = mChildren.size() - 1; j >= 0; j--) {
             final WindowContainer wc = mChildren.get(j);
-            if (wc.isAnimating()) {
+            if (wc.isSelfOrChildAnimating()) {
                 return true;
             }
         }
         return false;
     }
 
+    /**
+     * @return Whether our own container is running an animation or our parent is animating. This
+     *         doesn't consider whether children are animating.
+     */
+    boolean isAnimating() {
+
+        // We are animating if we ourselves are animating or if our parent is animating.
+        return isSelfAnimating() || mParent != null && mParent.isAnimating();
+    }
+
+    /**
+     * @return Whether our own container running an animation at the moment.
+     */
+    boolean isSelfAnimating() {
+        return mSurfaceAnimator.isAnimating();
+    }
+
     void sendAppVisibilityToClients() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
@@ -743,6 +784,15 @@
                 .setParent(mSurfaceControl);
     }
 
+    @Override
+    public SurfaceControl getParentSurfaceControl() {
+        final WindowContainer parent = getParent();
+        if (parent == null) {
+            return null;
+        }
+        return parent.getSurfaceControl();
+    }
+
     /**
      * @return Whether this WindowContainer should be magnified by the accessibility magnifier.
      */
@@ -765,7 +815,10 @@
     void assignLayer(Transaction t, int layer) {
         final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
         if (mSurfaceControl != null && changed) {
-            t.setLayer(mSurfaceControl, layer);
+
+            // Route through surface animator to accommodate that our surface control might be
+            // attached to the leash, and leash is attached to parent container.
+            mSurfaceAnimator.setLayer(t, layer);
             mLastLayer = layer;
             mLastRelativeToLayer = null;
         }
@@ -774,7 +827,10 @@
     void assignRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
         final boolean changed = layer != mLastLayer || mLastRelativeToLayer != relativeTo;
         if (mSurfaceControl != null && changed) {
-            t.setRelativeLayer(mSurfaceControl, relativeTo, layer);
+
+            // Route through surface animator to accommodate that our surface control might be
+            // attached to the leash, and leash is attached to parent container.
+            mSurfaceAnimator.setRelativeLayer(t, relativeTo, layer);
             mLastLayer = layer;
             mLastRelativeToLayer = relativeTo;
         }
@@ -897,7 +953,8 @@
         }
     }
 
-    SurfaceControl getSurfaceControl() {
+    @Override
+    public SurfaceControl getSurfaceControl() {
         return mSurfaceControl;
     }
 
@@ -907,13 +964,105 @@
      * rather than an intentional design, so please take care when
      * expanding use.
      */
-    void destroyAfterPendingTransaction(SurfaceControl surface) {
+    @Override
+    public void destroyAfterPendingTransaction(SurfaceControl surface) {
         if (mParent != null) {
             mParent.destroyAfterPendingTransaction(surface);
         }
     }
-    
-    Transaction getPendingTransaction() {
+
+    @Override
+    public Transaction getPendingTransaction() {
         return mPendingTransaction;
     }
+
+    /**
+     * Starts an animation on the container.
+     *
+     * @param anim The animation to run.
+     * @param hidden Whether our container is currently hidden. TODO This should use isVisible at
+     *               some point but the meaning is too weird to work for all containers.
+     */
+    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
+        if (DEBUG_ANIM) Slog.v(TAG, "Starting animation on " + this + ": " + anim);
+
+        // TODO: This should use isVisible() but because isVisible has a really weird meaning at
+        // the moment this doesn't work for all animatable window containers.
+        mSurfaceAnimator.startAnimation(t, anim, hidden);
+    }
+
+    void cancelAnimation() {
+        mSurfaceAnimator.cancelAnimation();
+    }
+
+    @Override
+    public Builder makeAnimationLeash() {
+        return makeSurface();
+    }
+
+    @Override
+    public void commitPendingTransaction() {
+        scheduleAnimation();
+    }
+
+    private void reassignLayer(Transaction t) {
+        final WindowContainer parent = getParent();
+        if (parent != null) {
+            parent.assignChildLayers(t);
+        }
+    }
+
+    @Override
+    public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+        reassignLayer(t);
+    }
+
+    @Override
+    public void onAnimationLeashDestroyed(Transaction t) {
+        reassignLayer(t);
+    }
+
+    /**
+     * Called when an animation has finished running.
+     */
+    protected void onAnimationFinished() {
+    }
+
+    /**
+     * @return The currently running animation, if any, or {@code null} otherwise.
+     */
+    AnimationAdapter getAnimation() {
+        return mSurfaceAnimator.getAnimation();
+    }
+
+    /**
+     * @see SurfaceAnimator#startDelayingAnimationStart
+     */
+    void startDelayingAnimationStart() {
+        mSurfaceAnimator.startDelayingAnimationStart();
+    }
+
+    /**
+     * @see SurfaceAnimator#endDelayingAnimationStart
+     */
+    void endDelayingAnimationStart() {
+        mSurfaceAnimator.endDelayingAnimationStart();
+    }
+
+    @Override
+    public int getSurfaceWidth() {
+        return mSurfaceControl.getWidth();
+    }
+
+    @Override
+    public int getSurfaceHeight() {
+        return mSurfaceControl.getHeight();
+    }
+
+    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+        if (mSurfaceAnimator.isAnimating()) {
+            pw.print(prefix); pw.println("ContainerAnimator:");
+            mSurfaceAnimator.dump(pw, prefix + "  ");
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8c9948e..7a3ba74 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -725,8 +725,6 @@
         }
     }
 
-    boolean mAnimateWallpaperWithTarget;
-
     // TODO: Move to RootWindowContainer
     AppWindowToken mFocusedApp = null;
 
@@ -2182,18 +2180,15 @@
         if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
             focusMayChange = isDefaultDisplay;
             win.mAnimatingExit = true;
-            win.mWinAnimator.mAnimating = true;
         } else if (win.mWinAnimator.isAnimationSet()) {
             // Currently in a hide animation... turn this into
             // an exit.
             win.mAnimatingExit = true;
-            win.mWinAnimator.mAnimating = true;
         } else if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) {
             // If the wallpaper is currently behind this
             // window, we need to change both of them inside
             // of a transaction to avoid artifacts.
             win.mAnimatingExit = true;
-            win.mWinAnimator.mAnimating = true;
         } else {
             if (mInputMethodWindow == win) {
                 setInputMethodWindowLocked(null);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e38605d..559d5b6 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -19,11 +19,11 @@
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.SurfaceControl.Transaction;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
-import static android.view.SurfaceControl.Transaction;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -91,6 +91,7 @@
 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.H.SEND_NEW_CONFIGURATION;
+import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -160,10 +161,14 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowInfo;
 import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.input.InputWindowHandle;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -594,6 +599,8 @@
      */
     private boolean mDrawnStateEvaluated;
 
+    private final Point mSurfacePosition = new Point();
+
     /**
      * Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
      * of z-order and 1 otherwise.
@@ -1470,9 +1477,9 @@
         final AppWindowToken atoken = mAppToken;
         if (atoken != null) {
             return ((!isParentWindowHidden() && !atoken.hiddenRequested)
-                    || mWinAnimator.mAnimation != null || atoken.mAppAnimator.animation != null);
+                    || mWinAnimator.isAnimationSet() || atoken.mAppAnimator.animation != null);
         }
-        return !isParentWindowHidden() || mWinAnimator.mAnimation != null;
+        return !isParentWindowHidden() || mWinAnimator.isAnimationSet();
     }
 
     /**
@@ -1505,7 +1512,7 @@
         }
         return mHasSurface && mPolicyVisibility && !mDestroying
                 && ((!isParentWindowHidden() && mViewVisibility == View.VISIBLE && !mToken.hidden)
-                        || mWinAnimator.mAnimation != null
+                        || mWinAnimator.isAnimationSet()
                         || ((mAppToken != null) && (mAppToken.mAppAnimator.animation != null)));
     }
 
@@ -1520,8 +1527,7 @@
         // started.
         final boolean appAnimationStarting = mAppToken != null
                 && mAppToken.mAppAnimator.isAnimationStarting();
-        final boolean exitingSelf = mAnimatingExit && (!mWinAnimator.isAnimationStarting()
-                && !appAnimationStarting);
+        final boolean exitingSelf = mAnimatingExit && !appAnimationStarting;
         final boolean appExiting = mAppToken != null && mAppToken.hidden && !appAnimationStarting;
 
         final boolean exiting = exitingSelf || mDestroying || appExiting;
@@ -1544,7 +1550,7 @@
         return isDrawnLw() && mPolicyVisibility
             && ((!isParentWindowHidden() &&
                     (atoken == null || !atoken.hiddenRequested))
-                        || mWinAnimator.mAnimating
+                        || mWinAnimator.isAnimationSet()
                         || (atoken != null && atoken.mAppAnimator.animation != null));
     }
 
@@ -1553,7 +1559,7 @@
      */
     @Override
     public boolean isAnimatingLw() {
-        return mWinAnimator.mAnimation != null
+        return mWinAnimator.isAnimationSet()
                 || (mAppToken != null && mAppToken.mAppAnimator.animation != null);
     }
 
@@ -1600,7 +1606,7 @@
         // to determine if it's occluding apps.
         return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE)
                 || (mIsWallpaper && mWallpaperVisible))
-                && isDrawnLw() && mWinAnimator.mAnimation == null
+                && isDrawnLw() && !mWinAnimator.isAnimationSet()
                 && (mAppToken == null || mAppToken.mAppAnimator.animation == null);
     }
 
@@ -1732,7 +1738,7 @@
      * listeners and optionally animate it. Simply checking a change of position is not enough,
      * because being move due to dock divider is not a trigger for animation.
      */
-    void handleWindowMovedIfNeeded() {
+    void handleWindowMovedIfNeeded(Transaction t) {
         if (!hasMoved()) {
             return;
         }
@@ -1750,7 +1756,7 @@
                 && !isDragResizing() && !adjustedForMinimizedDockOrIme
                 && getWindowConfiguration().hasMovementAnimations()
                 && !mWinAnimator.mLastHidden) {
-            mWinAnimator.setMoveAnimation(left, top);
+            startMoveAnimation(t, left, top);
         }
 
         //TODO (multidisplay): Accessibility supported only for the default display.
@@ -2457,10 +2463,10 @@
         if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
         if (doAnimation) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
-                    + mPolicyVisibility + " mAnimation=" + mWinAnimator.mAnimation);
+                    + mPolicyVisibility + " isAnimationSet=" + mWinAnimator.isAnimationSet());
             if (!mToken.okToAnimate()) {
                 doAnimation = false;
-            } else if (mPolicyVisibility && mWinAnimator.mAnimation == null) {
+            } else if (mPolicyVisibility && !mWinAnimator.isAnimationSet()) {
                 // Check for the case where we are currently visible and
                 // not animating; we do not want to do animation at such a
                 // point to become visible when we already are.
@@ -2499,7 +2505,7 @@
         }
         if (doAnimation) {
             mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
-            if (mWinAnimator.mAnimation == null) {
+            if (!mWinAnimator.isAnimationSet()) {
                 doAnimation = false;
             }
         }
@@ -2599,14 +2605,6 @@
         return mAnimatingExit || (mService.mClosingApps.contains(mAppToken));
     }
 
-    @Override
-    boolean isAnimating() {
-        if (mWinAnimator.isAnimationSet() || mAnimatingExit) {
-            return true;
-        }
-        return super.isAnimating();
-    }
-
     void addWinAnimatorToList(ArrayList<WindowStateAnimator> animators) {
         animators.add(mWinAnimator);
 
@@ -3133,6 +3131,7 @@
         proto.end(token);
     }
 
+    @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         final TaskStack stack = getStack();
         pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
@@ -3275,6 +3274,7 @@
                     pw.print(" cutout=" + mLastDisplayCutout);
                     pw.println();
         }
+        super.dump(pw, prefix, dumpAll);
         pw.print(prefix); pw.print(mWinAnimator); pw.println(":");
         mWinAnimator.dump(pw, prefix + "  ", dumpAll);
         if (mAnimatingExit || mRemoveOnExit || mDestroying || mRemoved) {
@@ -3333,7 +3333,7 @@
     @Override
     String getName() {
         return Integer.toHexString(System.identityHashCode(this))
-            + " " + getWindowTag();
+                + " " + getWindowTag();
     }
 
     CharSequence getWindowTag() {
@@ -3694,7 +3694,7 @@
                     + " tok.hiddenRequested="
                     + (mAppToken != null && mAppToken.hiddenRequested)
                     + " tok.hidden=" + (mAppToken != null && mAppToken.hidden)
-                    + " animating=" + mWinAnimator.mAnimating
+                    + " animationSet=" + mWinAnimator.isAnimationSet()
                     + " tok animating="
                     + (mWinAnimator.mAppAnimator != null && mWinAnimator.mAppAnimator.animating)
                     + " Callers=" + Debug.getCallers(4));
@@ -3900,23 +3900,10 @@
         return null;
     }
 
-    boolean isWindowAnimationSet() {
-        if (mWinAnimator.isWindowAnimationSet()) {
-            return true;
-        }
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = mChildren.get(i);
-            if (c.isWindowAnimationSet()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void onExitAnimationDone() {
         if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
                 + ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
-                + " windowAnimating=" + mWinAnimator.isWindowAnimationSet());
+                + " selfAnimating=" + isSelfAnimating());
 
         if (!mChildren.isEmpty()) {
             // Copying to a different list as multiple children can be removed.
@@ -3940,18 +3927,16 @@
             }
         }
 
-        if (!mWinAnimator.isWindowAnimationSet()) {
-            //TODO (multidisplay): Accessibility is supported only for the default display.
-            if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
-                mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
-            }
-        }
-
-        if (!mAnimatingExit) {
+        if (isSelfAnimating()) {
             return;
         }
 
-        if (mWinAnimator.isWindowAnimationSet()) {
+        //TODO (multidisplay): Accessibility is supported only for the default display.
+        if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+            mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+        }
+
+        if (!mAnimatingExit) {
             return;
         }
 
@@ -4005,10 +3990,6 @@
                 mAnimatingExit = false;
                 didSomething = true;
             }
-            if (mWinAnimator.mAnimating) {
-                mWinAnimator.mAnimating = false;
-                didSomething = true;
-            }
             if (mDestroying) {
                 mDestroying = false;
                 mService.mDestroySurface.remove(this);
@@ -4097,7 +4078,7 @@
                         + " mDrawState=" + mWinAnimator.mDrawState
                         + " ph=" + isParentWindowHidden()
                         + " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false)
-                        + " a=" + mWinAnimator.mAnimating);
+                        + " a=" + mWinAnimator.isAnimationSet());
             }
         }
 
@@ -4300,6 +4281,35 @@
         mLastDisplayCutout = mDisplayCutout;
     }
 
+    void startAnimation(Animation anim) {
+        final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
+        anim.initialize(mFrame.width(), mFrame.height(),
+                displayInfo.appWidth, displayInfo.appHeight);
+        anim.restrictDuration(MAX_ANIMATION_DURATION);
+        anim.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
+        final AnimationAdapter adapter = new LocalAnimationAdapter(
+                new WindowAnimationSpec(anim, mSurfacePosition), mService.mSurfaceAnimationRunner);
+        startAnimation(mPendingTransaction, adapter);
+        commitPendingTransaction();
+    }
+
+    private void startMoveAnimation(Transaction t, int left, int top) {
+        if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
+        final AnimationAdapter adapter = new LocalAnimationAdapter(
+                new MoveAnimationSpec(mLastFrame.left, mLastFrame.top, left, top),
+                mService.mSurfaceAnimationRunner);
+        startAnimation(t, adapter);
+    }
+
+    private void startAnimation(Transaction t, AnimationAdapter adapter) {
+        startAnimation(t, adapter, mWinAnimator.mLastHidden);
+    }
+
+    @Override
+    protected void onAnimationFinished() {
+        mWinAnimator.onAnimationFinished();
+    }
+
     // TODO: Hack to work around the number of states AppWindowToken needs to access without having
     // access to its windows children. Need to investigate re-writing
     // {@link AppWindowToken#updateReportedVisibilityLocked} so this can be removed.
@@ -4381,11 +4391,6 @@
         return getAnimLayerAdjustment() > 0 || mWillReplaceWindow;
     }
 
-    @Override
-    SurfaceControl.Builder makeSurface() {
-        return getParent().makeChildSurface(this);
-    }
-
     private void applyDims(Dimmer dimmer) {
         if (!mAnimatingExit && mAppDied) {
             mIsDimming = true;
@@ -4405,11 +4410,47 @@
             applyDims(dimmer);
         }
 
+        updateSurfacePosition(mPendingTransaction);
+
         mWinAnimator.prepareSurfaceLocked(true);
         super.prepareSurfaces();
     }
 
     @Override
+    public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+        super.onAnimationLeashCreated(t, leash);
+
+        // Leash is now responsible for position, so set our position to 0.
+        t.setPosition(mSurfaceControl, 0, 0);
+    }
+
+    @Override
+    public void onAnimationLeashDestroyed(Transaction t) {
+        super.onAnimationLeashDestroyed(t);
+        updateSurfacePosition(t);
+    }
+
+    void updateSurfacePosition(Transaction t) {
+        if (mSurfaceControl == null) {
+            return;
+        }
+
+        int left = mFrame.left;
+        int top = mFrame.top;
+        if (isChildWindow()) {
+            // TODO: This probably falls apart at some point and we should
+            // actually compute relative coordinates.
+            final WindowState parent = getParentWindow();
+            left -= parent.mFrame.left;
+            top -= parent.mFrame.top;
+        }
+        mSurfacePosition.set(left, top);
+        if (!mSurfaceAnimator.hasLeash()) {
+            t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+        }
+    }
+
+    @Override
     void assignLayer(Transaction t, int layer) {
         // See comment in assignRelativeLayerForImeTargetChild
         if (!isChildWindow()
@@ -4449,4 +4490,34 @@
             layer++;
         }
     }
+
+    private final class MoveAnimationSpec implements AnimationSpec {
+
+        private final long mDuration;
+        private Interpolator mInterpolator;
+        private Point mFrom = new Point();
+        private Point mTo = new Point();
+
+        private MoveAnimationSpec(int fromX, int fromY, int toX, int toY) {
+            final Animation anim = AnimationUtils.loadAnimation(mContext,
+                    com.android.internal.R.anim.window_move_from_decor);
+            mDuration = anim.computeDurationHint();
+            mInterpolator = anim.getInterpolator();
+            mFrom.set(fromX, fromY);
+            mTo.set(toX, toY);
+        }
+
+        @Override
+        public long getDuration() {
+            return mDuration;
+        }
+
+        @Override
+        public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
+            final float fraction = (float) currentPlayTime / getDuration();
+            final float v = mInterpolator.getInterpolation(fraction);
+            t.setPosition(leash, mFrom.x + (mTo.x - mFrom.x) * v,
+                    mFrom.y + (mTo.y - mFrom.y) * v);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 54c84ea..e3d2d1c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,16 +23,16 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 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_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_STARTING_WINDOW_VERBOSE;
 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;
@@ -40,7 +40,6 @@
 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.logWithStack;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
@@ -65,14 +64,13 @@
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Transformation;
 
 import com.android.server.policy.WindowManagerPolicy;
 
-import java.io.PrintWriter;
 import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 /**
  * Keep track of animations and surface operations for a single WindowState.
@@ -112,20 +110,9 @@
     final boolean mIsWallpaper;
     private final WallpaperController mWallpaperControllerLocked;
 
-    // Currently running animation.
-    boolean mAnimating;
-    boolean mLocalAnimating;
-    Animation mAnimation;
     boolean mAnimationIsEntrance;
-    boolean mHasTransformation;
-    boolean mHasLocalTransformation;
-    final Transformation mTransformation = new Transformation();
-    boolean mWasAnimating;      // Were we animating going into the most recent animation step?
     int mAnimLayer;
     int mLastLayer;
-    long mAnimationStartTime;
-    long mLastAnimationTime;
-    int mStackClip = STACK_CLIP_BEFORE_ANIM;
 
     /**
      * Set when we have changed the size of the surface, to know that
@@ -168,13 +155,6 @@
     private final Rect mSystemDecorRect = new Rect();
     private final Rect mLastSystemDecorRect = new Rect();
 
-    // Used to save animation distances between the time they are calculated and when they are used.
-    private int mAnimDx;
-    private int mAnimDy;
-
-    /** Is the next animation to be started a window move animation? */
-    private boolean mAnimateMove = false;
-
     float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
     private float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
 
@@ -226,8 +206,6 @@
 
     int mAttrType;
 
-    static final long PENDING_TRANSACTION_FINISH_WAIT_TIME = 100;
-
     boolean mForceScaleUntilResize;
 
     // WindowState.mHScale and WindowState.mVScale contain the
@@ -247,15 +225,6 @@
         mAnimator = service.mAnimator;
         mPolicy = service.mPolicy;
         mContext = service.mContext;
-        final DisplayContent displayContent = win.getDisplayContent();
-        if (displayContent != null) {
-            final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-            mAnimDx = displayInfo.appWidth;
-            mAnimDy = displayInfo.appHeight;
-        } else {
-            Slog.w(TAG, "WindowStateAnimator ctor: Display has been removed");
-            // This is checked on return and dealt with.
-        }
 
         mWin = win;
         mParentWinAnimator = !win.isChildWindow() ? null : win.getParentWindow().mWinAnimator;
@@ -266,54 +235,11 @@
         mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
     }
 
-    public void setAnimation(Animation anim, long startTime, int stackClip) {
-        if (localLOGV) Slog.v(TAG, "Setting animation in " + this + ": " + anim);
-        mAnimating = false;
-        mLocalAnimating = false;
-        mAnimation = anim;
-        mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
-        mAnimation.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
-        // Start out animation gone if window is gone, or visible if window is visible.
-        mTransformation.clear();
-        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, STACK_CLIP_AFTER_ANIM);
-    }
-
-    public void clearAnimation() {
-        if (mAnimation != null) {
-            mAnimating = true;
-            mLocalAnimating = false;
-            mAnimation.cancel();
-            mAnimation = null;
-            mStackClip = STACK_CLIP_BEFORE_ANIM;
-        }
-    }
-
     /**
      * Is the window or its container currently set to animate or currently animating?
      */
     boolean isAnimationSet() {
-        return mAnimation != null
-                || (mParentWinAnimator != null && mParentWinAnimator.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;
+        return mWin.isAnimating();
     }
 
     /** Is the window animating the DummyAnimation? */
@@ -323,13 +249,6 @@
     }
 
     /**
-     * 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() {
@@ -341,130 +260,23 @@
         if (DEBUG_ANIM) Slog.d(TAG,
                 "cancelExitAnimationForNextAnimationLocked: " + mWin);
 
-        if (mAnimation != null) {
-            mAnimation.cancel();
-            mAnimation = null;
-            mLocalAnimating = false;
-            mWin.destroySurfaceUnchecked();
-        }
+        mWin.cancelAnimation();
+        mWin.destroySurfaceUnchecked();
     }
 
-    private boolean stepAnimation(long currentTime) {
-        if ((mAnimation == null) || !mLocalAnimating) {
-            return false;
-        }
-        currentTime = getAnimationFrameTime(mAnimation, currentTime);
-        mTransformation.clear();
-        final boolean more = mAnimation.getTransformation(currentTime, 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 (mWin.mToken.okToAnimate()) {
-            // We will run animations as long as the display isn't frozen.
-
-            if (mWin.isDrawnLw() && mAnimation != null) {
-                mHasTransformation = true;
-                mHasLocalTransformation = true;
-                if (!mLocalAnimating) {
-                    if (DEBUG_ANIM) Slog.v(
-                        TAG, "Starting animation in " + this +
-                        " @ " + currentTime + ": ww=" + mWin.mFrame.width() +
-                        " wh=" + mWin.mFrame.height() +
-                        " dx=" + mAnimDx + " dy=" + mAnimDy +
-                        " scale=" + mService.getWindowAnimationScaleLocked());
-                    final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-                    if (mAnimateMove) {
-                        mAnimateMove = false;
-                        mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
-                                mAnimDx, mAnimDy);
-                    } else {
-                        mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
-                                displayInfo.appWidth, displayInfo.appHeight);
-                    }
-                    mAnimDx = displayInfo.appWidth;
-                    mAnimDy = displayInfo.appHeight;
-                    mAnimation.setStartTime(mAnimationStartTime != -1
-                            ? mAnimationStartTime
-                            : currentTime);
-                    mLocalAnimating = true;
-                    mAnimating = true;
-                }
-                if ((mAnimation != null) && mLocalAnimating) {
-                    mLastAnimationTime = currentTime;
-                    if (stepAnimation(currentTime)) {
-                        return true;
-                    }
-                }
-                if (DEBUG_ANIM) Slog.v(
-                    TAG, "Finished animation in " + this +
-                    " @ " + currentTime);
-                //WindowManagerService.this.dump();
-            }
-            mHasLocalTransformation = false;
-            if ((!mLocalAnimating || mAnimationIsEntrance) && mAppAnimator != null
-                    && mAppAnimator.animation != null) {
-                // When our app token is animating, we kind-of pretend like
-                // we are as well.  Note the mLocalAnimating mAnimationIsEntrance
-                // part of this check means that we will only do this if
-                // our window is not currently exiting, or it is not
-                // locally animating itself.  The idea being that one that
-                // is exiting and doing a local animation should be removed
-                // once that animation is done.
-                mAnimating = true;
-                mHasTransformation = true;
-                mTransformation.clear();
-                return false;
-            } else if (mHasTransformation) {
-                // Little trick to get through the path below to act like
-                // we have finished an animation.
-                mAnimating = true;
-            } else if (isAnimationSet()) {
-                mAnimating = true;
-            }
-        } else if (mAnimation != null) {
-            // If the display is frozen, and there is a pending animation,
-            // clear it and make sure we run the cleanup code.
-            mAnimating = true;
-        }
-
-        if (!mAnimating && !mLocalAnimating) {
-            return false;
-        }
-
+    void onAnimationFinished() {
         // Done animating, clean up.
         if (DEBUG_ANIM) Slog.v(
-            TAG, "Animation done in " + this + ": exiting=" + mWin.mAnimatingExit
-            + ", reportedVisible="
-            + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
+                TAG, "Animation done in " + this + ": exiting=" + mWin.mAnimatingExit
+                        + ", reportedVisible="
+                        + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
 
-        mAnimating = false;
-        mLocalAnimating = false;
-        if (mAnimation != null) {
-            mAnimation.cancel();
-            mAnimation = null;
-        }
         if (mAnimator.mWindowDetachedWallpaper == mWin) {
             mAnimator.mWindowDetachedWallpaper = null;
         }
-        mAnimLayer = mWin.getSpecialWindowAnimLayerAdjustment();
-        if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer);
-        mHasTransformation = false;
-        mHasLocalTransformation = false;
-        mStackClip = STACK_CLIP_BEFORE_ANIM;
+
         mWin.checkPolicyVisibilityChange();
-        mTransformation.clear();
+        final DisplayContent displayContent = mWin.getDisplayContent();
         if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
             // Upon completion of a not-visible to visible status bar animation a relayout is
             // required.
@@ -475,7 +287,11 @@
 
         mWin.onExitAnimationDone();
         final int displayId = mWin.getDisplayId();
-        mAnimator.setPendingLayoutChanges(displayId, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
+        int pendingLayoutChanges = FINISH_LAYOUT_REDO_ANIM;
+        if (displayContent.mWallpaperController.isWallpaperTarget(mWin)) {
+            pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+        }
+        mAnimator.setPendingLayoutChanges(displayId, pendingLayoutChanges);
         if (DEBUG_LAYOUT_REPEATS)
             mService.mWindowPlacerLocked.debugLayoutRepeats(
                     "WindowStateAnimator", mAnimator.getPendingLayoutChanges(displayId));
@@ -483,8 +299,6 @@
         if (mWin.mAppToken != null) {
             mWin.mAppToken.updateReportedVisibilityLocked();
         }
-
-        return false;
     }
 
     void hide(String reason) {
@@ -683,8 +497,8 @@
             }
 
             mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
-                    attrs.getTitle().toString(),
-                    width, height, format, flags, this, windowType, ownerUid);
+                    attrs.getTitle().toString(), width, height, format, flags, this,
+                    windowType, ownerUid);
             mSurfaceFormat = format;
 
             w.setHasSurface(true);
@@ -853,35 +667,8 @@
     }
 
     void computeShownFrameLocked() {
-        final boolean selfTransformation = mHasLocalTransformation;
         Transformation appTransformation = (mAppAnimator != null && mAppAnimator.hasTransformation)
                 ? mAppAnimator.transformation : null;
-        Transformation wallpaperTargetTransformation = null;
-
-        // Wallpapers are animated based on the "real" window they
-        // are currently targeting.
-        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()) {
-                wallpaperTargetTransformation = wallpaperAnimator.mTransformation;
-                if (DEBUG_WALLPAPER && wallpaperTargetTransformation != null) {
-                    Slog.v(TAG, "WP target attached xform: " + wallpaperTargetTransformation);
-                }
-            }
-            final AppWindowAnimator wpAppAnimator = wallpaperTarget.mAppToken == null ?
-                    null : wallpaperTarget.mAppToken.mAppAnimator;
-                if (wpAppAnimator != null && wpAppAnimator.hasTransformation
-                    && wpAppAnimator.animation != null
-                    && !wpAppAnimator.animation.getDetachWallpaper()) {
-                appTransformation = wpAppAnimator.transformation;
-                if (DEBUG_WALLPAPER && appTransformation != null) {
-                    Slog.v(TAG, "WP target app xform: " + appTransformation);
-                }
-            }
-        }
 
         final int displayId = mWin.getDisplayId();
         final ScreenRotationAnimation screenRotationAnimation =
@@ -890,8 +677,7 @@
                 screenRotationAnimation != null && screenRotationAnimation.isAnimating();
 
         mHasClipRect = false;
-        if (selfTransformation || wallpaperTargetTransformation != null
-                || appTransformation != null || screenAnimation) {
+        if (appTransformation != null || screenAnimation) {
             // cache often used attributes locally
             final Rect frame = mWin.mFrame;
             final float tmpFloats[] = mService.mTmpFloats;
@@ -917,30 +703,13 @@
                 tmpMatrix.reset();
             }
             tmpMatrix.postScale(mWin.mGlobalScale, mWin.mGlobalScale);
-            if (selfTransformation) {
-                tmpMatrix.postConcat(mTransformation.getMatrix());
-            }
 
-            if (wallpaperTargetTransformation != null) {
-                tmpMatrix.postConcat(wallpaperTargetTransformation.getMatrix());
-            }
             if (appTransformation != null) {
                 tmpMatrix.postConcat(appTransformation.getMatrix());
             }
 
-            int left = frame.left;
-            int top = frame.top;
-            if (mWin.isChildWindow()) {
-                WindowState parent = mWin.getParentWindow();
-                left -= parent.mFrame.left;
-                top  -= parent.mFrame.top;
-            }
+            tmpMatrix.postTranslate(mWin.mXOffset, mWin.mYOffset);
 
-            // 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(left + mWin.mXOffset, top + mWin.mYOffset);
             if (screenAnimation) {
                 tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
             }
@@ -972,12 +741,6 @@
                     || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)
                             && x == frame.left && y == frame.top))) {
                 //Slog.i(TAG_WM, "Applying alpha transform");
-                if (selfTransformation) {
-                    mShownAlpha *= mTransformation.getAlpha();
-                }
-                if (wallpaperTargetTransformation != null) {
-                    mShownAlpha *= wallpaperTargetTransformation.getAlpha();
-                }
                 if (appTransformation != null) {
                     mShownAlpha *= appTransformation.getAlpha();
                     if (appTransformation.hasClipRect()) {
@@ -1006,9 +769,6 @@
             if ((DEBUG_ANIM || WindowManagerService.localLOGV)
                     && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v(
                     TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha
-                    + " self=" + (selfTransformation ? mTransformation.getAlpha() : "null")
-                    + " attached=" + (wallpaperTargetTransformation == null ?
-                            "null" : wallpaperTargetTransformation.getAlpha())
                     + " app=" + (appTransformation == null ? "null" : appTransformation.getAlpha())
                     + " screen=" + (screenAnimation ?
                             screenRotationAnimation.getEnterTransformation().getAlpha() : "null"));
@@ -1028,10 +788,7 @@
                 TAG, "computeShownFrameLocked: " + this +
                 " not attached, mAlpha=" + mAlpha);
 
-        mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
-        if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
-            mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
-        }
+        mWin.mShownPosition.set(mWin.mXOffset, mWin.mYOffset);
         mShownAlpha = mAlpha;
         mHaveMatrix = false;
         mDsDx = mWin.mGlobalScale;
@@ -1182,7 +939,7 @@
         if (mAppAnimator != null && mAppAnimator.animation != null) {
             return mAppAnimator.getStackClip();
         } else {
-            return mStackClip;
+            return STACK_CLIP_AFTER_ANIM;
         }
     }
 
@@ -1434,7 +1191,7 @@
         if (mSurfaceResized) {
             mReportSurfaceResized = true;
             mAnimator.setPendingLayoutChanges(w.getDisplayId(),
-                    WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
+                    FINISH_LAYOUT_REDO_WALLPAPER);
         }
     }
 
@@ -1546,7 +1303,7 @@
                         // Run another pass through performLayout to set mHasContent in the
                         // LogicalDisplay.
                         mAnimator.setPendingLayoutChanges(w.getDisplayId(),
-                                WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
+                                FINISH_LAYOUT_REDO_ANIM);
                     } else {
                         w.setOrientationChanging(false);
                     }
@@ -1720,12 +1477,18 @@
      * @return true if an animation has been loaded.
      */
     boolean applyAnimationLocked(int transit, boolean isEntrance) {
-        if (mLocalAnimating && mAnimationIsEntrance == isEntrance) {
+        if (mWin.isSelfAnimating() && mAnimationIsEntrance == isEntrance) {
             // If we are trying to apply an animation, but already running
             // an animation of the same type, then just leave that one alone.
             return true;
         }
 
+        if (isEntrance && mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+            mWin.getDisplayContent().adjustForImeIfNeeded();
+            mWin.setDisplayLayoutNeeded();
+            mService.mWindowPlacerLocked.requestTraversal();
+        }
+
         // Only apply an animation if the display isn't frozen.  If it is
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
@@ -1764,44 +1527,19 @@
                     + " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
             if (a != null) {
                 if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
-                setAnimation(a);
+                mWin.startAnimation(a);
                 mAnimationIsEntrance = isEntrance;
             }
         } else {
-            clearAnimation();
+            mWin.cancelAnimation();
         }
-        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
 
-        if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+        if (!isEntrance && mWin.mAttrs.type == TYPE_INPUT_METHOD) {
             mWin.getDisplayContent().adjustForImeIfNeeded();
-            if (isEntrance) {
-                mWin.setDisplayLayoutNeeded();
-                mService.mWindowPlacerLocked.requestTraversal();
-            }
         }
-        return mAnimation != null;
-    }
 
-    private void applyFadeoutDuringKeyguardExitAnimation() {
-        long startTime = mAnimation.getStartTime();
-        long duration = mAnimation.getDuration();
-        long elapsed = mLastAnimationTime - startTime;
-        long fadeDuration = duration - elapsed;
-        if (fadeDuration <= 0) {
-            // Never mind, this would be no visible animation, so abort the animation change.
-            return;
-        }
-        AnimationSet newAnimation = new AnimationSet(false /* shareInterpolator */);
-        newAnimation.setDuration(duration);
-        newAnimation.setStartTime(startTime);
-        newAnimation.addAnimation(mAnimation);
-        Animation fadeOut = AnimationUtils.loadAnimation(
-                mContext, com.android.internal.R.anim.app_starting_exit);
-        fadeOut.setDuration(fadeDuration);
-        fadeOut.setStartOffset(elapsed);
-        newAnimation.addAnimation(fadeOut);
-        newAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mAnimDx, mAnimDy);
-        mAnimation = newAnimation;
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        return isAnimationSet();
     }
 
     void writeToProto(ProtoOutputStream proto, long fieldId) {
@@ -1814,20 +1552,8 @@
     }
 
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
-        if (mAnimating || mLocalAnimating || mAnimationIsEntrance
-                || mAnimation != null) {
-            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.print(mAnimation);
-                    pw.print(" mStackClip="); pw.println(mStackClip);
-        }
-        if (mHasTransformation || mHasLocalTransformation) {
-            pw.print(prefix); pw.print("XForm: has=");
-                    pw.print(mHasTransformation);
-                    pw.print(" hasLocal="); pw.print(mHasLocalTransformation);
-                    pw.print(" "); mTransformation.printShortString(pw);
-                    pw.println();
+        if (mAnimationIsEntrance) {
+            pw.print(prefix); pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
         }
         if (mSurfaceController != null) {
             mSurfaceController.dump(pw, prefix, dumpAll);
@@ -1907,44 +1633,6 @@
         }
     }
 
-    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;
-        }
-        mSurfaceController.deferTransactionUntil(
-                mWin.getParentWindow().mWinAnimator.mSurfaceController.getHandle(), frameNumber);
-    }
-
-    /**
-     * 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) {
@@ -2026,4 +1714,8 @@
             mSurfaceController.detachChildren();
         }
     }
+
+    int getLayer() {
+        return mLastLayer;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 6746754..e26a362 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -59,8 +59,8 @@
     private boolean mSurfaceShown = false;
     private float mSurfaceX = 0;
     private float mSurfaceY = 0;
-    private float mSurfaceW = 0;
-    private float mSurfaceH = 0;
+    private int mSurfaceW = 0;
+    private int mSurfaceH = 0;
 
     // Initialize to the identity matrix.
     private float mLastDsdx = 1;
@@ -517,11 +517,11 @@
         return mSurfaceY;
     }
 
-    float getWidth() {
+    int getWidth() {
         return mSurfaceW;
     }
 
-    float getHeight() {
+    int getHeight() {
         return mSurfaceH;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 169d0a3..d8e7457 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -462,6 +462,7 @@
             appAnimator.setNullAnimation();
             // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
             //       animating?
+            wtoken.setAllAppWinAnimators();
             wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
             wtoken.updateReportedVisibilityLocked();
             // setAllAppWinAnimators so the windows get onExitAnimationDone once the animation is
@@ -606,7 +607,7 @@
                         + ", oldWallpaper=" + oldWallpaper
                         + ", openingApps=" + openingApps
                         + ", closingApps=" + closingApps);
-        mService.mAnimateWallpaperWithTarget = false;
+
         if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
             transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
@@ -645,8 +646,6 @@
                 transit = TRANSIT_WALLPAPER_OPEN;
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
                         + AppTransition.appTransitionToString(transit));
-            } else {
-                mService.mAnimateWallpaperWithTarget = true;
             }
         }
         return transit;
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index 4dd51eb..70906df 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -53,12 +53,12 @@
         }
 
         @Override
-        SurfaceControl getSurfaceControl() {
+        public SurfaceControl getSurfaceControl() {
             return mControl;
         }
 
         @Override
-        SurfaceControl.Transaction getPendingTransaction() {
+        public SurfaceControl.Transaction getPendingTransaction() {
             return mTransaction;
         }
     }
@@ -93,12 +93,12 @@
         }
 
         @Override
-        SurfaceControl getSurfaceControl() {
+        public SurfaceControl getSurfaceControl() {
             return mHostControl;
         }
 
         @Override
-        SurfaceControl.Transaction getPendingTransaction() {
+        public SurfaceControl.Transaction getPendingTransaction() {
             return mHostTransaction;
         }
     }
@@ -110,8 +110,8 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-
         mHost = new MockSurfaceBuildingContainer();
+
         mTransaction = mock(SurfaceControl.Transaction.class);
         mDimmer = new Dimmer(mHost);
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 9ecf51e..28b0c55 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -54,7 +54,7 @@
 /**
  * Test class for {@link SurfaceAnimationRunner}.
  *
- * runtest frameworks-services -c com.android.server.wm.SurfaceAnimationRunnerTest
+ * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimationRunnerTest
  */
 @SmallTest
 @Presubmit
@@ -104,7 +104,8 @@
 
     @Test
     public void testCancel_notStarted() throws Exception {
-        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), mMockTransaction);
+        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(),
+                mMockTransaction);
         mSurfaceAnimationRunner
                 .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
                 this::finishedCallback);
@@ -112,12 +113,12 @@
         waitUntilHandlersIdle();
         assertTrue(mSurfaceAnimationRunner.mPendingAnimations.isEmpty());
         assertFinishCallbackNotCalled();
-        //verify(mMockSurface).release();
     }
 
     @Test
     public void testCancel_running() throws Exception {
-        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), mMockTransaction);
+        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(),
+                mMockTransaction);
         mSurfaceAnimationRunner
                 .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
                 this::finishedCallback);
@@ -127,7 +128,6 @@
         assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
         waitUntilHandlersIdle();
         assertFinishCallbackNotCalled();
-        //verify(mMockSurface).release();
     }
 
     private void waitUntilNextFrame() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 9a52042..6f739ca 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -33,8 +33,6 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
 
-import com.google.android.collect.Lists;
-
 import com.android.server.wm.SurfaceAnimator.Animatable;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
@@ -50,27 +48,127 @@
 /**
  * Test class for {@link SurfaceAnimatorTest}.
  *
- * runtest frameworks-services -c com.android.server.wm.SurfaceAnimatorTest
+ * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimatorTest
  */
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class SurfaceAnimatorTest extends WindowTestsBase {
 
-    @Mock
-    AnimationAdapter mSpec;
-    @Mock
-    AnimationAdapter mSpec2;
+    @Mock AnimationAdapter mSpec;
+    @Mock AnimationAdapter mSpec2;
     @Mock Transaction mTransaction;
 
-    private SurfaceAnimator mSurfaceAnimator;
-    private SurfaceControl mParent;
-    private SurfaceControl mSurface;
-    private boolean mFinishedCallbackCalled;
-    private SurfaceControl mLeash;
     private SurfaceSession mSession = new SurfaceSession();
+    private MyAnimatable mAnimatable;
 
-    private final Animatable mAnimatable = new Animatable() {
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mAnimatable = new MyAnimatable();
+    }
+
+    @Test
+    public void testRunAnimation() throws Exception {
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
+                OnAnimationFinishedCallback.class);
+
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+        assertNotNull(mAnimatable.mSurfaceAnimator.getAnimation());
+        verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash.getHandle()));
+        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+        callbackCaptor.getValue().onAnimationFinished(mSpec);
+        assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+        assertNull(mAnimatable.mSurfaceAnimator.getAnimation());
+        assertTrue(mAnimatable.mFinishedCallbackCalled);
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+        // TODO: Verify reparenting once we use mPendingTransaction to reparent it back
+    }
+
+    @Test
+    public void testOverrideAnimation() throws Exception {
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        final SurfaceControl firstLeash = mAnimatable.mLeash;
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
+
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(firstLeash));
+        assertFalse(mAnimatable.mFinishedCallbackCalled);
+
+        final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
+                OnAnimationFinishedCallback.class);
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+        assertNotNull(mAnimatable.mSurfaceAnimator.getAnimation());
+        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+        // First animation was finished, but this shouldn't cancel the second animation
+        callbackCaptor.getValue().onAnimationFinished(mSpec);
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+
+        // Second animation was finished
+        verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture());
+        callbackCaptor.getValue().onAnimationFinished(mSpec2);
+        assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+        assertTrue(mAnimatable.mFinishedCallbackCalled);
+    }
+
+    @Test
+    public void testCancelAnimation() throws Exception {
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+        mAnimatable.mSurfaceAnimator.cancelAnimation();
+        assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+        verify(mSpec).onAnimationCancelled(any());
+        assertTrue(mAnimatable.mFinishedCallbackCalled);
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+    }
+
+    @Test
+    public void testDelayingAnimationStart() throws Exception {
+        mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        verifyZeroInteractions(mSpec);
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+        mAnimatable.mSurfaceAnimator.endDelayingAnimationStart();
+        verify(mSpec).startAnimation(any(), any(), any());
+    }
+
+    @Test
+    public void testDelayingAnimationStartAndCancelled() throws Exception {
+        mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        mAnimatable.mSurfaceAnimator.cancelAnimation();
+        verifyZeroInteractions(mSpec);
+        assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+        assertTrue(mAnimatable.mFinishedCallbackCalled);
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+    }
+
+    private class MyAnimatable implements Animatable {
+
+        final SurfaceControl mParent;
+        final SurfaceControl mSurface;
+        final ArrayList<SurfaceControl> mPendingDestroySurfaces = new ArrayList<>();
+        final SurfaceAnimator mSurfaceAnimator;
+        SurfaceControl mLeash;
+        boolean mFinishedCallbackCalled;
+
+        MyAnimatable() {
+            mParent = sWm.makeSurfaceBuilder(mSession)
+                    .setName("test surface parent")
+                    .setSize(3000, 3000)
+                    .build();
+            mSurface = sWm.makeSurfaceBuilder(mSession)
+                    .setName("test surface")
+                    .setSize(1, 1)
+                    .build();
+            mFinishedCallbackCalled = false;
+            mLeash = null;
+            mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, sWm);
+        }
+
         @Override
         public Transaction getPendingTransaction() {
             return mTransaction;
@@ -81,15 +179,20 @@
         }
 
         @Override
-        public void onLeashCreated(Transaction t, SurfaceControl leash) {
+        public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
         }
 
         @Override
-        public void onLeashDestroyed(Transaction t) {
+        public void onAnimationLeashDestroyed(Transaction t) {
         }
 
         @Override
-        public Builder makeLeash() {
+        public void destroyAfterPendingTransaction(SurfaceControl surface) {
+            mPendingDestroySurfaces.add(surface);
+        }
+
+        @Override
+        public Builder makeAnimationLeash() {
             return new SurfaceControl.Builder(mSession) {
 
                 @Override
@@ -101,12 +204,12 @@
         }
 
         @Override
-        public SurfaceControl getSurface() {
+        public SurfaceControl getSurfaceControl() {
             return mSurface;
         }
 
         @Override
-        public SurfaceControl getParentSurface() {
+        public SurfaceControl getParentSurfaceControl() {
             return mParent;
         }
 
@@ -119,99 +222,9 @@
         public int getSurfaceHeight() {
             return 1;
         }
-    };
 
-    private final Runnable mFinishedCallback = () -> {
-        mFinishedCallbackCalled = true;
-    };
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-        MockitoAnnotations.initMocks(this);
-        mParent = sWm.makeSurfaceBuilder(mSession)
-                .setName("test surface parent")
-                .setSize(3000, 3000)
-                .build();
-        mSurface = sWm.makeSurfaceBuilder(mSession)
-                .setName("test surface")
-                .setSize(1, 1)
-                .build();
-        mFinishedCallbackCalled = false;
-        mLeash = null;
-        mSurfaceAnimator = new SurfaceAnimator(mAnimatable, mFinishedCallback, sWm);
-    }
-
-    @Test
-    public void testRunAnimation() throws Exception {
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
-        final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
-                OnAnimationFinishedCallback.class);
-
-        assertTrue(mSurfaceAnimator.isAnimating());
-        assertNotNull(mSurfaceAnimator.getAnimation());
-        verify(mTransaction).reparent(eq(mSurface), eq(mLeash.getHandle()));
-        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
-
-        callbackCaptor.getValue().onAnimationFinished(mSpec);
-        assertFalse(mSurfaceAnimator.isAnimating());
-        assertNull(mSurfaceAnimator.getAnimation());
-        assertTrue(mFinishedCallbackCalled);
-
-        // TODO: Verify reparenting once we use mPendingTransaction to reparent it back
-    }
-
-    @Test
-    public void testOverrideAnimation() throws Exception {
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
-
-        assertFalse(mFinishedCallbackCalled);
-
-        final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
-                OnAnimationFinishedCallback.class);
-        assertTrue(mSurfaceAnimator.isAnimating());
-        assertNotNull(mSurfaceAnimator.getAnimation());
-        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
-
-        // First animation was finished, but this shouldn't cancel the second animation
-        callbackCaptor.getValue().onAnimationFinished(mSpec);
-        assertTrue(mSurfaceAnimator.isAnimating());
-
-        // Second animation was finished
-        verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture());
-        callbackCaptor.getValue().onAnimationFinished(mSpec2);
-        assertFalse(mSurfaceAnimator.isAnimating());
-        assertTrue(mFinishedCallbackCalled);
-    }
-
-    @Test
-    public void testCancelAnimation() throws Exception {
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
-        assertTrue(mSurfaceAnimator.isAnimating());
-        mSurfaceAnimator.cancelAnimation();
-        assertFalse(mSurfaceAnimator.isAnimating());
-        verify(mSpec).onAnimationCancelled(any());
-        assertTrue(mFinishedCallbackCalled);
-    }
-
-    @Test
-    public void testDelayingAnimationStart() throws Exception {
-        mSurfaceAnimator.startDelayingAnimationStart();
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
-        verifyZeroInteractions(mSpec);
-        assertTrue(mSurfaceAnimator.isAnimating());
-        mSurfaceAnimator.endDelayingAnimationStart();
-        verify(mSpec).startAnimation(any(), any(), any());
-    }
-
-    @Test
-    public void testDelayingAnimationStartAndCancelled() throws Exception {
-        mSurfaceAnimator.startDelayingAnimationStart();
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
-        mSurfaceAnimator.cancelAnimation();
-        verifyZeroInteractions(mSpec);
-        assertFalse(mSurfaceAnimator.isAnimating());
-        assertTrue(mFinishedCallbackCalled);
+        private final Runnable mFinishedCallback = () -> {
+            mFinishedCallbackCalled = true;
+        };
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index 5cb9467..307deb4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -29,8 +29,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
@@ -333,12 +331,19 @@
         final TestWindowContainer child12 = child1.addChildWindow(builder.setIsAnimating(true));
         final TestWindowContainer child21 = child2.addChildWindow();
 
-        assertTrue(root.isAnimating());
+        assertFalse(root.isAnimating());
         assertTrue(child1.isAnimating());
-        assertFalse(child11.isAnimating());
+        assertTrue(child11.isAnimating());
         assertTrue(child12.isAnimating());
         assertFalse(child2.isAnimating());
         assertFalse(child21.isAnimating());
+
+        assertTrue(root.isSelfOrChildAnimating());
+        assertTrue(child1.isSelfOrChildAnimating());
+        assertFalse(child11.isSelfOrChildAnimating());
+        assertTrue(child12.isSelfOrChildAnimating());
+        assertFalse(child2.isSelfOrChildAnimating());
+        assertFalse(child21.isSelfOrChildAnimating());
     }
 
     @Test
@@ -630,8 +635,8 @@
         }
 
         @Override
-        boolean isAnimating() {
-            return mIsAnimating || super.isAnimating();
+        boolean isSelfAnimating() {
+            return mIsAnimating;
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index b2334e8..5f58744 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -189,7 +189,6 @@
     public static class TestTask extends Task {
         boolean mShouldDeferRemoval = false;
         boolean mOnDisplayChangedCalled = false;
-        private boolean mUseLocalIsAnimating = false;
         private boolean mIsAnimating = false;
 
         TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
@@ -214,12 +213,11 @@
         }
 
         @Override
-        boolean isAnimating() {
-            return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating();
+        boolean isSelfAnimating() {
+            return mIsAnimating;
         }
 
         void setLocalIsAnimating(boolean isAnimating) {
-            mUseLocalIsAnimating = true;
             mIsAnimating = isAnimating;
         }
     }