One thread to rule them all

Moving all quickstep event handling to main thread and removing
multi-thread logic.
Using system input dispatcher for handling event queue to that
velocity tracker works properly on different threads.

Bug: 123833655
Change-Id: I7c5004c32411da4144103112905ff6b40ed700ab
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 42fb42d..9ccd477 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java
deleted file mode 100644
index 1633485..0000000
--- a/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2018 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.quickstep;
-
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.view.Choreographer;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-
-/**
- * A TouchConsumer which defers all events on the UIThread until the consumer is created.
- */
-@TargetApi(Build.VERSION_CODES.P)
-public class DeferredTouchConsumer implements TouchConsumer {
-
-    private final VelocityTracker mVelocityTracker;
-    private final DeferredTouchProvider mTouchProvider;
-
-    private MotionEventQueue mMyQueue;
-    private TouchConsumer mTarget;
-
-    public DeferredTouchConsumer(DeferredTouchProvider touchProvider) {
-        mVelocityTracker = VelocityTracker.obtain();
-        mTouchProvider = touchProvider;
-    }
-
-    @Override
-    public void accept(MotionEvent event) {
-        mTarget.accept(event);
-    }
-
-    @Override
-    public void reset() {
-        mTarget.reset();
-    }
-
-    @Override
-    public void onQuickScrubStart() {
-        mTarget.onQuickScrubStart();
-    }
-
-    @Override
-    public void onQuickScrubEnd() {
-        mTarget.onQuickScrubEnd();
-    }
-
-    @Override
-    public void onQuickScrubProgress(float progress) {
-        mTarget.onQuickScrubProgress(progress);
-    }
-
-    @Override
-    public void onQuickStep(MotionEvent ev) {
-        mTarget.onQuickStep(ev);
-    }
-
-    @Override
-    public void onCommand(int command) {
-        mTarget.onCommand(command);
-    }
-
-    @Override
-    public void preProcessMotionEvent(MotionEvent ev) {
-        mVelocityTracker.addMovement(ev);
-    }
-
-    @Override
-    public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
-        mMyQueue = queue;
-        return null;
-    }
-
-    @Override
-    public void deferInit() {
-        mTarget = mTouchProvider.createTouchConsumer(mVelocityTracker);
-        mTarget.getIntrimChoreographer(mMyQueue);
-    }
-
-    @Override
-    public boolean forceToLauncherConsumer() {
-        return mTarget.forceToLauncherConsumer();
-    }
-
-    @Override
-    public OtherActivityTouchConsumer.RecentsAnimationState getRecentsAnimationStateToReuse() {
-        return mTarget.getRecentsAnimationStateToReuse();
-    }
-
-    @Override
-    public boolean deferNextEventToMainThread() {
-        // If our target is still null, defer the next target as well
-        TouchConsumer target = mTarget;
-        return target == null ? true : target.deferNextEventToMainThread();
-    }
-
-    @Override
-    public void onShowOverviewFromAltTab() {
-        mTarget.onShowOverviewFromAltTab();
-    }
-
-    public interface DeferredTouchProvider {
-
-        TouchConsumer createTouchConsumer(VelocityTracker tracker);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java
index 8a598a3..b61268b 100644
--- a/quickstep/src/com/android/quickstep/MotionEventQueue.java
+++ b/quickstep/src/com/android/quickstep/MotionEventQueue.java
@@ -15,21 +15,23 @@
  */
 package com.android.quickstep;
 
-import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_MASK;
-import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
 
 import android.annotation.TargetApi;
 import android.os.Build;
+import android.os.Looper;
 import android.util.Log;
+import android.util.Pair;
 import android.view.Choreographer;
+import android.view.InputEvent;
 import android.view.MotionEvent;
 
-import com.android.systemui.shared.system.ChoreographerCompat;
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher;
+import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
 
-import java.util.ArrayList;
+import java.util.function.Supplier;
 
 /**
  * Helper class for batching input events
@@ -49,153 +51,88 @@
             ACTION_VIRTUAL | (3 << ACTION_POINTER_INDEX_SHIFT);
     private static final int ACTION_RESET =
             ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_DEFER_INIT =
-            ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
     private static final int ACTION_SHOW_OVERVIEW_FROM_ALT_TAB =
-            ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
+            ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
     private static final int ACTION_QUICK_STEP =
-            ACTION_VIRTUAL | (7 << ACTION_POINTER_INDEX_SHIFT);
+            ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
     private static final int ACTION_COMMAND =
+            ACTION_VIRTUAL | (7 << ACTION_POINTER_INDEX_SHIFT);
+    private static final int ACTION_SWITCH_CONSUMER =
             ACTION_VIRTUAL | (8 << ACTION_POINTER_INDEX_SHIFT);
 
-    private final EventArray mEmptyArray = new EventArray();
-    private final Object mExecutionLock = new Object();
+    private final InputEventDispatcher mDispatcher;
+    private final InputEventReceiver mReceiver;
 
-    // We use two arrays and swap the current index when one array is being consumed
-    private final EventArray[] mArrays = new EventArray[] {new EventArray(), new EventArray()};
-    private int mCurrentIndex = 0;
+    private final Object mConsumerParamsLock = new Object();
+    private Supplier[] mConsumerParams = new Supplier[2];
 
-    private final Runnable mMainFrameCallback = this::frameCallbackForMainChoreographer;
-    private final Runnable mInterimFrameCallback = this::frameCallbackForInterimChoreographer;
+    private TouchConsumer mConsumer;
 
-    private final Choreographer mMainChoreographer;
+    public MotionEventQueue(Looper looper, Choreographer choreographer) {
+        Pair<InputEventDispatcher, InputEventReceiver> pair = InputChannelCompat.createPair(
+                "sysui-callbacks", looper, choreographer, this::onInputEvent);
 
-    private final TouchConsumer mConsumer;
-
-    private Choreographer mInterimChoreographer;
-    private Choreographer mCurrentChoreographer;
-
-    private Runnable mCurrentRunnable;
-
-    public MotionEventQueue(Choreographer choreographer, TouchConsumer consumer) {
-        mMainChoreographer = choreographer;
-        mConsumer = consumer;
-        mCurrentChoreographer = mMainChoreographer;
-        mCurrentRunnable = mMainFrameCallback;
-
-        setInterimChoreographer(consumer.getIntrimChoreographer(this));
+        mConsumer = TouchConsumer.NO_OP;
+        mDispatcher = pair.first;
+        mReceiver = pair.second;
     }
 
-    public void setInterimChoreographer(Choreographer choreographer) {
-        synchronized (mExecutionLock) {
-            synchronized (mArrays) {
-                setInterimChoreographerLocked(choreographer);
-                ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable);
-            }
+    private void onInputEvent(InputEvent ev) {
+        if (!(ev instanceof MotionEvent)) {
+            throw new IllegalStateException("Unknown event " + ev);
         }
-    }
-
-    private void  setInterimChoreographerLocked(Choreographer choreographer) {
-        mInterimChoreographer = choreographer;
-        if (choreographer == null) {
-            mCurrentChoreographer = mMainChoreographer;
-            mCurrentRunnable = mMainFrameCallback;
+        MotionEvent event = (MotionEvent) ev;
+        if (event.getActionMasked() == ACTION_VIRTUAL) {
+            switch (event.getAction()) {
+                case ACTION_QUICK_SCRUB_START:
+                    mConsumer.onQuickScrubStart();
+                    break;
+                case ACTION_QUICK_SCRUB_PROGRESS:
+                    mConsumer.onQuickScrubProgress(event.getX());
+                    break;
+                case ACTION_QUICK_SCRUB_END:
+                    mConsumer.onQuickScrubEnd();
+                    break;
+                case ACTION_RESET:
+                    mConsumer.reset();
+                    break;
+                case ACTION_SHOW_OVERVIEW_FROM_ALT_TAB:
+                    mConsumer.onShowOverviewFromAltTab();
+                    mConsumer.onQuickScrubStart();
+                    break;
+                case ACTION_QUICK_STEP:
+                    mConsumer.onQuickStep(event);
+                    break;
+                case ACTION_COMMAND:
+                    mConsumer.onCommand(event.getSource());
+                    break;
+                case ACTION_SWITCH_CONSUMER:
+                    synchronized (mConsumerParamsLock) {
+                        int index = event.getSource();
+                        mConsumer = (TouchConsumer) mConsumerParams[index].get();
+                        mConsumerParams[index] = null;
+                    }
+                    break;
+                default:
+                    Log.e(TAG, "Invalid virtual event: " + event.getAction());
+            }
         } else {
-            mCurrentChoreographer = mInterimChoreographer;
-            mCurrentRunnable = mInterimFrameCallback;
+            mConsumer.accept(event);
         }
     }
 
     public void queue(MotionEvent event) {
-        mConsumer.preProcessMotionEvent(event);
-        queueNoPreProcess(event);
+        mDispatcher.dispatch(event);
     }
 
-    private void queueNoPreProcess(MotionEvent event) {
-        synchronized (mArrays) {
-            EventArray array = mArrays[mCurrentIndex];
-            if (array.isEmpty()) {
-                ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable);
-            }
-
-            int eventAction = event.getAction();
-            if (eventAction == ACTION_MOVE && array.lastEventAction == ACTION_MOVE) {
-                // Replace and recycle the last event
-                array.set(array.size() - 1, event).recycle();
-            } else {
-                array.add(event);
-                array.lastEventAction = eventAction;
-            }
-        }
+    private void queueVirtualAction(int action, float param) {
+        queue(MotionEvent.obtain(0, 0, action, param, 0, 0));
     }
 
-    private void frameCallbackForMainChoreographer() {
-        runFor(mMainChoreographer);
-    }
-
-    private void frameCallbackForInterimChoreographer() {
-        runFor(mInterimChoreographer);
-    }
-
-    private void runFor(Choreographer caller) {
-        synchronized (mExecutionLock) {
-            EventArray array = swapAndGetCurrentArray(caller);
-            int size = array.size();
-            for (int i = 0; i < size; i++) {
-                MotionEvent event = array.get(i);
-                if (event.getActionMasked() == ACTION_VIRTUAL) {
-                    switch (event.getAction()) {
-                        case ACTION_QUICK_SCRUB_START:
-                            mConsumer.onQuickScrubStart();
-                            break;
-                        case ACTION_QUICK_SCRUB_PROGRESS:
-                            mConsumer.onQuickScrubProgress(event.getX());
-                            break;
-                        case ACTION_QUICK_SCRUB_END:
-                            mConsumer.onQuickScrubEnd();
-                            break;
-                        case ACTION_RESET:
-                            mConsumer.reset();
-                            break;
-                        case ACTION_DEFER_INIT:
-                            mConsumer.deferInit();
-                            break;
-                        case ACTION_SHOW_OVERVIEW_FROM_ALT_TAB:
-                            mConsumer.onShowOverviewFromAltTab();
-                            mConsumer.onQuickScrubStart();
-                            break;
-                        case ACTION_QUICK_STEP:
-                            mConsumer.onQuickStep(event);
-                            break;
-                        case ACTION_COMMAND:
-                            mConsumer.onCommand(event.getSource());
-                            break;
-                        default:
-                            Log.e(TAG, "Invalid virtual event: " + event.getAction());
-                    }
-                } else {
-                    mConsumer.accept(event);
-                }
-                event.recycle();
-            }
-            array.clear();
-            array.lastEventAction = ACTION_CANCEL;
-        }
-    }
-
-    private EventArray swapAndGetCurrentArray(Choreographer caller) {
-        synchronized (mArrays) {
-            if (caller != mCurrentChoreographer) {
-                return mEmptyArray;
-            }
-            EventArray current = mArrays[mCurrentIndex];
-            mCurrentIndex = mCurrentIndex ^ 1;
-            return current;
-        }
-    }
-
-    private void queueVirtualAction(int action, float progress) {
-        queueNoPreProcess(MotionEvent.obtain(0, 0, action, progress, 0, 0));
+    private void queueVirtualAction(int action, int param) {
+        MotionEvent ev = MotionEvent.obtain(0, 0, action, 0, 0, 0);
+        ev.setSource(param);
+        queue(ev);
     }
 
     public void onQuickScrubStart() {
@@ -216,33 +153,44 @@
 
     public void onQuickStep(MotionEvent event) {
         event.setAction(ACTION_QUICK_STEP);
-        queueNoPreProcess(event);
+        queue(event);
     }
 
     public void reset() {
         queueVirtualAction(ACTION_RESET, 0);
     }
 
-    public void deferInit() {
-        queueVirtualAction(ACTION_DEFER_INIT, 0);
+    public void onCommand(int command) {
+        queueVirtualAction(ACTION_COMMAND, command);
     }
 
-    public void onCommand(int command) {
-        MotionEvent ev = MotionEvent.obtain(0, 0, ACTION_COMMAND, 0, 0, 0);
-        ev.setSource(command);
-        queueNoPreProcess(ev);
+    public void switchConsumer(Supplier<TouchConsumer> consumer) {
+        int index = -1;
+        synchronized (mConsumerParamsLock) {
+            // Find a null index
+            for (int i = 0; i < mConsumerParams.length; i++) {
+                if (mConsumerParams[i] == null) {
+                    index = i;
+                    break;
+                }
+            }
+            if (index < 0) {
+                index = mConsumerParams.length;
+                final Supplier[] newValues = new Supplier[index + 1];
+                System.arraycopy(mConsumerParams, 0, newValues, 0, index);
+                mConsumerParams = newValues;
+            }
+            mConsumerParams[index] = consumer;
+        }
+        queueVirtualAction(ACTION_SWITCH_CONSUMER, index);
     }
 
     public TouchConsumer getConsumer() {
         return mConsumer;
     }
 
-    private static class EventArray extends ArrayList<MotionEvent> {
-
-        public int lastEventAction = ACTION_CANCEL;
-
-        public EventArray() {
-            super(4);
-        }
+    public void dispose() {
+        mDispatcher.dispose();
+        mReceiver.dispose();
     }
 }
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index 1592a97..a408f95 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -15,8 +15,12 @@
  */
 package com.android.quickstep;
 
+import static com.android.quickstep.WindowTransformSwipeHandler.STATES;
+
+import android.util.Log;
 import android.util.SparseArray;
 
+import java.util.StringJoiner;
 import java.util.function.Consumer;
 
 /**
@@ -24,6 +28,9 @@
  */
 public class MultiStateCallback {
 
+    private static final String TAG = "MultiStateCallback";
+    private static final boolean DEBUG_STATES = false;
+
     private final SparseArray<Runnable> mCallbacks = new SparseArray<>();
     private final SparseArray<Consumer<Boolean>> mStateChangeHandlers = new SparseArray<>();
 
@@ -97,4 +104,25 @@
     public boolean hasStates(int stateMask) {
         return (mState & stateMask) == stateMask;
     }
+
+    private void debugNewState(int stateFlag) {
+        if (!DEBUG_STATES) {
+            return;
+        }
+
+        int state = getState();
+        StringJoiner currentStateStr = new StringJoiner(", ", "[", "]");
+        String stateFlagStr = "Unknown-" + stateFlag;
+        for (int i = 0; i < STATES.length; i++) {
+            if ((state & (i << i)) != 0) {
+                currentStateStr.add(STATES[i]);
+            }
+            if (stateFlag == (1 << i)) {
+                stateFlagStr = STATES[i] + " (" + stateFlag + ")";
+            }
+        }
+        Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding " + stateFlagStr + " to "
+                + currentStateStr);
+    }
+
 }
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 13a9341..5801c10 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -21,6 +21,7 @@
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.MotionEvent.INVALID_POINTER_ID;
+
 import static com.android.launcher3.util.RaceConditionTracker.ENTER;
 import static com.android.launcher3.util.RaceConditionTracker.EXIT;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -35,10 +36,8 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Looper;
 import android.os.SystemClock;
 import android.util.SparseArray;
-import android.view.Choreographer;
 import android.view.Display;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -63,9 +62,6 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
 import androidx.annotation.Nullable;
 
 /**
@@ -74,7 +70,6 @@
 @TargetApi(Build.VERSION_CODES.P)
 public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {
 
-    private static final long LAUNCHER_DRAW_TIMEOUT_MS = 150;
     public static final String DOWN_EVT = "OtherActivityTouchConsumer.DOWN";
     private static final String UP_EVT = "OtherActivityTouchConsumer.UP";
 
@@ -84,12 +79,15 @@
     private final Intent mHomeIntent;
     private final ActivityControlHelper mActivityControlHelper;
     private final MainThreadExecutor mMainThreadExecutor;
-    private final Choreographer mBackgroundThreadChoreographer;
     private final OverviewCallbacks mOverviewCallbacks;
     private final TaskOverlayFactory mTaskOverlayFactory;
     private final TouchInteractionLog mTouchInteractionLog;
     private final InputConsumerController mInputConsumer;
 
+    private final MotionEventQueue mEventQueue;
+    private final MotionPauseDetector mMotionPauseDetector;
+    private VelocityTracker mVelocityTracker;
+
     private final boolean mIsDeferredDownTarget;
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
@@ -103,29 +101,28 @@
     private Rect mStableInsets = new Rect();
     private boolean mCanGestureBeContinued;
 
-    private VelocityTracker mVelocityTracker;
-    private MotionPauseDetector mMotionPauseDetector;
-    private MotionEventQueue mEventQueue;
     private boolean mIsGoingToLauncher;
     private RecentsAnimationState mRecentsAnimationState;
 
     public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
             RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
-            MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
+            MainThreadExecutor mainThreadExecutor,
             @HitTarget int downHitTarget, OverviewCallbacks overviewCallbacks,
             TaskOverlayFactory taskOverlayFactory, InputConsumerController inputConsumer,
-            VelocityTracker velocityTracker, TouchInteractionLog touchInteractionLog,
+            TouchInteractionLog touchInteractionLog, MotionEventQueue eventQueue,
             @Nullable RecentsAnimationState recentsAnimationStateToReuse) {
         super(base);
 
         mRunningTask = runningTaskInfo;
         mRecentsModel = recentsModel;
         mHomeIntent = homeIntent;
-        mVelocityTracker = velocityTracker;
+
         mMotionPauseDetector = new MotionPauseDetector(base);
+        mEventQueue = eventQueue;
+        mVelocityTracker = VelocityTracker.obtain();
+
         mActivityControlHelper = activityControl;
         mMainThreadExecutor = mainThreadExecutor;
-        mBackgroundThreadChoreographer = backgroundThreadChoreographer;
         mIsDeferredDownTarget = activityControl.deferStartingActivity(downHitTarget);
         mOverviewCallbacks = overviewCallbacks;
         mTaskOverlayFactory = taskOverlayFactory;
@@ -145,6 +142,12 @@
         if (mVelocityTracker == null) {
             return;
         }
+        mVelocityTracker.addMovement(ev);
+        if (ev.getActionMasked() == ACTION_POINTER_UP) {
+            mVelocityTracker.clear();
+            mMotionPauseDetector.clear();
+        }
+
         mTouchInteractionLog.addMotionEvent(ev);
         switch (ev.getActionMasked()) {
             case ACTION_DOWN: {
@@ -275,56 +278,33 @@
         mInteractionHandler = handler;
         handler.setGestureEndCallback(mEventQueue::reset);
         mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);
-
-        CountDownLatch drawWaitLock = new CountDownLatch(1);
-        handler.setLauncherOnDrawCallback(() -> {
-            drawWaitLock.countDown();
-            if (handler == mInteractionHandler) {
-                switchToMainChoreographer();
-            }
-        });
         handler.initWhenReady();
 
         TraceHelper.beginSection("RecentsController");
 
-        AssistDataReceiver assistDataReceiver = !mTaskOverlayFactory.needAssist() ? null :
-                new AssistDataReceiver() {
-                    @Override
-                    public void onHandleAssistData(Bundle bundle) {
-                        if (mInteractionHandler == null) {
-                            // Interaction is probably complete
-                            mRecentsModel.preloadAssistData(mRunningTask.id, bundle);
-                        } else if (handler == mInteractionHandler) {
-                            handler.onAssistDataReceived(bundle);
-                        }
-                    }
-                };
-
-        Runnable startActivity;
         if (reuseOldAnimState) {
-            startActivity = () -> {
-                handler.onRecentsAnimationStart(mRecentsAnimationState.mController,
-                        mRecentsAnimationState.mTargets, mRecentsAnimationState.mHomeContentInsets,
-                        mRecentsAnimationState.mMinimizedHomeBounds);
-            };
+            handler.onRecentsAnimationStart(mRecentsAnimationState.mController,
+                    mRecentsAnimationState.mTargets, mRecentsAnimationState.mHomeContentInsets,
+                    mRecentsAnimationState.mMinimizedHomeBounds);
         } else {
-            startActivity = () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
-                    mHomeIntent, assistDataReceiver, mRecentsAnimationState, null, null);
+            AssistDataReceiver assistDataReceiver = !mTaskOverlayFactory.needAssist() ? null :
+                    new AssistDataReceiver() {
+                        @Override
+                        public void onHandleAssistData(Bundle bundle) {
+                            if (mInteractionHandler == null) {
+                                // Interaction is probably complete
+                                mRecentsModel.preloadAssistData(mRunningTask.id, bundle);
+                            } else if (handler == mInteractionHandler) {
+                                handler.onAssistDataReceived(bundle);
+                            }
+                        }
+                    };
+
+            BackgroundExecutor.get().submit(
+                    () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
+                            mHomeIntent, assistDataReceiver, mRecentsAnimationState, null, null));
         }
 
-
-        if (Looper.myLooper() != Looper.getMainLooper()) {
-            startActivity.run();
-            try {
-                drawWaitLock.await(LAUNCHER_DRAW_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (Exception e) {
-                // We have waited long enough for launcher to draw
-            }
-        } else {
-            // We should almost always get touch-town on background thread. This is an edge case
-            // when the background Choreographer has not yet initialized.
-            BackgroundExecutor.get().submit(startActivity);
-        }
     }
 
     @Override
@@ -380,12 +360,6 @@
     }
 
     @Override
-    public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
-        mEventQueue = queue;
-        return mBackgroundThreadChoreographer;
-    }
-
-    @Override
     public void onQuickScrubStart() {
         if (!mPassedInitialSlop && mIsDeferredDownTarget && mInteractionHandler == null) {
             // If we deferred starting the window animation on touch down, then
@@ -444,21 +418,6 @@
         return displacement;
     }
 
-    public void switchToMainChoreographer() {
-        mEventQueue.setInterimChoreographer(null);
-    }
-
-    @Override
-    public void preProcessMotionEvent(MotionEvent ev) {
-        if (mVelocityTracker != null) {
-           mVelocityTracker.addMovement(ev);
-           if (ev.getActionMasked() == ACTION_POINTER_UP) {
-               mVelocityTracker.clear();
-               mMotionPauseDetector.clear();
-           }
-        }
-    }
-
     @Override
     public boolean forceToLauncherConsumer() {
         return mIsGoingToLauncher;
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
index 60bd9fb..c2d4d80 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -23,6 +23,7 @@
 
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.util.LooperExecutor;
+import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.quickstep.util.RemoteAnimationTargetSet;
@@ -33,6 +34,8 @@
 import java.util.concurrent.ExecutorService;
 import java.util.function.Supplier;
 
+import androidx.annotation.UiThread;
+
 /**
  * Wrapper around RecentsAnimationController to help with some synchronization
  */
@@ -67,8 +70,10 @@
         mTouchProxySupplier = touchProxySupplier;
     }
 
+    @UiThread
     public synchronized void setController(
             RecentsAnimationControllerCompat controller, RemoteAnimationTargetSet targetSet) {
+        Preconditions.assertUIThread();
         TraceHelper.partitionSection("RecentsController", "Set controller " + controller);
         this.mController = controller;
         this.targetSet = targetSet;
diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java
index 1d5ffe7..ef37595 100644
--- a/quickstep/src/com/android/quickstep/TouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/TouchConsumer.java
@@ -17,7 +17,6 @@
 
 import android.annotation.TargetApi;
 import android.os.Build;
-import android.view.Choreographer;
 import android.view.MotionEvent;
 
 import androidx.annotation.IntDef;
@@ -56,18 +55,6 @@
 
     default void onCommand(int command) { }
 
-    /**
-     * Called on the binder thread to allow the consumer to process the motion event before it is
-     * posted on a handler thread.
-     */
-    default void preProcessMotionEvent(MotionEvent ev) { }
-
-    default Choreographer getIntrimChoreographer(MotionEventQueue queue) {
-        return null;
-    }
-
-    default void deferInit() { }
-
     default boolean deferNextEventToMainThread() {
         return false;
     }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 7da7bcd..0f52deb 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -23,8 +23,7 @@
 import static android.view.MotionEvent.ACTION_UP;
 
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.systemui.shared.system.ActivityManagerWrapper
-        .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
 
 import android.annotation.TargetApi;
@@ -33,14 +32,12 @@
 import android.content.Intent;
 import android.graphics.PointF;
 import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Choreographer;
 import android.view.MotionEvent;
-import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 
 import com.android.launcher3.BaseDraggingActivity;
@@ -52,7 +49,6 @@
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ChoreographerCompat;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
 
@@ -78,11 +74,6 @@
 
     private static final String TAG = "TouchInteractionService";
 
-    /**
-     * A background thread used for handling UI for another window.
-     */
-    private static HandlerThread sRemoteUiThread;
-
     private final IBinder mMyBinder = new IOverviewProxy.Stub() {
 
         @Override
@@ -191,9 +182,6 @@
     private TouchInteractionLog mTouchInteractionLog;
     private InputConsumerController mInputConsumer;
 
-    private Choreographer mMainThreadChoreographer;
-    private Choreographer mBackgroundThreadChoreographer;
-
     @Override
     public void onCreate() {
         super.onCreate();
@@ -202,8 +190,7 @@
         mMainThreadExecutor = new MainThreadExecutor();
         mOverviewComponentObserver = new OverviewComponentObserver(this);
         mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver);
-        mMainThreadChoreographer = Choreographer.getInstance();
-        mEventQueue = new MotionEventQueue(mMainThreadChoreographer, TouchConsumer.NO_OP);
+        mEventQueue = new MotionEventQueue(Looper.myLooper(), Choreographer.getInstance());
         mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
         mOverviewCallbacks = OverviewCallbacks.get(this);
         mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this);
@@ -215,13 +202,13 @@
 
         // Temporarily disable model preload
         // new ModelPreload().start(this);
-        initBackgroundChoreographer();
     }
 
     @Override
     public void onDestroy() {
         mInputConsumer.unregisterInputConsumer();
         mOverviewComponentObserver.onDestroy();
+        mEventQueue.dispose();
         sConnected = false;
         super.onDestroy();
     }
@@ -236,20 +223,18 @@
         mEventQueue.reset();
         TouchConsumer oldConsumer = mEventQueue.getConsumer();
         if (oldConsumer.deferNextEventToMainThread()) {
-            mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
-                    new DeferredTouchConsumer((v) -> getCurrentTouchConsumer(downHitTarget,
-                            oldConsumer.forceToLauncherConsumer(),
-                            oldConsumer.getRecentsAnimationStateToReuse(), v)));
-            mEventQueue.deferInit();
+            mEventQueue.switchConsumer(() -> getCurrentTouchConsumer(downHitTarget,
+                    oldConsumer.forceToLauncherConsumer(),
+                    oldConsumer.getRecentsAnimationStateToReuse()));
+
         } else {
-            mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
-                    getCurrentTouchConsumer(downHitTarget, false, null, null));
+            TouchConsumer consumer = getCurrentTouchConsumer(downHitTarget, false, null);
+            mEventQueue.switchConsumer(() -> consumer);
         }
     }
 
     private TouchConsumer getCurrentTouchConsumer(@HitTarget int downHitTarget,
-            boolean forceToLauncher, RecentsAnimationState recentsAnimationStateToReuse,
-            VelocityTracker tracker) {
+            boolean forceToLauncher, RecentsAnimationState recentsAnimationStateToReuse) {
         RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
 
         if (runningTaskInfo == null && !forceToLauncher) {
@@ -265,27 +250,15 @@
                     mOverviewComponentObserver.getActivityControlHelper(), false,
                     mTouchInteractionLog, false /* waitForWindowAvailable */);
         } else {
-            if (tracker == null) {
-                tracker = VelocityTracker.obtain();
-            }
             return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel,
                     mOverviewComponentObserver.getOverviewIntent(),
                     mOverviewComponentObserver.getActivityControlHelper(), mMainThreadExecutor,
-                    mBackgroundThreadChoreographer, downHitTarget, mOverviewCallbacks,
-                    mTaskOverlayFactory, mInputConsumer, tracker, mTouchInteractionLog,
+                    downHitTarget, mOverviewCallbacks,
+                    mTaskOverlayFactory, mInputConsumer, mTouchInteractionLog, mEventQueue,
                     recentsAnimationStateToReuse);
         }
     }
 
-    private void initBackgroundChoreographer() {
-        if (sRemoteUiThread == null) {
-            sRemoteUiThread = new HandlerThread("remote-ui");
-            sRemoteUiThread.start();
-        }
-        new Handler(sRemoteUiThread.getLooper()).post(() ->
-                mBackgroundThreadChoreographer = ChoreographerCompat.getSfInstance());
-    }
-
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mTouchInteractionLog.dump(pw);
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 6564950..0e9ddee 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -60,7 +60,6 @@
 import android.os.Looper;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.util.Log;
 import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 import android.view.View;
@@ -68,11 +67,9 @@
 import android.view.WindowManager;
 import android.view.animation.Interpolator;
 
-import androidx.annotation.AnyThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
@@ -113,13 +110,11 @@
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 import com.android.systemui.shared.system.WindowCallbacksCompat;
 
-import java.util.StringJoiner;
 import java.util.function.BiFunction;
 
 @TargetApi(Build.VERSION_CODES.O)
 public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
     private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
-    private static final boolean DEBUG_STATES = false;
 
     // Launcher UI related states
     private static final int STATE_LAUNCHER_PRESENT = 1 << 0;
@@ -171,7 +166,7 @@
             | STATE_QUICK_SCRUB_START | STATE_APP_CONTROLLER_RECEIVED;
 
     // For debugging, keep in sync with above states
-    private static final String[] STATES = new String[] {
+    public static final String[] STATES = new String[] {
             "STATE_LAUNCHER_PRESENT",
             "STATE_LAUNCHER_STARTED",
             "STATE_LAUNCHER_DRAWN",
@@ -280,8 +275,6 @@
     private QuickScrubController mQuickScrubController;
     private AnimationFactory mAnimationFactory = (t, i) -> { };
 
-    private Runnable mLauncherDrawnCallback;
-
     private boolean mWasLauncherAlreadyVisible;
 
     private boolean mPassedOverviewThreshold;
@@ -297,8 +290,7 @@
     private final long mTouchTimeMs;
     private long mLauncherFrameDrawnTime;
 
-    private boolean mBgLongSwipeMode = false;
-    private boolean mUiLongSwipeMode = false;
+    private boolean mLongSwipeMode = false;
     private float mLongSwipeDisplacement = 0;
     private LongSwipeHelper mLongSwipeController;
 
@@ -326,13 +318,7 @@
     }
 
     private void initStateCallbacks() {
-        mStateCallback = new MultiStateCallback() {
-            @Override
-            public void setState(int stateFlag) {
-                debugNewState(stateFlag);
-                super.setState(stateFlag);
-            }
-        };
+        mStateCallback = new MultiStateCallback();
 
         // Re-setup the recents UI when gesture starts, as the state could have been changed during
         // that time by a previous window transition.
@@ -416,14 +402,6 @@
         }
     }
 
-    private void executeOnUiThread(Runnable action) {
-        if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
-            action.run();
-        } else {
-            postAsyncCallback(mMainThreadHandler, action);
-        }
-    }
-
     private void setStateOnUiThread(int stateFlag) {
         if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
             mStateCallback.setState(stateFlag);
@@ -484,7 +462,7 @@
         });
         mRecentsView.setEnableFreeScroll(false);
         mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
-            if (!mBgLongSwipeMode && mGestureEndTarget != HOME) {
+            if (!mLongSwipeMode && mGestureEndTarget != HOME) {
                 updateFinalShift();
             }
         });
@@ -551,10 +529,6 @@
         mRecentsView.setRunningTaskIconScaledDown(true);
     }
 
-    public void setLauncherOnDrawCallback(Runnable callback) {
-        mLauncherDrawnCallback = callback;
-    }
-
     private void launcherFrameDrawn() {
         AlphaProperty property = mActivityControlHelper.getAlphaProperty(mActivity);
         if (property.getValue() < 1) {
@@ -575,9 +549,6 @@
                 mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE);
             }
         }
-        if (mLauncherDrawnCallback != null) {
-            mLauncherDrawnCallback.run();
-        }
         mLauncherFrameDrawnTime = SystemClock.uptimeMillis();
     }
 
@@ -632,25 +603,11 @@
         return TaskView.getCurveScaleForInterpolation(interpolation);
     }
 
-    @WorkerThread
-    @SuppressWarnings("WrongThread")
+    @UiThread
     public void dispatchMotionEventToRecentsView(MotionEvent event, @Nullable Float velocityX) {
         if (mRecentsView == null) {
             return;
         }
-        if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
-            dispatchMotionEventToRecentsViewUi(event, velocityX);
-        } else {
-            MotionEvent ev = MotionEvent.obtain(event);
-            postAsyncCallback(mMainThreadHandler, () -> {
-                dispatchMotionEventToRecentsViewUi(ev, velocityX);
-                ev.recycle();
-            });
-        }
-    }
-
-    @UiThread
-    private void dispatchMotionEventToRecentsViewUi(MotionEvent event, @Nullable Float velocityX) {
         // Pass the motion events to RecentsView to allow scrolling during swipe up.
         if (!mDispatchedDownEvent) {
             // The first event we dispatch should be ACTION_DOWN.
@@ -666,23 +623,21 @@
         mRecentsView.simulateTouchEvent(event, velocityX);
     }
 
-    @WorkerThread
+    @UiThread
     public void updateDisplacement(float displacement) {
         // We are moving in the negative x/y direction
         displacement = -displacement;
         if (displacement > mTransitionDragLength && mTransitionDragLength > 0) {
             mCurrentShift.updateValue(1);
 
-            if (!mBgLongSwipeMode && !FeatureFlags.SWIPE_HOME.get()) {
-                mBgLongSwipeMode = true;
-                executeOnUiThread(this::onLongSwipeEnabledUi);
+            if (!mLongSwipeMode && !FeatureFlags.SWIPE_HOME.get()) {
+                onLongSwipeEnabled();
             }
             mLongSwipeDisplacement = displacement - mTransitionDragLength;
-            executeOnUiThread(this::onLongSwipeDisplacementUpdated);
+            onLongSwipeDisplacementUpdated();
         } else {
-            if (mBgLongSwipeMode) {
-                mBgLongSwipeMode = false;
-                executeOnUiThread(this::onLongSwipeDisabledUi);
+            if (mLongSwipeMode) {
+                onLongSwipeDisabled();
             }
             float translation = Math.max(displacement, 0);
             float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
@@ -694,16 +649,15 @@
         setShelfState(isPaused ? PEEK : HIDE, FAST_OUT_SLOW_IN, SHELF_ANIM_DURATION);
     }
 
+    @UiThread
     public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
         if (mInteractionType == INTERACTION_NORMAL) {
-            executeOnUiThread(() -> {
-                mAnimationFactory.setShelfState(shelfState, interpolator, duration);
-                mIsShelfPeeking = shelfState == PEEK;
-                if (mRecentsView != null && shelfState.shouldPreformHaptic) {
-                    mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
-                            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-                }
-            });
+            mAnimationFactory.setShelfState(shelfState, interpolator, duration);
+            mIsShelfPeeking = shelfState == PEEK;
+            if (mRecentsView != null && shelfState.shouldPreformHaptic) {
+                mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+            }
         }
     }
 
@@ -721,7 +675,7 @@
         updateLauncherTransitionProgress();
     }
 
-    @WorkerThread
+    @UiThread
     private void updateFinalShift() {
         float shift = mCurrentShift.value;
 
@@ -736,12 +690,8 @@
             }
             float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
                     mClipAnimationHelper.getTargetRect().width());
-            SyncRtSurfaceTransactionApplierCompat syncTransactionApplier
-                    = Looper.myLooper() == mMainThreadHandler.getLooper()
-                            ? mSyncTransactionApplier
-                            : null;
             mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale)
-                    .setSyncTransactionApplier(syncTransactionApplier);
+                    .setSyncTransactionApplier(mSyncTransactionApplier);
             mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
                     mTransformParams);
 
@@ -752,14 +702,10 @@
             }
         }
 
-        executeOnUiThread(this::updateFinalShiftUi);
-    }
-
-    private void updateFinalShiftUi() {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             if (mRecentsAnimationWrapper.getController() != null && mLayoutListener != null) {
                 mLayoutListener.open();
-                mLayoutListener.update(mCurrentShift.value > 1, mUiLongSwipeMode,
+                mLayoutListener.update(mCurrentShift.value > 1, mLongSwipeMode,
                         mClipAnimationHelper.getCurrentRectWithInsets(),
                         mClipAnimationHelper.getCurrentCornerRadius());
             }
@@ -801,6 +747,7 @@
                         ? 0 : (progress - mShiftAtGestureStart) / (1 - mShiftAtGestureStart));
     }
 
+    @UiThread
     public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller,
             RemoteAnimationTargetSet targets, Rect homeContentInsets, Rect minimizedHomeBounds) {
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
@@ -842,6 +789,7 @@
         mPassedOverviewThreshold = false;
     }
 
+    @UiThread
     public void onRecentsAnimationCanceled() {
         mRecentsAnimationWrapper.setController(null, null);
         mActivityInitListener.unregister();
@@ -849,6 +797,7 @@
         mTouchInteractionLog.cancelRecentsAnimation();
     }
 
+    @UiThread
     public void onGestureStarted() {
         notifyGestureStartedAsync();
         mShiftAtGestureStart = mCurrentShift.value;
@@ -860,10 +809,9 @@
     }
 
     /**
-     * Notifies the launcher that the swipe gesture has started. This can be called multiple times
-     * on both background and UI threads
+     * Notifies the launcher that the swipe gesture has started. This can be called multiple times.
      */
-    @AnyThread
+    @UiThread
     private void notifyGestureStartedAsync() {
         final T curActivity = mActivity;
         if (curActivity != null) {
@@ -873,7 +821,7 @@
         }
     }
 
-    @WorkerThread
+    @UiThread
     public void onGestureEnded(float endVelocity, float velocityX) {
         float flingThreshold = mContext.getResources()
                 .getDimension(R.dimen.quickstep_fling_threshold_velocity);
@@ -882,8 +830,8 @@
 
         mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
 
-        if (mBgLongSwipeMode) {
-            executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling, velocityX));
+        if (mLongSwipeMode) {
+            onLongSwipeGestureFinish(endVelocity, isFling, velocityX);
         } else {
             handleNormalGestureEnd(endVelocity, isFling, velocityX);
         }
@@ -904,6 +852,7 @@
                 mTouchInteractionLog);
     }
 
+    @UiThread
     private void handleNormalGestureEnd(float endVelocity, boolean isFling, float velocityX) {
         float velocityPxPerMs = endVelocity / 1000;
         float velocityXPxPerMs = velocityX / 1000;
@@ -1033,12 +982,14 @@
     }
 
     /** Animates to the given progress, where 0 is the current app and 1 is overview. */
+    @UiThread
     private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
             GestureEndTarget target, float velocityPxPerMs) {
         mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
                 interpolator, target, velocityPxPerMs));
     }
 
+    @UiThread
     private void animateToProgressInternal(float start, float end, long duration,
             Interpolator interpolator, GestureEndTarget target, float velocityPxPerMs) {
         mGestureEndTarget = target;
@@ -1080,44 +1031,37 @@
             }
         });
         windowAnim.start();
-        long startMillis = SystemClock.uptimeMillis();
         // Always play the entire launcher animation when going home, since it is separate from
         // the animation that has been controlled thus far.
-        final float finalStart = mGestureEndTarget == HOME ? 0 : start;
-        executeOnUiThread(() -> {
-            // Animate the launcher components at the same time as the window, always on UI thread.
-            // Adjust start progress and duration in case we are on a different thread.
-            long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
-            elapsedMillis = Utilities.boundToRange(elapsedMillis, 0, duration);
-            float elapsedProgress = (float) elapsedMillis / duration;
-            float adjustedStart = Utilities.mapRange(elapsedProgress, finalStart, end);
-            long adjustedDuration = duration - elapsedMillis;
-            // We want to use the same interpolator as the window, but need to adjust it to
-            // interpolate over the remaining progress (end - start).
-            TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress(
-                    interpolator, adjustedStart, end);
-            if (homeAnimFactory != null) {
-                Animator homeAnim = homeAnimFactory.createActivityAnimationToHome();
-                homeAnim.setDuration(adjustedDuration).setInterpolator(adjustedInterpolator);
-                homeAnim.start();
-                mLauncherTransitionController = null;
-            }
-            if (mLauncherTransitionController == null) {
-                return;
-            }
-            if (finalStart == end || duration <= 0) {
-                mLauncherTransitionController.dispatchSetInterpolator(t -> end);
-                mLauncherTransitionController.getAnimationPlayer().end();
-            } else {
-                mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator);
-                mLauncherTransitionController.getAnimationPlayer().setDuration(adjustedDuration);
+        if (mGestureEndTarget == HOME) {
+            start = 0;
+        }
 
-                if (QUICKSTEP_SPRINGS.get()) {
-                    mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs);
-                }
-                mLauncherTransitionController.getAnimationPlayer().start();
+        // We want to use the same interpolator as the window, but need to adjust it to
+        // interpolate over the remaining progress (end - start).
+        TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress(
+                interpolator, start, end);
+        if (homeAnimFactory != null) {
+            Animator homeAnim = homeAnimFactory.createActivityAnimationToHome();
+            homeAnim.setDuration(duration).setInterpolator(adjustedInterpolator);
+            homeAnim.start();
+            mLauncherTransitionController = null;
+        }
+        if (mLauncherTransitionController == null) {
+            return;
+        }
+        if (start == end || duration <= 0) {
+            mLauncherTransitionController.dispatchSetInterpolator(t -> end);
+            mLauncherTransitionController.getAnimationPlayer().end();
+        } else {
+            mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator);
+            mLauncherTransitionController.getAnimationPlayer().setDuration(duration);
+
+            if (QUICKSTEP_SPRINGS.get()) {
+                mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs);
             }
-        });
+            mLauncherTransitionController.getAnimationPlayer().start();
+        }
     }
 
     /**
@@ -1148,12 +1092,8 @@
                     originalTarget, finalTarget));
             currentRect.set(rectFEvaluator.evaluate(progress, startRect, targetRect));
             float alpha = 1 - interpolatedProgress;
-            SyncRtSurfaceTransactionApplierCompat syncTransactionApplier
-                    = Looper.myLooper() == mMainThreadHandler.getLooper()
-                            ? mSyncTransactionApplier
-                            : null;
             mTransformParams.setCurrentRectAndTargetAlpha(currentRect, alpha)
-                    .setSyncTransactionApplier(syncTransactionApplier);
+                    .setSyncTransactionApplier(mSyncTransactionApplier);
             mClipAnimationHelper.applyTransform(targetSet, mTransformParams);
         });
         anim.addListener(new AnimationSuccessListener() {
@@ -1404,8 +1344,8 @@
 
     public void onQuickScrubProgress(float progress) {
         mCurrentQuickScrubProgress = progress;
-        if (Looper.myLooper() != Looper.getMainLooper() || mQuickScrubController == null
-                || mQuickScrubBlocked || !mStateCallback.hasStates(QUICK_SCRUB_START_UI_STATE)) {
+        if (mQuickScrubController == null || mQuickScrubBlocked ||
+                !mStateCallback.hasStates(QUICK_SCRUB_START_UI_STATE)) {
             return;
         }
         mQuickScrubController.onQuickScrubProgress(progress);
@@ -1427,39 +1367,19 @@
         setStateOnUiThread(STATE_HANDLER_INVALIDATED);
     }
 
-    private void debugNewState(int stateFlag) {
-        if (!DEBUG_STATES) {
-            return;
-        }
-
-        int state = mStateCallback.getState();
-        StringJoiner currentStateStr = new StringJoiner(", ", "[", "]");
-        String stateFlagStr = "Unknown-" + stateFlag;
-        for (int i = 0; i < STATES.length; i++) {
-            if ((state & (i << i)) != 0) {
-                currentStateStr.add(STATES[i]);
-            }
-            if (stateFlag == (1 << i)) {
-                stateFlagStr = STATES[i] + " (" + stateFlag + ")";
-            }
-        }
-        Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding " + stateFlagStr + " to "
-                + currentStateStr);
-    }
-
     public void setGestureEndCallback(Runnable gestureEndCallback) {
         mGestureEndCallback = gestureEndCallback;
     }
 
     // Handling long swipe
-    private void onLongSwipeEnabledUi() {
-        mUiLongSwipeMode = true;
+    private void onLongSwipeEnabled() {
+        mLongSwipeMode = true;
         checkLongSwipeCanEnter();
         checkLongSwipeCanStart();
     }
 
-    private void onLongSwipeDisabledUi() {
-        mUiLongSwipeMode = false;
+    private void onLongSwipeDisabled() {
+        mLongSwipeMode = false;
         mStateCallback.clearState(STATE_SCREENSHOT_VIEW_SHOWN);
 
         if (mLongSwipeController != null) {
@@ -1472,7 +1392,7 @@
     }
 
     private void onLongSwipeDisplacementUpdated() {
-        if (!mUiLongSwipeMode || mLongSwipeController == null) {
+        if (!mLongSwipeMode || mLongSwipeController == null) {
             return;
         }
 
@@ -1480,7 +1400,7 @@
     }
 
     private void checkLongSwipeCanEnter() {
-        if (!mUiLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_ENTER_STATE)
+        if (!mLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_ENTER_STATE)
                 || !mActivityControlHelper.supportsLongSwipe(mActivity)) {
             return;
         }
@@ -1491,7 +1411,7 @@
     }
 
     private void checkLongSwipeCanStart() {
-        if (!mUiLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_START_STATE)
+        if (!mLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_START_STATE)
                 || !mActivityControlHelper.supportsLongSwipe(mActivity)) {
             return;
         }
@@ -1511,13 +1431,13 @@
         }
     }
 
-    private void onLongSwipeGestureFinishUi(float velocity, boolean isFling, float velocityX) {
-        if (!mUiLongSwipeMode || mLongSwipeController == null) {
-            mUiLongSwipeMode = false;
+    private void onLongSwipeGestureFinish(float velocity, boolean isFling, float velocityX) {
+        if (!mLongSwipeMode || mLongSwipeController == null) {
+            mLongSwipeMode = false;
             handleNormalGestureEnd(velocity, isFling, velocityX);
             return;
         }
-        mUiLongSwipeMode = false;
+        mLongSwipeMode = false;
         finishCurrentTransitionToRecents();
         mLongSwipeController.end(velocity, isFling,
                 () -> setStateOnUiThread(STATE_HANDLER_INVALIDATED));