Loading services/inputflinger/include/InputReaderBase.h +6 −0 Original line number Diff line number Diff line Loading @@ -394,6 +394,12 @@ public: /* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */ virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0; /* Get the ID of the InputDevice that was used most recently. * * Returns ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID if no device has been used since boot. */ virtual DeviceId getLastUsedInputDeviceId() = 0; }; // --- TouchAffineTransformation --- Loading services/inputflinger/reader/InputReader.cpp +37 −3 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ using android::base::StringPrintf; namespace android { namespace { /** * Determines if the identifiers passed are a sub-devices. Sub-devices are physical devices * that expose multiple input device paths such a keyboard that also has a touchpad input. Loading @@ -49,7 +51,7 @@ namespace android { * inputs versus the same device plugged into multiple ports. */ static bool isSubDevice(const InputDeviceIdentifier& identifier1, bool isSubDevice(const InputDeviceIdentifier& identifier1, const InputDeviceIdentifier& identifier2) { return (identifier1.vendor == identifier2.vendor && identifier1.product == identifier2.product && identifier1.bus == identifier2.bus && Loading @@ -58,7 +60,7 @@ static bool isSubDevice(const InputDeviceIdentifier& identifier1, identifier1.location == identifier2.location); } static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) { bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) { const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action); if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER && actionMasked != AMOTION_EVENT_ACTION_DOWN && Loading @@ -69,6 +71,28 @@ static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) { return isStylusToolType(motionArgs.pointerProperties[actionIndex].toolType); } bool isNewGestureStart(const NotifyMotionArgs& motion) { return motion.action == AMOTION_EVENT_ACTION_DOWN || motion.action == AMOTION_EVENT_ACTION_HOVER_ENTER; } bool isNewGestureStart(const NotifyKeyArgs& key) { return key.action == AKEY_EVENT_ACTION_DOWN; } // Return the event's device ID if it marks the start of a new gesture. std::optional<DeviceId> getDeviceIdOfNewGesture(const NotifyArgs& args) { if (const auto* motion = std::get_if<NotifyMotionArgs>(&args); motion != nullptr) { return isNewGestureStart(*motion) ? std::make_optional(motion->deviceId) : std::nullopt; } if (const auto* key = std::get_if<NotifyKeyArgs>(&args); key != nullptr) { return isNewGestureStart(*key) ? std::make_optional(key->deviceId) : std::nullopt; } return std::nullopt; } } // namespace // --- InputReader --- InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, Loading Loading @@ -162,6 +186,11 @@ void InputReader::loopOnce() { } std::swap(notifyArgs, mPendingArgs); // Keep track of the last used device for (const NotifyArgs& args : notifyArgs) { mLastUsedDeviceId = getDeviceIdOfNewGesture(args).value_or(mLastUsedDeviceId); } } // release lock // Flush queued events out to the listener. Loading Loading @@ -883,6 +912,11 @@ void InputReader::sysfsNodeChanged(const std::string& sysfsNodePath) { mEventHub->sysfsNodeChanged(sysfsNodePath); } DeviceId InputReader::getLastUsedInputDeviceId() { std::scoped_lock _l(mLock); return mLastUsedDeviceId; } void InputReader::dump(std::string& dump) { std::scoped_lock _l(mLock); Loading services/inputflinger/reader/include/InputReader.h +5 −0 Original line number Diff line number Diff line Loading @@ -118,6 +118,8 @@ public: void sysfsNodeChanged(const std::string& sysfsNodePath) override; DeviceId getLastUsedInputDeviceId() override; protected: // These members are protected so they can be instrumented by test cases. virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId, Loading Loading @@ -200,6 +202,9 @@ private: // records timestamp of the last key press on the physical keyboard nsecs_t mLastKeyDownTimestamp GUARDED_BY(mLock){0}; // The input device that produced a new gesture most recently. DeviceId mLastUsedDeviceId GUARDED_BY(mLock){ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID}; // low-level input event decoding and device management [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count) REQUIRES(mLock); Loading services/inputflinger/tests/InputReader_test.cpp +77 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <JoystickInputMapper.h> #include <KeyboardInputMapper.h> #include <MultiTouchInputMapper.h> #include <NotifyArgsBuilders.h> #include <PeripheralController.h> #include <SensorInputMapper.h> #include <SingleTouchInputMapper.h> Loading Loading @@ -1183,6 +1184,82 @@ TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) { mFakeListener->assertNotifyCaptureWasNotCalled(); } TEST_F(InputReaderTest, GetLastUsedInputDeviceId) { constexpr int32_t FIRST_DEVICE_ID = END_RESERVED_ID + 1000; constexpr int32_t SECOND_DEVICE_ID = FIRST_DEVICE_ID + 1; FakeInputMapper& firstMapper = addDeviceWithFakeInputMapper(FIRST_DEVICE_ID, FIRST_DEVICE_ID, "first", InputDeviceClass::KEYBOARD, AINPUT_SOURCE_KEYBOARD, /*configuration=*/nullptr); FakeInputMapper& secondMapper = addDeviceWithFakeInputMapper(SECOND_DEVICE_ID, SECOND_DEVICE_ID, "second", InputDeviceClass::TOUCH_MT, AINPUT_SOURCE_STYLUS, /*configuration=*/nullptr); ASSERT_EQ(ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID, mReader->getLastUsedInputDeviceId()); // Start a new key gesture from the first device firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) .deviceId(FIRST_DEVICE_ID) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(firstMapper.getDeviceId(), mReader->getLastUsedInputDeviceId()); // Start a new touch gesture from the second device secondMapper.setProcessResult( {MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_STYLUS) .deviceId(SECOND_DEVICE_ID) .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER)) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId()); // Releasing the key is not a new gesture, so it does not update the last used device firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD) .deviceId(FIRST_DEVICE_ID) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId()); // But pressing a new key does start a new gesture firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) .deviceId(FIRST_DEVICE_ID) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId()); // Moving or ending a touch gesture does not update the last used device secondMapper.setProcessResult( {MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS) .deviceId(SECOND_DEVICE_ID) .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS)) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId()); secondMapper.setProcessResult({MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS) .deviceId(SECOND_DEVICE_ID) .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS)) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId()); // Starting a new hover gesture updates the last used device secondMapper.setProcessResult( {MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) .deviceId(SECOND_DEVICE_ID) .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS)) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId()); } class FakeVibratorInputMapper : public FakeInputMapper { public: FakeVibratorInputMapper(InputDeviceContext& deviceContext, Loading services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -169,6 +169,8 @@ public: reader->sysfsNodeChanged(sysfsNodePath); } DeviceId getLastUsedInputDeviceId() override { return reader->getLastUsedInputDeviceId(); } private: std::unique_ptr<InputReaderInterface> reader; }; Loading Loading
services/inputflinger/include/InputReaderBase.h +6 −0 Original line number Diff line number Diff line Loading @@ -394,6 +394,12 @@ public: /* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */ virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0; /* Get the ID of the InputDevice that was used most recently. * * Returns ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID if no device has been used since boot. */ virtual DeviceId getLastUsedInputDeviceId() = 0; }; // --- TouchAffineTransformation --- Loading
services/inputflinger/reader/InputReader.cpp +37 −3 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ using android::base::StringPrintf; namespace android { namespace { /** * Determines if the identifiers passed are a sub-devices. Sub-devices are physical devices * that expose multiple input device paths such a keyboard that also has a touchpad input. Loading @@ -49,7 +51,7 @@ namespace android { * inputs versus the same device plugged into multiple ports. */ static bool isSubDevice(const InputDeviceIdentifier& identifier1, bool isSubDevice(const InputDeviceIdentifier& identifier1, const InputDeviceIdentifier& identifier2) { return (identifier1.vendor == identifier2.vendor && identifier1.product == identifier2.product && identifier1.bus == identifier2.bus && Loading @@ -58,7 +60,7 @@ static bool isSubDevice(const InputDeviceIdentifier& identifier1, identifier1.location == identifier2.location); } static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) { bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) { const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action); if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER && actionMasked != AMOTION_EVENT_ACTION_DOWN && Loading @@ -69,6 +71,28 @@ static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) { return isStylusToolType(motionArgs.pointerProperties[actionIndex].toolType); } bool isNewGestureStart(const NotifyMotionArgs& motion) { return motion.action == AMOTION_EVENT_ACTION_DOWN || motion.action == AMOTION_EVENT_ACTION_HOVER_ENTER; } bool isNewGestureStart(const NotifyKeyArgs& key) { return key.action == AKEY_EVENT_ACTION_DOWN; } // Return the event's device ID if it marks the start of a new gesture. std::optional<DeviceId> getDeviceIdOfNewGesture(const NotifyArgs& args) { if (const auto* motion = std::get_if<NotifyMotionArgs>(&args); motion != nullptr) { return isNewGestureStart(*motion) ? std::make_optional(motion->deviceId) : std::nullopt; } if (const auto* key = std::get_if<NotifyKeyArgs>(&args); key != nullptr) { return isNewGestureStart(*key) ? std::make_optional(key->deviceId) : std::nullopt; } return std::nullopt; } } // namespace // --- InputReader --- InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, Loading Loading @@ -162,6 +186,11 @@ void InputReader::loopOnce() { } std::swap(notifyArgs, mPendingArgs); // Keep track of the last used device for (const NotifyArgs& args : notifyArgs) { mLastUsedDeviceId = getDeviceIdOfNewGesture(args).value_or(mLastUsedDeviceId); } } // release lock // Flush queued events out to the listener. Loading Loading @@ -883,6 +912,11 @@ void InputReader::sysfsNodeChanged(const std::string& sysfsNodePath) { mEventHub->sysfsNodeChanged(sysfsNodePath); } DeviceId InputReader::getLastUsedInputDeviceId() { std::scoped_lock _l(mLock); return mLastUsedDeviceId; } void InputReader::dump(std::string& dump) { std::scoped_lock _l(mLock); Loading
services/inputflinger/reader/include/InputReader.h +5 −0 Original line number Diff line number Diff line Loading @@ -118,6 +118,8 @@ public: void sysfsNodeChanged(const std::string& sysfsNodePath) override; DeviceId getLastUsedInputDeviceId() override; protected: // These members are protected so they can be instrumented by test cases. virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId, Loading Loading @@ -200,6 +202,9 @@ private: // records timestamp of the last key press on the physical keyboard nsecs_t mLastKeyDownTimestamp GUARDED_BY(mLock){0}; // The input device that produced a new gesture most recently. DeviceId mLastUsedDeviceId GUARDED_BY(mLock){ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID}; // low-level input event decoding and device management [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count) REQUIRES(mLock); Loading
services/inputflinger/tests/InputReader_test.cpp +77 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <JoystickInputMapper.h> #include <KeyboardInputMapper.h> #include <MultiTouchInputMapper.h> #include <NotifyArgsBuilders.h> #include <PeripheralController.h> #include <SensorInputMapper.h> #include <SingleTouchInputMapper.h> Loading Loading @@ -1183,6 +1184,82 @@ TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) { mFakeListener->assertNotifyCaptureWasNotCalled(); } TEST_F(InputReaderTest, GetLastUsedInputDeviceId) { constexpr int32_t FIRST_DEVICE_ID = END_RESERVED_ID + 1000; constexpr int32_t SECOND_DEVICE_ID = FIRST_DEVICE_ID + 1; FakeInputMapper& firstMapper = addDeviceWithFakeInputMapper(FIRST_DEVICE_ID, FIRST_DEVICE_ID, "first", InputDeviceClass::KEYBOARD, AINPUT_SOURCE_KEYBOARD, /*configuration=*/nullptr); FakeInputMapper& secondMapper = addDeviceWithFakeInputMapper(SECOND_DEVICE_ID, SECOND_DEVICE_ID, "second", InputDeviceClass::TOUCH_MT, AINPUT_SOURCE_STYLUS, /*configuration=*/nullptr); ASSERT_EQ(ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID, mReader->getLastUsedInputDeviceId()); // Start a new key gesture from the first device firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) .deviceId(FIRST_DEVICE_ID) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(firstMapper.getDeviceId(), mReader->getLastUsedInputDeviceId()); // Start a new touch gesture from the second device secondMapper.setProcessResult( {MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_STYLUS) .deviceId(SECOND_DEVICE_ID) .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER)) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId()); // Releasing the key is not a new gesture, so it does not update the last used device firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD) .deviceId(FIRST_DEVICE_ID) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId()); // But pressing a new key does start a new gesture firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) .deviceId(FIRST_DEVICE_ID) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId()); // Moving or ending a touch gesture does not update the last used device secondMapper.setProcessResult( {MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS) .deviceId(SECOND_DEVICE_ID) .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS)) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId()); secondMapper.setProcessResult({MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS) .deviceId(SECOND_DEVICE_ID) .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS)) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId()); // Starting a new hover gesture updates the last used device secondMapper.setProcessResult( {MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) .deviceId(SECOND_DEVICE_ID) .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS)) .build()}); mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0); mReader->loopOnce(); ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId()); } class FakeVibratorInputMapper : public FakeInputMapper { public: FakeVibratorInputMapper(InputDeviceContext& deviceContext, Loading
services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -169,6 +169,8 @@ public: reader->sysfsNodeChanged(sysfsNodePath); } DeviceId getLastUsedInputDeviceId() override { return reader->getLastUsedInputDeviceId(); } private: std::unique_ptr<InputReaderInterface> reader; }; Loading