Ensure that we use SF Vsync Choreographer for the PiP transition.

- Move the bounds animation onto the animation thread
- Remove existing code referencing the old sf-vsync choreographer
- Add ability for ValueAnimator subclasses to reference a different
  AnimationHandler, which uses a different FrameCallbackProvider with the
  sf-vsync choreographer in the animations that require it
- Ensure that PiP touch events are batched and sent aligned with the
  sf-vsync
- Move GC onto its own thread to not block other BackgroundThread calls

Bug: 36371375
Test: android.server.cts.ActivityManagerPinnedStackTests
Test: bit FrameworksServicesTests:com.android.server.wm.BoundsAnimationControllerTests
Test: go/wm-smoke

Change-Id: I6a41b35a4e4d4d6dbea82c2673452825fe3ffa58
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index e686a89..ee89ca8 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1469,24 +1469,21 @@
         if (!mSelfPulse) {
             return;
         }
-        AnimationHandler handler = AnimationHandler.getInstance();
-        handler.addOneShotCommitCallback(this);
+        getAnimationHandler().addOneShotCommitCallback(this);
     }
 
     private void removeAnimationCallback() {
         if (!mSelfPulse) {
             return;
         }
-        AnimationHandler handler = AnimationHandler.getInstance();
-        handler.removeCallback(this);
+        getAnimationHandler().removeCallback(this);
     }
 
     private void addAnimationCallback(long delay) {
         if (!mSelfPulse) {
             return;
         }
-        AnimationHandler handler = AnimationHandler.getInstance();
-        handler.addAnimationFrameCallback(this, delay);
+        getAnimationHandler().addAnimationFrameCallback(this, delay);
     }
 
     /**
@@ -1643,4 +1640,12 @@
     public void setAllowRunningAsynchronously(boolean mayRunAsync) {
         // It is up to subclasses to support this, if they can.
     }
+
+    /**
+     * @return The {@link AnimationHandler} that will be used to schedule updates for this animator.
+     * @hide
+     */
+    public AnimationHandler getAnimationHandler() {
+        return AnimationHandler.getInstance();
+    }
 }
diff --git a/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java b/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java
new file mode 100644
index 0000000..931eb99
--- /dev/null
+++ b/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.graphics;
+
+import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
+import android.view.Choreographer;
+
+/**
+ * Provider of timing pulse that uses SurfaceFlinger Vsync Choreographer for frame callbacks.
+ *
+ * @hide
+ */
+public final class SfVsyncFrameCallbackProvider implements AnimationFrameCallbackProvider {
+
+    private final Choreographer mChoreographer = Choreographer.getSfInstance();
+
+    @Override
+    public void postFrameCallback(Choreographer.FrameCallback callback) {
+        mChoreographer.postFrameCallback(callback);
+    }
+
+    @Override
+    public void postCommitCallback(Runnable runnable) {
+        mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
+    }
+
+    @Override
+    public long getFrameTime() {
+        return mChoreographer.getFrameTime();
+    }
+
+    @Override
+    public long getFrameDelay() {
+        return Choreographer.getFrameDelay();
+    }
+
+    @Override
+    public void setFrameDelay(long delay) {
+        Choreographer.setFrameDelay(delay);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
index 867c15c..6733421 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
@@ -21,12 +21,15 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.BatchedInputEventReceiver;
+import android.view.Choreographer;
 import android.view.InputChannel;
 import android.view.InputEvent;
-import android.view.InputEventReceiver;
 import android.view.IWindowManager;
 import android.view.MotionEvent;
 
+import com.android.systemui.recents.misc.Utilities;
+
 import java.io.PrintWriter;
 
 /**
@@ -52,12 +55,13 @@
     }
 
     /**
-     * Input handler used for the PiP input consumer.
+     * Input handler used for the PiP input consumer. Input events are batched and consumed with the
+     * SurfaceFlinger vsync.
      */
-    private final class PipInputEventReceiver extends InputEventReceiver {
+    private final class PipInputEventReceiver extends BatchedInputEventReceiver {
 
         public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
-            super(inputChannel, looper);
+            super(inputChannel, looper, Choreographer.getSfInstance());
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 013b9ac..0f69f47 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -573,13 +573,11 @@
     }
 
     private void cancelDelayedFinish() {
-        View v = getWindow().getDecorView();
-        v.removeCallbacks(mFinishRunnable);
+        mHandler.removeCallbacks(mFinishRunnable);
     }
 
     private void repostDelayedFinish(long delay) {
-        View v = getWindow().getDecorView();
-        v.removeCallbacks(mFinishRunnable);
-        v.postDelayed(mFinishRunnable, delay);
+        mHandler.removeCallbacks(mFinishRunnable);
+        mHandler.postDelayed(mFinishRunnable, delay);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 9fa7ff6..b8771d7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -22,6 +22,7 @@
 import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN;
 
+import android.animation.AnimationHandler;
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
@@ -36,14 +37,15 @@
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.Handler;
+import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
-import android.view.Choreographer;
 import android.view.animation.Interpolator;
 
-import com.android.internal.os.BackgroundThread;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.policy.PipSnapAlgorithm;
-import com.android.internal.view.SurfaceFlingerVsyncChoreographer;
+import com.android.systemui.recents.misc.ForegroundThread;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 
@@ -52,7 +54,7 @@
 /**
  * A helper to animate and manipulate the PiP.
  */
-public class PipMotionHelper {
+public class PipMotionHelper implements Handler.Callback {
 
     private static final String TAG = "PipMotionHelper";
     private static final boolean DEBUG = false;
@@ -74,38 +76,34 @@
     // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
     private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f;
 
+    private static final int MSG_RESIZE_IMMEDIATE = 1;
+    private static final int MSG_RESIZE_ANIMATE = 2;
+
     private Context mContext;
     private IActivityManager mActivityManager;
-    private SurfaceFlingerVsyncChoreographer mVsyncChoreographer;
     private Handler mHandler;
 
     private PipMenuActivityController mMenuController;
     private PipSnapAlgorithm mSnapAlgorithm;
     private FlingAnimationUtils mFlingAnimationUtils;
+    private AnimationHandler mAnimationHandler;
 
     private final Rect mBounds = new Rect();
     private final Rect mStableInsets = new Rect();
 
     private ValueAnimator mBoundsAnimator = null;
-    private ValueAnimator.AnimatorUpdateListener mUpdateBoundsListener =
-            new AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    mBounds.set((Rect) animation.getAnimatedValue());
-                }
-            };
 
     public PipMotionHelper(Context context, IActivityManager activityManager,
             PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm,
             FlingAnimationUtils flingAnimationUtils) {
         mContext = context;
-        mHandler = BackgroundThread.getHandler();
+        mHandler = new Handler(ForegroundThread.get().getLooper(), this);
         mActivityManager = activityManager;
         mMenuController = menuController;
         mSnapAlgorithm = snapAlgorithm;
         mFlingAnimationUtils = flingAnimationUtils;
-        mVsyncChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, mContext.getDisplay(),
-                Choreographer.getInstance());
+        mAnimationHandler = new AnimationHandler();
+        mAnimationHandler.setProvider(new SfVsyncFrameCallbackProvider());
         onConfigurationChanged();
     }
 
@@ -252,8 +250,7 @@
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
                 0 /* velocityX */, velocityY);
         if (!mBounds.equals(toBounds)) {
-            mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN,
-                    mUpdateBoundsListener);
+            mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN);
             mFlingAnimationUtils.apply(mBoundsAnimator, 0,
                     distanceBetweenRectOffsets(mBounds, toBounds),
                     velocityY);
@@ -271,7 +268,7 @@
         Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds);
         if (!mBounds.equals(toBounds)) {
             mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
-                    MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN, mUpdateBoundsListener);
+                    MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN);
             if (updateListener != null) {
                 mBoundsAnimator.addUpdateListener(updateListener);
             }
@@ -289,8 +286,7 @@
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
                 velocityX, velocityY);
         if (!mBounds.equals(toBounds)) {
-            mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN,
-                    mUpdateBoundsListener);
+            mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN);
             mFlingAnimationUtils.apply(mBoundsAnimator, 0,
                     distanceBetweenRectOffsets(mBounds, toBounds),
                     velocity);
@@ -314,7 +310,7 @@
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds);
         if (!mBounds.equals(toBounds)) {
             mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, SNAP_STACK_DURATION,
-                    FAST_OUT_SLOW_IN, mUpdateBoundsListener);
+                    FAST_OUT_SLOW_IN);
             if (updateListener != null) {
                 mBoundsAnimator.addUpdateListener(updateListener);
             }
@@ -379,7 +375,7 @@
         Rect toBounds = new Rect(pipBounds);
         toBounds.offsetTo(p.x, p.y);
         mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, DRAG_TO_DISMISS_STACK_DURATION,
-                FAST_OUT_LINEAR_IN, mUpdateBoundsListener);
+                FAST_OUT_LINEAR_IN);
         mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -411,16 +407,20 @@
      * Creates an animation to move the PiP to give given {@param toBounds}.
      */
     private ValueAnimator createAnimationToBounds(Rect fromBounds, Rect toBounds, int duration,
-            Interpolator interpolator, ValueAnimator.AnimatorUpdateListener updateListener) {
-        ValueAnimator anim = ValueAnimator.ofObject(RECT_EVALUATOR, fromBounds, toBounds);
+            Interpolator interpolator) {
+        ValueAnimator anim = new ValueAnimator() {
+            @Override
+            public AnimationHandler getAnimationHandler() {
+                return mAnimationHandler;
+            }
+        };
+        anim.setObjectValues(fromBounds, toBounds);
+        anim.setEvaluator(RECT_EVALUATOR);
         anim.setDuration(duration);
         anim.setInterpolator(interpolator);
         anim.addUpdateListener((ValueAnimator animation) -> {
             resizePipUnchecked((Rect) animation.getAnimatedValue());
         });
-        if (updateListener != null) {
-            anim.addUpdateListener(updateListener);
-        }
         return anim;
     }
 
@@ -433,14 +433,9 @@
                     + " callers=\n" + Debug.getCallers(5, "    "));
         }
         if (!toBounds.equals(mBounds)) {
-            mVsyncChoreographer.scheduleAtSfVsync(() -> {
-                try {
-                    mActivityManager.resizePinnedStack(toBounds, null /* tempPinnedTaskBounds */);
-                    mBounds.set(toBounds);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e);
-                }
-            });
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = toBounds;
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args));
         }
     }
 
@@ -453,23 +448,10 @@
                     + " duration=" + duration + " callers=\n" + Debug.getCallers(5, "    "));
         }
         if (!toBounds.equals(mBounds)) {
-            mHandler.post(() -> {
-                try {
-                    StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
-                    if (stackInfo == null) {
-                        // In the case where we've already re-expanded or dismissed the PiP, then
-                        // just skip the resize
-                        return;
-                    }
-
-                    mActivityManager.resizeStack(PINNED_STACK_ID, toBounds,
-                            false /* allowResizeInDockedMode */, true /* preserveWindows */,
-                            true /* animate */, duration);
-                    mBounds.set(toBounds);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
-                }
-            });
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = toBounds;
+            args.argi1 = duration;
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
         }
     }
 
@@ -524,6 +506,50 @@
         return PointF.length(r1.left - r2.left, r1.top - r2.top);
     }
 
+    /**
+     * Handles messages to be processed on the background thread.
+     */
+    public boolean handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_RESIZE_IMMEDIATE: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                Rect toBounds = (Rect) args.arg1;
+                try {
+                    mActivityManager.resizePinnedStack(toBounds, null /* tempPinnedTaskBounds */);
+                    mBounds.set(toBounds);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e);
+                }
+                return true;
+            }
+
+            case MSG_RESIZE_ANIMATE: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                Rect toBounds = (Rect) args.arg1;
+                int duration = args.argi1;
+                try {
+                    StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+                    if (stackInfo == null) {
+                        // In the case where we've already re-expanded or dismissed the PiP, then
+                        // just skip the resize
+                        return true;
+                    }
+
+                    mActivityManager.resizeStack(PINNED_STACK_ID, toBounds,
+                            false /* allowResizeInDockedMode */, true /* preserveWindows */,
+                            true /* animate */, duration);
+                    mBounds.set(toBounds);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
+                }
+                return true;
+            }
+
+            default:
+                return false;
+        }
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 1f13830..c66b2dd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -154,6 +154,13 @@
     Canvas mBgProtectionCanvas;
 
     private final Handler mHandler = new H();
+    private final Runnable mGcRunnable = new Runnable() {
+        @Override
+        public void run() {
+            System.gc();
+            System.runFinalization();
+        }
+    };
 
     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
 
@@ -365,13 +372,7 @@
      * Requests a gc() from the background thread.
      */
     public void gc() {
-        BackgroundThread.getHandler().post(new Runnable() {
-            @Override
-            public void run() {
-                System.gc();
-                System.runFinalization();
-            }
-        });
+        BackgroundThread.getHandler().post(mGcRunnable);
     }
 
     /**
@@ -799,11 +800,8 @@
         if (RecentsDebugFlags.Static.EnableMockTasks) return;
 
         // Remove the task.
-        BackgroundThread.getHandler().post(new Runnable() {
-            @Override
-            public void run() {
-                mAm.removeTask(taskId);
-            }
+        mUiOffloadThread.submit(() -> {
+            mAm.removeTask(taskId);
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index 1f82c16..308cece 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -20,9 +20,9 @@
 import android.os.UserHandle;
 
 import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.BackgroundThread;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.PackagesChangedEvent;
+import com.android.systemui.recents.misc.ForegroundThread;
 
 /**
  * The package monitor listens for changes from PackageManager to update the contents of the
@@ -36,7 +36,7 @@
             // We register for events from all users, but will cross-reference them with
             // packages for the current user and any profiles they have.  Ensure that events are
             // handled in a background thread.
-            register(context, BackgroundThread.get().getLooper(), UserHandle.ALL, true);
+            register(context, ForegroundThread.get().getLooper(), UserHandle.ALL, true);
         } catch (IllegalStateException e) {
             e.printStackTrace();
         }
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 410efcd..7d13889 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -20,6 +20,8 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.animation.AnimationHandler;
+import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
@@ -30,11 +32,13 @@
 import android.os.Debug;
 import android.util.ArrayMap;
 import android.util.Slog;
+import android.view.Choreographer;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.view.WindowManagerInternal;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -49,7 +53,7 @@
  *
  * The object that is resized needs to implement {@link BoundsAnimationTarget} interface.
  *
- * NOTE: All calls to methods in this class should be done on the UI thread
+ * NOTE: All calls to methods in this class should be done on the Animation thread
  */
 public class BoundsAnimationController {
     private static final boolean DEBUG_LOCAL = false;
@@ -111,20 +115,24 @@
     private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier();
     private final Interpolator mFastOutSlowInInterpolator;
     private boolean mFinishAnimationAfterTransition = false;
+    private final AnimationHandler mAnimationHandler;
 
     private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000;
 
-    BoundsAnimationController(Context context, AppTransition transition, Handler handler) {
+    BoundsAnimationController(Context context, AppTransition transition, Handler handler,
+            AnimationHandler animationHandler) {
         mHandler = handler;
         mAppTransition = transition;
         mAppTransition.registerListenerLocked(mAppTransitionNotifier);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_slow_in);
+        mAnimationHandler = animationHandler;
     }
 
     @VisibleForTesting
     final class BoundsAnimator extends ValueAnimator
             implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
+
         private final BoundsAnimationTarget mTarget;
         private final Rect mFrom = new Rect();
         private final Rect mTo = new Rect();
@@ -350,6 +358,14 @@
         public void onAnimationRepeat(Animator animation) {
             // Do nothing
         }
+
+        @Override
+        public AnimationHandler getAnimationHandler() {
+            if (mAnimationHandler != null) {
+                return mAnimationHandler;
+            }
+            return super.getAnimationHandler();
+        }
     }
 
     public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 768b03a..77e2f71 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -103,6 +103,7 @@
 
 import android.Manifest;
 import android.Manifest.permission;
+import android.animation.AnimationHandler;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -211,6 +212,7 @@
 
 import com.android.internal.R;
 import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
@@ -1046,8 +1048,10 @@
         mAppTransition = new AppTransition(context, this);
         mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
 
+        final AnimationHandler animationHandler = new AnimationHandler();
+        animationHandler.setProvider(new SfVsyncFrameCallbackProvider());
         mBoundsAnimationController = new BoundsAnimationController(context, mAppTransition,
-                UiThread.getHandler());
+                AnimationThread.getHandler(), animationHandler);
 
         mActivityManager = ActivityManager.getService();
         mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index ee09f4b..9d32496 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -395,7 +395,7 @@
         mMockAppTransition = new MockAppTransition(context);
         mMockAnimator = new MockValueAnimator();
         mTarget = new TestBoundsAnimationTarget();
-        mController = new BoundsAnimationController(context, mMockAppTransition, handler);
+        mController = new BoundsAnimationController(context, mMockAppTransition, handler, null);
         mDriver = new BoundsAnimationDriver(mController, mTarget);
     }