Set up a GestureInterpreter and feed it events

This is a minimal setup of the gestures library, in that it doesn't have
a timer provider or property provider, but it's enough to be able to
feed it events and have it report the gestures it has detected.

This CL depends on https://r.android.com/2310648 to pull in the
Android.bp file and `SetCallback` rename from upstream, and
https://r.android.com/2314880 to pull in the deprecation of screen DPI
hardware properties.

Bug: 251196347
Test: connect Apple Magic Trackpad 2, make gestures, check logcat

Change-Id: Ib40880360c3865f0a98b7a067b7efe9fd8178212
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 551ad7a..f5ac8de 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -132,6 +132,7 @@
         // This should consist only of dependencies from inputflinger. Other dependencies should be
         // in cc_defaults so that they are included in the tests.
         "libinputflinger_base",
+        "libjsoncpp",
     ],
     export_header_lib_headers: [
         "libinputreader_headers",
@@ -146,5 +147,6 @@
     },
     static_libs: [
         "libc++fs",
+        "libchrome-gestures",
     ],
 }
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 6ca6298..150a8aa 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -211,7 +211,8 @@
     // Touchscreens and touchpad devices.
     // TODO(b/251196347): replace this with a proper flag.
     constexpr bool ENABLE_NEW_TOUCHPAD_STACK = false;
-    if (ENABLE_NEW_TOUCHPAD_STACK && classes.test(InputDeviceClass::TOUCHPAD)) {
+    if (ENABLE_NEW_TOUCHPAD_STACK && classes.test(InputDeviceClass::TOUCHPAD) &&
+        classes.test(InputDeviceClass::TOUCH_MT)) {
         mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr));
     } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
         mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 3ad1de4..8c5bce7 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -16,21 +16,165 @@
 
 #include "../Macros.h"
 
+#include <log/log_main.h>
+#include <chrono>
 #include "TouchpadInputMapper.h"
 
 namespace android {
 
+namespace {
+
+short getMaxTouchCount(const InputDeviceContext& context) {
+    if (context.hasKeyCode(BTN_TOOL_QUINTTAP)) return 5;
+    if (context.hasKeyCode(BTN_TOOL_QUADTAP)) return 4;
+    if (context.hasKeyCode(BTN_TOOL_TRIPLETAP)) return 3;
+    if (context.hasKeyCode(BTN_TOOL_DOUBLETAP)) return 2;
+    if (context.hasKeyCode(BTN_TOOL_FINGER)) return 1;
+    return 0;
+}
+
+HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
+    HardwareProperties props;
+    RawAbsoluteAxisInfo absMtPositionX;
+    context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
+    props.left = absMtPositionX.minValue;
+    props.right = absMtPositionX.maxValue;
+    props.res_x = absMtPositionX.resolution;
+
+    RawAbsoluteAxisInfo absMtPositionY;
+    context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
+    props.top = absMtPositionY.minValue;
+    props.bottom = absMtPositionY.maxValue;
+    props.res_y = absMtPositionY.resolution;
+
+    RawAbsoluteAxisInfo absMtOrientation;
+    context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
+    props.orientation_minimum = absMtOrientation.minValue;
+    props.orientation_maximum = absMtOrientation.maxValue;
+
+    RawAbsoluteAxisInfo absMtSlot;
+    context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
+    props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
+    props.max_touch_cnt = getMaxTouchCount(context);
+
+    // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
+    // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads
+    // that did this, so assume false.
+    props.supports_t5r2 = false;
+
+    props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT);
+    props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD);
+
+    // Mouse-only properties, which will always be false.
+    props.has_wheel = false;
+    props.wheel_is_hi_res = false;
+
+    // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads
+    // are haptic.
+    props.is_haptic_pad = false;
+    return props;
+}
+
+void gestureInterpreterCallback(void* clientData, const struct Gesture* gesture) {
+    // TODO(b/251196347): turn the gesture into a NotifyArgs and dispatch it.
+    ALOGD("Gesture ready: %s", gesture->String().c_str());
+}
+
+} // namespace
+
 TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
-      : InputMapper(deviceContext) {}
+      : InputMapper(deviceContext),
+        mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
+        mTouchButtonAccumulator(deviceContext) {
+    mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
+    mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
+    mGestureInterpreter->SetCallback(gestureInterpreterCallback, nullptr);
+    // TODO(b/251196347): set a property provider, so we can change gesture properties.
+    // TODO(b/251196347): set a timer provider, so the library can use timers.
+
+    RawAbsoluteAxisInfo slotAxisInfo;
+    getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
+    if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
+        ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
+              "properly.",
+              getDeviceName().c_str());
+    }
+    mMotionAccumulator.configure(getDeviceContext(), slotAxisInfo.maxValue + 1, true);
+    mTouchButtonAccumulator.configure();
+}
 
 uint32_t TouchpadInputMapper::getSources() const {
     return AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD;
 }
 
+std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
+    mCursorButtonAccumulator.reset(getDeviceContext());
+    mTouchButtonAccumulator.reset();
+    mMscTimestamp = 0;
+    return InputMapper::reset(when);
+}
+
 std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
-    ALOGD("TODO: process event type=0x%x code=0x%x value=0x%x", rawEvent->type, rawEvent->code,
-          rawEvent->value);
+    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+        sync(rawEvent->when);
+    }
+    if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
+        mMscTimestamp = rawEvent->value;
+    }
+    mCursorButtonAccumulator.process(rawEvent);
+    mMotionAccumulator.process(rawEvent);
+    mTouchButtonAccumulator.process(rawEvent);
     return {};
 }
 
+void TouchpadInputMapper::sync(nsecs_t when) {
+    HardwareState hwState;
+    // The gestures library uses doubles to represent timestamps in seconds.
+    hwState.timestamp = std::chrono::duration<stime_t>(std::chrono::nanoseconds(when)).count();
+    hwState.msc_timestamp =
+            std::chrono::duration<stime_t>(std::chrono::microseconds(mMscTimestamp)).count();
+
+    hwState.buttons_down = 0;
+    if (mCursorButtonAccumulator.isLeftPressed()) {
+        hwState.buttons_down |= GESTURES_BUTTON_LEFT;
+    }
+    if (mCursorButtonAccumulator.isMiddlePressed()) {
+        hwState.buttons_down |= GESTURES_BUTTON_MIDDLE;
+    }
+    if (mCursorButtonAccumulator.isRightPressed()) {
+        hwState.buttons_down |= GESTURES_BUTTON_RIGHT;
+    }
+    if (mCursorButtonAccumulator.isBackPressed() || mCursorButtonAccumulator.isSidePressed()) {
+        hwState.buttons_down |= GESTURES_BUTTON_BACK;
+    }
+    if (mCursorButtonAccumulator.isForwardPressed() || mCursorButtonAccumulator.isExtraPressed()) {
+        hwState.buttons_down |= GESTURES_BUTTON_FORWARD;
+    }
+
+    std::vector<FingerState> fingers;
+    for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
+        MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
+        if (slot.isInUse()) {
+            FingerState& fingerState = fingers.emplace_back();
+            fingerState = {};
+            fingerState.touch_major = slot.getTouchMajor();
+            fingerState.touch_minor = slot.getTouchMinor();
+            fingerState.width_major = slot.getToolMajor();
+            fingerState.width_minor = slot.getToolMinor();
+            fingerState.pressure = slot.getPressure();
+            fingerState.orientation = slot.getOrientation();
+            fingerState.position_x = slot.getX();
+            fingerState.position_y = slot.getY();
+            fingerState.tracking_id = slot.getTrackingId();
+        }
+    }
+    hwState.fingers = fingers.data();
+    hwState.finger_cnt = fingers.size();
+    hwState.touch_cnt = mTouchButtonAccumulator.getTouchCount();
+
+    mGestureInterpreter->PushHardwareState(&hwState);
+    mMotionAccumulator.finishSync();
+    mMscTimestamp = 0;
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 81a76d2..9d3a4b3 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -16,10 +16,17 @@
 
 #pragma once
 
+#include <memory>
+
 #include "EventHub.h"
 #include "InputDevice.h"
 #include "InputMapper.h"
 #include "NotifyArgs.h"
+#include "accumulator/CursorButtonAccumulator.h"
+#include "accumulator/MultiTouchMotionAccumulator.h"
+#include "accumulator/TouchButtonAccumulator.h"
+
+#include "include/gestures.h"
 
 namespace android {
 
@@ -27,8 +34,20 @@
 public:
     explicit TouchpadInputMapper(InputDeviceContext& deviceContext);
 
-    virtual uint32_t getSources() const override;
-    [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+    uint32_t getSources() const override;
+    [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+    [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+
+private:
+    void sync(nsecs_t when);
+
+    std::unique_ptr<gestures::GestureInterpreter, void (*)(gestures::GestureInterpreter*)>
+            mGestureInterpreter;
+
+    CursorButtonAccumulator mCursorButtonAccumulator;
+    MultiTouchMotionAccumulator mMotionAccumulator;
+    TouchButtonAccumulator mTouchButtonAccumulator;
+    int32_t mMscTimestamp = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index ed4c789..1380604 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -32,6 +32,14 @@
     void process(const RawEvent* rawEvent);
 
     uint32_t getButtonState() const;
+    inline bool isLeftPressed() const { return mBtnLeft; }
+    inline bool isRightPressed() const { return mBtnRight; }
+    inline bool isMiddlePressed() const { return mBtnMiddle; }
+    inline bool isBackPressed() const { return mBtnBack; }
+    inline bool isSidePressed() const { return mBtnSide; }
+    inline bool isForwardPressed() const { return mBtnForward; }
+    inline bool isExtraPressed() const { return mBtnExtra; }
+    inline bool isTaskPressed() const { return mBtnTask; }
 
 private:
     bool mBtnLeft;
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index bc23a8e..6601702 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -48,6 +48,7 @@
     mBtnToolDoubleTap = mDeviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
     mBtnToolTripleTap = mDeviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
     mBtnToolQuadTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
+    mBtnToolQuintTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUINTTAP);
     mHidUsageAccumulator.reset();
 }
 
@@ -100,6 +101,9 @@
             case BTN_TOOL_QUADTAP:
                 mBtnToolQuadTap = rawEvent->value;
                 break;
+            case BTN_TOOL_QUINTTAP:
+                mBtnToolQuintTap = rawEvent->value;
+                break;
             default:
                 processMappedKey(rawEvent->code, rawEvent->value);
         }
@@ -147,7 +151,8 @@
     if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
         return AMOTION_EVENT_TOOL_TYPE_STYLUS;
     }
-    if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
+    if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap ||
+        mBtnToolQuintTap) {
         return AMOTION_EVENT_TOOL_TYPE_FINGER;
     }
     return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
@@ -156,7 +161,7 @@
 bool TouchButtonAccumulator::isToolActive() const {
     return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush ||
             mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens ||
-            mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
+            mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap || mBtnToolQuintTap;
 }
 
 bool TouchButtonAccumulator::isHovering() const {
@@ -171,4 +176,15 @@
     return mHaveBtnTouch;
 }
 
+int TouchButtonAccumulator::getTouchCount() const {
+    if (mBtnTouch) {
+        if (mBtnToolQuintTap) return 5;
+        if (mBtnToolQuadTap) return 4;
+        if (mBtnToolTripleTap) return 3;
+        if (mBtnToolDoubleTap) return 2;
+        if (mBtnToolFinger) return 1;
+    }
+    return 0;
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index c2de23c..2e70e2e 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -41,6 +41,7 @@
     bool isHovering() const;
     bool hasStylus() const;
     bool hasButtonTouch() const;
+    int getTouchCount() const;
 
 private:
     bool mHaveBtnTouch{};
@@ -60,6 +61,7 @@
     bool mBtnToolDoubleTap{};
     bool mBtnToolTripleTap{};
     bool mBtnToolQuadTap{};
+    bool mBtnToolQuintTap{};
 
     HidUsageAccumulator mHidUsageAccumulator{};