Merge "Fix two bugs with rotation in split select state" into tm-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 03caf90..9a1ed4d 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -122,8 +122,6 @@
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.StaggeredWorkspaceAnim;
-import com.android.quickstep.util.SurfaceTransaction;
-import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.WorkspaceRevealAnim;
 import com.android.quickstep.views.FloatingWidgetView;
@@ -136,6 +134,7 @@
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.RemoteTransitionCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 import com.android.wm.shell.startingsurface.IStartingWindowListener;
 
 import java.util.ArrayList;
@@ -814,11 +813,10 @@
                     return;
                 }
 
-                SurfaceTransaction transaction = new SurfaceTransaction();
-
+                ArrayList<SurfaceParams> params = new ArrayList<>();
                 for (int i = appTargets.length - 1; i >= 0; i--) {
                     RemoteAnimationTargetCompat target = appTargets[i];
-                    SurfaceProperties builder = transaction.forSurface(target.leash);
+                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
 
                     if (target.mode == MODE_OPENING) {
                         matrix.setScale(scale, scale);
@@ -839,11 +837,11 @@
 
                         floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,
                                 mWindowRadius.value * scale, true /* isOpening */);
-                        builder.setMatrix(matrix)
-                                .setWindowCrop(crop)
-                                .setAlpha(1f - mIconAlpha.value)
-                                .setCornerRadius(mWindowRadius.value)
-                                .setShadowRadius(mShadowRadius.value);
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(1f - mIconAlpha.value)
+                                .withCornerRadius(mWindowRadius.value)
+                                .withShadowRadius(mShadowRadius.value);
                     } else if (target.mode == MODE_CLOSING) {
                         if (target.localBounds != null) {
                             final Rect localBounds = target.localBounds;
@@ -863,26 +861,29 @@
                             tmpPos.y = tmp;
                         }
                         matrix.setTranslate(tmpPos.x, tmpPos.y);
-                        builder.setMatrix(matrix)
-                                .setWindowCrop(crop)
-                                .setAlpha(1f);
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(1f);
                     }
+                    params.add(builder.build());
                 }
 
                 if (navBarTarget != null) {
-                    SurfaceProperties navBuilder =
-                            transaction.forSurface(navBarTarget.leash);
+                    final SurfaceParams.Builder navBuilder =
+                            new SurfaceParams.Builder(navBarTarget.leash);
                     if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
                         matrix.setScale(scale, scale);
                         matrix.postTranslate(windowTransX0, windowTransY0);
-                        navBuilder.setMatrix(matrix)
-                                .setWindowCrop(crop)
-                                .setAlpha(mNavFadeIn.value);
+                        navBuilder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(mNavFadeIn.value);
                     } else {
-                        navBuilder.setAlpha(mNavFadeOut.value);
+                        navBuilder.withAlpha(mNavFadeOut.value);
                     }
+                    params.add(navBuilder.build());
                 }
-                surfaceApplier.scheduleApply(transaction);
+
+                surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
             }
         };
         appAnimator.addUpdateListener(listener);
@@ -998,33 +999,37 @@
                 matrix.postScale(mAppWindowScale, mAppWindowScale, widgetBackgroundBounds.left,
                         widgetBackgroundBounds.top);
 
-                SurfaceTransaction transaction = new SurfaceTransaction();
+                ArrayList<SurfaceParams> params = new ArrayList<>();
                 float floatingViewAlpha = appTargetsAreTranslucent ? 1 - mPreviewAlpha.value : 1;
                 for (int i = appTargets.length - 1; i >= 0; i--) {
                     RemoteAnimationTargetCompat target = appTargets[i];
-                    SurfaceProperties builder = transaction.forSurface(target.leash);
+                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
                     if (target.mode == MODE_OPENING) {
                         floatingView.update(widgetBackgroundBounds, floatingViewAlpha,
                                 mWidgetForegroundAlpha.value, mWidgetFallbackBackgroundAlpha.value,
                                 mCornerRadiusProgress.value);
-                        builder.setMatrix(matrix)
-                                .setWindowCrop(appWindowCrop)
-                                .setAlpha(mPreviewAlpha.value)
-                                .setCornerRadius(mWindowRadius.value / mAppWindowScale);
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(appWindowCrop)
+                                .withAlpha(mPreviewAlpha.value)
+                                .withCornerRadius(mWindowRadius.value / mAppWindowScale);
                     }
+                    params.add(builder.build());
                 }
 
                 if (navBarTarget != null) {
-                    SurfaceProperties navBuilder = transaction.forSurface(navBarTarget.leash);
+                    final SurfaceParams.Builder navBuilder =
+                            new SurfaceParams.Builder(navBarTarget.leash);
                     if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
-                        navBuilder.setMatrix(matrix)
-                                .setWindowCrop(appWindowCrop)
-                                .setAlpha(mNavFadeIn.value);
+                        navBuilder.withMatrix(matrix)
+                                .withWindowCrop(appWindowCrop)
+                                .withAlpha(mNavFadeIn.value);
                     } else {
-                        navBuilder.setAlpha(mNavFadeOut.value);
+                        navBuilder.withAlpha(mNavFadeOut.value);
                     }
+                    params.add(navBuilder.build());
                 }
-                surfaceApplier.scheduleApply(transaction);
+
+                surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
             }
         });
 
@@ -1219,15 +1224,16 @@
         unlockAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                SurfaceTransaction transaction = new SurfaceTransaction();
+                SurfaceParams[] params = new SurfaceParams[appTargets.length];
                 for (int i = appTargets.length - 1; i >= 0; i--) {
                     RemoteAnimationTargetCompat target = appTargets[i];
-                    transaction.forSurface(target.leash)
-                            .setAlpha(1f)
-                            .setWindowCrop(target.screenSpaceBounds)
-                            .setCornerRadius(cornerRadius);
+                    params[i] = new SurfaceParams.Builder(target.leash)
+                            .withAlpha(1f)
+                            .withWindowCrop(target.screenSpaceBounds)
+                            .withCornerRadius(cornerRadius)
+                            .build();
                 }
-                surfaceApplier.scheduleApply(transaction);
+                surfaceApplier.scheduleApply(params);
             }
         });
         return unlockAnimator;
@@ -1445,10 +1451,10 @@
 
             @Override
             public void onUpdate(float percent, boolean initOnly) {
-                SurfaceTransaction transaction = new SurfaceTransaction();
+                SurfaceParams[] params = new SurfaceParams[appTargets.length];
                 for (int i = appTargets.length - 1; i >= 0; i--) {
                     RemoteAnimationTargetCompat target = appTargets[i];
-                    SurfaceProperties builder = transaction.forSurface(target.leash);
+                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
 
                     if (target.localBounds != null) {
                         tmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1470,19 +1476,20 @@
                                 tmpRect.centerY());
                         matrix.postTranslate(0, mDy.value);
                         matrix.postTranslate(tmpPos.x, tmpPos.y);
-                        builder.setMatrix(matrix)
-                                .setWindowCrop(crop)
-                                .setAlpha(mAlpha.value)
-                                .setCornerRadius(windowCornerRadius)
-                                .setShadowRadius(mShadowRadius.value);
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(mAlpha.value)
+                                .withCornerRadius(windowCornerRadius)
+                                .withShadowRadius(mShadowRadius.value);
                     } else if (target.mode == MODE_OPENING) {
                         matrix.setTranslate(tmpPos.x, tmpPos.y);
-                        builder.setMatrix(matrix)
-                                .setWindowCrop(crop)
-                                .setAlpha(1f);
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(1f);
                     }
+                    params[i] = builder.build();
                 }
-                surfaceApplier.scheduleApply(transaction);
+                surfaceApplier.scheduleApply(params);
             }
         });
 
@@ -1852,10 +1859,10 @@
 
         @Override
         public void onUpdate(RectF currentRectF, float progress) {
-            SurfaceTransaction transaction = new SurfaceTransaction();
+            SurfaceParams[] params = new SurfaceParams[mAppTargets.length];
             for (int i = mAppTargets.length - 1; i >= 0; i--) {
                 RemoteAnimationTargetCompat target = mAppTargets[i];
-                SurfaceProperties builder = transaction.forSurface(target.leash);
+                SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
 
                 if (target.localBounds != null) {
                     mTmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1890,17 +1897,18 @@
                     mMatrix.setScale(scale, scale);
                     mMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
 
-                    builder.setMatrix(mMatrix)
-                            .setWindowCrop(mTmpRect)
-                            .setAlpha(getWindowAlpha(progress))
-                            .setCornerRadius(getCornerRadius(progress) / scale);
+                    builder.withMatrix(mMatrix)
+                            .withWindowCrop(mTmpRect)
+                            .withAlpha(getWindowAlpha(progress))
+                            .withCornerRadius(getCornerRadius(progress) / scale);
                 } else if (target.mode == MODE_OPENING) {
                     mMatrix.setTranslate(mTmpPos.x, mTmpPos.y);
-                    builder.setMatrix(mMatrix)
-                            .setAlpha(1f);
+                    builder.withMatrix(mMatrix)
+                            .withAlpha(1f);
                 }
+                params[i] = builder.build();
             }
-            mSurfaceApplier.scheduleApply(transaction);
+            mSurfaceApplier.scheduleApply(params);
         }
 
         protected float getWindowAlpha(float progress) {
diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
index a0cf6cb..9a682c6 100644
--- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
+++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
@@ -15,14 +15,16 @@
  */
 package com.android.launcher3.popup;
 
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
+
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
 import android.util.Log;
 import android.view.View;
 
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
@@ -70,9 +72,10 @@
             }
 
             RecentsView recentsView = mTarget.getOverviewPanel();
+            StatsLogManager.EventEnum splitEvent = getLogEventForPosition(mPosition.stagePosition);
             recentsView.initiateSplitSelect(
                     new SplitSelectSource(mOriginalView, new BitmapDrawable(bitmap), intent,
-                            mPosition, mItemInfo.user));
+                            mPosition, mItemInfo, splitEvent));
         }
     }
 
@@ -82,15 +85,18 @@
         public final Drawable drawable;
         public final Intent intent;
         public final SplitPositionOption position;
-        public final UserHandle user;
+        public final ItemInfo mItemInfo;
+        public final StatsLogManager.EventEnum splitEvent;
 
         public SplitSelectSource(View view, Drawable drawable, Intent intent,
-                SplitPositionOption position, UserHandle user) {
+                SplitPositionOption position, ItemInfo itemInfo,
+                StatsLogManager.EventEnum splitEvent) {
             this.view = view;
             this.drawable = drawable;
             this.intent = intent;
             this.position = position;
-            this.user = user;
+            this.mItemInfo = itemInfo;
+            this.splitEvent = splitEvent;
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 97029fe..9a1e064 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -32,6 +32,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
+import android.util.Pair;
 import android.view.DragEvent;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
@@ -42,7 +43,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.InstanceId;
-import com.android.internal.logging.InstanceIdSequence;
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DragSource;
@@ -69,6 +69,7 @@
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.quickstep.util.LogUtils;
 import com.android.systemui.shared.recents.model.Task;
 
 import java.io.PrintWriter;
@@ -359,11 +360,11 @@
         }
 
         if (clipDescription != null && intent != null) {
+            Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+                    LogUtils.getShellShareableInstanceId();
             // Need to share the same InstanceId between launcher3 and WM Shell (internal).
-            InstanceId internalInstanceId = new InstanceIdSequence(
-                    com.android.launcher3.logging.InstanceId.INSTANCE_ID_MAX).newInstanceId();
-            com.android.launcher3.logging.InstanceId launcherInstanceId =
-                    new com.android.launcher3.logging.InstanceId(internalInstanceId.getId());
+            InstanceId internalInstanceId = instanceIds.first;
+            com.android.launcher3.logging.InstanceId launcherInstanceId = instanceIds.second;
 
             intent.putExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID, internalInstanceId);
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 7b4501a..49d0873 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -15,14 +15,20 @@
  */
 package com.android.launcher3.taskbar;
 
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
+
+import android.content.ClipDescription;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.graphics.Point;
+import android.os.Bundle;
+import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.logging.InstanceId;
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.LauncherSettings;
@@ -47,6 +53,7 @@
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.LogUtils;
 
 import java.io.PrintWriter;
 import java.util.HashMap;
@@ -263,8 +270,14 @@
 
         @Override
         public void onClick(View view) {
-            AbstractFloatingView.closeAllOpenViews(mTarget);
+            Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+                    LogUtils.getShellShareableInstanceId();
+            mTarget.getStatsLogManager().logger()
+                    .withItemInfo(mItemInfo)
+                    .withInstanceId(instanceIds.second)
+                    .log(getLogEventForPosition(mPosition.stagePosition));
 
+            AbstractFloatingView.closeAllOpenViews(mTarget);
             if (mItemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                 WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo;
                 SystemUiProxy.INSTANCE.get(mTarget).startShortcut(
@@ -272,7 +285,8 @@
                         workspaceItemInfo.getDeepShortcutId(),
                         mPosition.stagePosition,
                         null,
-                        workspaceItemInfo.user);
+                        workspaceItemInfo.user,
+                        instanceIds.first);
             } else {
                 SystemUiProxy.INSTANCE.get(mTarget).startIntent(
                         mTarget.getSystemService(LauncherApps.class).getMainActivityLaunchIntent(
@@ -281,7 +295,8 @@
                                 mItemInfo.user),
                         new Intent(),
                         mPosition.stagePosition,
-                        null);
+                        null,
+                        instanceIds.first);
             }
         }
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
index f131595..c10b57a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
@@ -17,24 +17,29 @@
 
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DEEP_SHORTCUTS;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.SHORTCUTS_AND_NOTIFICATIONS;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
 
 import android.content.Intent;
 import android.content.pm.LauncherApps;
+import android.util.Pair;
 import android.view.KeyEvent;
 import android.view.View;
 
+import com.android.internal.logging.InstanceId;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.LogUtils;
 
 import java.util.List;
 
@@ -49,10 +54,12 @@
     public static final int MOVE_TO_BOTTOM_OR_RIGHT = R.id.action_move_to_bottom_or_right;
 
     private final LauncherApps mLauncherApps;
+    private final StatsLogManager mStatsLogManager;
 
     public TaskbarShortcutMenuAccessibilityDelegate(TaskbarActivityContext context) {
         super(context);
         mLauncherApps = context.getSystemService(LauncherApps.class);
+        mStatsLogManager = context.getStatsLogManager();
 
         mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS,
                 R.string.action_deep_shortcut, KeyEvent.KEYCODE_S));
@@ -82,7 +89,14 @@
                 && (action == MOVE_TO_TOP_OR_LEFT || action == MOVE_TO_BOTTOM_OR_RIGHT)) {
             WorkspaceItemInfo info = (WorkspaceItemInfo) item;
             int side = action == MOVE_TO_TOP_OR_LEFT
-                    ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+                    ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
+
+            Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+                    LogUtils.getShellShareableInstanceId();
+            mStatsLogManager.logger()
+                    .withItemInfo(item)
+                    .withInstanceId(instanceIds.second)
+                    .log(getLogEventForPosition(side));
 
             if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                 SystemUiProxy.INSTANCE.get(mContext).startShortcut(
@@ -90,14 +104,15 @@
                         info.getDeepShortcutId(),
                         side,
                         /* bundleOpts= */ null,
-                        info.user);
+                        info.user,
+                        instanceIds.first);
             } else {
                 SystemUiProxy.INSTANCE.get(mContext).startIntent(
                         mLauncherApps.getMainActivityLaunchIntent(
                                 item.getIntent().getComponent(),
                                 /* startActivityOptions= */null,
                                 item.user),
-                        new Intent(), side, null);
+                        new Intent(), side, null, instanceIds.first);
             }
             return true;
         } else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index fea127a..1316ddd 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -121,7 +121,6 @@
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.TouchController;
-import com.android.launcher3.util.ViewCapture;
 import com.android.launcher3.widget.LauncherAppWidgetHost;
 import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.RecentsModel;
@@ -135,6 +134,7 @@
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
 import com.android.quickstep.util.SplitSelectStateController;
 import com.android.quickstep.util.TISBindHelper;
+import com.android.quickstep.util.ViewCapture;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -191,7 +191,7 @@
         RecentsView overviewPanel = (RecentsView) getOverviewPanel();
         SplitSelectStateController controller =
                 new SplitSelectStateController(this, mHandler, getStateManager(),
-                        getDepthController());
+                        getDepthController(), getStatsLogManager());
         overviewPanel.init(mActionsView, controller);
         mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
         mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
@@ -586,6 +586,8 @@
     public void onWidgetsTransition(float progress) {
         super.onWidgetsTransition(progress);
         onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX);
+        // Change of wallpaper depth in widget picker is disabled for tests as it causes flakiness
+        // on very slow cuttlefish devices.
         if (ENABLE_WIDGET_PICKER_DEPTH.get() && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
             WIDGET_DEPTH.set(getDepthController(),
                     Utilities.mapToRange(progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth,
@@ -898,8 +900,8 @@
             outState.putIBinder(PENDING_SPLIT_SELECT_INFO, ObjectWrapper.wrap(
                     new PendingSplitSelectInfo(
                             splitSelectStateController.getInitialTaskId(),
-                            splitSelectStateController.getActiveSplitStagePosition()
-                    )
+                            splitSelectStateController.getActiveSplitStagePosition(),
+                            splitSelectStateController.getSplitEvent())
             ));
             outState.putInt(RUNTIME_STATE, OVERVIEW.ordinal);
         }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index a580b42..4e635a7 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -50,7 +50,6 @@
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
 import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -827,10 +826,6 @@
     public void onRecentsAnimationStart(RecentsAnimationController controller,
             RecentsAnimationTargets targets) {
         super.onRecentsAnimationStart(controller, targets);
-        ActiveGestureLog.INSTANCE.addLog(
-                /* event= */ "startRecentsAnimationCallback",
-                /* extras= */ targets.apps.length,
-                /* gestureEvent= */ START_RECENTS_ANIMATION);
         mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets);
         mRecentsAnimationController = controller;
         mRecentsAnimationTargets = targets;
@@ -1449,6 +1444,7 @@
         }
     }
 
+    @Nullable
     private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory,
             RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
         // Directly animate the app to PiP (picture-in-picture) mode
@@ -1474,6 +1470,10 @@
                         runningTaskTarget.taskInfo.pictureInPictureParams,
                         homeRotation,
                         hotseatKeepClearArea);
+        if (destinationBounds == null) {
+            // No destination bounds returned from SystemUI, bail early.
+            return null;
+        }
         final Rect appBounds = new Rect();
         final WindowConfiguration winConfig = taskInfo.configuration.windowConfiguration;
         // Adjust the appBounds for TaskBar by using the calculated window crop Rect
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index e55e966..99f7bdd 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -65,12 +65,12 @@
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.util.TransformParams.BuilderProxy;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -125,18 +125,18 @@
         }
     }
 
-    private void updateHomeActivityTransformDuringSwipeUp(SurfaceProperties builder,
+    private void updateHomeActivityTransformDuringSwipeUp(SurfaceParams.Builder builder,
             RemoteAnimationTargetCompat app, TransformParams params) {
         setHomeScaleAndAlpha(builder, app, mCurrentShift.value,
                 Utilities.boundToRange(1 - mCurrentShift.value, 0, 1));
     }
 
-    private void setHomeScaleAndAlpha(SurfaceProperties builder,
+    private void setHomeScaleAndAlpha(SurfaceParams.Builder builder,
             RemoteAnimationTargetCompat app, float verticalShift, float alpha) {
         float scale = Utilities.mapRange(verticalShift, 1, mMaxLauncherScale);
         mTmpMatrix.setScale(scale, scale,
                 app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
-        builder.setMatrix(mTmpMatrix).setAlpha(alpha);
+        builder.withMatrix(mTmpMatrix).withAlpha(alpha);
     }
 
     @Override
@@ -279,12 +279,12 @@
             return mTargetRect;
         }
 
-        private void updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder,
+        private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
                 RemoteAnimationTargetCompat app, TransformParams params) {
-            builder.setAlpha(mRecentsAlpha.value);
+            builder.withAlpha(mRecentsAlpha.value);
         }
 
-        private void updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder,
+        private void updateHomeActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
                 RemoteAnimationTargetCompat app, TransformParams params) {
             setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value);
         }
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index ee3b075..7a281dd 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -46,6 +46,7 @@
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 
 /**
  * Controls the animation of swiping back and returning to launcher.
@@ -241,17 +242,20 @@
 
     /** Transform the target window to match the target rect. */
     private void applyTransform(RectF targetRect, float cornerRadius) {
+        SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
+                new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
         final float scale = targetRect.width() / mStartRect.width();
         mTransformMatrix.reset();
         mTransformMatrix.setScale(scale, scale);
         mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
+        builder.withMatrix(mTransformMatrix)
+                .withWindowCrop(mStartRect)
+                .withCornerRadius(cornerRadius);
+        SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
 
-        if (mBackTarget.leash.isValid()) {
-            mTransaction.setMatrix(mBackTarget.leash, mTransformMatrix, new float[9]);
-            mTransaction.setWindowCrop(mBackTarget.leash, mStartRect);
-            mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius);
+        if (surfaceParams.surface.isValid()) {
+            surfaceParams.applyTo(mTransaction);
         }
-
         mTransaction.apply();
     }
 
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index a2f19f1..c879494 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -131,7 +131,7 @@
 
         SplitSelectStateController controller =
                 new SplitSelectStateController(this, mHandler, getStateManager(),
-                        null /* depthController */);
+                        /* depthController */ null, getStatsLogManager());
         mDragLayer.recreateControllers();
         mFallbackRecentsView.init(mActionsView, controller);
 
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 04c0ab0..b233521 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
 
 import android.graphics.Rect;
 import android.util.ArraySet;
@@ -114,6 +115,10 @@
                     homeContentInsets, minimizedHomeBounds);
 
             Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+                ActiveGestureLog.INSTANCE.addLog(
+                        /* event= */ "RecentsAnimationCallbacks.onAnimationStart",
+                        /* extras= */ targets.apps.length,
+                        /* gestureEvent= */ START_RECENTS_ANIMATION);
                 for (RecentsAnimationListener listener : getListeners()) {
                     listener.onRecentsAnimationStart(mController, targets);
                 }
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 71e8a77..baeb514 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -37,11 +37,11 @@
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.util.TransformParams.BuilderProxy;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 import java.util.Arrays;
 import java.util.function.Consumer;
@@ -335,11 +335,11 @@
         }
 
         @Override
-        public void onBuildTargetParams(SurfaceProperties builder, RemoteAnimationTargetCompat app,
-                TransformParams params) {
-            builder.setMatrix(mMatrix)
-                    .setWindowCrop(mCropRect)
-                    .setCornerRadius(params.getCornerRadius());
+        public void onBuildTargetParams(
+                Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+            builder.withMatrix(mMatrix)
+                    .withWindowCrop(mCropRect)
+                    .withCornerRadius(params.getCornerRadius());
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 3a7ade3..24d2b9b 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -45,8 +45,10 @@
 import android.view.SurfaceControl;
 import android.window.IOnBackInvokedCallback;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 
+import com.android.internal.logging.InstanceId;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -480,6 +482,10 @@
         mPipAnimationListener = listener;
     }
 
+    /**
+     * @return Destination bounds of auto-pip animation, {@code null} if the animation is not ready.
+     */
+    @Nullable
     public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
             PictureInPictureParams pictureInPictureParams, int launcherRotation,
             Rect hotseatKeepClearArea) {
@@ -539,11 +545,11 @@
     /** Start multiple tasks in split-screen simultaneously. */
     public void startTasks(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions,
             @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
-            RemoteTransitionCompat remoteTransition) {
+            RemoteTransitionCompat remoteTransition, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startTasks(mainTaskId, mainOptions, sideTaskId, sideOptions,
-                        sidePosition, splitRatio, remoteTransition.getTransition());
+                        sidePosition, splitRatio, remoteTransition.getTransition(), instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startTask");
             }
@@ -555,11 +561,11 @@
      */
     public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId,
             Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,
-            float splitRatio, RemoteAnimationAdapter adapter) {
+            float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId,
-                        sideOptions, sidePosition, splitRatio, adapter);
+                        sideOptions, sidePosition, splitRatio, adapter, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startTasksWithLegacyTransition");
             }
@@ -569,11 +575,12 @@
     public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
             Intent fillInIntent, int taskId, Bundle mainOptions, Bundle sideOptions,
             @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
-            RemoteAnimationAdapter adapter) {
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
-                        taskId, mainOptions, sideOptions, sidePosition, splitRatio, adapter);
+                        taskId, mainOptions, sideOptions, sidePosition, splitRatio, adapter,
+                        instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startIntentAndTaskWithLegacyTransition");
             }
@@ -583,11 +590,11 @@
     public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, int taskId,
             Bundle mainOptions, Bundle sideOptions,
             @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
-            RemoteAnimationAdapter adapter) {
+            RemoteAnimationAdapter adapter, InstanceId instanceId) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId,
-                        mainOptions, sideOptions, sidePosition, splitRatio, adapter);
+                        mainOptions, sideOptions, sidePosition, splitRatio, adapter, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startShortcutAndTaskWithLegacyTransition");
             }
@@ -595,11 +602,11 @@
     }
 
     public void startShortcut(String packageName, String shortcutId, int position,
-            Bundle options, UserHandle user) {
+            Bundle options, UserHandle user, InstanceId instanceId) {
         if (mSplitScreen != null) {
             try {
                 mSplitScreen.startShortcut(packageName, shortcutId, position, options,
-                        user);
+                        user, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startShortcut");
             }
@@ -607,10 +614,10 @@
     }
 
     public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
-            Bundle options) {
+            Bundle options, InstanceId instanceId) {
         if (mSplitScreen != null) {
             try {
-                mSplitScreen.startIntent(intent, fillInIntent, position, options);
+                mSplitScreen.startIntent(intent, fillInIntent, position, options, instanceId);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startIntent");
             }
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index be82f76..b9a1b06 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -36,8 +36,6 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
-import com.android.quickstep.util.ActiveGestureErrorDetector;
-import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -137,8 +135,6 @@
                     // handling this call entirely
                     return;
                 }
-                ActiveGestureLog.INSTANCE.addLog("TaskAnimationManager.startRecentsAnimation",
-                        ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION);
                 mController = controller;
                 mTargets = targets;
                 mLastAppearedTaskTarget = mTargets.findTask(mLastGestureState.getRunningTaskId());
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 41972c6..a96524e 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -72,8 +72,6 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
 import com.android.quickstep.util.MultiValueUpdateListener;
-import com.android.quickstep.util.SurfaceTransaction;
-import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
@@ -84,6 +82,7 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -253,24 +252,21 @@
 
                     @Override
                     public void onUpdate(float percent, boolean initOnly) {
-
+                        final SurfaceParams.Builder navBuilder =
+                                new SurfaceParams.Builder(navBarTarget.leash);
 
                         // TODO Do we need to operate over multiple TVSs for the navbar leash?
                         for (RemoteTargetHandle handle : remoteTargetHandles) {
-                            SurfaceTransaction transaction = new SurfaceTransaction();
-                            SurfaceProperties navBuilder =
-                                    transaction.forSurface(navBarTarget.leash);
-
                             if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
                                 TaskViewSimulator taskViewSimulator = handle.getTaskViewSimulator();
                                 taskViewSimulator.getCurrentCropRect().round(cropRect);
-                                navBuilder.setMatrix(taskViewSimulator.getCurrentMatrix())
-                                        .setWindowCrop(cropRect)
-                                        .setAlpha(mNavFadeIn.value);
+                                navBuilder.withMatrix(taskViewSimulator.getCurrentMatrix())
+                                        .withWindowCrop(cropRect)
+                                        .withAlpha(mNavFadeIn.value);
                             } else {
-                                navBuilder.setAlpha(mNavFadeOut.value);
+                                navBuilder.withAlpha(mNavFadeOut.value);
                             }
-                            handle.getTransformParams().applySurfaceParams(transaction);
+                            handle.getTransformParams().applySurfaceParams(navBuilder.build());
                         }
                     }
                 });
@@ -478,7 +474,7 @@
      * If {@param launchingTaskView} is not null, then this will play the tasks launch animation
      * from the position of the GroupedTaskView (when user taps on the TaskView to start it).
      * Technically this case should be taken care of by
-     * {@link #composeRecentsSplitLaunchAnimatorLegacy} below, but the way we launch tasks whether
+     * {@link #composeRecentsSplitLaunchAnimatorLegacy()} below, but the way we launch tasks whether
      * it's a single task or multiple tasks results in different entry-points.
      *
      * If it is null, then it will simply fade in the starting apps and fade out launcher (for the
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 040c55b..4923948 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -82,7 +82,6 @@
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.ViewCapture;
 import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
 import com.android.quickstep.inputconsumers.AssistantInputConsumer;
@@ -101,6 +100,7 @@
 import com.android.quickstep.util.ProtoTracer;
 import com.android.quickstep.util.ProxyScreenStatusProvider;
 import com.android.quickstep.util.SplitScreenBounds;
+import com.android.quickstep.util.ViewCapture;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -709,16 +709,19 @@
     }
 
     public GestureState createGestureState(GestureState previousGestureState) {
-        GestureState gestureState = new GestureState(mOverviewComponentObserver,
-                ActiveGestureLog.INSTANCE.incrementLogId());
+        final GestureState gestureState;
         TopTaskTracker.CachedTaskInfo taskInfo;
         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+            gestureState = new GestureState(mOverviewComponentObserver,
+                    ActiveGestureLog.INSTANCE.getLogId());
             taskInfo = previousGestureState.getRunningTask();
             gestureState.updateRunningTask(taskInfo);
             gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId());
             gestureState.updatePreviouslyAppearedTaskIds(
                     previousGestureState.getPreviouslyAppearedTaskIds());
         } else {
+            gestureState = new GestureState(mOverviewComponentObserver,
+                    ActiveGestureLog.INSTANCE.incrementLogId());
             taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false);
             gestureState.updateRunningTask(taskInfo);
         }
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index e981993..93eb0f1 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.popup.QuickstepSystemShortcut;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.SplitConfigurationOptions;
@@ -213,8 +214,9 @@
 
     @Override
     public void initiateSplitSelect(TaskView taskView,
-            @SplitConfigurationOptions.StagePosition int stagePosition) {
-        super.initiateSplitSelect(taskView, stagePosition);
+            @SplitConfigurationOptions.StagePosition int stagePosition,
+            StatsLogManager.EventEnum splitEvent) {
+        super.initiateSplitSelect(taskView, stagePosition, splitEvent);
         mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT);
     }
 
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index c131c05..6bc24f2 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -53,13 +53,13 @@
 import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.util.TransformParams.BuilderProxy;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 import java.util.HashMap;
 
@@ -290,9 +290,9 @@
 
     @Override
     public void onBuildTargetParams(
-            SurfaceProperties builder, RemoteAnimationTargetCompat app, TransformParams params) {
+            Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
         mMatrix.setTranslate(0, mProgress.value * mMaxTranslationY);
-        builder.setMatrix(mMatrix);
+        builder.withMatrix(mMatrix);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 401df24..fa7bc04 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -33,6 +33,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
+import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
@@ -52,11 +53,9 @@
 import com.android.quickstep.RemoteTargetGluer;
 import com.android.quickstep.SwipeUpAnimationLogic;
 import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim;
-import com.android.quickstep.util.RecordingSurfaceTransaction;
-import com.android.quickstep.util.RecordingSurfaceTransaction.MockProperties;
 import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SurfaceTransaction;
 import com.android.quickstep.util.TransformParams;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 @TargetApi(Build.VERSION_CODES.R)
 abstract class SwipeUpGestureTutorialController extends TutorialController {
@@ -416,23 +415,21 @@
     private class FakeTransformParams extends TransformParams {
 
         @Override
-        public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
-            RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
-            proxy.onBuildTargetParams(transaction.mockProperties, null, this);
-            return transaction;
+        public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
+            SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
+            proxy.onBuildTargetParams(builder, null, this);
+            return new SurfaceParams[] {builder.build()};
         }
 
         @Override
-        public void applySurfaceParams(SurfaceTransaction params) {
-            if (params instanceof RecordingSurfaceTransaction) {
-                MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
-                mFakeTaskView.setAnimationMatrix(p.matrix);
-                mFakePreviousTaskView.setAnimationMatrix(p.matrix);
-                mFakeTaskViewRect.set(p.windowCrop);
-                mFakeTaskViewRadius = p.cornerRadius;
-                mFakeTaskView.invalidateOutline();
-                mFakePreviousTaskView.invalidateOutline();
-            }
+        public void applySurfaceParams(SurfaceParams[] params) {
+            SurfaceParams p = params[0];
+            mFakeTaskView.setAnimationMatrix(p.matrix);
+            mFakePreviousTaskView.setAnimationMatrix(p.matrix);
+            mFakeTaskViewRect.set(p.windowCrop);
+            mFakeTaskViewRadius = p.cornerRadius;
+            mFakeTaskView.invalidateOutline();
+            mFakePreviousTaskView.invalidateOutline();
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
index 40eb31b..23fdd58 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
@@ -161,16 +161,14 @@
         ArrayList<EventLog> eventLogs = new ArrayList<>();
 
         for (int i = 0; i < logs.length; i++) {
-            EventLog eventLog = logs[(nextIndex + logs.length - i - 1) % logs.length];
+            EventLog eventLog = logs[(nextIndex + i) % logs.length];
             if (eventLog == null) {
                 continue;
             }
             eventLogs.add(eventLog);
             writer.println(prefix + "\tLogs for logId: " + eventLog.logId);
 
-            List<EventEntry> eventEntries = eventLog.eventEntries;
-            for (int j = eventEntries.size() - 1; j >= 0; j--) {
-                EventEntry eventEntry = eventEntries.get(j);
+            for (EventEntry eventEntry : eventLog.eventEntries) {
                 date.setTime(eventEntry.time);
 
                 StringBuilder msg = new StringBuilder(prefix + "\t\t").append(sdf.format(date))
@@ -215,6 +213,11 @@
         return mCurrentLogId++;
     }
 
+    /** Returns the current log ID. This should be used when a log trace is being reused. */
+    public int getLogId() {
+        return mCurrentLogId;
+    }
+
     private boolean isEntrySame(
             EventEntry entry,
             int type,
diff --git a/quickstep/src/com/android/quickstep/util/LogUtils.kt b/quickstep/src/com/android/quickstep/util/LogUtils.kt
new file mode 100644
index 0000000..bad8506
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/LogUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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.util
+
+import android.util.Pair
+import com.android.internal.logging.InstanceIdSequence
+import com.android.launcher3.logging.InstanceId
+
+object LogUtils {
+  /**
+   * @return a [Pair] of two InstanceIds but with different types, one that can be used by framework
+   * (if needing to pass through an intent or such) and one used in Launcher
+   */
+  @JvmStatic
+  fun getShellShareableInstanceId():
+    Pair<com.android.internal.logging.InstanceId, InstanceId> {
+    val internalInstanceId = InstanceIdSequence(InstanceId.INSTANCE_ID_MAX).newInstanceId()
+    val launcherInstanceId = InstanceId(internalInstanceId.id)
+    return Pair(internalInstanceId, launcherInstanceId)
+  }
+}
diff --git a/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
deleted file mode 100644
index a56c851..0000000
--- a/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2022 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.util;
-
-import android.graphics.Matrix;
-import android.graphics.Rect;
-
-/**
- * Extension for {@link SurfaceTransaction} which records the commands for mocking
- */
-public class RecordingSurfaceTransaction extends SurfaceTransaction {
-
-    /**
-     * A mock builder which can be used for recording values
-     */
-    public final MockProperties mockProperties = new MockProperties();
-
-    /**
-     * Extension of {@link SurfaceProperties} which just stores all the values locally
-     */
-    public class MockProperties extends SurfaceProperties {
-
-        public float alpha = -1;
-        public Matrix matrix = null;
-        public Rect windowCrop = null;
-        public float cornerRadius = 0;
-        public float shadowRadius = 0;
-
-        MockProperties() {
-            super(null);
-        }
-
-        @Override
-        public SurfaceProperties setAlpha(float alpha) {
-            this.alpha = alpha;
-            return this;
-        }
-
-        @Override
-        public SurfaceProperties setMatrix(Matrix matrix) {
-            this.matrix = matrix;
-            return this;
-        }
-
-        @Override
-        public SurfaceProperties setWindowCrop(Rect windowCrop) {
-            this.windowCrop = windowCrop;
-            return this;
-        }
-
-        @Override
-        public SurfaceProperties setLayer(int relativeLayer) {
-            return this;
-        }
-
-        @Override
-        public SurfaceProperties setCornerRadius(float radius) {
-            this.cornerRadius = radius;
-            return this;
-        }
-
-        @Override
-        public SurfaceProperties setShadowRadius(float radius) {
-            this.shadowRadius = radius;
-            return this;
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index b2e159e..81c124f 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -19,10 +19,10 @@
 
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.view.SurfaceControl.Transaction;
 
 import com.android.quickstep.RemoteAnimationTargets;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.TransactionCompat;
 
 /**
  * Animation listener which fades out the closing targets
@@ -40,7 +40,7 @@
 
     @Override
     public void onAnimationUpdate(ValueAnimator valueAnimator) {
-        Transaction t = new Transaction();
+        TransactionCompat t = new TransactionCompat();
         if (mFirstFrame) {
             for (RemoteAnimationTargetCompat target : mTarget.unfilteredApps) {
                 t.show(target.leash);
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 7efb1a5..efbe783 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -38,12 +38,16 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.view.RemoteAnimationAdapter;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 
 import androidx.annotation.Nullable;
 
+import com.android.internal.logging.InstanceId;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager;
@@ -74,10 +78,12 @@
 
     private final Context mContext;
     private final Handler mHandler;
+    private StatsLogManager mStatsLogManager;
     private final SystemUiProxy mSystemUiProxy;
     private final StateManager mStateManager;
     private final DepthController mDepthController;
     private @StagePosition int mStagePosition;
+    private ItemInfo mItemInfo;
     private Intent mInitialTaskIntent;
     private int mInitialTaskId = INVALID_TASK_ID;
     private int mSecondTaskId = INVALID_TASK_ID;
@@ -88,11 +94,14 @@
     /** If not null, this is the TaskView we want to launch from */
     @Nullable
     private GroupedTaskView mLaunchingTaskView;
+    /** Represents where split is intended to be invoked from. */
+    private StatsLogManager.EventEnum mSplitEvent;
 
     public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
-            DepthController depthController) {
+            DepthController depthController, StatsLogManager statsLogManager) {
         mContext = context;
         mHandler = handler;
+        mStatsLogManager = statsLogManager;
         mSystemUiProxy = SystemUiProxy.INSTANCE.get(mContext);
         mStateManager = stateManager;
         mDepthController = depthController;
@@ -101,19 +110,25 @@
     /**
      * To be called after first task selected
      */
-    public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition) {
+    public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition,
+            StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
         mInitialTaskId = taskId;
-        mStagePosition = stagePosition;
-        mInitialTaskIntent = null;
-        mUser = null;
+        setInitialData(stagePosition, splitEvent, itemInfo);
     }
 
     public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition,
-            @Nullable UserHandle user) {
+            @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent) {
         mInitialTaskIntent = intent;
+        mUser = itemInfo.user;
+        mItemInfo = itemInfo;
+        setInitialData(stagePosition, splitEvent, itemInfo);
+    }
+
+    private void setInitialData(@StagePosition int stagePosition,
+            StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
+        mItemInfo = itemInfo;
         mStagePosition = stagePosition;
-        mInitialTaskId = INVALID_TASK_ID;
-        mUser = user;
+        mSplitEvent = splitEvent;
     }
 
     /**
@@ -137,8 +152,16 @@
                 FLAG_MUTABLE, null /* options */, mUser)
                 : PendingIntent.getActivity(mContext, 0, mInitialTaskIntent, FLAG_MUTABLE));
 
+        Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+                LogUtils.getShellShareableInstanceId();
         launchTasks(mInitialTaskId, pendingIntent, fillInIntent, mSecondTaskId, mStagePosition,
-                callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO);
+                callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
+                instanceIds.first);
+
+        mStatsLogManager.logger()
+                .withItemInfo(mItemInfo)
+                .withInstanceId(instanceIds.second)
+                .log(mSplitEvent);
     }
 
 
@@ -174,7 +197,7 @@
     public void launchTasks(int taskId1, int taskId2, @StagePosition int stagePosition,
             Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
         launchTasks(taskId1, null /* taskPendingIntent */, null /* fillInIntent */, taskId2,
-                stagePosition, callback, freezeTaskList, splitRatio);
+                stagePosition, callback, freezeTaskList, splitRatio, null);
     }
 
     /**
@@ -183,10 +206,14 @@
      * fill in intent with a taskId2 are set.
      * @param taskPendingIntent is null when split is initiated from Overview
      * @param stagePosition representing location of task1
+     * @param shellInstanceId loggingId to be used by shell, will be non-null for actions that create
+     *                   a split instance, null for cases that bring existing instaces to the
+     *                   foreground (quickswitch, launching previous pairs from overview)
      */
     public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent,
             @Nullable Intent fillInIntent, int taskId2, @StagePosition int stagePosition,
-            Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
+            Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio,
+            @Nullable InstanceId shellInstanceId) {
         TestLogging.recordEvent(
                 TestProtocol.SEQUENCE_MAIN, "launchSplitTasks");
         // Assume initial task is for top/left part of screen
@@ -200,7 +227,8 @@
             mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
                     null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio,
                     new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
-                            ActivityThread.currentActivityThread().getApplicationThread()));
+                            ActivityThread.currentActivityThread().getApplicationThread()),
+                    shellInstanceId);
             // TODO(b/237635859): handle intent/shortcut + task with shell transition
         } else {
             RemoteSplitLaunchAnimationRunner animationRunner =
@@ -218,18 +246,18 @@
             if (taskPendingIntent == null) {
                 mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(),
                         taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
-                        splitRatio, adapter);
+                        splitRatio, adapter, shellInstanceId);
             } else {
                 final ShortcutInfo shortcutInfo = getShortcutInfo(mInitialTaskIntent,
                         taskPendingIntent.getCreatorUserHandle());
                 if (shortcutInfo != null) {
                     mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId2,
                             mainOpts.toBundle(), null /* sideOptions */, stagePosition, splitRatio,
-                            adapter);
+                            adapter, shellInstanceId);
                 } else {
                     mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
                             fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */,
-                            stagePosition, splitRatio, adapter);
+                            stagePosition, splitRatio, adapter, shellInstanceId);
                 }
             }
         }
@@ -239,6 +267,10 @@
         return mStagePosition;
     }
 
+    public StatsLogManager.EventEnum getSplitEvent() {
+        return mSplitEvent;
+    }
+
     public void setRecentsAnimationRunning(boolean running) {
         this.mRecentsAnimationRunning = running;
     }
@@ -358,6 +390,8 @@
         mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
         mRecentsAnimationRunning = false;
         mLaunchingTaskView = null;
+        mItemInfo = null;
+        mSplitEvent = null;
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
deleted file mode 100644
index 6bdcf4d..0000000
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2022 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.util;
-
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-
-/**
- * Helper class for building a {@link Transaction}.
- */
-public class SurfaceTransaction {
-
-    private final Transaction mTransaction = new Transaction();
-    private final float[] mTmpValues = new float[9];
-
-    /**
-     * Creates a new builder for the provided surface
-     */
-    public SurfaceProperties forSurface(SurfaceControl surface) {
-        return new SurfaceProperties(surface);
-    }
-
-    /**
-     * Returns the final transaction
-     */
-    public Transaction getTransaction() {
-        return mTransaction;
-    }
-
-    /**
-     * Utility class to update surface params in a transaction
-     */
-    public class SurfaceProperties {
-
-        private final SurfaceControl mSurface;
-
-        SurfaceProperties(SurfaceControl surface) {
-            mSurface = surface;
-        }
-
-        /**
-         * @param alpha The alpha value to apply to the surface.
-         * @return this Builder
-         */
-        public SurfaceProperties setAlpha(float alpha) {
-            mTransaction.setAlpha(mSurface, alpha);
-            return this;
-        }
-
-        /**
-         * @param matrix The matrix to apply to the surface.
-         * @return this Builder
-         */
-        public SurfaceProperties setMatrix(Matrix matrix) {
-            mTransaction.setMatrix(mSurface, matrix, mTmpValues);
-            return this;
-        }
-
-        /**
-         * @param windowCrop The window crop to apply to the surface.
-         * @return this Builder
-         */
-        public SurfaceProperties setWindowCrop(Rect windowCrop) {
-            mTransaction.setWindowCrop(mSurface, windowCrop);
-            return this;
-        }
-
-        /**
-         * @param relativeLayer The relative layer.
-         * @return this Builder
-         */
-        public SurfaceProperties setLayer(int relativeLayer) {
-            mTransaction.setLayer(mSurface, relativeLayer);
-            return this;
-        }
-
-        /**
-         * @param radius the Radius for rounded corners to apply to the surface.
-         * @return this Builder
-         */
-        public SurfaceProperties setCornerRadius(float radius) {
-            mTransaction.setCornerRadius(mSurface, radius);
-            return this;
-        }
-
-        /**
-         * @param radius the Radius for the shadows to apply to the surface.
-         * @return this Builder
-         */
-        public SurfaceProperties setShadowRadius(float radius) {
-            mTransaction.setShadowRadius(mSurface, radius);
-            return this;
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 95473dc..1200208 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -25,6 +25,7 @@
 import android.view.ViewRootImpl;
 
 import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 import java.util.function.Consumer;
 
@@ -69,12 +70,18 @@
      * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
      *               this method to avoid synchronization issues.
      */
-    public void scheduleApply(SurfaceTransaction params) {
+    public void scheduleApply(final SurfaceParams... params) {
         View view = mTargetViewRootImpl.getView();
         if (view == null) {
             return;
         }
-        Transaction t = params.getTransaction();
+        Transaction t = new Transaction();
+        for (int i = params.length - 1; i >= 0; i--) {
+            SurfaceParams surfaceParams = params[i];
+            if (surfaceParams.surface.isValid()) {
+                surfaceParams.applyTo(t);
+            }
+        }
 
         mLastSequenceNumber++;
         final int toApplySeqNo = mLastSequenceNumber;
@@ -95,7 +102,7 @@
     }
 
     /**
-     * Creates an instance of SurfaceTransactionApplier, deferring until the target view is
+     * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
      * attached if necessary.
      */
     public static void create(
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index faa5b64..1a026fc 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -47,11 +47,11 @@
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
 import com.android.quickstep.views.TaskView.FullscreenDrawParams;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 /**
  * A utility class which emulates the layout behavior of TaskView and RecentsView
@@ -387,10 +387,10 @@
 
     @Override
     public void onBuildTargetParams(
-            SurfaceProperties builder, RemoteAnimationTargetCompat app, TransformParams params) {
-        builder.setMatrix(mMatrix)
-                .setWindowCrop(mTmpCropRect)
-                .setCornerRadius(getCurrentCornerRadius());
+            Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+        builder.withMatrix(mMatrix)
+                .withWindowCrop(mTmpCropRect)
+                .withCornerRadius(getCurrentCornerRadius());
 
         // If mDrawsBelowRecents is unset, no reordering will be enforced.
         if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mDrawsBelowRecents != null) {
@@ -399,7 +399,7 @@
             // conflict with layers that WM core positions (ie. the input consumers).  For shell
             // transitions, the animation leashes are reparented to an animation container so we
             // can bump layers as needed.
-            builder.setLayer(mDrawsBelowRecents
+            builder.withLayer(mDrawsBelowRecents
                     ? Integer.MIN_VALUE + 1
                     : ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0);
         }
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index 3d505c6..a7f25d4 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -21,8 +21,10 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
 import com.android.quickstep.RemoteAnimationTargets;
-import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+import com.android.systemui.shared.system.TransactionCompat;
 
 public class TransformParams {
 
@@ -111,7 +113,8 @@
      * Sets the SyncRtSurfaceTransactionApplierCompat that will apply the SurfaceParams that
      * are computed based on these TransformParams.
      */
-    public TransformParams setSyncTransactionApplier(SurfaceTransactionApplier applier) {
+    public TransformParams setSyncTransactionApplier(
+            SurfaceTransactionApplier applier) {
         mSyncTransactionApplier = applier;
         return this;
     }
@@ -134,14 +137,16 @@
         return this;
     }
 
-    public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
+    public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
         RemoteAnimationTargets targets = mTargetSet;
-        SurfaceTransaction transaction = new SurfaceTransaction();
+        final int appLength =  targets.unfilteredApps.length;
+        final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
+        SurfaceParams[] surfaceParams = new SurfaceParams[appLength + wallpaperLength];
         mRecentsSurface = getRecentsSurface(targets);
 
-        for (int i = 0; i < targets.unfilteredApps.length; i++) {
+        for (int i = 0; i < appLength; i++) {
             RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
-            SurfaceProperties builder = transaction.forSurface(app.leash);
+            SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
 
             if (app.mode == targets.targetMode) {
                 if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
@@ -151,9 +156,9 @@
                     if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
                             && app.isNotInRecents) {
                         float progress = Utilities.boundToRange(getProgress(), 0, 1);
-                        builder.setAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
+                        builder.withAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
                     } else {
-                        builder.setAlpha(getTargetAlpha());
+                        builder.withAlpha(getTargetAlpha());
                     }
 
                     proxy.onBuildTargetParams(builder, app, this);
@@ -161,15 +166,15 @@
             } else {
                 mBaseBuilderProxy.onBuildTargetParams(builder, app, this);
             }
+            surfaceParams[i] = builder.build();
         }
-
         // always put wallpaper layer to bottom.
-        final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
         for (int i = 0; i < wallpaperLength; i++) {
             RemoteAnimationTargetCompat wallpaper = targets.wallpapers[i];
-            transaction.forSurface(wallpaper.leash).setLayer(Integer.MIN_VALUE);
+            surfaceParams[appLength + i] = new SurfaceParams.Builder(wallpaper.leash)
+                    .withLayer(Integer.MIN_VALUE).build();
         }
-        return transaction;
+        return surfaceParams;
     }
 
     private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
@@ -208,11 +213,15 @@
         return mTargetSet;
     }
 
-    public void applySurfaceParams(SurfaceTransaction builder) {
+    public void applySurfaceParams(SurfaceParams... params) {
         if (mSyncTransactionApplier != null) {
-            mSyncTransactionApplier.scheduleApply(builder);
+            mSyncTransactionApplier.scheduleApply(params);
         } else {
-            builder.getTransaction().apply();
+            TransactionCompat t = new TransactionCompat();
+            for (SurfaceParams param : params) {
+                SyncRtSurfaceTransactionApplierCompat.applyParams(t, param);
+            }
+            t.apply();
         }
     }
 
@@ -220,9 +229,9 @@
     public interface BuilderProxy {
 
         BuilderProxy NO_OP = (builder, app, params) -> { };
-        BuilderProxy ALWAYS_VISIBLE = (builder, app, params) -> builder.setAlpha(1);
+        BuilderProxy ALWAYS_VISIBLE = (builder, app, params) ->builder.withAlpha(1);
 
-        void onBuildTargetParams(SurfaceProperties builder,
+        void onBuildTargetParams(SurfaceParams.Builder builder,
                 RemoteAnimationTargetCompat app, TransformParams params);
     }
 }
diff --git a/src/com/android/launcher3/util/ViewCapture.java b/quickstep/src/com/android/quickstep/util/ViewCapture.java
similarity index 98%
rename from src/com/android/launcher3/util/ViewCapture.java
rename to quickstep/src/com/android/quickstep/util/ViewCapture.java
index 0cf3ad7..6071bc8 100644
--- a/src/com/android/launcher3/util/ViewCapture.java
+++ b/quickstep/src/com/android/quickstep/util/ViewCapture.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.util;
+package com.android.quickstep.util;
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -44,6 +44,9 @@
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.LooperExecutor;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.view.ViewCaptureData.ExportedData;
 import com.android.launcher3.view.ViewCaptureData.FrameData;
 import com.android.launcher3.view.ViewCaptureData.ViewNode;
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 3920c56..44ea0a0 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -167,7 +167,7 @@
     @Override
     protected boolean showTaskMenuWithContainer(IconView iconView) {
         boolean showedTaskMenu = super.showTaskMenuWithContainer(iconView);
-        if (iconView == mIconView2 && showedTaskMenu && !isGridTask()) {
+        if (iconView == mIconView2 && showedTaskMenu && !mActivity.getDeviceProfile().isTablet) {
             // Adjust the position of the secondary task's menu view (only on phones)
             TaskMenuView taskMenuView = getAnyView(mActivity, AbstractFloatingView.TYPE_TASK_MENU);
             DeviceProfile deviceProfile = mActivity.getDeviceProfile();
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index de7ccad..364cf7b 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -33,6 +33,7 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.popup.QuickstepSystemShortcut;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager.StateListener;
@@ -94,7 +95,7 @@
             if (recoveryData.getStagedTaskId() == taskId) {
                 initiateSplitSelect(
                         getTaskViewByTaskId(recoveryData.getStagedTaskId()),
-                        recoveryData.getStagePosition()
+                        recoveryData.getStagePosition(), recoveryData.getSource()
                 );
                 mActivity.finishSplitSelectRecovery();
             }
@@ -104,7 +105,6 @@
     @Override
     public void reset() {
         super.reset();
-
         setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0);
     }
 
@@ -192,8 +192,9 @@
 
     @Override
     public void initiateSplitSelect(TaskView taskView,
-            @SplitConfigurationOptions.StagePosition int stagePosition) {
-        super.initiateSplitSelect(taskView, stagePosition);
+            @SplitConfigurationOptions.StagePosition int stagePosition,
+            StatsLogManager.EventEnum splitEvent) {
+        super.initiateSplitSelect(taskView, stagePosition, splitEvent);
         mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 3687f36..6a94094 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -42,6 +42,7 @@
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_0_85;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_ACTIONS_SPLIT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
@@ -134,6 +135,7 @@
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.cache.HandlerRunnable;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.popup.QuickstepSystemShortcut;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.BaseState;
@@ -175,7 +177,6 @@
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.SplitScreenBounds;
 import com.android.quickstep.util.SplitSelectStateController;
-import com.android.quickstep.util.SurfaceTransaction;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TaskVisualsChangeListener;
@@ -188,6 +189,7 @@
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.wm.shell.pip.IPipAnimationListener;
@@ -1115,15 +1117,14 @@
             appAnimator.setInterpolator(ACCEL_DEACCEL);
             appAnimator.addUpdateListener(valueAnimator -> {
                 float percent = valueAnimator.getAnimatedFraction();
-                SurfaceTransaction transaction = new SurfaceTransaction();
+                SurfaceParams.Builder builder = new SurfaceParams.Builder(
+                        apps[apps.length - 1].leash);
                 Matrix matrix = new Matrix();
                 matrix.postScale(percent, percent);
                 matrix.postTranslate(mActivity.getDeviceProfile().widthPx * (1 - percent) / 2,
                         mActivity.getDeviceProfile().heightPx * (1 - percent) / 2);
-                transaction.forSurface(apps[apps.length - 1].leash)
-                        .setAlpha(percent)
-                        .setMatrix(matrix);
-                surfaceApplier.scheduleApply(transaction);
+                builder.withAlpha(percent).withMatrix(matrix);
+                surfaceApplier.scheduleApply(builder.build());
             });
             anim.play(appAnimator);
             anim.addListener(new AnimatorListenerAdapter() {
@@ -4152,16 +4153,21 @@
         }
     }
 
+    /**
+     * Primarily used by overview actions to initiate split from focused task, logs the source
+     * of split invocation as such.
+     */
     public void initiateSplitSelect(TaskView taskView) {
         int defaultSplitPosition = mOrientationHandler
                 .getDefaultSplitPosition(mActivity.getDeviceProfile());
-        initiateSplitSelect(taskView, defaultSplitPosition);
+        initiateSplitSelect(taskView, defaultSplitPosition, LAUNCHER_OVERVIEW_ACTIONS_SPLIT);
     }
 
-    public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition) {
+    public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition,
+            StatsLogManager.EventEnum splitEvent) {
         mSplitHiddenTaskView = taskView;
         mSplitSelectStateController.setInitialTaskSelect(taskView.getTask().key.id,
-                stagePosition);
+                stagePosition, splitEvent, taskView.getItemInfo());
         mSplitHiddenTaskViewIndex = indexOfChild(taskView);
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
@@ -4172,7 +4178,8 @@
     public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
         mSplitSelectSource = splitSelectSource;
         mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
-                splitSelectSource.position.stagePosition, splitSelectSource.user);
+                splitSelectSource.position.stagePosition, splitSelectSource.mItemInfo,
+                splitSelectSource.splitEvent);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 7df21d5..ee8489d 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -32,6 +32,7 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
@@ -1496,7 +1497,8 @@
     }
 
     public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
-        getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
+        getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition,
+                getLogEventForPosition(splitPositionOption.stagePosition));
     }
 
     /**
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index b903691..c1b3beb 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -36,6 +36,7 @@
 import android.content.ComponentName;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -110,6 +111,7 @@
         doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle()));
         doAnswer(i -> {
             String pkg = i.getArgument(0);
+            Log.e("Hello", "Getting v " + pkg);
             return TextUtils.isEmpty(pkg) ? allWidgets : allWidgets.stream()
                     .filter(a -> pkg.equals(a.provider.getPackageName()))
                     .collect(Collectors.toList());
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index c2667b8..190b002 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -24,6 +24,7 @@
 import android.graphics.RectF;
 import android.util.ArrayMap;
 import android.view.Surface;
+import android.view.SurfaceControl;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -41,8 +42,8 @@
 import com.android.launcher3.util.window.WindowManagerProxy;
 import com.android.quickstep.FallbackActivityInterface;
 import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.util.RecordingSurfaceTransaction.MockProperties;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 import org.hamcrest.Description;
 import org.hamcrest.TypeSafeMatcher;
@@ -206,21 +207,17 @@
         }
 
         @Override
-        public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
-            RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
-            proxy.onBuildTargetParams(
-                    transaction.mockProperties, mock(RemoteAnimationTargetCompat.class), this);
-            return transaction;
+        public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
+            SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
+            proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
+            return new SurfaceParams[] {builder.build()};
         }
 
         @Override
-        public void applySurfaceParams(SurfaceTransaction params) {
-            Assert.assertTrue(params instanceof RecordingSurfaceTransaction);
-            MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
-
+        public void applySurfaceParams(SurfaceParams[] params) {
             // Verify that the task position remains the same
             RectF newAppBounds = new RectF(mAppBounds);
-            p.matrix.mapRect(newAppBounds);
+            params[0].matrix.mapRect(newAppBounds);
             Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
 
             System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 747b755..2f927d3 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -24,7 +24,6 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -92,8 +91,7 @@
     protected int getAvailableScrollHeight() {
         // AvailableScrollHeight = Total height of the all items - first page height
         int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
-        int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ getAdapter().getItemCount());
-        int availableScrollHeight = totalHeightOfAllItems - firstPageHeight;
+        int availableScrollHeight = computeVerticalScrollRange() - firstPageHeight;
         return Math.max(0, availableScrollHeight);
     }
 
@@ -146,10 +144,7 @@
 
         // IF scroller is at the very top OR there is no scroll bar because there is probably not
         // enough items to scroll, THEN it's okay for the container to be pulled down.
-        if (getCurrentScrollY() == 0) {
-            return true;
-        }
-        return getAdapter() == null || getAdapter().getItemCount() == 0;
+        return computeVerticalScrollOffset() == 0;
     }
 
     /**
@@ -160,53 +155,6 @@
     }
 
     /**
-     * @return the scroll top of this recycler view.
-     */
-    public int getCurrentScrollY() {
-        Adapter adapter = getAdapter();
-        if (adapter == null) {
-            return -1;
-        }
-        if (adapter.getItemCount() == 0 || getChildCount() == 0) {
-            return -1;
-        }
-
-        int itemPosition = NO_POSITION;
-        View child = null;
-
-        LayoutManager layoutManager = getLayoutManager();
-        if (layoutManager instanceof LinearLayoutManager) {
-            // Use the LayoutManager as the source of truth for visible positions. During
-            // animations, the view group child may not correspond to the visible views that appear
-            // at the top.
-            itemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
-            child = layoutManager.findViewByPosition(itemPosition);
-        }
-
-        if (child == null) {
-            // If the layout manager returns null for any reason, which can happen before layout
-            // has occurred for the position, then look at the child of this view as a ViewGroup.
-            child = getChildAt(0);
-            itemPosition = getChildAdapterPosition(child);
-        }
-        if (itemPosition == NO_POSITION) {
-            return -1;
-        }
-        return getPaddingTop() + getItemsHeight(itemPosition)
-                - layoutManager.getDecoratedTop(child);
-    }
-
-    /**
-     * Returns the sum of the height, in pixels, of this list adapter's items from index
-     * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
-     * it returns the full height of all the items.
-     *
-     * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
-     * sum of all items' height.
-     */
-    protected abstract int getItemsHeight(int untilIndex);
-
-    /**
      * Maps the touch (from 0..1) to the adapter position that should be visible.
      * <p>Override in each subclass of this base class.
      */
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ce95b15..17fb7eb 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -114,6 +114,7 @@
 import android.view.animation.OvershootInterpolator;
 import android.widget.ImageView;
 import android.widget.Toast;
+import android.window.OnBackInvokedDispatcher;
 
 import androidx.annotation.CallSuper;
 import androidx.annotation.NonNull;
@@ -543,6 +544,8 @@
             getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
         }
         setTitle(R.string.home_screen);
+
+        registerOnBackInvokedCallback();
     }
 
     protected LauncherOverlayManager getDefaultOverlay() {
@@ -2062,6 +2065,17 @@
         mStateManager.getState().onBackPressed(this);
     }
 
+    private void registerOnBackInvokedCallback() {
+        if (Utilities.ATLEAST_T) {
+            getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+                    OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+                    () -> {
+                        onBackPressed();
+                        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
+                    });
+        }
+    }
+
     protected void onScreenOff() {
         // Reset AllApps to its initial state only if we are not in the middle of
         // processing a multi-step drop
@@ -2780,7 +2794,7 @@
             View v = getFirstMatch(Collections.singletonList(activeRecyclerView),
                     preferredItem, packageAndUserAndApp);
 
-            if (v != null && activeRecyclerView.getCurrentScrollY() > 0) {
+            if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) {
                 RectF locationBounds = new RectF();
                 FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds,
                         new Rect());
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index aa9cfd1..c1eaa16 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -84,6 +84,7 @@
 
     /** Invoke when the current search session is finished. */
     public void onClearSearchResult() {
+        getMainAdapterProvider().clearHighlightedItem();
         animateToSearchState(false);
         rebindAdapters();
     }
@@ -92,6 +93,7 @@
      * Sets results list for search
      */
     public void setSearchResults(ArrayList<AdapterItem> results) {
+        getMainAdapterProvider().clearHighlightedItem();
         if (getSearchResultList().setSearchResults(results)) {
             getSearchRecyclerView().onSearchResultsChanged();
         }
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 33d2f2b..e0b4951 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -26,7 +26,9 @@
 import androidx.core.view.accessibility.AccessibilityRecordCompat;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
 
+import com.android.launcher3.util.ScrollableLayoutManager;
 import com.android.launcher3.views.ActivityContext;
 
 import java.util.List;
@@ -62,10 +64,10 @@
     /**
      * A subclass of GridLayoutManager that overrides accessibility values during app search.
      */
-    public class AppsGridLayoutManager extends GridLayoutManager {
+    public class AppsGridLayoutManager extends ScrollableLayoutManager {
 
         public AppsGridLayoutManager(Context context) {
-            super(context, 1, GridLayoutManager.VERTICAL, false);
+            super(context);
         }
 
         @Override
@@ -125,6 +127,15 @@
             }
             return extraRows;
         }
+
+        @Override
+        protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
+            AllAppsGridAdapter.AdapterItem item = mApps.getAdapterItems().get(position);
+            // only account for the first icon in the row since they are the same size within a row
+            return (isIconViewType(item.viewType) && item.rowAppIndex != 0)
+                    ? heightUntilLastPos
+                    : (heightUntilLastPos + mCachedSizes.get(item.viewType));
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 21a7dfb..3d06fb5 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.allapps;
 
-import static android.view.View.MeasureSpec.UNSPECIFIED;
-
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
@@ -26,7 +24,6 @@
 import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.SparseIntArray;
 
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -49,40 +46,10 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_LATENCY = Utilities.isPropertyEnabled(SEARCH_LOGGING);
 
-    protected AlphabeticalAppsList<?> mApps;
     protected final int mNumAppsPerRow;
-
-    // The specific view heights that we use to calculate scroll
-    private final SparseIntArray mViewHeights = new SparseIntArray();
-    private final SparseIntArray mCachedScrollPositions = new SparseIntArray();
     private final AllAppsFastScrollHelper mFastScrollHelper;
 
-
-    private final AdapterDataObserver mObserver = new RecyclerView.AdapterDataObserver() {
-        public void onChanged() {
-            mCachedScrollPositions.clear();
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount) {
-            onChanged();
-        }
-
-        @Override
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            onChanged();
-        }
-
-        @Override
-        public void onItemRangeRemoved(int positionStart, int itemCount) {
-            onChanged();
-        }
-
-        @Override
-        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
-            onChanged();
-        }
-    };
+    protected AlphabeticalAppsList<?> mApps;
 
     public AllAppsRecyclerView(Context context) {
         this(context, null);
@@ -122,12 +89,8 @@
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows
                 * (mNumAppsPerRow + 1));
-
-        mViewHeights.clear();
-        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, grid.allAppsCellHeightPx);
     }
 
-
     @Override
     public void onDraw(Canvas c) {
         if (DEBUG) {
@@ -200,17 +163,6 @@
     }
 
     @Override
-    public void setAdapter(Adapter adapter) {
-        if (getAdapter() != null) {
-            getAdapter().unregisterAdapterDataObserver(mObserver);
-        }
-        super.setAdapter(adapter);
-        if (adapter != null) {
-            adapter.registerAdapterDataObserver(mObserver);
-        }
-    }
-
-    @Override
     protected boolean isPaddingOffsetRequired() {
         return true;
     }
@@ -231,13 +183,13 @@
         List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
 
         // Skip early if there are no items or we haven't been measured
-        if (items.isEmpty() || mNumAppsPerRow == 0) {
+        if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
             mScrollbar.setThumbOffsetY(-1);
             return;
         }
 
         // Skip early if, there no child laid out in the container.
-        int scrollY = getCurrentScrollY();
+        int scrollY = computeVerticalScrollOffset();
         if (scrollY < 0) {
             mScrollbar.setThumbOffsetY(-1);
             return;
@@ -292,51 +244,6 @@
         }
     }
 
-    @Override
-    protected int getItemsHeight(int position) {
-        List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
-        AllAppsGridAdapter.AdapterItem posItem = position < items.size()
-                ? items.get(position) : null;
-        int y = mCachedScrollPositions.get(position, -1);
-        if (y < 0) {
-            y = 0;
-            for (int i = 0; i < position; i++) {
-                AllAppsGridAdapter.AdapterItem item = items.get(i);
-                if (AllAppsGridAdapter.isIconViewType(item.viewType)) {
-                    // Break once we reach the desired row
-                    if (posItem != null && posItem.viewType == item.viewType &&
-                            posItem.rowIndex == item.rowIndex) {
-                        break;
-                    }
-                    // Otherwise, only account for the first icon in the row since they are the same
-                    // size within a row
-                    if (item.rowAppIndex == 0) {
-                        y += mViewHeights.get(item.viewType, 0);
-                    }
-                } else {
-                    // Rest of the views span the full width
-                    int elHeight = mViewHeights.get(item.viewType);
-                    if (elHeight == 0) {
-                        ViewHolder holder = findViewHolderForAdapterPosition(i);
-                        if (holder == null) {
-                            holder = getAdapter().createViewHolder(this, item.viewType);
-                            getAdapter().onBindViewHolder(holder, i);
-                            holder.itemView.measure(UNSPECIFIED, UNSPECIFIED);
-                            elHeight = holder.itemView.getMeasuredHeight();
-
-                            getRecycledViewPool().putRecycledView(holder);
-                        } else {
-                            elHeight = holder.itemView.getMeasuredHeight();
-                        }
-                    }
-                    y += elHeight;
-                }
-            }
-            mCachedScrollPositions.put(position, y);
-        }
-        return y;
-    }
-
     public int getScrollBarTop() {
         return getResources().getDimensionPixelOffset(R.dimen.all_apps_header_top_padding);
     }
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 879a159..3983f40 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -103,7 +103,8 @@
             new RecyclerView.OnScrollListener() {
                 @Override
                 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
-                    updateHeaderScroll(((AllAppsRecyclerView) recyclerView).getCurrentScrollY());
+                    updateHeaderScroll(
+                            ((AllAppsRecyclerView) recyclerView).computeVerticalScrollOffset());
                 }
             };
 
@@ -491,6 +492,7 @@
 
         mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
         mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
+        mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView);
 
         if (mUsingTabs) {
             mAH.get(AdapterHolder.MAIN).setup(mViewPager.getChildAt(0), mPersonalMatcher);
@@ -523,6 +525,7 @@
 
         mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
         mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
+        mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView);
     }
 
     protected void updateSearchResultsVisibility() {
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 8ec2aeb..73efd3f 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -68,7 +68,7 @@
                         mAnimator.cancel();
                     }
 
-                    int current = -mCurrentRV.getCurrentScrollY();
+                    int current = -mCurrentRV.computeVerticalScrollOffset();
                     boolean headerCollapsed = mHeaderCollapsed;
                     moved(current);
                     applyVerticalMove();
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
index 03529af..4be715b 100644
--- a/src/com/android/launcher3/allapps/SearchTransitionController.java
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -233,6 +233,7 @@
     private void onSearchChildAttached(View child) {
         // Avoid allocating hardware layers for alpha changes.
         child.forceHasOverlappingRendering(false);
+        child.setPivotY(0);
         if (mSearchToAzProgress > 0) {
             // Before the child is rendered, apply the animation including it to avoid flicker.
             updateSearchRecyclerViewProgress();
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index a95bd51..4fb732d 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -84,4 +84,9 @@
     public RecyclerView.ItemDecoration getDecorator() {
         return mDecoration;
     }
+
+    @Override
+    public void clearHighlightedItem() {
+        mHighlightedView = null;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
index bc52784..3890741 100644
--- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -58,4 +58,9 @@
      * Returns the item decorator.
      */
     public abstract RecyclerView.ItemDecoration getDecorator();
+
+    /**
+     * Clear the highlighted view.
+     */
+    public abstract void clearHighlightedItem();
 }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 0e36dc0..ef6350e 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -289,10 +289,8 @@
             "Enable option to log the keyboard latency for both atomic and controlled keyboard "
                     + "animations on an EditText");
 
-    // Change of wallpaper depth in widget picker is disabled for tests as it causes flakiness on
-    // very slow cuttlefish devices.
     public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag(
-            "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker.");
+            "ENABLE_WIDGET_PICKER_DEPTH", false, "Enable changing depth in widget picker.");
 
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 466b268..05b1984 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -31,6 +31,7 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherApps.PinItemRequest;
 import android.content.pm.ShortcutInfo;
 import android.content.res.Configuration;
@@ -62,6 +63,8 @@
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.PinRequestHelper;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.views.AbstractSlideInView;
 import com.android.launcher3.views.BaseDragLayer;
@@ -136,13 +139,25 @@
         mAccessibilityManager =
                 getApplicationContext().getSystemService(AccessibilityManager.class);
 
-        if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
-            setupShortcut();
-        } else {
-            if (!setupWidget()) {
-                // TODO: show error toast?
-                finish();
-            }
+        PackageUserKey targetApp = null;
+        switch (mRequest.getRequestType()) {
+            case PinItemRequest.REQUEST_TYPE_SHORTCUT:
+                targetApp = setupShortcut();
+                break;
+            case PinItemRequest.REQUEST_TYPE_APPWIDGET:
+                targetApp = setupWidget();
+                break;
+        }
+        if (targetApp == null) {
+            // TODO: show error toast?
+            finish();
+            return;
+        }
+        ApplicationInfo info = new PackageManagerHelper(this)
+                .getApplicationInfo(targetApp.mPackageName, targetApp.mUser, 0);
+        if (info == null) {
+            finish();
+            return;
         }
 
         WidgetCellPreview previewContainer = mWidgetCell.findViewById(
@@ -156,8 +171,10 @@
             logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_START);
         }
 
+        // Set the label synchronously instead of via IconCache as this is the first thing
+        // user sees
         TextView widgetAppName = findViewById(R.id.widget_appName);
-        widgetAppName.setText(getApplicationInfo().labelRes);
+        widgetAppName.setText(info.loadLabel(getPackageManager()));
 
         mSlideInView = findViewById(R.id.add_item_bottom_sheet);
         mSlideInView.addOnCloseListener(this);
@@ -246,20 +263,23 @@
         }
     }
 
-    private void setupShortcut() {
+    private PackageUserKey setupShortcut() {
         PinShortcutRequestActivityInfo shortcutInfo =
                 new PinShortcutRequestActivityInfo(mRequest, this);
         mWidgetCell.getWidgetView().setTag(new PendingAddShortcutInfo(shortcutInfo));
         applyWidgetItemAsync(
                 () -> new WidgetItem(shortcutInfo, mApp.getIconCache(), getPackageManager()));
+        return new PackageUserKey(
+                mRequest.getShortcutInfo().getPackage(),
+                mRequest.getShortcutInfo().getUserHandle());
     }
 
-    private boolean setupWidget() {
+    private PackageUserKey setupWidget() {
         LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
                 .fromProviderInfo(this, mRequest.getAppWidgetProviderInfo(this));
         if (widgetInfo.minSpanX > mIdp.numColumns || widgetInfo.minSpanY > mIdp.numRows) {
             // Cannot add widget
-            return false;
+            return null;
         }
         mWidgetCell.setRemoteViewsPreview(PinItemDragListener.getPreview(mRequest));
 
@@ -274,7 +294,7 @@
         mWidgetCell.getWidgetView().setTag(pendingInfo);
 
         applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
-        return true;
+        return new PackageUserKey(widgetInfo.provider.getPackageName(), widgetInfo.getUser());
     }
 
     private void applyWidgetItemAsync(final Supplier<WidgetItem> itemProvider) {
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 651372f..32237e2 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -593,6 +593,12 @@
 
         @UiEvent(doc = "User tapped on Share app system shortcut.")
         LAUNCHER_SYSTEM_SHORTCUT_APP_SHARE_TAP(1075),
+
+        @UiEvent(doc = "User has invoked split to right half from an app icon menu")
+        LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM(1199),
+
+        @UiEvent(doc = "User has invoked split to left half from an app icon menu")
+        LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP(1200)
         ;
 
         // ADD MORE
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index d3c9bc9..269baf0 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -112,12 +112,12 @@
 
             case TestProtocol.REQUEST_APPS_LIST_SCROLL_Y: {
                 return getLauncherUIProperty(Bundle::putInt,
-                        l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
+                        l -> l.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset());
             }
 
             case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
                 return getLauncherUIProperty(Bundle::putInt,
-                        l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
+                        l -> WidgetsFullSheet.getWidgetsView(l).computeVerticalScrollOffset());
             }
 
             case TestProtocol.REQUEST_TARGET_INSETS: {
diff --git a/src/com/android/launcher3/util/PendingSplitSelectInfo.java b/src/com/android/launcher3/util/PendingSplitSelectInfo.java
index ed02465..58c3be5 100644
--- a/src/com/android/launcher3/util/PendingSplitSelectInfo.java
+++ b/src/com/android/launcher3/util/PendingSplitSelectInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.util;
 
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
 
 /**
@@ -28,10 +29,13 @@
 
     private final int mStagedTaskId;
     private final int mStagePosition;
+    private final StatsLogManager.EventEnum mSource;
 
-    public PendingSplitSelectInfo(int stagedTaskId, int stagePosition) {
+    public PendingSplitSelectInfo(int stagedTaskId, int stagePosition,
+            StatsLogManager.EventEnum source) {
         this.mStagedTaskId = stagedTaskId;
         this.mStagePosition = stagePosition;
+        this.mSource = source;
     }
 
     public int getStagedTaskId() {
@@ -41,4 +45,8 @@
     public @StagePosition int getStagePosition() {
         return mStagePosition;
     }
+
+    public StatsLogManager.EventEnum getSource() {
+        return mSource;
+    }
 }
diff --git a/src/com/android/launcher3/util/ScrollableLayoutManager.java b/src/com/android/launcher3/util/ScrollableLayoutManager.java
new file mode 100644
index 0000000..17eaefd
--- /dev/null
+++ b/src/com/android/launcher3/util/ScrollableLayoutManager.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 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.launcher3.util;
+
+import android.content.Context;
+import android.util.SparseIntArray;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import androidx.recyclerview.widget.RecyclerView.State;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Extension of {@link GridLayoutManager} with support for smooth scrolling
+ */
+public class ScrollableLayoutManager extends GridLayoutManager {
+
+    // keyed on item type
+    protected final SparseIntArray mCachedSizes = new SparseIntArray();
+
+    private RecyclerView mRv;
+
+    /**
+     * Precalculated total height keyed on the item position. This is always incremental.
+     * Subclass can override {@link #incrementTotalHeight} to incorporate the layout logic.
+     * For example all-apps should have same values for items in same row,
+     *     sample values: 0, 10, 10, 10, 10, 20, 20, 20, 20
+     * whereas widgets will have strictly increasing values
+     *     sample values: 0, 10, 50, 60, 110
+     */
+
+    //
+    private int[] mTotalHeightCache = new int[1];
+    private int mLastValidHeightIndex = 0;
+
+    public ScrollableLayoutManager(Context context) {
+        super(context, 1, GridLayoutManager.VERTICAL, false);
+    }
+
+    @Override
+    public void onAttachedToWindow(RecyclerView view) {
+        super.onAttachedToWindow(view);
+        mRv = view;
+    }
+
+    @Override
+    public void layoutDecorated(@NonNull View child, int left, int top, int right, int bottom) {
+        super.layoutDecorated(child, left, top, right, bottom);
+        mCachedSizes.put(
+                mRv.getChildViewHolder(child).getItemViewType(), child.getMeasuredHeight());
+    }
+
+    @Override
+    public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right,
+            int bottom) {
+        super.layoutDecoratedWithMargins(child, left, top, right, bottom);
+        mCachedSizes.put(
+                mRv.getChildViewHolder(child).getItemViewType(), child.getMeasuredHeight());
+    }
+
+    @Override
+    public int computeVerticalScrollExtent(State state) {
+        return mRv == null ? 0 : mRv.getHeight();
+    }
+
+    @Override
+    public int computeVerticalScrollOffset(State state) {
+        Adapter adapter = mRv == null ? null : mRv.getAdapter();
+        if (adapter == null) {
+            return 0;
+        }
+        if (adapter.getItemCount() == 0 || getChildCount() == 0) {
+            return 0;
+        }
+        View child = getChildAt(0);
+        ViewHolder holder = mRv.findContainingViewHolder(child);
+        if (holder == null) {
+            return 0;
+        }
+        int itemPosition = holder.getLayoutPosition();
+        if (itemPosition < 0) {
+            return 0;
+        }
+        return getPaddingTop() + getItemsHeight(adapter, itemPosition) - getDecoratedTop(child);
+    }
+
+    @Override
+    public int computeVerticalScrollRange(State state) {
+        Adapter adapter = mRv == null ? null : mRv.getAdapter();
+        return adapter == null ? 0 : getItemsHeight(adapter, adapter.getItemCount());
+    }
+
+    /**
+     * Returns the sum of the height, in pixels, of this list adapter's items from index
+     * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
+     * it returns the full height of all the items.
+     *
+     * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
+     * sum of all items' height.
+     */
+    private int getItemsHeight(Adapter adapter, int untilIndex) {
+        final int totalItems = adapter.getItemCount();
+        if (mTotalHeightCache.length < (totalItems + 1)) {
+            mTotalHeightCache = new int[totalItems + 1];
+            mLastValidHeightIndex = 0;
+        }
+        if (untilIndex > totalItems) {
+            untilIndex = totalItems;
+        } else if (untilIndex < 0) {
+            untilIndex = 0;
+        }
+        if (untilIndex <= mLastValidHeightIndex) {
+            return mTotalHeightCache[untilIndex];
+        }
+
+        int totalItemsHeight = mTotalHeightCache[mLastValidHeightIndex];
+        for (int i = mLastValidHeightIndex; i < untilIndex; i++) {
+            totalItemsHeight = incrementTotalHeight(adapter, i, totalItemsHeight);
+            mTotalHeightCache[i + 1] = totalItemsHeight;
+        }
+        mLastValidHeightIndex = untilIndex;
+        return totalItemsHeight;
+    }
+
+    /**
+     * The current implementation assumes a linear list with every item taking up the whole row.
+     * Subclasses should override this method to account for any spanning logic
+     */
+    protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
+        return heightUntilLastPos + mCachedSizes.get(adapter.getItemViewType(position));
+    }
+
+    private void invalidateScrollCache() {
+        mLastValidHeightIndex = 0;
+    }
+
+    @Override
+    public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
+        super.onItemsAdded(recyclerView, positionStart, itemCount);
+        invalidateScrollCache();
+    }
+
+    @Override
+    public void onItemsChanged(RecyclerView recyclerView) {
+        super.onItemsChanged(recyclerView);
+        invalidateScrollCache();
+    }
+
+    @Override
+    public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
+        super.onItemsRemoved(recyclerView, positionStart, itemCount);
+        invalidateScrollCache();
+    }
+
+    @Override
+    public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
+        super.onItemsMoved(recyclerView, from, to, itemCount);
+        invalidateScrollCache();
+    }
+
+    @Override
+    public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
+            Object payload) {
+        super.onItemsUpdated(recyclerView, positionStart, itemCount, payload);
+        invalidateScrollCache();
+    }
+}
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index f14d985..88e1b22 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -16,12 +16,17 @@
 
 package com.android.launcher3.util;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
+
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.graphics.Rect;
 
 import androidx.annotation.IntDef;
 
+import com.android.launcher3.logging.StatsLogManager;
+
 import java.lang.annotation.Retention;
 
 public final class SplitConfigurationOptions {
@@ -170,4 +175,10 @@
         @StageType
         public int stageType = STAGE_TYPE_UNDEFINED;
     }
+
+    public static StatsLogManager.EventEnum getLogEventForPosition(@StagePosition int position) {
+        return position == STAGE_POSITION_TOP_OR_LEFT
+                ? LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP
+                : LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
+    }
 }
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 40e4ce1..3af2e3c 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -119,7 +119,6 @@
     // prevent jumping, this offset is applied as the user scrolls.
     protected int mTouchOffsetY;
     protected int mThumbOffsetY;
-    protected int mRvOffsetY;
 
     // Fast scroller popup
     private TextView mPopupView;
@@ -207,16 +206,11 @@
 
     public void setThumbOffsetY(int y) {
         if (mThumbOffsetY == y) {
-            int rvCurrentOffsetY = mRv.getCurrentScrollY();
-            if (mRvOffsetY != rvCurrentOffsetY) {
-                mRvOffsetY = mRv.getCurrentScrollY();
-            }
             return;
         }
         updatePopupY(y);
         mThumbOffsetY = y;
         invalidate();
-        mRvOffsetY = mRv.getCurrentScrollY();
     }
 
     public int getThumbOffsetY() {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 35fa7a4..5969e3e 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.util.AttributeSet;
-import android.util.SparseIntArray;
 import android.view.MotionEvent;
 
 import androidx.recyclerview.widget.LinearLayoutManager;
@@ -28,6 +27,7 @@
 
 import com.android.launcher3.FastScrollRecyclerView;
 import com.android.launcher3.R;
+import com.android.launcher3.util.ScrollableLayoutManager;
 
 /**
  * The widgets recycler view.
@@ -42,14 +42,6 @@
     private boolean mTouchDownOnScroller;
     private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
 
-    /**
-     * There is always 1 or 0 item of VIEW_TYPE_WIDGETS_LIST. Other types have fixes sizes, so the
-     * the size can be used for all other items of same type. Caching the last know size for
-     * VIEW_TYPE_WIDGETS_LIST allows us to use it to estimate full size even when
-     * VIEW_TYPE_WIDGETS_LIST is not visible on the screen.
-     */
-    private final SparseIntArray mCachedSizes = new SparseIntArray();
-
     public WidgetsRecyclerView(Context context) {
         this(context, null);
     }
@@ -68,9 +60,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        // create a layout manager with Launcher's context so that scroll position
-        // can be preserved during screen rotation.
-        setLayoutManager(new LinearLayoutManager(getContext()));
+        setLayoutManager(new ScrollableLayoutManager(getContext()));
     }
 
     @Override
@@ -114,7 +104,7 @@
         }
 
         // Skip early if, there no child laid out in the container.
-        int scrollY = getCurrentScrollY();
+        int scrollY = computeVerticalScrollOffset();
         if (scrollY < 0) {
             mScrollbar.setThumbOffsetY(-1);
             return;
@@ -164,39 +154,6 @@
     }
 
     /**
-     * Returns the sum of the height, in pixels, of this list adapter's items from index 0 until
-     * {@code untilIndex}.
-     *
-     * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
-     * sum of all items' height.
-     */
-    @Override
-    protected int getItemsHeight(int untilIndex) {
-        // Initialize cache
-        int childCount = getChildCount();
-        int startPosition;
-        if (childCount > 0
-                && ((startPosition = getChildAdapterPosition(getChildAt(0))) != NO_POSITION)) {
-            int loopCount = Math.min(getChildCount(), getAdapter().getItemCount() - startPosition);
-            for (int i = 0; i < loopCount; i++) {
-                mCachedSizes.put(
-                        mAdapter.getItemViewType(startPosition + i),
-                        getChildAt(i).getMeasuredHeight());
-            }
-        }
-
-        if (untilIndex > mAdapter.getItems().size()) {
-            untilIndex = mAdapter.getItems().size();
-        }
-        int totalItemsHeight = 0;
-        for (int i = 0; i < untilIndex; i++) {
-            int type = mAdapter.getItemViewType(i);
-            totalItemsHeight += mCachedSizes.get(type);
-        }
-        return totalItemsHeight;
-    }
-
-    /**
      * Provides dimensions of the header view that is shown at the top of a
      * {@link WidgetsRecyclerView}.
      */
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index a66b09a..df3cbff 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -525,7 +525,7 @@
     }
 
     protected int getAllAppsScroll(Launcher launcher) {
-        return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
+        return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset();
     }
 
     private void checkLauncherIntegrity(
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index be49974..07bfe4c 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -303,7 +303,7 @@
     }
 
     private int getWidgetsScroll(Launcher launcher) {
-        return getWidgetsView(launcher).getCurrentScrollY();
+        return getWidgetsView(launcher).computeVerticalScrollOffset();
     }
 
     private boolean isOptionsPopupVisible(Launcher launcher) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 80bc402..43766bf 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -110,8 +110,11 @@
     static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
     static final Pattern EVENT_TOUCH_CANCEL_TIS = getTouchEventPatternTIS("ACTION_CANCEL");
 
-    static final Pattern EVENT_KEY_BACK_DOWN = getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK");
-    static final Pattern EVENT_KEY_BACK_UP = getKeyEventPattern("ACTION_UP", "KEYCODE_BACK");
+    private static final Pattern EVENT_KEY_BACK_DOWN =
+            getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK");
+    private static final Pattern EVENT_KEY_BACK_UP =
+            getKeyEventPattern("ACTION_UP", "KEYCODE_BACK");
+    private static final Pattern EVENT_ON_BACK_INVOKED = Pattern.compile("onBackInvoked");
 
     private final String mLauncherPackage;
     private Boolean mIsLauncher3;
@@ -983,8 +986,12 @@
                 }
             }
             if (launcherVisible) {
-                expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
-                expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP);
+                if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) {
+                    expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ON_BACK_INVOKED);
+                } else {
+                    expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
+                    expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP);
+                }
             }
         }
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 5fab7eb..2c9fdb3 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -475,7 +475,9 @@
             // Since the destination can be on another page, we need to drag to the edge first
             // until we reach the target page
             while (targetDest.x > displayX || targetDest.x < 0) {
-                int edgeX = targetDest.x > 0 ? displayX : 0;
+                // Don't drag all the way to the edge to prevent touch events from getting out of
+                //screen bounds.
+                int edgeX = targetDest.x > 0 ? displayX - 1 : 1;
                 Point screenEdge = new Point(edgeX, targetDest.y);
                 Point finalDragStart = dragStart;
                 executeAndWaitForPageScroll(launcher,