Delay onBackStarted() until the pointer is pilfered.

This only delays the dispatching of the start event. We still start back navigation as early as the first MOVE, to allow more time to receive back navigation info and avoid jank.

Bug: 308550613
Bug: 301195601
Test: atest OnBackInvokedCallbackGestureTest
Test: atest BackAnimationControllerTest
Flag: NONE

Change-Id: I0802fd716dc5dbce7071e083bd373df5c5ddcc4c
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
index c980906..8d8dc10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -49,6 +49,11 @@
             @BackEvent.SwipeEdge int swipeEdge);
 
     /**
+     * Called when the input pointers are pilfered.
+     */
+    void onPilferPointers();
+
+    /**
      * Sets whether the back gesture is past the trigger threshold or not.
      */
     void setTriggerBack(boolean triggerBack);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 5843635..3e1ef8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -110,6 +110,7 @@
 
     /** Tracks if we should start the back gesture on the next motion move event */
     private boolean mShouldStartOnNextMoveEvent = false;
+    private boolean mOnBackStartDispatched = false;
 
     private final FlingAnimationUtils mFlingAnimationUtils;
 
@@ -291,6 +292,11 @@
         }
 
         @Override
+        public void onPilferPointers() {
+            BackAnimationController.this.onPilferPointers();
+        }
+
+        @Override
         public void setTriggerBack(boolean triggerBack) {
             mShellExecutor.execute(() -> BackAnimationController.this.setTriggerBack(triggerBack));
         }
@@ -371,6 +377,16 @@
         return null;
     }
 
+    @VisibleForTesting
+    void onPilferPointers() {
+        mCurrentTracker.updateStartLocation();
+        // Dispatch onBackStarted, only to app callbacks.
+        // System callbacks will receive onBackStarted when the remote animation starts.
+        if (!shouldDispatchToAnimator()) {
+            tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+        }
+    }
+
     /**
      * Called when a new motion event needs to be transferred to this
      * {@link BackAnimationController}
@@ -470,12 +486,15 @@
             mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
             // App is handling back animation. Cancel system animation latency tracking.
             cancelLatencyTracking();
-            dispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+            tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
         }
     }
 
     private void onMove() {
-        if (!mBackGestureStarted || mBackNavigationInfo == null || mActiveCallback == null) {
+        if (!mBackGestureStarted
+                || mBackNavigationInfo == null
+                || mActiveCallback == null
+                || !mOnBackStartDispatched) {
             return;
         }
         // Skip dispatching if the move corresponds to the queued instead of the current gesture
@@ -511,13 +530,14 @@
                 && mBackNavigationInfo.isPrepareRemoteAnimation();
     }
 
-    private void dispatchOnBackStarted(IOnBackInvokedCallback callback,
+    private void tryDispatchOnBackStarted(IOnBackInvokedCallback callback,
             BackMotionEvent backEvent) {
-        if (callback == null) {
+        if (callback == null || mOnBackStartDispatched) {
             return;
         }
         try {
             callback.onBackStarted(backEvent);
+            mOnBackStartDispatched = true;
         } catch (RemoteException e) {
             Log.e(TAG, "dispatchOnBackStarted error: ", e);
         }
@@ -815,6 +835,8 @@
     void finishBackNavigation(boolean triggerBack) {
         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()");
         mActiveCallback = null;
+        mShouldStartOnNextMoveEvent = false;
+        mOnBackStartDispatched = false;
         mShellBackAnimationRegistry.resetDefaultCrossActivity();
         cancelLatencyTracking();
         if (mBackNavigationInfo != null) {
@@ -896,7 +918,8 @@
                                                                     ::onBackAnimationFinished));
 
                                     if (apps.length >= 1) {
-                                        dispatchOnBackStarted(
+                                        mCurrentTracker.updateStartLocation();
+                                        tryDispatchOnBackStarted(
                                                 mActiveCallback,
                                                 mCurrentTracker.createStartEvent(apps[0]));
                                     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index 19eb928..4bd56d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -104,6 +104,15 @@
         mStartThresholdX = mInitTouchX;
     }
 
+    /** Update the start location used to compute the progress
+     * to the latest touch location.
+     */
+    void updateStartLocation() {
+        mInitTouchX = mLatestTouchX;
+        mInitTouchY = mLatestTouchY;
+        mStartThresholdX = mInitTouchX;
+    }
+
     void reset() {
         mInitTouchX = 0;
         mInitTouchY = 0;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 0395a9b..771876f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -182,8 +182,7 @@
     }
 
     private void triggerBackGesture() {
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        doMotionEvent(MotionEvent.ACTION_MOVE, 0);
+        doStartEvents(0, 0);
         mController.setTriggerBack(true);
     }
 
@@ -244,10 +243,7 @@
                 /* enableAnimation = */ true,
                 /* isAnimationCallback = */ false);
 
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-
-        // Check that back start and progress is dispatched when first move.
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
 
@@ -270,10 +266,8 @@
                 /* enableAnimation = */ true,
                 /* isAnimationCallback = */ true);
 
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-
         // Check that back start and progress is dispatched when first move.
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100, 3000);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
 
@@ -359,8 +353,7 @@
                 .injectInputEvent(any(KeyEvent.class), any(Integer.class));
 
         // Verify that we start accepting gestures again once transition finishes.
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
         verify(mAnimatorCallback).onBackStarted(any());
@@ -399,8 +392,7 @@
                 .injectInputEvent(any(KeyEvent.class), any(Integer.class));
 
         // Verify that we start accepting gestures again once transition finishes.
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
         verify(mAnimatorCallback).onBackStarted(any());
@@ -427,8 +419,7 @@
         mShellExecutor.flushAll();
         reset(mAnimatorCallback);
 
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
         simulateRemoteAnimationStart();
         verify(mAnimatorCallback).onBackStarted(any());
     }
@@ -441,9 +432,7 @@
                 /* enableAnimation = */ true,
                 /* isAnimationCallback = */ false);
 
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        // Check that back start and progress is dispatched when first move.
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
         verify(mAnimatorCallback).onBackStarted(any());
@@ -563,10 +552,8 @@
                 /* enableAnimation = */ true,
                 /* isAnimationCallback = */ false);
 
-        doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-
         // Check that back start and progress is dispatched when first move.
-        doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        doStartEvents(0, 100);
 
         simulateRemoteAnimationStart();
 
@@ -593,6 +580,15 @@
                 /* swipeEdge */ BackEvent.EDGE_LEFT);
     }
 
+    /**
+     * Simulate event sequence that starts a back navigation.
+     */
+    private void doStartEvents(int startX, int moveX) {
+        doMotionEvent(MotionEvent.ACTION_DOWN, startX);
+        mController.onPilferPointers();
+        doMotionEvent(MotionEvent.ACTION_MOVE, moveX);
+    }
+
     private void simulateRemoteAnimationStart() throws RemoteException {
         RemoteAnimationTarget animationTarget = createAnimationTarget();
         RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget};
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 3dfd292..13058eb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -69,7 +69,6 @@
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
@@ -81,6 +80,7 @@
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputChannelCompat;
@@ -1108,6 +1108,7 @@
                             // Capture inputs
                             mInputMonitor.pilferPointers();
                             if (mBackAnimation != null) {
+                                mBackAnimation.onPilferPointers();
                                 // Notify FalsingManager that an intentional gesture has occurred.
                                 mFalsingManager.isFalseTouch(BACK_GESTURE);
                             }