Expand on gesture navigation error detection.

- Added some missing error detection:
    1. screenshot capture errors
    2. recents scrolling errors
    3. end-of-gesture callbacks
- Added some more explanation for OverviewInputConsumer selection reason
- Added logging the current task's package name to help identify gestures

Bug: 227514916
Bug: 243471493
Test: Ran launcher, performed multiple gestures and checked logs
Change-Id: I8b10cc75f8640a674c6fed6b06efa4763c9635a2
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 6e616f3..5a59f85 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -333,6 +333,12 @@
             return ActiveGestureErrorDetector.GestureEvent.STATE_GESTURE_COMPLETED;
         } else if (stateFlag == STATE_GESTURE_CANCELLED) {
             return ActiveGestureErrorDetector.GestureEvent.STATE_GESTURE_CANCELLED;
+        } else if (stateFlag == STATE_SCREENSHOT_CAPTURED) {
+            return ActiveGestureErrorDetector.GestureEvent.STATE_SCREENSHOT_CAPTURED;
+        } else if (stateFlag == STATE_CAPTURE_SCREENSHOT) {
+            return ActiveGestureErrorDetector.GestureEvent.STATE_CAPTURE_SCREENSHOT;
+        } else if (stateFlag == STATE_HANDLER_INVALIDATED) {
+            return ActiveGestureErrorDetector.GestureEvent.STATE_HANDLER_INVALIDATED;
         }
         return null;
     }
@@ -1221,6 +1227,8 @@
         // Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
         // or resumeLastTask().
         if (mRecentsView != null) {
+            ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
+                    .SET_ON_PAGE_TRANSITION_END_CALLBACK);
             mRecentsView.setOnPageTransitionEndCallback(
                     () -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED));
         } else {
@@ -1668,6 +1676,9 @@
      * handler (in case of quick switch).
      */
     private void cancelCurrentAnimation() {
+        ActiveGestureLog.INSTANCE.addLog(
+                "AbsSwipeUpHandler.cancelCurrentAnimation",
+                ActiveGestureErrorDetector.GestureEvent.CANCEL_CURRENT_ANIMATION);
         mCanceled = true;
         mCurrentShift.cancelAnimation();
 
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index bc2f551..38bf1fd 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -189,6 +189,8 @@
             return ActiveGestureErrorDetector.GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED;
         } else if (stateFlag == STATE_RECENTS_SCROLLING_FINISHED) {
             return ActiveGestureErrorDetector.GestureEvent.STATE_RECENTS_SCROLLING_FINISHED;
+        } else if (stateFlag == STATE_RECENTS_ANIMATION_CANCELED) {
+            return ActiveGestureErrorDetector.GestureEvent.STATE_RECENTS_ANIMATION_CANCELED;
         }
         return null;
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index fefef2f..542c0d4 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -32,6 +32,8 @@
 
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.util.ActiveGestureErrorDetector;
+import com.android.quickstep.util.ActiveGestureLog;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -172,7 +174,12 @@
      */
     @UiThread
     public void cleanupScreenshot() {
-        UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
+        UI_HELPER_EXECUTOR.execute(() -> {
+            ActiveGestureLog.INSTANCE.addLog(
+                    "cleanupScreenshot",
+                    ActiveGestureErrorDetector.GestureEvent.CLEANUP_SCREENSHOT);
+            mController.cleanupScreenshot();
+        });
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 0314761..5fb806d 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -36,6 +36,8 @@
 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;
@@ -136,6 +138,8 @@
                     // 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/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 82be3ec..ba4f549 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -721,8 +721,10 @@
             gestureState.updateRunningTask(taskInfo);
         }
         // Log initial state for the gesture.
-        ActiveGestureLog.INSTANCE.addLog(
-                "Current SystemUi state flags= " + mDeviceState.getSystemUiStateString());
+        ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current running task package name=")
+                .append(taskInfo == null ? "no running task" : taskInfo.getPackageName()));
+        ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current SystemUi state flags=")
+                .append(mDeviceState.getSystemUiStateString()));
         return gestureState;
     }
 
@@ -1024,12 +1026,27 @@
                             .append("activity == null, trying to use default input consumer"));
         }
 
-        if (activity.getRootView().hasWindowFocus()
-                || previousGestureState.isRunningAnimationToLauncher()
-                || (ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
-                    && forceOverviewInputConsumer)
-                || (ENABLE_QUICKSTEP_LIVE_TILE.get()
-                && gestureState.getActivityInterface().isInLiveTileMode())) {
+        boolean hasWindowFocus = activity.getRootView().hasWindowFocus();
+        boolean isPreviousGestureAnimatingToLauncher =
+                previousGestureState.isRunningAnimationToLauncher();
+        boolean forcingOverviewInputConsumer =
+                ASSISTANT_GIVES_LAUNCHER_FOCUS.get() && forceOverviewInputConsumer;
+        boolean isInLiveTileMode = ENABLE_QUICKSTEP_LIVE_TILE.get()
+                && gestureState.getActivityInterface().isInLiveTileMode();
+        reasonString.append(SUBSTRING_PREFIX)
+                .append(hasWindowFocus
+                        ? "activity has window focus"
+                        : (isPreviousGestureAnimatingToLauncher
+                                ? "previous gesture is still animating to launcher"
+                                : (forcingOverviewInputConsumer
+                                        ? "assistant gives launcher focus and forcing focus"
+                                        : (isInLiveTileMode
+                                                ? "device is in live mode"
+                                                : "all overview focus conditions failed"))));
+        if (hasWindowFocus
+                || isPreviousGestureAnimatingToLauncher
+                || forcingOverviewInputConsumer
+                || isInLiveTileMode) {
             reasonString.append(SUBSTRING_PREFIX)
                     .append("overview should have focus, using OverviewInputConsumer");
             return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat,
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
index 2462394..b9b5e7c 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -28,6 +28,8 @@
 import android.graphics.Point;
 import android.view.MotionEvent;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
@@ -41,6 +43,7 @@
 import com.android.quickstep.RecentsAnimationController;
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.util.ActiveGestureErrorDetector;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -99,7 +102,8 @@
         mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize;
 
         // Init states
-        mStateCallback = new MultiStateCallback(STATE_NAMES);
+        mStateCallback = new MultiStateCallback(
+                STATE_NAMES, ProgressDelegateInputConsumer::getTrackedEventForState);
         mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
                 this::endRemoteAnimation);
         mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED,
@@ -109,6 +113,14 @@
         mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
     }
 
+    @Nullable
+    private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) {
+        if (stateFlag == STATE_HANDLER_INVALIDATED) {
+            return ActiveGestureErrorDetector.GestureEvent.STATE_HANDLER_INVALIDATED;
+        }
+        return null;
+    }
+
     @Override
     public int getType() {
         return TYPE_PROGRESS_DELEGATE;
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
index 78075de..54f632a 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -29,11 +29,24 @@
  */
 public class ActiveGestureErrorDetector {
 
+    /**
+     * Enums associated to gesture navigation events.
+     */
     public enum GestureEvent {
         MOTION_DOWN, MOTION_UP, SET_END_TARGET, ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION,
-        FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, STATE_GESTURE_STARTED,
-        STATE_GESTURE_COMPLETED, STATE_GESTURE_CANCELLED, STATE_END_TARGET_ANIMATION_FINISHED,
-        STATE_RECENTS_SCROLLING_FINISHED
+        FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK,
+        CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT,
+
+        /**
+         * These GestureEvents are specifically associated to state flags that get set in
+         * {@link com.android.quickstep.MultiStateCallback}. If a state flag needs to be tracked
+         * for error detection, an enum should be added here and that state flag-enum pair should
+         * be added to the state flag's container class' {@code getTrackedEventForState} method.
+         */
+        STATE_GESTURE_STARTED, STATE_GESTURE_COMPLETED, STATE_GESTURE_CANCELLED,
+        STATE_END_TARGET_ANIMATION_FINISHED, STATE_RECENTS_SCROLLING_FINISHED,
+        STATE_CAPTURE_SCREENSHOT, STATE_SCREENSHOT_CAPTURED, STATE_HANDLER_INVALIDATED,
+        STATE_RECENTS_ANIMATION_CANCELED
     }
 
     private ActiveGestureErrorDetector() {}
@@ -90,6 +103,14 @@
                                         + "before/without startRecentsAnimation.",
                                 writer);
                         break;
+                    case CLEANUP_SCREENSHOT:
+                        errorDetected |= printErrorIfTrue(
+                                !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
+                                /* errorMessage= */ prefix + "\t\trecents activity screenshot was "
+                                        + "cleaned up before/without STATE_SCREENSHOT_CAPTURED "
+                                        + "being set.",
+                                writer);
+                        break;
                     case STATE_GESTURE_COMPLETED:
                         errorDetected |= printErrorIfTrue(
                                 !encounteredEvents.contains(GestureEvent.MOTION_UP),
@@ -114,12 +135,39 @@
                                         + "before/without STATE_GESTURE_STARTED.",
                                 writer);
                         break;
+                    case STATE_SCREENSHOT_CAPTURED:
+                        errorDetected |= printErrorIfTrue(
+                                !encounteredEvents.contains(GestureEvent.STATE_CAPTURE_SCREENSHOT),
+                                /* errorMessage= */ prefix + "\t\tSTATE_SCREENSHOT_CAPTURED set "
+                                        + "before/without STATE_CAPTURE_SCREENSHOT.",
+                                writer);
+                        break;
+                    case STATE_RECENTS_SCROLLING_FINISHED:
+                        errorDetected |= printErrorIfTrue(
+                                !encounteredEvents.contains(
+                                        GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK),
+                                /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_SCROLLING_FINISHED "
+                                        + "set before/without calling "
+                                        + "setOnPageTransitionEndCallback.",
+                                writer);
+                        break;
+                    case STATE_RECENTS_ANIMATION_CANCELED:
+                        errorDetected |= printErrorIfTrue(
+                                !encounteredEvents.contains(
+                                        GestureEvent.START_RECENTS_ANIMATION),
+                                /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED "
+                                        + "set before/without startRecentsAnimation.",
+                                writer);
+                        break;
                     case MOTION_DOWN:
                     case SET_END_TARGET:
                     case START_RECENTS_ANIMATION:
+                    case SET_ON_PAGE_TRANSITION_END_CALLBACK:
+                    case CANCEL_CURRENT_ANIMATION:
                     case STATE_GESTURE_STARTED:
                     case STATE_END_TARGET_ANIMATION_FINISHED:
-                    case STATE_RECENTS_SCROLLING_FINISHED:
+                    case STATE_CAPTURE_SCREENSHOT:
+                    case STATE_HANDLER_INVALIDATED:
                     default:
                         // No-Op
                 }
@@ -183,6 +231,39 @@
                             + "STATE_GESTURE_COMPLETED and STATE_GESTURE_CANCELLED weren't.",
                     writer);
 
+            errorDetected |= printErrorIfTrue(
+                    /* condition= */ encounteredEvents.contains(
+                            GestureEvent.STATE_CAPTURE_SCREENSHOT)
+                            && !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED),
+                    /* errorMessage= */ prefix + "\t\tSTATE_CAPTURE_SCREENSHOT was set, but "
+                            + "STATE_SCREENSHOT_CAPTURED wasn't.",
+                    writer);
+
+            errorDetected |= printErrorIfTrue(
+                    /* condition= */ encounteredEvents.contains(
+                            GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK)
+                            && !encounteredEvents.contains(
+                                    GestureEvent.STATE_RECENTS_SCROLLING_FINISHED),
+                    /* errorMessage= */ prefix + "\t\tsetOnPageTransitionEndCallback called, but "
+                            + "STATE_RECENTS_SCROLLING_FINISHED wasn't set.",
+                    writer);
+
+            errorDetected |= printErrorIfTrue(
+                    /* condition= */ !encounteredEvents.contains(
+                            GestureEvent.CANCEL_CURRENT_ANIMATION)
+                            && !encounteredEvents.contains(GestureEvent.STATE_HANDLER_INVALIDATED),
+                    /* errorMessage= */ prefix + "\t\tAbsSwipeUpHandler.cancelCurrentAnimation "
+                            + "wasn't called and STATE_HANDLER_INVALIDATED wasn't set.",
+                    writer);
+
+            errorDetected |= printErrorIfTrue(
+                    /* condition= */ encounteredEvents.contains(
+                            GestureEvent.STATE_RECENTS_ANIMATION_CANCELED)
+                            && !encounteredEvents.contains(GestureEvent.CLEANUP_SCREENSHOT),
+                    /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED was set but "
+                            + "the task screenshot wasn't cleaned up.",
+                    writer);
+
             if (!errorDetected) {
                 writer.println(prefix + "\t\tNo errors detected.");
             }
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
index 9f08010..40eb31b 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
@@ -138,14 +138,10 @@
         List<EventEntry> lastEventEntries = lastEventLog.eventEntries;
         EventEntry lastEntry = lastEventEntries.size() > 0
                 ? lastEventEntries.get(lastEventEntries.size() - 1) : null;
-        EventEntry secondLastEntry = lastEventEntries.size() > 1
-                ? lastEventEntries.get(lastEventEntries.size() - 2) : null;
 
         // Update the last EventEntry if it's a duplicate
-        if (isEntrySame(lastEntry, type, event, compoundString, gestureEvent)
-                && isEntrySame(secondLastEntry, type, event, compoundString, gestureEvent)) {
-            lastEntry.update(type, event, extras, compoundString, gestureEvent);
-            secondLastEntry.duplicateCount++;
+        if (isEntrySame(lastEntry, type, event, extras, compoundString, gestureEvent)) {
+            lastEntry.duplicateCount++;
             return;
         }
         EventEntry eventEntry = new EventEntry();
@@ -223,11 +219,13 @@
             EventEntry entry,
             int type,
             String event,
+            float extras,
             CompoundString compoundString,
             ActiveGestureErrorDetector.GestureEvent gestureEvent) {
         return entry != null
                 && entry.type == type
                 && entry.event.equals(event)
+                && Float.compare(entry.extras, extras) == 0
                 && entry.mCompoundString.equals(compoundString)
                 && entry.gestureEvent == gestureEvent;
     }
@@ -342,7 +340,7 @@
                 return false;
             }
             CompoundString other = (CompoundString) obj;
-            return mIsNoOp && other.mIsNoOp && Objects.equals(mSubstrings, other.mSubstrings);
+            return (mIsNoOp == other.mIsNoOp) && Objects.equals(mSubstrings, other.mSubstrings);
         }
     }
 }