GestureConverter: add option to make 3-finger tap a shortcut

By default, it will remain as middle-click, but if the user chooses to
customize it we'll handle it like a special keyboard shortcut by sending
a JNI call up to InputManagerService.

Test: 3-finger tap with flag enabled, check log line appears
Test: 3-finger tap with flag disabled, check button events appear at
    https://w3c.github.io/uievents/tools/mouse-event-viewer.html
Test: atest inputflinger_test:GestureConverterTest
Test: m checkinput
Flag: com.android.hardware.input.touchpad_three_finger_tap_shortcut
Bug: 365063048
Change-Id: I771d46e1aafe9164dd32771cfdb55a3b259b5a0d
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 7c5f350..67b1e8c 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -80,6 +80,17 @@
     ASSERT_TRUE(success) << "Timed out waiting for hardware state to be notified";
 }
 
+void FakeInputReaderPolicy::assertTouchpadThreeFingerTapNotified() {
+    std::unique_lock lock(mLock);
+    base::ScopedLockAssertion assumeLocked(mLock);
+
+    const bool success =
+            mTouchpadThreeFingerTapNotified.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                return mTouchpadThreeFingerTapHasBeenReported;
+            });
+    ASSERT_TRUE(success) << "Timed out waiting for three-finger tap to be notified";
+}
+
 void FakeInputReaderPolicy::clearViewports() {
     mViewports.clear();
     mConfig.setDisplayViewports(mViewports);
@@ -259,6 +270,12 @@
     std::scoped_lock lock(mLock);
 }
 
+void FakeInputReaderPolicy::notifyTouchpadThreeFingerTap() {
+    std::scoped_lock lock(mLock);
+    mTouchpadThreeFingerTapHasBeenReported = true;
+    mTouchpadThreeFingerTapNotified.notify_all();
+}
+
 std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
         const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
     return nullptr;
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 3a2b4e9..42c9567 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -43,6 +43,7 @@
     void assertStylusGestureNotified(int32_t deviceId);
     void assertStylusGestureNotNotified();
     void assertTouchpadHardwareStateNotified();
+    void assertTouchpadThreeFingerTapNotified();
 
     virtual void clearViewports();
     std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const;
@@ -86,6 +87,7 @@
     void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
                                      int32_t deviceId) override;
     void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override;
+    void notifyTouchpadThreeFingerTap() override;
     std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
             const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
     std::string getDeviceAlias(const InputDeviceIdentifier&) override;
@@ -109,6 +111,9 @@
     std::condition_variable mTouchpadHardwareStateNotified;
     std::optional<SelfContainedHardwareState> mTouchpadHardwareState GUARDED_BY(mLock){};
 
+    std::condition_variable mTouchpadThreeFingerTapNotified;
+    bool mTouchpadThreeFingerTapHasBeenReported{false};
+
     uint32_t mNextPointerCaptureSequenceNumber{0};
 };
 
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 225ae0f..fad8f05 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -1272,6 +1272,27 @@
                               WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
 }
 
+TEST_F(GestureConverterTest, ThreeFingerTap_TriggersShortcut) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+    converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+    converter.setThreeFingerTapShortcutEnabled(true);
+
+    Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*vx=*/0,
+                         /*vy=*/0, GESTURES_FLING_TAP_DOWN);
+    std::list<NotifyArgs> args =
+            converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+    // We don't need to check args here, since it's covered by the FlingTapDown test.
+
+    Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+                       /*down=*/GESTURES_BUTTON_MIDDLE, /*up=*/GESTURES_BUTTON_MIDDLE,
+                       /*is_tap=*/true);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture);
+
+    ASSERT_TRUE(args.empty());
+    mFakePolicy->assertTouchpadThreeFingerTapNotified();
+}
+
 TEST_F(GestureConverterTest, Click) {
     // Click should produce button press/release events
     InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 60c676d..a1da39a 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -288,6 +288,7 @@
     void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
                                      int32_t deviceId) override {}
     void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override {}
+    void notifyTouchpadThreeFingerTap() override {}
     std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
             const InputDeviceIdentifier& identifier,
             const std::optional<KeyboardLayoutInfo> layoutInfo) override {