diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index cd968067e2893499cf6787fe5dde36dba56f0e66..fa76774a604f0b82fb5edb47b699550975a9692e 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -520,11 +520,37 @@ class InsetsSourceProvider {
         updateVisibility();
         mControl = new InsetsSourceControl(mSource.getId(), mSource.getType(), leash,
                 mClientVisible, surfacePosition, getInsetsHint());
+        mStateController.notifySurfaceTransactionReady(this, getSurfaceTransactionId(leash), true);
 
         ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
                 "InsetsSource Control %s for target %s", mControl, mControlTarget);
     }
 
+    private long getSurfaceTransactionId(SurfaceControl leash) {
+        // Here returns mNativeObject (long) as the ID instead of the leash itself so that
+        // InsetsStateController won't keep referencing the leash unexpectedly.
+        return leash != null ? leash.mNativeObject : 0;
+    }
+
+    /**
+     * This is called when the surface transaction of the leash initialization has been committed.
+     *
+     * @param id Indicates which transaction is committed so that stale callbacks can be dropped.
+     */
+    void onSurfaceTransactionCommitted(long id) {
+        if (mIsLeashReadyForDispatching) {
+            return;
+        }
+        if (mControl == null) {
+            return;
+        }
+        if (id != getSurfaceTransactionId(mControl.getLeash())) {
+            return;
+        }
+        mIsLeashReadyForDispatching = true;
+        mStateController.notifySurfaceTransactionReady(this, 0, false);
+    }
+
     void startSeamlessRotation() {
         if (!mSeamlessRotating) {
             mSeamlessRotating = true;
@@ -545,10 +571,6 @@ class InsetsSourceProvider {
         return true;
     }
 
-    void onSurfaceTransactionApplied() {
-        mIsLeashReadyForDispatching = true;
-    }
-
     void setClientVisible(boolean clientVisible) {
         if (mClientVisible == clientVisible) {
             return;
@@ -733,6 +755,7 @@ class InsetsSourceProvider {
         public void onAnimationCancelled(SurfaceControl animationLeash) {
             if (mAdapter == this) {
                 mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this);
+                mStateController.notifySurfaceTransactionReady(InsetsSourceProvider.this, 0, false);
                 mControl = null;
                 mControlTarget = null;
                 mAdapter = null;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 6b9fcf411ce18477b71d898566630d5eca005d24..ba578f64242963ce627a64544998e48766b04aed 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -34,6 +34,7 @@ import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.SparseArray;
+import android.util.SparseLongArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
@@ -58,6 +59,7 @@ class InsetsStateController {
     private final DisplayContent mDisplayContent;
 
     private final SparseArray<InsetsSourceProvider> mProviders = new SparseArray<>();
+    private final SparseLongArray mSurfaceTransactionIds = new SparseLongArray();
     private final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>>
             mControlTargetProvidersMap = new ArrayMap<>();
     private final SparseArray<InsetsControlTarget> mIdControlTargetMap = new SparseArray<>();
@@ -360,14 +362,32 @@ class InsetsStateController {
         notifyPendingInsetsControlChanged();
     }
 
+    void notifySurfaceTransactionReady(InsetsSourceProvider provider, long id, boolean ready) {
+        if (ready) {
+            mSurfaceTransactionIds.put(provider.getSource().getId(), id);
+        } else {
+            mSurfaceTransactionIds.delete(provider.getSource().getId());
+        }
+    }
+
     private void notifyPendingInsetsControlChanged() {
         if (mPendingControlChanged.isEmpty()) {
             return;
         }
+        final int size = mSurfaceTransactionIds.size();
+        final SparseLongArray surfaceTransactionIds = new SparseLongArray(size);
+        for (int i = 0; i < size; i++) {
+            surfaceTransactionIds.append(
+                    mSurfaceTransactionIds.keyAt(i), mSurfaceTransactionIds.valueAt(i));
+        }
         mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
-            for (int i = mProviders.size() - 1; i >= 0; i--) {
-                final InsetsSourceProvider provider = mProviders.valueAt(i);
-                provider.onSurfaceTransactionApplied();
+            for (int i = 0; i < size; i++) {
+                final int sourceId = surfaceTransactionIds.keyAt(i);
+                final InsetsSourceProvider provider = mProviders.get(sourceId);
+                if (provider == null) {
+                    continue;
+                }
+                provider.onSurfaceTransactionCommitted(surfaceTransactionIds.valueAt(i));
             }
             final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
             int displayId = mDisplayContent.getDisplayId();
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index b43a4540bbde56d629d4940ab91a275120545a28..5e7f1cbdd06e0b6111d50c268ce46dc0e13f39a1 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -28,6 +28,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.content.Context;
+import android.os.HandlerExecutor;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.TimeUtils;
@@ -69,6 +70,8 @@ public class WindowAnimator {
 
     private Choreographer mChoreographer;
 
+    private final HandlerExecutor mExecutor;
+
     /**
      * Indicates whether we have an animation frame callback scheduled, which will happen at
      * vsync-app and then schedule the animation tick at the right time (vsync-sf).
@@ -80,8 +83,7 @@ public class WindowAnimator {
      * A list of runnable that need to be run after {@link WindowContainer#prepareSurfaces} is
      * executed and the corresponding transaction is closed and applied.
      */
-    private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
-    private boolean mInExecuteAfterPrepareSurfacesRunnables;
+    private ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
 
     private final SurfaceControl.Transaction mTransaction;
 
@@ -92,6 +94,7 @@ public class WindowAnimator {
         mTransaction = service.mTransactionFactory.get();
         service.mAnimationHandler.runWithScissors(
                 () -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
+        mExecutor = new HandlerExecutor(service.mAnimationHandler);
 
         mAnimationFrameCallback = frameTimeNs -> {
             synchronized (mService.mGlobalLock) {
@@ -197,6 +200,19 @@ public class WindowAnimator {
             updateRunningExpensiveAnimationsLegacy();
         }
 
+        final ArrayList<Runnable> afterPrepareSurfacesRunnables = mAfterPrepareSurfacesRunnables;
+        if (!afterPrepareSurfacesRunnables.isEmpty()) {
+            mAfterPrepareSurfacesRunnables = new ArrayList<>();
+            mTransaction.addTransactionCommittedListener(mExecutor, () -> {
+                synchronized (mService.mGlobalLock) {
+                    // Traverse in order they were added.
+                    for (int i = 0, size = afterPrepareSurfacesRunnables.size(); i < size; i++) {
+                        afterPrepareSurfacesRunnables.get(i).run();
+                    }
+                    afterPrepareSurfacesRunnables.clear();
+                }
+            });
+        }
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "applyTransaction");
         mTransaction.apply();
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
@@ -204,7 +220,6 @@ public class WindowAnimator {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
 
         mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
-        executeAfterPrepareSurfacesRunnables();
 
         if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: exit"
@@ -286,34 +301,10 @@ public class WindowAnimator {
 
     /**
      * Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and
-     * the corresponding transaction is closed and applied.
+     * the corresponding transaction is closed, applied, and committed.
      */
     void addAfterPrepareSurfacesRunnable(Runnable r) {
-        // If runnables are already being handled in executeAfterPrepareSurfacesRunnable, then just
-        // immediately execute the runnable passed in.
-        if (mInExecuteAfterPrepareSurfacesRunnables) {
-            r.run();
-            return;
-        }
-
         mAfterPrepareSurfacesRunnables.add(r);
         scheduleAnimation();
     }
-
-    void executeAfterPrepareSurfacesRunnables() {
-
-        // Don't even think about to start recursing!
-        if (mInExecuteAfterPrepareSurfacesRunnables) {
-            return;
-        }
-        mInExecuteAfterPrepareSurfacesRunnables = true;
-
-        // Traverse in order they were added.
-        final int size = mAfterPrepareSurfacesRunnables.size();
-        for (int i = 0; i < size; i++) {
-            mAfterPrepareSurfacesRunnables.get(i).run();
-        }
-        mAfterPrepareSurfacesRunnables.clear();
-        mInExecuteAfterPrepareSurfacesRunnables = false;
-    }
 }
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
index e27bb4c8c3b6b26a5398d41ae021a4b6d015e76c..b9ece9360980c0ad7d7d72507f475d535c1a3b39 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
@@ -40,12 +40,19 @@ import java.util.concurrent.Executor;
 public class StubTransaction extends SurfaceControl.Transaction {
 
     private HashSet<Runnable> mWindowInfosReportedListeners = new HashSet<>();
+    private HashSet<SurfaceControl.TransactionCommittedListener> mTransactionCommittedListeners =
+            new HashSet<>();
 
     @Override
     public void apply() {
         for (Runnable listener : mWindowInfosReportedListeners) {
             listener.run();
         }
+        for (SurfaceControl.TransactionCommittedListener listener
+                : mTransactionCommittedListeners) {
+            listener.onTransactionCommitted();
+        }
+        mTransactionCommittedListeners.clear();
     }
 
     @Override
@@ -239,6 +246,9 @@ public class StubTransaction extends SurfaceControl.Transaction {
     @Override
     public SurfaceControl.Transaction addTransactionCommittedListener(Executor executor,
             SurfaceControl.TransactionCommittedListener listener) {
+        SurfaceControl.TransactionCommittedListener listenerInner =
+                () -> executor.execute(listener::onTransactionCommitted);
+        mTransactionCommittedListeners.add(listenerInner);
         return this;
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 0c1fbf3cb3d7bfd7681903a17311742545e2414e..1a1fe95756d74adf4bf6b0901dfda21206d6bc56 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -94,6 +94,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
     public void setUp() throws Exception {
         assumeFalse(WindowManagerService.sEnableShellTransitions);
         mAppTransitionController = new AppTransitionController(mWm, mDisplayContent);
+        mWm.mAnimator.ready();
     }
 
     @Test
@@ -855,7 +856,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
 
         // Prepare and start transition.
         prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
 
         // Animation run by the remote handler.
         assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -886,7 +887,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
         // Prepare and start transition.
         prepareAndTriggerAppTransition(openingActivity, closingActivity,
                 null /* changingTaskFragment */);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
 
         // Animation is not run by the remote handler because the activity is filling the Task.
         assertFalse(remoteAnimationRunner.isAnimationStarted());
@@ -921,7 +922,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
         // Prepare and start transition.
         prepareAndTriggerAppTransition(openingActivity, closingActivity,
                 null /* changingTaskFragment */);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
 
         // Animation run by the remote handler.
         assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -946,7 +947,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
 
         // Prepare and start transition.
         prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
 
         // Animation run by the remote handler.
         assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -973,7 +974,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
 
         // Prepare and start transition.
         prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
 
         // Animation run by the remote handler.
         assertTrue(remoteAnimationRunner.isAnimationStarted());
@@ -997,7 +998,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
 
         // Prepare and start transition.
         prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
 
         // Animation not run by the remote handler.
         assertFalse(remoteAnimationRunner.isAnimationStarted());
@@ -1024,7 +1025,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
 
         // Prepare and start transition.
         prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
 
         // Animation should not run by the remote handler when there are non-embedded activities of
         // different UID.
@@ -1051,7 +1052,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
 
         // Prepare and start transition.
         prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
 
         // Animation should not run by the remote handler when there is wallpaper in the transition.
         assertFalse(remoteAnimationRunner.isAnimationStarted());
@@ -1085,7 +1086,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
 
         // Prepare and start transition.
         prepareAndTriggerAppTransition(activity1, null /* closingActivity */, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
 
         // The animation will be animated remotely by client and all activities are input disabled
         // for untrusted animation.
@@ -1136,7 +1137,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
 
         // Prepare and start transition.
         prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
 
         // The animation will be animated remotely by client and all activities are input disabled
         // for untrusted animation.
@@ -1178,7 +1179,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
 
         // Prepare and start transition.
         prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
 
         // The animation will be animated remotely by client, but input should not be dropped for
         // fully trusted.
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 2085d6140f68817ddbadd9cd36fbf11f844a9494..1f15ec3be3a8754e09a55f7e5e69815ee76a2eeb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -371,6 +371,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
         mDisplayContent.getInsetsPolicy().updateBarControlTarget(app);
         mDisplayContent.getInsetsPolicy().showTransient(statusBars(),
                 true /* isGestureOnSystemBar */);
+        mWm.mAnimator.ready();
         waitUntilWindowAnimatorIdle();
 
         assertTrue(mDisplayContent.getInsetsPolicy().isTransient(statusBars()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 11d9629cf25e61e5c5d9af823bebbfdb3cdcd8a5..a1638019359b14e7951d21223be969c0631b86a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -43,7 +43,6 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -113,6 +112,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
         runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0);
         mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter,
                 mHandler, false /*isActivityEmbedding*/);
+        mWm.mAnimator.ready();
     }
 
     private WindowState createAppOverlayWindow() {
@@ -136,7 +136,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -168,7 +168,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -290,7 +290,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
         final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -336,7 +336,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
         task.applyAnimationUnchecked(null /* lp */, true /* enter */, TRANSIT_OLD_TASK_OPEN,
                 false /* isVoiceInteraction */, null /* sources */);
         mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
         final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         try {
@@ -363,7 +363,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -417,7 +417,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -471,7 +471,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -526,7 +526,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -559,7 +559,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -595,7 +595,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -645,7 +645,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -782,7 +782,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
 
             mDisplayContent.applySurfaceChangesTransaction();
             mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
 
             verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN),
                     any(), any(), any(), any());
@@ -810,7 +810,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         mController.goodToGo(transit);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
         return adapter;
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index a8f6fe86c8232df3681287711e8403d742dac02e..7ab093d0ae135b7076b7213c5082140422190f9b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -546,7 +546,7 @@ public class SystemServicesTestRule implements TestRule {
         // This makes sure all previous messages in the handler are fully processed vs. just popping
         // them from the message queue.
         final AtomicBoolean currentMessagesProcessed = new AtomicBoolean(false);
-        wm.mAnimator.getChoreographer().postFrameCallback(time -> {
+        wm.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             synchronized (currentMessagesProcessed) {
                 currentMessagesProcessed.set(true);
                 currentMessagesProcessed.notifyAll();