Use TWO_FINGER_SWIPE classification for touchpad scroll events

This allows apps to distinguish the fake finger created for touchpad
scrolling from an actual finger.

Bug: 246758376
Test: add classification to InputDispatcher's outbound event logs and
      check the new value is used when scrolling
Change-Id: Ia90f9984e75ad6fde2d0e42628ab42eab371b7a5
diff --git a/include/android/input.h b/include/android/input.h
index 8cd9e95..7080386 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -840,6 +840,12 @@
      * This classification type should be used to accelerate the long press behaviour.
      */
     AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS = 2,
+    /**
+     * Classification constant: touchpad two-finger swipe.
+     *
+     * The current event stream represents the user swiping with two fingers on a touchpad.
+     */
+    AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE = 3,
 };
 
 /**
diff --git a/include/input/Input.h b/include/input/Input.h
index 2dd651e..172e5b4 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -289,6 +289,10 @@
      * The current gesture likely represents a user intentionally exerting force on the touchscreen.
      */
     DEEP_PRESS = AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS,
+    /**
+     * The current gesture represents the user swiping with two fingers on a touchpad.
+     */
+    TWO_FINGER_SWIPE = AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE,
 };
 
 /**
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 2b7483d..579b28e 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -87,6 +87,8 @@
             return "AMBIGUOUS_GESTURE";
         case MotionClassification::DEEP_PRESS:
             return "DEEP_PRESS";
+        case MotionClassification::TWO_FINGER_SWIPE:
+            return "TWO_FINGER_SWIPE";
     }
 }
 
diff --git a/services/inputflinger/InputProcessor.cpp b/services/inputflinger/InputProcessor.cpp
index 0749441..126cb33 100644
--- a/services/inputflinger/InputProcessor.cpp
+++ b/services/inputflinger/InputProcessor.cpp
@@ -22,6 +22,7 @@
 #include <android-base/stringprintf.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
+#include <input/Input.h>
 #include <inttypes.h>
 #include <log/log.h>
 #include <algorithm>
@@ -436,7 +437,13 @@
             mQueuedListener.notifyMotion(args);
         } else {
             NotifyMotionArgs newArgs(*args);
-            newArgs.classification = mMotionClassifier->classify(newArgs);
+            const MotionClassification newClassification = mMotionClassifier->classify(newArgs);
+            LOG_ALWAYS_FATAL_IF(args->classification != MotionClassification::NONE &&
+                                        newClassification != MotionClassification::NONE,
+                                "Conflicting classifications %s (new) and %s (old)!",
+                                motionClassificationToString(newClassification),
+                                motionClassificationToString(args->classification));
+            newArgs.classification = newClassification;
             mQueuedListener.notifyMotion(&newArgs);
         }
     } // release lock
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 8c241f2..4cd2cce 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1950,7 +1950,8 @@
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                       MotionClassification::NONE);
         mCurrentMotionAborted = true;
     }
 }
@@ -1970,7 +1971,8 @@
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                           MotionClassification::NONE);
         }
     } else {
         // There may be pointers going up and pointers going down and pointers moving
@@ -2005,7 +2007,8 @@
                            mLastCookedState.cookedPointerData.pointerProperties,
                            mLastCookedState.cookedPointerData.pointerCoords,
                            mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                           MotionClassification::NONE);
             dispatchedIdBits.clearBit(upId);
             mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId);
         }
@@ -2020,7 +2023,8 @@
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                           MotionClassification::NONE);
         }
 
         // Dispatch pointer down events using the new pointer locations.
@@ -2038,7 +2042,8 @@
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
-                           downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                           downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                           MotionClassification::NONE);
         }
     }
 }
@@ -2054,7 +2059,7 @@
                        mLastCookedState.cookedPointerData.pointerCoords,
                        mLastCookedState.cookedPointerData.idToIndex,
                        mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision,
-                       mOrientedYPrecision, mDownTime);
+                       mOrientedYPrecision, mDownTime, MotionClassification::NONE);
         mSentHoverEnter = false;
     }
 }
@@ -2071,7 +2076,8 @@
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex,
                            mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
-                           mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                           mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                           MotionClassification::NONE);
             mSentHoverEnter = true;
         }
 
@@ -2081,7 +2087,8 @@
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex,
                        mCurrentCookedState.cookedPointerData.hoveringIdBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                       MotionClassification::NONE);
     }
 }
 
@@ -2098,7 +2105,8 @@
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                       MotionClassification::NONE);
     }
 }
 
@@ -2115,7 +2123,8 @@
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
-                       mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+                       mOrientedXPrecision, mOrientedYPrecision, mDownTime,
+                       MotionClassification::NONE);
     }
 }
 
@@ -2502,6 +2511,10 @@
     // Send events!
     int32_t metaState = getContext()->getGlobalMetaState();
     int32_t buttonState = mCurrentCookedState.buttonState;
+    const MotionClassification classification =
+            mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE
+            ? MotionClassification::TWO_FINGER_SWIPE
+            : MotionClassification::NONE;
 
     uint32_t flags = 0;
 
@@ -2542,7 +2555,7 @@
                            flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                            mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
                            mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
-                           mPointerGesture.downTime);
+                           mPointerGesture.downTime, classification);
 
             dispatchedGestureIdBits.clear();
         } else {
@@ -2561,7 +2574,7 @@
                                AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
                                mPointerGesture.lastGestureCoords,
                                mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
-                               0, mPointerGesture.downTime);
+                               0, mPointerGesture.downTime, classification);
 
                 dispatchedGestureIdBits.clearBit(id);
             }
@@ -2575,7 +2588,7 @@
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
                        mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
-                       mPointerGesture.downTime);
+                       mPointerGesture.downTime, classification);
     }
 
     // Send motion events for all pointers that went down.
@@ -2595,7 +2608,7 @@
                            mPointerGesture.currentGestureProperties,
                            mPointerGesture.currentGestureCoords,
                            mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
-                           0, mPointerGesture.downTime);
+                           0, mPointerGesture.downTime, classification);
         }
     }
 
@@ -2606,7 +2619,8 @@
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
                        mPointerGesture.currentGestureIdToIndex,
-                       mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime);
+                       mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime,
+                       MotionClassification::NONE);
     } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) {
         // Synthesize a hover move event after all pointers go up to indicate that
         // the pointer is hovering again even if the user is not currently touching
@@ -2653,6 +2667,10 @@
 }
 
 void TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+    const MotionClassification classification =
+            mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE
+            ? MotionClassification::TWO_FINGER_SWIPE
+            : MotionClassification::NONE;
     // Cancel previously dispatches pointers.
     if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
@@ -2661,7 +2679,7 @@
                        metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
                        mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
-                       0, 0, mPointerGesture.downTime);
+                       0, 0, mPointerGesture.downTime, classification);
     }
 
     // Reset the current pointer gesture.
@@ -3611,7 +3629,8 @@
                                       int32_t edgeFlags, const PointerProperties* properties,
                                       const PointerCoords* coords, const uint32_t* idToIndex,
                                       BitSet32 idBits, int32_t changedId, float xPrecision,
-                                      float yPrecision, nsecs_t downTime) {
+                                      float yPrecision, nsecs_t downTime,
+                                      MotionClassification classification) {
     PointerCoords pointerCoords[MAX_POINTERS];
     PointerProperties pointerProperties[MAX_POINTERS];
     uint32_t pointerCount = 0;
@@ -3659,9 +3678,9 @@
                   [this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); });
     NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
                           policyFlags, action, actionButton, flags, metaState, buttonState,
-                          MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
-                          pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
-                          downTime, std::move(frames));
+                          classification, edgeFlags, pointerCount, pointerProperties, pointerCoords,
+                          xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime,
+                          std::move(frames));
     getListener().notifyMotion(&args);
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index bd4cff6..31806e1 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -779,7 +779,8 @@
                         int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
                         int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties,
                         const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
-                        int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
+                        int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime,
+                        MotionClassification classification);
 
     // Updates pointer coords and properties for pointers with specified ids that have moved.
     // Returns true if any of them changed.
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index ee6993c..097659b 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -9751,6 +9751,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(
             assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
 
@@ -9772,6 +9773,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], 0,
                                                 movingDistance * mPointerMovementScale, 1, 0, 0, 0,
                                                 0, 0, 0, 0));
@@ -9809,6 +9811,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(
             assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
 
@@ -9830,6 +9833,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
     // New coordinate is the scaled relative coordinate from the initial coordinate.
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], 0,
                                                 movingDistance * mPointerMovementScale, 1, 0, 0, 0,
@@ -9863,6 +9867,7 @@
     ASSERT_EQ(1U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     // One pointer for PRESS, and its coordinate is used as the origin for pointer coordinates.
     ASSERT_NO_FATAL_FAILURE(
             assertPointerCoords(motionArgs.pointerCoords[0], 0, 0, 1, 0, 0, 0, 0, 0, 0, 0));
@@ -9892,9 +9897,11 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_EQ(2U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN, motionArgs.action & AMOTION_EVENT_ACTION_MASK);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     // Two pointers' scaled relative coordinates from their initial centroid.
     // Initial y coordinates are 0 as y1 and y2 have the same value.
     float cookedX1 = (x1 - x2) / 2 * mPointerXZoomScale;
@@ -9924,6 +9931,7 @@
     ASSERT_EQ(2U, motionArgs.pointerCount);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], cookedX1,
                                                 movingDistance * 2 * mPointerMovementScale, 1, 0, 0,
                                                 0, 0, 0, 0, 0));