Report motion offsets for touchpad swipes

Adds two new axes, AXIS_GESTURE_X_OFFSET and AXIS_GESTURE_Y_OFFSET,
which report the movement of swipe gestures on the touchpad as a
fraction of the touchpad's size.

Bug: 246758376
Test: check axis values come through in a test app
Change-Id: I313410053a8db13273bd05a33d3a6a1f75081dae
diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp
index 23b6f57..6db89d4 100644
--- a/services/inputflinger/InputCommonConverter.cpp
+++ b/services/inputflinger/InputCommonConverter.cpp
@@ -263,8 +263,11 @@
 static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_14) == common::Axis::GENERIC_14);
 static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_15) == common::Axis::GENERIC_15);
 static_assert(static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_16) == common::Axis::GENERIC_16);
+// TODO(hcutts): add GESTURE_X_OFFSET and GESTURE_Y_OFFSET.
+// If you added a new axis, consider whether this should also be exposed as a HAL axis. Update the
+// static_assert below and add the new axis here, or leave a comment summarizing your decision.
 static_assert(static_cast<common::Axis>(AMOTION_EVENT_MAXIMUM_VALID_AXIS_VALUE) ==
-              static_cast<common::Axis>(AMOTION_EVENT_AXIS_GENERIC_16));
+              static_cast<common::Axis>(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET));
 
 static common::VideoFrame getHalVideoFrame(const TouchVideoFrame& frame) {
     common::VideoFrame out;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index da58efd..7f6785e 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -3175,7 +3175,7 @@
         mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits;
 
         // Add delta for all fingers and calculate a common movement delta.
-        float commonDeltaX = 0, commonDeltaY = 0;
+        int32_t commonDeltaRawX = 0, commonDeltaRawY = 0;
         BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value &
                               mCurrentCookedState.fingerIdBits.value);
         for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) {
@@ -3188,11 +3188,11 @@
             delta.dy += cpd.y - lpd.y;
 
             if (first) {
-                commonDeltaX = delta.dx;
-                commonDeltaY = delta.dy;
+                commonDeltaRawX = delta.dx;
+                commonDeltaRawY = delta.dy;
             } else {
-                commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
-                commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
+                commonDeltaRawX = calculateCommonVector(commonDeltaRawX, delta.dx);
+                commonDeltaRawY = calculateCommonVector(commonDeltaRawY, delta.dy);
             }
         }
 
@@ -3298,7 +3298,7 @@
         // Move the reference points based on the overall group motion of the fingers
         // except in PRESS mode while waiting for a transition to occur.
         if (mPointerGesture.currentGestureMode != PointerGesture::Mode::PRESS &&
-            (commonDeltaX || commonDeltaY)) {
+            (commonDeltaRawX || commonDeltaRawY)) {
             for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) {
                 uint32_t id = idBits.clearFirstMarkedBit();
                 PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
@@ -3306,11 +3306,11 @@
                 delta.dy = 0;
             }
 
-            mPointerGesture.referenceTouchX += commonDeltaX;
-            mPointerGesture.referenceTouchY += commonDeltaY;
+            mPointerGesture.referenceTouchX += commonDeltaRawX;
+            mPointerGesture.referenceTouchY += commonDeltaRawY;
 
-            commonDeltaX *= mPointerXMovementScale;
-            commonDeltaY *= mPointerYMovementScale;
+            float commonDeltaX = commonDeltaRawX * mPointerXMovementScale;
+            float commonDeltaY = commonDeltaRawY * mPointerYMovementScale;
 
             rotateDelta(mInputDeviceOrientation, &commonDeltaX, &commonDeltaY);
             mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
@@ -3341,6 +3341,16 @@
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
                                                                  mPointerGesture.referenceGestureY);
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
+            if (mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
+                float xOffset = static_cast<float>(commonDeltaRawX) /
+                        (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue);
+                float yOffset = static_cast<float>(commonDeltaRawY) /
+                        (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue);
+                mPointerGesture.currentGestureCoords[0]
+                        .setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset);
+                mPointerGesture.currentGestureCoords[0]
+                        .setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
+            }
         } else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
             // FREEFORM mode.
             ALOGD_IF(DEBUG_GESTURES,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index dded6a1..1e26265 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -10085,6 +10085,52 @@
                                                 0, 0, 0, 0, 0));
 }
 
+TEST_F(MultiTouchPointerModeTest, TwoFingerSwipeOffsets) {
+    preparePointerMode(25 /*xResolution*/, 25 /*yResolution*/);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+    NotifyMotionArgs motionArgs;
+
+    // Place two fingers down.
+    int32_t x1 = 100, y1 = 125, x2 = 550, y2 = 125;
+
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1, y1);
+    processMTSync(mapper);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2, y2);
+    processMTSync(mapper);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(1U, motionArgs.pointerCount);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(MotionClassification::NONE, motionArgs.classification);
+    ASSERT_EQ(0, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET));
+    ASSERT_EQ(0, motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET));
+
+    // Move the two fingers down and to the left.
+    int32_t movingDistance = 200;
+    x1 -= movingDistance;
+    y1 += movingDistance;
+    x2 -= movingDistance;
+    y2 += movingDistance;
+
+    processId(mapper, FIRST_TRACKING_ID);
+    processPosition(mapper, x1, y1);
+    processMTSync(mapper);
+    processId(mapper, SECOND_TRACKING_ID);
+    processPosition(mapper, x2, y2);
+    processMTSync(mapper);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(1U, motionArgs.pointerCount);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(MotionClassification::TWO_FINGER_SWIPE, motionArgs.classification);
+    ASSERT_LT(motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET), 0);
+    ASSERT_GT(motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET), 0);
+}
+
 // --- JoystickInputMapperTest ---
 
 class JoystickInputMapperTest : public InputMapperTest {