Merge changes from topics "input-device-bluetooth-address", "notify-stylus-presence"

* changes:
  InputReader: Get Bluetooth address from InputDeviceIdentifier
  Determine the bluetooth address of an input device from its uniqueId
  Notify the policy when a stylus gesture starts
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index ac9c5a5..e911734 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -58,6 +58,9 @@
     // reuse values that are not associated with an input anymore.
     uint16_t nonce;
 
+    // The bluetooth address of the device, if known.
+    std::optional<std::string> bluetoothAddress;
+
     /**
      * Return InputDeviceIdentifier.name that has been adjusted as follows:
      *     - all characters besides alphanumerics, dash,
diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h
index 55f730b..e24344b 100644
--- a/include/input/PrintTools.h
+++ b/include/input/PrintTools.h
@@ -24,16 +24,20 @@
 namespace android {
 
 template <typename T>
-std::string constToString(const T& v) {
+inline std::string constToString(const T& v) {
     return std::to_string(v);
 }
 
+inline std::string constToString(const std::string& s) {
+    return s;
+}
+
 /**
  * Convert an optional type to string.
  */
 template <typename T>
-std::string toString(const std::optional<T>& optional,
-                     std::string (*toString)(const T&) = constToString) {
+inline std::string toString(const std::optional<T>& optional,
+                            std::string (*toString)(const T&) = constToString) {
     return optional ? toString(*optional) : "<not set>";
 }
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index cacb63c..3b0f2ac 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -142,6 +142,9 @@
     virtual std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) = 0;
     /* Get light player ID */
     virtual std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) = 0;
+
+    /* Get the Bluetooth address of an input device, if known. */
+    virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0;
 };
 
 // --- InputReaderConfiguration ---
@@ -393,6 +396,8 @@
     /* Gets the affine calibration associated with the specified device. */
     virtual TouchAffineTransformation getTouchAffineTransformation(
             const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
+    /* Notifies the input reader policy that a stylus gesture has started. */
+    virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index b97c466..18d03f8 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -43,6 +43,7 @@
 #include <ftl/enum.h>
 #include <input/KeyCharacterMap.h>
 #include <input/KeyLayoutMap.h>
+#include <input/PrintTools.h>
 #include <input/VirtualKeyMap.h>
 #include <openssl/sha.h>
 #include <statslog.h>
@@ -134,10 +135,6 @@
                                                                   {"green", LightColor::GREEN},
                                                                   {"blue", LightColor::BLUE}};
 
-static inline const char* toString(bool value) {
-    return value ? "true" : "false";
-}
-
 static std::string sha1(const std::string& in) {
     SHA_CTX ctx;
     SHA1_Init(&ctx);
@@ -2128,6 +2125,17 @@
         identifier.uniqueId = buffer;
     }
 
+    // Attempt to get the bluetooth address of an input device from the uniqueId.
+    if (identifier.bus == BUS_BLUETOOTH &&
+        std::regex_match(identifier.uniqueId,
+                         std::regex("^[A-Fa-f0-9]{2}(?::[A-Fa-f0-9]{2}){5}$"))) {
+        identifier.bluetoothAddress = identifier.uniqueId;
+        // The Bluetooth stack requires alphabetic characters to be uppercase in a valid address.
+        for (auto& c : *identifier.bluetoothAddress) {
+            c = ::toupper(c);
+        }
+    }
+
     // Fill in the descriptor.
     assignDescriptorLocked(identifier);
 
@@ -2625,9 +2633,10 @@
             dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber);
             dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str());
             dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, "
-                                         "product=0x%04x, version=0x%04x\n",
+                                         "product=0x%04x, version=0x%04x, bluetoothAddress=%s\n",
                                  device->identifier.bus, device->identifier.vendor,
-                                 device->identifier.product, device->identifier.version);
+                                 device->identifier.product, device->identifier.version,
+                                 toString(device->identifier.bluetoothAddress).c_str());
             dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n",
                                  device->keyMap.keyLayoutFile.c_str());
             dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n",
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 428e999..f8b1b3f 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -58,6 +58,18 @@
             identifier1.location == identifier2.location);
 }
 
+static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
+    const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action);
+    if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER &&
+        actionMasked != AMOTION_EVENT_ACTION_DOWN &&
+        actionMasked != AMOTION_EVENT_ACTION_POINTER_DOWN) {
+        return false;
+    }
+    const auto actionIndex = MotionEvent::getActionIndex(motionArgs.action);
+    return motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
+            motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER;
+}
+
 // --- InputReader ---
 
 InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -101,8 +113,10 @@
 void InputReader::loopOnce() {
     int32_t oldGeneration;
     int32_t timeoutMillis;
+    // Copy some state so that we can access it outside the lock later.
     bool inputDevicesChanged = false;
     std::vector<InputDeviceInfo> inputDevices;
+    std::list<NotifyArgs> notifyArgs;
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -127,7 +141,7 @@
         mReaderIsAliveCondition.notify_all();
 
         if (!events.empty()) {
-            notifyAll(processEventsLocked(events.data(), events.size()));
+            notifyArgs += processEventsLocked(events.data(), events.size());
         }
 
         if (mNextTimeout != LLONG_MAX) {
@@ -137,7 +151,7 @@
                     ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
                 }
                 mNextTimeout = LLONG_MAX;
-                notifyAll(timeoutExpiredLocked(now));
+                notifyArgs += timeoutExpiredLocked(now);
             }
         }
 
@@ -152,6 +166,16 @@
         mPolicy->notifyInputDevicesChanged(inputDevices);
     }
 
+    // Notify the policy of the start of every new stylus gesture outside the lock.
+    for (const auto& args : notifyArgs) {
+        const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args);
+        if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) {
+            mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime);
+        }
+    }
+
+    notifyAll(std::move(notifyArgs));
+
     // Flush queued events out to the listener.
     // This must happen outside of the lock because the listener could potentially call
     // back into the InputReader's methods, such as getScanCodeState, or become blocked
@@ -851,6 +875,16 @@
     return std::nullopt;
 }
 
+std::optional<std::string> InputReader::getBluetoothAddress(int32_t deviceId) const {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (device) {
+        return device->getBluetoothAddress();
+    }
+    return std::nullopt;
+}
+
 bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
     std::scoped_lock _l(mLock);
 
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index afb1bed..b9a2b4c 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -51,6 +51,9 @@
     inline int32_t getGeneration() const { return mGeneration; }
     inline const std::string getName() const { return mIdentifier.name; }
     inline const std::string getDescriptor() { return mIdentifier.descriptor; }
+    inline std::optional<std::string> getBluetoothAddress() const {
+        return mIdentifier.bluetoothAddress;
+    }
     inline ftl::Flags<InputDeviceClass> getClasses() const { return mClasses; }
     inline uint32_t getSources() const { return mSources; }
     inline bool hasEventHubDevices() const { return !mDevices.empty(); }
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index de268cf..4f2503a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -113,6 +113,8 @@
 
     std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) override;
 
+    std::optional<std::string> getBluetoothAddress(int32_t deviceId) const override;
+
 protected:
     // These members are protected so they can be instrumented by test cases.
     virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index bc70584..f333306 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:
@@ -2699,6 +2782,7 @@
     static const int32_t DEVICE_CONTROLLER_NUMBER;
     static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
     static const int32_t EVENTHUB_ID;
+    static const std::string DEVICE_BLUETOOTH_ADDRESS;
 
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
@@ -2715,6 +2799,7 @@
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
         identifier.location = DEVICE_LOCATION;
+        identifier.bluetoothAddress = DEVICE_BLUETOOTH_ADDRESS;
         mDevice = std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, DEVICE_GENERATION,
                                                 identifier);
         mReader->pushNextDevice(mDevice);
@@ -2736,6 +2821,7 @@
 const ftl::Flags<InputDeviceClass> InputDeviceTest::DEVICE_CLASSES =
         InputDeviceClass::KEYBOARD | InputDeviceClass::TOUCH | InputDeviceClass::JOYSTICK;
 const int32_t InputDeviceTest::EVENTHUB_ID = 1;
+const std::string InputDeviceTest::DEVICE_BLUETOOTH_ADDRESS = "11:AA:22:BB:33:CC";
 
 TEST_F(InputDeviceTest, ImmutableProperties) {
     ASSERT_EQ(DEVICE_ID, mDevice->getId());
@@ -3002,6 +3088,12 @@
     device.dump(dumpStr, eventHubDevStr);
 }
 
+TEST_F(InputDeviceTest, GetBluetoothAddress) {
+    const auto& address = mReader->getBluetoothAddress(DEVICE_ID);
+    ASSERT_TRUE(address);
+    ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address);
+}
+
 // --- InputMapperTest ---
 
 class InputMapperTest : public testing::Test {
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index ff7455b..e48f1d9 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -19,6 +19,7 @@
 #include <android/input.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <input/Input.h>
 
 namespace android {
 
@@ -62,6 +63,13 @@
     return argPressure;
 }
 
+MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
+    const auto argToolType = arg.pointerProperties[0].toolType;
+    *result_listener << "expected tool type " << motionToolTypeToString(toolType) << ", but got "
+                     << motionToolTypeToString(argToolType);
+    return argToolType == toolType;
+}
+
 MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
     *result_listener << "expected flags " << flags << ", but got " << arg.flags;
     return arg.flags == flags;
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index a23c873..626ad67 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -158,6 +158,8 @@
     device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1;
     device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN;
     device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
+    device->absmin[ABS_MT_TOOL_TYPE] = MT_TOOL_FINGER;
+    device->absmax[ABS_MT_TOOL_TYPE] = MT_TOOL_MAX;
 }
 
 void UinputTouchScreen::sendSlot(int32_t slot) {
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index a9f5a3a..2eed997 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -157,6 +157,10 @@
         return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
     }
 
+    std::optional<std::string> getBluetoothAddress(int32_t deviceId) const {
+        return reader->getBluetoothAddress(deviceId);
+    }
+
 private:
     std::unique_ptr<InputReaderInterface> reader;
 };
@@ -273,6 +277,7 @@
                                          std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()),
                                          std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()));
                 },
+                [&]() -> void { reader->getBluetoothAddress(fdp->ConsumeIntegral<int32_t>()); },
         })();
     }
 
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index bd81761..64316ba 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -315,6 +315,7 @@
         return mTransform;
     }
     void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
+    void notifyStylusGestureStarted(int32_t, nsecs_t) {}
 };
 
 class FuzzInputListener : public virtual InputListenerInterface {
@@ -363,6 +364,7 @@
 
     void updateLedMetaState(int32_t metaState) override{};
     int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
+    void notifyStylusGestureStarted(int32_t, nsecs_t) {}
 };
 
 } // namespace android