Merge "Add scrim below TaskMenu" into sc-v2-dev
diff --git a/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
index d6160de..534f241 100644
--- a/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
+++ b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
@@ -16,7 +16,7 @@
   -->
 
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/taskbar_icon_selection_ripple">
+    android:color="@color/taskbar_nav_icon_selection_ripple">
     <item android:id="@android:id/mask">
         <shape android:shape="rectangle">
             <solid android:color="@android:color/white" />
diff --git a/quickstep/res/layout/taskbar_nav_button.xml b/quickstep/res/layout/taskbar_nav_button.xml
index 4ffb8d8..aea4885 100644
--- a/quickstep/res/layout/taskbar_nav_button.xml
+++ b/quickstep/res/layout/taskbar_nav_button.xml
@@ -15,7 +15,10 @@
 -->
 <ImageView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="@dimen/taskbar_nav_buttons_size"
     android:layout_height="@dimen/taskbar_nav_buttons_size"
     android:background="@drawable/taskbar_icon_click_feedback_roundrect"
-    android:scaleType="center"/>
\ No newline at end of file
+    android:scaleType="center"
+    android:tint="@color/taskbar_nav_icon_light_color"
+    tools:ignore="UseAppTint" />
\ No newline at end of file
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index f237d26..671a617 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -25,14 +25,14 @@
 
     <!-- Taskbar -->
     <color name="taskbar_background">@color/overview_scrim_dark</color>
-    <color name="taskbar_icon_selection_ripple">#E0E0E0</color>
-
+    <color name="taskbar_nav_icon_selection_ripple">#E0E0E0</color>
+    <color name="taskbar_nav_icon_light_color">#ffffff</color>
+    <!-- The dark navigation button color is only used in the rare cases that taskbar isn't drawing
+    its background and the underlying app has requested dark buttons. -->
+    <color name="taskbar_nav_icon_dark_color">#99000000</color>
     <color name="taskbar_stashed_handle_light_color">#EBffffff</color>
     <color name="taskbar_stashed_handle_dark_color">#99000000</color>
 
-    <color name="rotation_button_light_color">#FFF</color>
-    <color name="rotation_button_dark_color">#99000000</color>
-
     <!-- Gesture navigation tutorial -->
     <color name="gesture_tutorial_back_arrow_color">#FFFFFFFF</color>
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 4b6dacd..0565f7e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -15,11 +15,8 @@
  */
 package com.android.launcher3.taskbar;
 
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
-
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y_LONG_CLICK;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
@@ -36,11 +33,11 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
 
+import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.annotation.DrawableRes;
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
-import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -92,9 +89,8 @@
 
     private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
 
-    private View.OnLongClickListener mA11yLongClickListener;
     private final ArrayList<StatePropertyHolder> mPropertyHolders = new ArrayList<>();
-    private final ArrayList<View> mAllButtons = new ArrayList<>();
+    private final ArrayList<ImageView> mAllButtons = new ArrayList<>();
     private int mState;
 
     private final TaskbarActivityContext mContext;
@@ -103,11 +99,17 @@
     // Used for IME+A11Y buttons
     private final ViewGroup mEndContextualContainer;
     private final ViewGroup mStartContextualContainer;
+    private final int mLightIconColor;
+    private final int mDarkIconColor;
 
     private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat(
             this::updateNavButtonTranslationY);
     private final AnimatedFloat mNavButtonTranslationYMultiplier = new AnimatedFloat(
             this::updateNavButtonTranslationY);
+    private final AnimatedFloat mTaskbarNavButtonDarkIntensity = new AnimatedFloat(
+            this::updateNavButtonDarkIntensity);
+    private final AnimatedFloat mNavButtonDarkIntensityMultiplier = new AnimatedFloat(
+            this::updateNavButtonDarkIntensity);
     private final RotationButtonListener mRotationButtonListener = new RotationButtonListener();
 
     private final Rect mFloatingRotationButtonBounds = new Rect();
@@ -125,6 +127,9 @@
         mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
         mEndContextualContainer = mNavButtonsView.findViewById(R.id.end_contextual_buttons);
         mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons);
+
+        mLightIconColor = context.getColor(R.color.taskbar_nav_icon_light_color);
+        mDarkIconColor = context.getColor(R.color.taskbar_nav_icon_dark_color);
     }
 
     /**
@@ -135,11 +140,6 @@
         mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize;
         mNavButtonTranslationYMultiplier.value = 1;
 
-        mA11yLongClickListener = view -> {
-            mControllers.navButtonController.onButtonClick(BUTTON_A11Y_LONG_CLICK);
-            return true;
-        };
-
         mPropertyHolders.add(new StatePropertyHolder(
                 mControllers.taskbarViewController.getTaskbarIconAlpha()
                         .getProperty(ALPHA_INDEX_IME),
@@ -278,7 +278,6 @@
         mPropertyHolders.add(new StatePropertyHolder(mA11yButton,
                 flags -> (flags & FLAG_A11Y_VISIBLE) != 0
                         && (flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0));
-        mA11yButton.setOnLongClickListener(mA11yLongClickListener);
     }
 
     private void parseSystemUiFlags(int sysUiStateFlags) {
@@ -379,6 +378,16 @@
         return mTaskbarNavButtonTranslationY;
     }
 
+    /** Use to set the dark intensity for the all nav+contextual buttons */
+    public AnimatedFloat getTaskbarNavButtonDarkIntensity() {
+        return mTaskbarNavButtonDarkIntensity;
+    }
+
+    /** Use to determine whether to use the dark intensity requested by the underlying app */
+    public AnimatedFloat getNavButtonDarkIntensityMultiplier() {
+        return mNavButtonDarkIntensityMultiplier;
+    }
+
     /**
      * Does not call {@link #applyState()}. Don't forget to!
      */
@@ -402,6 +411,16 @@
                 * mNavButtonTranslationYMultiplier.value);
     }
 
+    private void updateNavButtonDarkIntensity() {
+        float darkIntensity = mTaskbarNavButtonDarkIntensity.value
+                * mNavButtonDarkIntensityMultiplier.value;
+        int iconColor = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, mLightIconColor,
+                mDarkIconColor);
+        for (ImageView button : mAllButtons) {
+            button.setImageTintList(ColorStateList.valueOf(iconColor));
+        }
+    }
+
     private ImageView addButton(@DrawableRes int drawableId, @TaskbarButton int buttonType,
             ViewGroup parent, TaskbarNavButtonController navButtonController, @IdRes int id) {
         return addButton(drawableId, buttonType, parent, navButtonController, id,
@@ -414,6 +433,8 @@
         ImageView buttonView = addButton(parent, id, layoutId);
         buttonView.setImageResource(drawableId);
         buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType));
+        buttonView.setOnLongClickListener(view ->
+                navButtonController.onButtonLongClick(buttonType));
         return buttonView;
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index cc83431..5354232 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -151,8 +151,8 @@
                 buttonController,
                 new NavbarButtonsViewController(this, navButtonsView),
                 new RotationButtonController(this,
-                        c.getColor(R.color.rotation_button_light_color),
-                        c.getColor(R.color.rotation_button_dark_color),
+                        c.getColor(R.color.taskbar_nav_icon_light_color),
+                        c.getColor(R.color.taskbar_nav_icon_dark_color),
                         R.drawable.ic_sysbar_rotate_button_ccw_start_0,
                         R.drawable.ic_sysbar_rotate_button_ccw_start_90,
                         R.drawable.ic_sysbar_rotate_button_cw_start_0,
@@ -394,6 +394,11 @@
         mControllers.rotationButtonController.onBehaviorChanged(displayId, behavior);
     }
 
+    public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+        mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity()
+                .updateValue(darkIntensity);
+    }
+
     /**
      * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 81039d4..248c40d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -52,6 +52,9 @@
 
     // Initialized in init.
     private TaskbarControllers mControllers;
+    private AnimatedFloat mNavButtonDarkIntensityMultiplier;
+
+    private float mLastSetBackgroundAlpha;
 
     public TaskbarDragLayerController(TaskbarActivityContext activity,
             TaskbarDragLayer taskbarDragLayer) {
@@ -65,6 +68,9 @@
         mControllers = controllers;
         mTaskbarDragLayer.init(new TaskbarDragLayerCallbacks());
 
+        mNavButtonDarkIntensityMultiplier = mControllers.navbarButtonsViewController
+                .getNavButtonDarkIntensityMultiplier();
+
         mBgTaskbar.value = 1;
         mKeyguardBgTaskbar.value = 1;
         mNotificationShadeBgTaskbar.value = 1;
@@ -114,13 +120,22 @@
         final float bgNavbar = mBgNavbar.value;
         final float bgTaskbar = mBgTaskbar.value * mKeyguardBgTaskbar.value
                 * mNotificationShadeBgTaskbar.value;
-        mTaskbarDragLayer.setTaskbarBackgroundAlpha(
-                mBgOverride.value * Math.max(bgNavbar, bgTaskbar)
-        );
+        mLastSetBackgroundAlpha = mBgOverride.value * Math.max(bgNavbar, bgTaskbar);
+        mTaskbarDragLayer.setTaskbarBackgroundAlpha(mLastSetBackgroundAlpha);
+
+        updateNavBarDarkIntensityMultiplier();
     }
 
     private void updateBackgroundOffset() {
         mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
+
+        updateNavBarDarkIntensityMultiplier();
+    }
+
+    private void updateNavBarDarkIntensityMultiplier() {
+        // Zero out the app-requested dark intensity when we're drawing our own background.
+        float effectiveBgAlpha = mLastSetBackgroundAlpha * (1 - mBgOffset.value);
+        mNavButtonDarkIntensityMultiplier.updateValue(1 - effectiveBgAlpha);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index b2b078c..6b7c597 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -42,7 +42,6 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.SettingsCache;
@@ -269,6 +268,12 @@
         }
     }
 
+    public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.onNavButtonsDarkIntensityChanged(darkIntensity);
+        }
+    }
+
     /**
      * Called when the manager is no longer needed
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index a8a0b59..ae23eda 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -16,9 +16,11 @@
 
 package com.android.launcher3.taskbar;
 
-import static android.view.Display.DEFAULT_DISPLAY;
 
-import android.view.inputmethod.InputMethodManager;
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
+
+import android.os.Bundle;
 
 import androidx.annotation.IntDef;
 
@@ -35,11 +37,9 @@
  * Controller for 3 button mode in the taskbar.
  * Handles all the functionality of the various buttons, making/routing the right calls into
  * launcher or sysui/system.
- *
- * TODO: Create callbacks to hook into UI layer since state will change for more context buttons/
- *       assistant invocation.
  */
 public class TaskbarNavButtonController {
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {
             BUTTON_BACK,
@@ -47,7 +47,6 @@
             BUTTON_RECENTS,
             BUTTON_IME_SWITCH,
             BUTTON_A11Y,
-            BUTTON_A11Y_LONG_CLICK
     })
 
     public @interface TaskbarButton {}
@@ -57,7 +56,6 @@
     static final int BUTTON_RECENTS = BUTTON_HOME << 1;
     static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
     static final int BUTTON_A11Y = BUTTON_IME_SWITCH << 1;
-    static final int BUTTON_A11Y_LONG_CLICK = BUTTON_A11Y << 1;
 
     private final TouchInteractionService mService;
 
@@ -82,9 +80,22 @@
             case BUTTON_A11Y:
                 notifyImeClick(false /* longClick */);
                 break;
-            case BUTTON_A11Y_LONG_CLICK:
+        }
+    }
+
+    public boolean onButtonLongClick(@TaskbarButton int buttonType) {
+        switch (buttonType) {
+            case BUTTON_HOME:
+                startAssistant();
+                return true;
+            case BUTTON_A11Y:
                 notifyImeClick(true /* longClick */);
-                break;
+                return true;
+            case BUTTON_BACK:
+            case BUTTON_IME_SWITCH:
+            case BUTTON_RECENTS:
+            default:
+                return false;
         }
     }
 
@@ -113,4 +124,11 @@
             systemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
         }
     }
+
+    private void startAssistant() {
+        Bundle args = new Bundle();
+        args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
+        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+        systemUiProxy.startAssistant(args);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index acb4aa8..60842c8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -282,6 +282,12 @@
             // Just hide/show the icons instead of stashing into a handle.
             mAnimator.play(mIconAlphaForStash.animateToValue(isStashed ? 0 : 1)
                     .setDuration(duration));
+            mAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mAnimator = null;
+                }
+            });
             return;
         }
 
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index c5f4a53..097850f 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -36,6 +36,7 @@
 import com.android.wm.shell.util.GroupedRecentTaskInfo;
 import com.android.wm.shell.util.StagedSplitBounds;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.function.Consumer;
@@ -219,6 +220,26 @@
         return newTasks;
     }
 
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + "RecentTasksList:");
+        writer.println(prefix + "  mChangeId=" + mChangeId);
+        writer.println(prefix + "  mResultsUi=[id=" + mResultsUi.mRequestId + ", tasks=");
+        for (GroupTask task : mResultsUi) {
+            writer.println(prefix + "    t1=" + task.task1.key.id
+                    + " t2=" + (task.hasMultipleTasks() ? task.task2.key.id : "-1"));
+        }
+        writer.println(prefix + "  ]");
+        int currentUserId = Process.myUserHandle().getIdentifier();
+        ArrayList<GroupedRecentTaskInfo> rawTasks =
+                mSysUiProxy.getRecentTasks(Integer.MAX_VALUE, currentUserId);
+        writer.println(prefix + "  rawTasks=[");
+        for (GroupedRecentTaskInfo task : rawTasks) {
+            writer.println(prefix + "    t1=" + task.mTaskInfo1.taskId
+                    + " t2=" + (task.mTaskInfo2 != null ? task.mTaskInfo2.taskId : "-1"));
+        }
+        writer.println(prefix + "  ]");
+    }
+
     private static class TaskLoadResult extends ArrayList<GroupTask> {
 
         final int mRequestId;
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index e539a8c..5d77a6e 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -43,6 +43,7 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -220,6 +221,11 @@
         mThumbnailChangeListeners.remove(listener);
     }
 
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + "RecentsModel:");
+        mTaskList.dump("  ", writer);
+    }
+
     /**
      * Listener for receiving various task properties changes
      */
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 68b7558..c8abd14 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -83,14 +83,16 @@
         MAIN_EXECUTOR.execute(() -> clearProxy());
     };
 
-    // Save the listeners passed into the proxy since when set/register these listeners,
-    // setProxy may not have been called, eg. OverviewProxyService is not connected yet.
-    private IPipAnimationListener mPendingPipAnimationListener;
-    private ISplitScreenListener mPendingSplitScreenListener;
-    private IStartingWindowListener mPendingStartingWindowListener;
-    private ISmartspaceCallback mPendingSmartspaceCallback;
-    private IRecentTasksListener mPendingRecentTasksListener;
-    private final ArrayList<RemoteTransitionCompat> mPendingRemoteTransitions = new ArrayList<>();
+    // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
+    // yet, and we'll need to set/register these listeners with SysUI when they do.  Note that it is
+    // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
+    // in case SysUI needs to rebind.
+    private IPipAnimationListener mPipAnimationListener;
+    private ISplitScreenListener mSplitScreenListener;
+    private IStartingWindowListener mStartingWindowListener;
+    private ISmartspaceCallback mSmartspaceCallback;
+    private IRecentTasksListener mRecentTasksListener;
+    private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
 
     // Used to dedupe calls to SystemUI
     private int mLastShelfHeight;
@@ -167,29 +169,23 @@
         mRecentTasks = recentTasks;
         linkToDeath();
         // re-attach the listeners once missing due to setProxy has not been initialized yet.
-        if (mPendingPipAnimationListener != null && mPip != null) {
-            setPinnedStackAnimationListener(mPendingPipAnimationListener);
-            mPendingPipAnimationListener = null;
+        if (mPipAnimationListener != null && mPip != null) {
+            setPinnedStackAnimationListener(mPipAnimationListener);
         }
-        if (mPendingSplitScreenListener != null && mSplitScreen != null) {
-            registerSplitScreenListener(mPendingSplitScreenListener);
-            mPendingSplitScreenListener = null;
+        if (mSplitScreenListener != null && mSplitScreen != null) {
+            registerSplitScreenListener(mSplitScreenListener);
         }
-        if (mPendingStartingWindowListener != null && mStartingWindow != null) {
-            setStartingWindowListener(mPendingStartingWindowListener);
-            mPendingStartingWindowListener = null;
+        if (mStartingWindowListener != null && mStartingWindow != null) {
+            setStartingWindowListener(mStartingWindowListener);
         }
-        if (mPendingSmartspaceCallback != null && mSmartspaceTransitionController != null) {
-            setSmartspaceCallback(mPendingSmartspaceCallback);
-            mPendingSmartspaceCallback = null;
+        if (mSmartspaceCallback != null && mSmartspaceTransitionController != null) {
+            setSmartspaceCallback(mSmartspaceCallback);
         }
-        for (int i = mPendingRemoteTransitions.size() - 1; i >= 0; --i) {
-            registerRemoteTransition(mPendingRemoteTransitions.get(i));
+        for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
+            registerRemoteTransition(mRemoteTransitions.get(i));
         }
-        mPendingRemoteTransitions.clear();
-        if (mPendingRecentTasksListener != null && mRecentTasks != null) {
-            registerRecentTasksListener(mPendingRecentTasksListener);
-            mPendingRecentTasksListener = null;
+        if (mRecentTasksListener != null && mRecentTasks != null) {
+            registerRecentTasksListener(mRecentTasksListener);
         }
 
         if (mPendingSetNavButtonAlpha != null) {
@@ -513,9 +509,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
             }
-        } else {
-            mPendingPipAnimationListener = listener;
         }
+        mPipAnimationListener = listener;
     }
 
     public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
@@ -553,9 +548,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call registerSplitScreenListener");
             }
-        } else {
-            mPendingSplitScreenListener = listener;
         }
+        mSplitScreenListener = listener;
     }
 
     public void unregisterSplitScreenListener(ISplitScreenListener listener) {
@@ -566,7 +560,7 @@
                 Log.w(TAG, "Failed call unregisterSplitScreenListener");
             }
         }
-        mPendingSplitScreenListener = null;
+        mSplitScreenListener = null;
     }
 
     /** Start multiple tasks in split-screen simultaneously. */
@@ -687,9 +681,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call registerRemoteTransition");
             }
-        } else {
-            mPendingRemoteTransitions.add(remoteTransition);
         }
+        mRemoteTransitions.add(remoteTransition);
     }
 
     public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
@@ -700,7 +693,7 @@
                 Log.w(TAG, "Failed call registerRemoteTransition");
             }
         }
-        mPendingRemoteTransitions.remove(remoteTransition);
+        mRemoteTransitions.remove(remoteTransition);
     }
 
     //
@@ -717,9 +710,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call setStartingWindowListener", e);
             }
-        } else {
-            mPendingStartingWindowListener = listener;
         }
+        mStartingWindowListener = listener;
     }
 
     //
@@ -733,9 +725,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call setStartingWindowListener", e);
             }
-        } else {
-            mPendingSmartspaceCallback = callback;
         }
+        mSmartspaceCallback = callback;
     }
 
     //
@@ -749,9 +740,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call registerRecentTasksListener", e);
             }
-        } else {
-            mPendingRecentTasksListener = listener;
         }
+        mRecentTasksListener = listener;
     }
 
     public void unregisterRecentTasksListener(IRecentTasksListener listener) {
@@ -762,7 +752,7 @@
                 Log.w(TAG, "Failed call unregisterRecentTasksListener");
             }
         }
-        mPendingRecentTasksListener = null;
+        mRecentTasksListener = null;
     }
 
     public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 142fafe..e77ec78 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -196,7 +196,8 @@
         boolean showAsGrid = dp.overviewShowAsGrid;
         boolean parallaxCenterAndAdjacentTask =
                 taskIndex != recentsView.getCurrentPage() && !showAsGrid;
-        int startScroll = recentsView.getScrollOffset(taskIndex);
+        int taskRectTranslationPrimary = recentsView.getScrollOffset(taskIndex);
+        int taskRectTranslationSecondary = showAsGrid ? (int) v.getGridTranslationY() : 0;
 
         RemoteTargetHandle[] topMostSimulators = null;
 
@@ -213,9 +214,10 @@
 
                 tvsLocal.fullScreenProgress.value = 0;
                 tvsLocal.recentsViewScale.value = 1;
-                tvsLocal.setScroll(startScroll);
                 tvsLocal.setIsGridTask(v.isGridTask());
-                tvsLocal.setGridTranslationY(v.getGridTranslationY());
+                tvsLocal.getOrientationState().getOrientationHandler().set(tvsLocal,
+                        TaskViewSimulator::setTaskRectTranslation, taskRectTranslationPrimary,
+                        taskRectTranslationSecondary);
 
                 // Fade in the task during the initial 20% of the animation
                 out.addFloat(targetHandle.getTransformParams(), TransformParams.TARGET_ALPHA, 0, 1,
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 377edbe..30dce61 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -289,6 +289,12 @@
                     .onSystemBarAttributesChanged(displayId, behavior));
         }
 
+        @Override
+        public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+            executeForTaskbarManager(() -> mTaskbarManager
+                    .onNavButtonsDarkIntensityChanged(darkIntensity));
+        }
+
         private void executeForTaskbarManager(final Runnable r) {
             MAIN_EXECUTOR.execute(() -> {
                 if (mTaskbarManager == null) {
@@ -970,6 +976,7 @@
             pw.println("  resumed=" + resumed);
             pw.println("  mConsumer=" + mConsumer.getName());
             ActiveGestureLog.INSTANCE.dump("", pw);
+            RecentsModel.INSTANCE.get(this).dump("", pw);
             pw.println("ProtoTrace:");
             pw.println("  file=" + ProtoTracer.INSTANCE.get(this).getTraceFile());
         }
diff --git a/quickstep/src/com/android/quickstep/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java
index 184ab17..e290be8 100644
--- a/quickstep/src/com/android/quickstep/ViewUtils.java
+++ b/quickstep/src/com/android/quickstep/ViewUtils.java
@@ -15,8 +15,10 @@
  */
 package com.android.quickstep;
 
+import android.graphics.HardwareRenderer;
 import android.os.Handler;
 import android.view.View;
+import android.view.ViewRootImpl;
 
 import com.android.launcher3.Utilities;
 import com.android.systemui.shared.system.ViewRootImplCompat;
@@ -45,9 +47,9 @@
         return new FrameHandler(view, onFinishRunnable, canceled).schedule();
     }
 
-    private static class FrameHandler implements LongConsumer {
+    private static class FrameHandler implements HardwareRenderer.FrameDrawingCallback {
 
-        final ViewRootImplCompat mViewRoot;
+        final ViewRootImpl mViewRoot;
         final Runnable mFinishCallback;
         final BooleanSupplier mCancelled;
         final Handler mHandler;
@@ -55,14 +57,14 @@
         int mDeferFrameCount = 1;
 
         FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) {
-            mViewRoot = new ViewRootImplCompat(view);
+            mViewRoot = view.getViewRootImpl();
             mFinishCallback = finishCallback;
             mCancelled = cancelled;
             mHandler = new Handler();
         }
 
         @Override
-        public void accept(long l) {
+        public void onFrameDraw(long frame) {
             Utilities.postAsyncCallback(mHandler, this::onFrame);
         }
 
@@ -83,7 +85,7 @@
         }
 
         private boolean schedule() {
-            if (mViewRoot.isValid()) {
+            if (mViewRoot.getView() != null) {
                 mViewRoot.registerRtFrameCallback(this);
                 mViewRoot.getView().invalidate();
                 return true;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
index 510820a..162ace4 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
@@ -24,6 +24,8 @@
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_GESTURE;
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
 import static com.android.launcher3.Utilities.squaredHypot;
 
 import android.animation.Animator;
@@ -64,8 +66,6 @@
     private static final String OPA_BUNDLE_TRIGGER = "triggered_by";
     // From //java/com/google/android/apps/gsa/assistant/shared/proto/opa_trigger.proto.
     private static final int OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE = 83;
-    private static final String INVOCATION_TYPE_KEY = "invocation_type";
-    private static final int INVOCATION_TYPE_GESTURE = 1;
 
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 3b4fd31..3b1c150 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -22,10 +22,10 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
+import android.view.ViewRootImpl;
 
 import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-import com.android.systemui.shared.system.ViewRootImplCompat;
 
 import java.util.function.Consumer;
 
@@ -41,7 +41,7 @@
     private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
 
     private final SurfaceControl mBarrierSurfaceControl;
-    private final ViewRootImplCompat mTargetViewRootImpl;
+    private final ViewRootImpl mTargetViewRootImpl;
     private final Handler mApplyHandler;
 
     private int mLastSequenceNumber = 0;
@@ -50,8 +50,8 @@
      * @param targetView The view in the surface that acts as synchronization anchor.
      */
     public SurfaceTransactionApplier(View targetView) {
-        mTargetViewRootImpl = new ViewRootImplCompat(targetView);
-        mBarrierSurfaceControl = mTargetViewRootImpl.getRenderSurfaceControl();
+        mTargetViewRootImpl = targetView.getViewRootImpl();
+        mBarrierSurfaceControl = mTargetViewRootImpl.getSurfaceControl();
         mApplyHandler = new Handler(this::onApplyMessage);
     }
 
@@ -109,7 +109,7 @@
         if (targetView == null) {
             // No target view, no applier
             callback.accept(null);
-        } else if (new ViewRootImplCompat(targetView).isValid()) {
+        } else if (targetView.isAttachedToWindow()) {
             // Already attached, we're good to go
             callback.accept(new SurfaceTransactionApplier(targetView));
         } else {
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 7d396ba..f676091 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -102,7 +102,8 @@
     private StagedSplitBounds mStagedSplitBounds;
     private boolean mDrawsBelowRecents;
     private boolean mIsGridTask;
-    private float mGridTranslationY;
+    private int mTaskRectTranslationX;
+    private int mTaskRectTranslationY;
 
     public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
         mContext = context;
@@ -157,15 +158,11 @@
             fullTaskSize = new Rect(mTaskRect);
             mOrientationState.getOrientationHandler()
                     .setSplitTaskSwipeRect(mDp, mTaskRect, mStagedSplitBounds, mStagePosition);
-            if (mIsGridTask) {
-                mTaskRect.offset(0, (int) mGridTranslationY);
-            }
+            mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
         } else {
             fullTaskSize = mTaskRect;
         }
-        if (mIsGridTask) {
-            fullTaskSize.offset(0, (int) mGridTranslationY);
-        }
+        fullTaskSize.offset(mTaskRectTranslationX, mTaskRectTranslationY);
         return mOrientationState.getFullScreenScaleAndPivot(fullTaskSize, mDp, mPivot);
     }
 
@@ -225,10 +222,11 @@
     }
 
     /**
-     * Sets the y-translation when overview is in grid.
+     * Apply translations on TaskRect's starting location.
      */
-    public void setGridTranslationY(float gridTranslationY) {
-        mGridTranslationY = gridTranslationY;
+    public void setTaskRectTranslation(int taskRectTranslationX, int taskRectTranslationY) {
+        mTaskRectTranslationX = taskRectTranslationX;
+        mTaskRectTranslationY = taskRectTranslationY;
     }
 
     /**
@@ -336,19 +334,19 @@
 
         // Apply TaskView matrix: taskRect, translate
         mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
-        mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+        mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE,
                 taskPrimaryTranslation.value);
         mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
                 taskSecondaryTranslation.value);
+        mOrientationState.getOrientationHandler().setPrimary(
+                mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value);
 
         // Apply RecentsView matrix
         mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);
         mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
                 recentsViewSecondaryTranslation.value);
-        mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+        mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE,
                 recentsViewPrimaryTranslation.value);
-        mOrientationState.getOrientationHandler().set(
-                mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value);
         applyWindowToHomeRotation(mMatrix);
 
         // Crop rect is the inverse of thumbnail matrix
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index c14cead..4b9b8a4 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -747,7 +747,7 @@
 
             int primarySize = mOrientationHandler.getPrimaryValue(getWidth(), getHeight());
             int scroll = OverScroll.dampedScroll(getUndampedOverScrollShift(), primarySize);
-            mOrientationHandler.set(canvas, CANVAS_TRANSLATE, scroll);
+            mOrientationHandler.setPrimary(canvas, CANVAS_TRANSLATE, scroll);
 
             if (mOverScrollShift != scroll) {
                 mOverScrollShift = scroll;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index cefadf7..1ce7ebe 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -252,7 +252,7 @@
         if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
             newPosition = getScrollForPage(mCurrentPage) + mCurrentPageScrollDiff;
         }
-        mOrientationHandler.set(this, VIEW_SCROLL_TO, newPosition);
+        mOrientationHandler.setPrimary(this, VIEW_SCROLL_TO, newPosition);
         mScroller.startScroll(mScroller.getCurrX(), 0, newPosition - mScroller.getCurrX(), 0);
         forceFinishScroller();
     }
@@ -556,7 +556,7 @@
             int oldPos = mOrientationHandler.getPrimaryScroll(this);
             int newPos = mScroller.getCurrX();
             if (oldPos != newPos) {
-                mOrientationHandler.set(this, VIEW_SCROLL_TO, mScroller.getCurrX());
+                mOrientationHandler.setPrimary(this, VIEW_SCROLL_TO, mScroller.getCurrX());
             }
 
             if (mAllowOverScroll) {
@@ -1280,7 +1280,7 @@
                 mLastMotionRemainder = delta - movedDelta;
 
                 if (delta != 0) {
-                    mOrientationHandler.set(this, VIEW_SCROLL_BY, movedDelta);
+                    mOrientationHandler.setPrimary(this, VIEW_SCROLL_BY, movedDelta);
 
                     if (mAllowOverScroll) {
                         final float pulledToX = oldScroll + delta;
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 498f6db..e127074 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -101,12 +101,12 @@
     }
 
     @Override
-    public <T> void set(T target, Int2DAction<T> action, int param) {
+    public <T> void setPrimary(T target, Int2DAction<T> action, int param) {
         action.call(target, 0, param);
     }
 
     @Override
-    public <T> void set(T target, Float2DAction<T> action, float param) {
+    public <T> void setPrimary(T target, Float2DAction<T> action, float param) {
         action.call(target, 0, param);
     }
 
@@ -116,6 +116,12 @@
     }
 
     @Override
+    public <T> void set(T target, Int2DAction<T> action, int primaryParam,
+            int secondaryParam) {
+        action.call(target, secondaryParam, primaryParam);
+    }
+
+    @Override
     public float getPrimaryDirection(MotionEvent event, int pointerIndex) {
         return event.getY(pointerIndex);
     }
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 95336cd..d954552 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -65,9 +65,10 @@
     Float2DAction<Canvas> CANVAS_TRANSLATE = Canvas::translate;
     Float2DAction<Matrix> MATRIX_POST_TRANSLATE = Matrix::postTranslate;
 
-    <T> void set(T target, Int2DAction<T> action, int param);
-    <T> void set(T target, Float2DAction<T> action, float param);
+    <T> void setPrimary(T target, Int2DAction<T> action, int param);
+    <T> void setPrimary(T target, Float2DAction<T> action, float param);
     <T> void setSecondary(T target, Float2DAction<T> action, float param);
+    <T> void set(T target, Int2DAction<T> action, int primaryParam, int secondaryParam);
     float getPrimaryDirection(MotionEvent event, int pointerIndex);
     float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId);
     int getMeasuredSize(View view);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 835c240..fbc335c 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -101,12 +101,12 @@
     }
 
     @Override
-    public <T> void set(T target, Int2DAction<T> action, int param) {
+    public <T> void setPrimary(T target, Int2DAction<T> action, int param) {
         action.call(target, param, 0);
     }
 
     @Override
-    public <T> void set(T target, Float2DAction<T> action, float param) {
+    public <T> void setPrimary(T target, Float2DAction<T> action, float param) {
         action.call(target, param, 0);
     }
 
@@ -116,6 +116,12 @@
     }
 
     @Override
+    public <T> void set(T target, Int2DAction<T> action, int primaryParam,
+            int secondaryParam) {
+        action.call(target, primaryParam, secondaryParam);
+    }
+
+    @Override
     public float getPrimaryDirection(MotionEvent event, int pointerIndex) {
         return event.getX(pointerIndex);
     }
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 2068c29..c050c6c 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -261,6 +261,13 @@
             PortraitSize realSize = new PortraitSize(newInfo.currentSize.x, newInfo.currentSize.y);
             PortraitSize expectedSize = oldInfo.mInternalDisplays.get(
                     ApiWrapper.getUniqueId(display));
+            if (newInfo.supportedBounds.size() != oldInfo.supportedBounds.size()) {
+                Log.e("b/198965093",
+                        "Inconsistent number of displays"
+                                + "\ndisplay state: " + display.getState()
+                                + "\noldInfo.supportedBounds: " + oldInfo.supportedBounds
+                                + "\nnewInfo.supportedBounds: " + newInfo.supportedBounds);
+            }
             if (!realSize.equals(expectedSize) && display.getState() == Display.STATE_OFF) {
                 Log.e("b/198965093", "Display size changed while display is off, ignoring change");
                 return;
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 44f2719..19dca45 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.system.OsConstants;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -83,6 +84,7 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -327,7 +329,12 @@
      */
     protected <T> T getOnUiThread(final Callable<T> callback) {
         try {
-            return mMainThreadExecutor.submit(callback).get();
+            return mMainThreadExecutor.submit(callback).get(DEFAULT_UI_TIMEOUT,
+                    TimeUnit.MILLISECONDS);
+        } catch (TimeoutException e) {
+            Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e);
+            Process.sendSignal(Process.myPid(), OsConstants.SIGABRT);
+            throw new RuntimeException(e);
         } catch (Throwable e) {
             throw new RuntimeException(e);
         }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 2fbe460..3485dd1 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -544,11 +544,11 @@
                     : TestHelpers.getSystemHealthMessage(getContext(), mTestStartTime);
 
             if (systemHealth != null) {
-                return message
-                        + ";\nPerhaps linked to system health problems:\n<<<<<<<<<<<<<<<<<<\n"
+                message += ";\nPerhaps linked to system health problems:\n<<<<<<<<<<<<<<<<<<\n"
                         + systemHealth + "\n>>>>>>>>>>>>>>>>>>";
             }
         }
+        Log.d(TAG, "About to throw the error: " + message, new Exception());
         return message;
     }