Jank: taking screenshot in helper thread for quick switch

This operation is prone to lock contention.

Test: Manual
Bug: 173640772, 173072373
Change-Id: I86088255fa0cd8b369d9efec53fdac81210e71db
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 4dbc0f3..6cac7f9 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -33,6 +33,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
 import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
 import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
@@ -1395,34 +1396,55 @@
             if (mRecentsAnimationController != null) {
                 // Update the screenshot of the task
                 if (mTaskSnapshot == null) {
-                    mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId);
+                    UI_HELPER_EXECUTOR.execute(() -> {
+                        final ThumbnailData taskSnapshot =
+                                mRecentsAnimationController.screenshotTask(runningTaskId);
+                        MAIN_EXECUTOR.execute(() -> {
+                            mTaskSnapshot = taskSnapshot;
+                            if (!updateThumbnail(runningTaskId)) {
+                                setScreenshotCapturedState();
+                            }
+                        });
+                    });
+                    return;
                 }
-                final TaskView taskView;
-                if (mGestureState.getEndTarget() == HOME) {
-                    // Capture the screenshot before finishing the transition to home to ensure it's
-                    // taken in the correct orientation, but no need to update the thumbnail.
-                    taskView = null;
-                } else {
-                    taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot);
-                }
-                if (taskView != null && !mCanceled) {
-                    // Defer finishing the animation until the next launcher frame with the
-                    // new thumbnail
-                    finishTransitionPosted = ViewUtils.postFrameDrawn(taskView,
-                            () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
-                            this::isCanceled);
-                }
+                finishTransitionPosted = updateThumbnail(runningTaskId);
             }
             if (!finishTransitionPosted) {
-                // If we haven't posted a draw callback, set the state immediately.
-                Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
-                        TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
-                mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
-                TraceHelper.INSTANCE.endSection(traceToken);
+                setScreenshotCapturedState();
             }
         }
     }
 
+    // Returns whether finish transition was posted.
+    private boolean updateThumbnail(int runningTaskId) {
+        boolean finishTransitionPosted = false;
+        final TaskView taskView;
+        if (mGestureState.getEndTarget() == HOME) {
+            // Capture the screenshot before finishing the transition to home to ensure it's
+            // taken in the correct orientation, but no need to update the thumbnail.
+            taskView = null;
+        } else {
+            taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot);
+        }
+        if (taskView != null && !mCanceled) {
+            // Defer finishing the animation until the next launcher frame with the
+            // new thumbnail
+            finishTransitionPosted = ViewUtils.postFrameDrawn(taskView,
+                    () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
+                    this::isCanceled);
+        }
+        return finishTransitionPosted;
+    }
+
+    private void setScreenshotCapturedState() {
+        // If we haven't posted a draw callback, set the state immediately.
+        Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
+                TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
+        mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+        TraceHelper.INSTANCE.endSection(traceToken);
+    }
+
     private void finishCurrentTransitionToRecents() {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);