Notify the policy when a stylus gesture starts

This will serve as a hint to the policy that a stylus is currently being
used.

Bug: 243005009
Test: atest inputflinger_tests
Change-Id: I2cc187af226fc3a1d4fda36becc280ea7934673a
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index bc70584..e51379c 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -244,6 +244,7 @@
     bool mInputDevicesChanged GUARDED_BY(mLock){false};
     std::vector<DisplayViewport> mViewports;
     TouchAffineTransformation transform;
+    std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};
 
 protected:
     virtual ~FakeInputReaderPolicy() {}
@@ -268,6 +269,18 @@
         });
     }
 
+    void assertStylusGestureNotified(int32_t deviceId) {
+        std::scoped_lock lock(mLock);
+        ASSERT_TRUE(mStylusGestureNotified);
+        ASSERT_EQ(deviceId, *mStylusGestureNotified);
+        mStylusGestureNotified.reset();
+    }
+
+    void assertStylusGestureNotNotified() {
+        std::scoped_lock lock(mLock);
+        ASSERT_FALSE(mStylusGestureNotified);
+    }
+
     virtual void clearViewports() {
         mViewports.clear();
         mConfig.setDisplayViewports(mViewports);
@@ -428,6 +441,11 @@
         ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
         mInputDevicesChanged = false;
     }
+
+    void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override {
+        std::scoped_lock<std::mutex> lock(mLock);
+        mStylusGestureNotified = deviceId;
+    }
 };
 
 // --- FakeEventHub ---
@@ -2329,6 +2347,15 @@
         mTestListener.reset();
         mFakePolicy.clear();
     }
+
+    std::optional<InputDeviceInfo> findDeviceByName(const std::string& name) {
+        const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
+        const auto& it = std::find_if(inputDevices.begin(), inputDevices.end(),
+                                      [&name](const InputDeviceInfo& info) {
+                                          return info.getIdentifier().name == name;
+                                      });
+        return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt;
+    }
 };
 
 TEST_F(InputReaderIntegrationTest, TestInvalidDevice) {
@@ -2450,6 +2477,9 @@
         mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
         ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+        const auto info = findDeviceByName(mDevice->getName());
+        ASSERT_TRUE(info);
+        mDeviceInfo = *info;
     }
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
@@ -2473,6 +2503,7 @@
     }
 
     std::unique_ptr<UinputTouchScreen> mDevice;
+    InputDeviceInfo mDeviceInfo;
 };
 
 TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
@@ -2689,6 +2720,58 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 }
 
+TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // Send down with the pen tool selected. The policy should be notified of the stylus presence.
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendToolType(MT_TOOL_PEN);
+    mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
+
+    // Release the stylus touch.
+    mDevice->sendUp();
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(
+            mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified());
+
+    // Touch down with the finger, without the pen tool selected. The policy is not notified.
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendToolType(MT_TOOL_FINGER);
+    mDevice->sendDown(centerPoint);
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER))));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotNotified());
+
+    mDevice->sendUp();
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(
+            mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+
+    // Send a move event with the stylus tool without BTN_TOUCH to generate a hover enter.
+    // The policy should be notified of the stylus presence.
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendToolType(MT_TOOL_PEN);
+    mDevice->sendMove(centerPoint);
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
+}
+
 // --- InputDeviceTest ---
 class InputDeviceTest : public testing::Test {
 protected: