Report three- and four-finger swipes

The dispatcher still needs to be modified to only dispatch these to
SysUI windows.

Bug: 251196347
Test: check events received by a custom tester app, and touches shown by
      pointer location overlay
Test: atest inputflinger_tests
Change-Id: I3a7211d4a67e6388231bef158d3748c2e72e128d
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 23d7fdf..3b51be8 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -92,7 +92,7 @@
         mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
         mPointerController(getContext()->getPointerController(getDeviceId())),
         mStateConverter(deviceContext),
-        mGestureConverter(*getContext(), getDeviceId()) {
+        mGestureConverter(*getContext(), deviceContext, getDeviceId()) {
     mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
     mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
     // Even though we don't explicitly delete copy/move semantics, it's safe to
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 8600065..ffc0523 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -17,6 +17,7 @@
 #include "gestures/GestureConverter.h"
 
 #include <android/input.h>
+#include <linux/input-event-codes.h>
 
 #include "TouchCursorInputMapperCommon.h"
 #include "input/Input.h"
@@ -44,10 +45,14 @@
 
 } // namespace
 
-GestureConverter::GestureConverter(InputReaderContext& readerContext, int32_t deviceId)
+GestureConverter::GestureConverter(InputReaderContext& readerContext,
+                                   const InputDeviceContext& deviceContext, int32_t deviceId)
       : mDeviceId(deviceId),
         mReaderContext(readerContext),
-        mPointerController(readerContext.getPointerController(deviceId)) {}
+        mPointerController(readerContext.getPointerController(deviceId)) {
+    deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
+    deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
+}
 
 void GestureConverter::reset() {
     mButtonState = 0;
@@ -60,6 +65,15 @@
             return {handleMove(when, readTime, gesture)};
         case kGestureTypeButtonsChange:
             return handleButtonsChange(when, readTime, gesture);
+        case kGestureTypeSwipe:
+            return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx,
+                                          gesture.details.swipe.dy);
+        case kGestureTypeFourFingerSwipe:
+            return handleMultiFingerSwipe(when, readTime, 4, gesture.details.four_finger_swipe.dx,
+                                          gesture.details.four_finger_swipe.dy);
+        case kGestureTypeSwipeLift:
+        case kGestureTypeFourFingerSwipeLift:
+            return handleMultiFingerSwipeLift(when, readTime);
         default:
             // TODO(b/251196347): handle more gesture types.
             return {};
@@ -67,11 +81,6 @@
 }
 
 NotifyArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
-    PointerProperties props;
-    props.clear();
-    props.id = 0;
-    props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
     float deltaX = gesture.details.move.dx;
     float deltaY = gesture.details.move.dy;
     rotateDelta(mOrientation, &deltaX, &deltaY);
@@ -93,7 +102,8 @@
 
     const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
     return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState,
-                          /* pointerCount= */ 1, &props, &coords, xCursorPosition, yCursorPosition);
+                          /* pointerCount= */ 1, mFingerProps.data(), &coords, xCursorPosition,
+                          yCursorPosition);
 }
 
 std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime,
@@ -103,11 +113,6 @@
     mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
     mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
 
-    PointerProperties props;
-    props.clear();
-    props.id = 0;
-    props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
     float xCursorPosition, yCursorPosition;
     mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
 
@@ -131,15 +136,16 @@
             newButtonState |= actionButton;
             pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
                                                  actionButton, newButtonState,
-                                                 /* pointerCount= */ 1, &props, &coords,
-                                                 xCursorPosition, yCursorPosition));
+                                                 /* pointerCount= */ 1, mFingerProps.data(),
+                                                 &coords, xCursorPosition, yCursorPosition));
         }
     }
     if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
         mDownTime = when;
         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
                                      /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
-                                     &props, &coords, xCursorPosition, yCursorPosition));
+                                     mFingerProps.data(), &coords, xCursorPosition,
+                                     yCursorPosition));
     }
     out.splice(out.end(), pressEvents);
 
@@ -155,19 +161,109 @@
             newButtonState &= ~actionButton;
             out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
                                          actionButton, newButtonState, /* pointerCount= */ 1,
-                                         &props, &coords, xCursorPosition, yCursorPosition));
+                                         mFingerProps.data(), &coords, xCursorPosition,
+                                         yCursorPosition));
         }
     }
     if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
         coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
         out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
-                                     newButtonState, /* pointerCount= */ 1, &props, &coords,
-                                     xCursorPosition, yCursorPosition));
+                                     newButtonState, /* pointerCount= */ 1, mFingerProps.data(),
+                                     &coords, xCursorPosition, yCursorPosition));
     }
     mButtonState = newButtonState;
     return out;
 }
 
+[[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when,
+                                                                             nsecs_t readTime,
+                                                                             uint32_t fingerCount,
+                                                                             float dx, float dy) {
+    std::list<NotifyArgs> out = {};
+    float xCursorPosition, yCursorPosition;
+    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
+        // If the user changes the number of fingers mid-way through a swipe (e.g. they start with
+        // three and then put a fourth finger down), the gesture library will treat it as two
+        // separate swipes with an appropriate lift event between them, so we don't have to worry
+        // about the finger count changing mid-swipe.
+        mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE;
+        mSwipeFingerCount = fingerCount;
+
+        constexpr float FAKE_FINGER_SPACING = 100;
+        float xCoord = xCursorPosition - FAKE_FINGER_SPACING * (mSwipeFingerCount - 1) / 2;
+        for (size_t i = 0; i < mSwipeFingerCount; i++) {
+            PointerCoords& coords = mFakeFingerCoords[i];
+            coords.clear();
+            coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCoord);
+            coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+            coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+            xCoord += FAKE_FINGER_SPACING;
+        }
+
+        mDownTime = when;
+        out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
+                                     /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
+                                     mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
+                                     yCursorPosition));
+        for (size_t i = 1; i < mSwipeFingerCount; i++) {
+            out.push_back(makeMotionArgs(when, readTime,
+                                         AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                                 (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                                         /* actionButton= */ 0, mButtonState,
+                                         /* pointerCount= */ i + 1, mFingerProps.data(),
+                                         mFakeFingerCoords.data(), xCursorPosition,
+                                         yCursorPosition));
+        }
+    }
+    for (size_t i = 0; i < mSwipeFingerCount; i++) {
+        PointerCoords& coords = mFakeFingerCoords[i];
+        coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) + dx);
+        // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate
+        // the Y values.
+        coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) - dy);
+    }
+    float xOffset = dx / (mXAxisInfo.maxValue - mXAxisInfo.minValue);
+    // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate the Y
+    // values.
+    float yOffset = -dy / (mYAxisInfo.maxValue - mYAxisInfo.minValue);
+    mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset);
+    mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
+    out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
+                                 mButtonState, /* pointerCount= */ mSwipeFingerCount,
+                                 mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
+                                 yCursorPosition));
+    return out;
+}
+
+[[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipeLift(nsecs_t when,
+                                                                                 nsecs_t readTime) {
+    std::list<NotifyArgs> out = {};
+    if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
+        return out;
+    }
+    float xCursorPosition, yCursorPosition;
+    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
+    mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);
+
+    for (size_t i = mSwipeFingerCount; i > 1; i--) {
+        out.push_back(makeMotionArgs(when, readTime,
+                                     AMOTION_EVENT_ACTION_POINTER_UP |
+                                             ((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                                     /* actionButton= */ 0, mButtonState, /* pointerCount= */ i,
+                                     mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
+                                     yCursorPosition));
+    }
+    out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP,
+                                 /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
+                                 mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
+                                 yCursorPosition));
+    mCurrentClassification = MotionClassification::NONE;
+    mSwipeFingerCount = 0;
+    return out;
+}
+
 NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
                                                   int32_t actionButton, int32_t buttonState,
                                                   uint32_t pointerCount,
@@ -181,10 +277,10 @@
                             mPointerController->getDisplayId(), /* policyFlags= */ 0, action,
                             /* actionButton= */ actionButton, /* flags= */ 0,
                             mReaderContext.getGlobalMetaState(), buttonState,
-                            MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
-                            pointerProperties, pointerCoords,
-                            /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, xCursorPosition,
-                            yCursorPosition, /* downTime= */ mDownTime, /* videoFrames= */ {});
+                            mCurrentClassification, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
+                            pointerProperties, pointerCoords, /* xPrecision= */ 1.0f,
+                            /* yPrecision= */ 1.0f, xCursorPosition, yCursorPosition,
+                            /* downTime= */ mDownTime, /* videoFrames= */ {});
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index d6a51d2..ae5581d 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -16,12 +16,15 @@
 
 #pragma once
 
+#include <array>
 #include <list>
 #include <memory>
 
 #include <PointerControllerInterface.h>
 #include <utils/Timers.h>
 
+#include "EventHub.h"
+#include "InputDevice.h"
 #include "InputReaderContext.h"
 #include "NotifyArgs.h"
 #include "ui/Rotation.h"
@@ -34,7 +37,8 @@
 // PointerController calls.
 class GestureConverter {
 public:
-    GestureConverter(InputReaderContext& readerContext, int32_t deviceId);
+    GestureConverter(InputReaderContext& readerContext, const InputDeviceContext& deviceContext,
+                     int32_t deviceId);
 
     void setOrientation(ui::Rotation orientation) { mOrientation = orientation; }
     void reset();
@@ -46,6 +50,10 @@
     NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
     [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
                                                             const Gesture& gesture);
+    [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime,
+                                                               uint32_t fingerCount, float dx,
+                                                               float dy);
+    [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipeLift(nsecs_t when, nsecs_t readTime);
 
     NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
                                     int32_t actionButton, int32_t buttonState,
@@ -59,10 +67,26 @@
     std::shared_ptr<PointerControllerInterface> mPointerController;
 
     ui::Rotation mOrientation = ui::ROTATION_0;
+    RawAbsoluteAxisInfo mXAxisInfo;
+    RawAbsoluteAxisInfo mYAxisInfo;
+
     // The current button state according to the gestures library, but converted into MotionEvent
     // button values (AMOTION_EVENT_BUTTON_...).
     uint32_t mButtonState = 0;
     nsecs_t mDownTime = 0;
+
+    MotionClassification mCurrentClassification = MotionClassification::NONE;
+    uint32_t mSwipeFingerCount = 0;
+    static constexpr size_t MAX_FAKE_FINGERS = 4;
+    // We never need any PointerProperties other than the finger tool type, so we can just keep a
+    // const array of them.
+    const std::array<PointerProperties, MAX_FAKE_FINGERS> mFingerProps = {{
+            {.id = 0, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+            {.id = 1, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+            {.id = 2, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+            {.id = 3, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+    }};
+    std::array<PointerCoords, MAX_FAKE_FINGERS> mFakeFingerCoords = {};
 };
 
 } // namespace android