diff options
5 files changed, 127 insertions, 3 deletions
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index e5c3aa08d1..62149c5a70 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -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 --- diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 12f52b899c..69555f8961 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -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. @@ -49,8 +51,8 @@ namespace android { * inputs versus the same device plugged into multiple ports. */ -static bool isSubDevice(const InputDeviceIdentifier& identifier1, - const InputDeviceIdentifier& identifier2) { +bool isSubDevice(const InputDeviceIdentifier& identifier1, + const InputDeviceIdentifier& identifier2) { return (identifier1.vendor == identifier2.vendor && identifier1.product == identifier2.product && identifier1.bus == identifier2.bus && identifier1.version == identifier2.version && @@ -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 && @@ -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, @@ -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. @@ -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); diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index d9ac917031..92a778a5bb 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -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, @@ -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); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index fcc52a8f31..e26cee4a53 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -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> @@ -1184,6 +1185,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, diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp index 34ea54ca4b..a19726a479 100644 --- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp @@ -169,6 +169,8 @@ public: reader->sysfsNodeChanged(sysfsNodePath); } + DeviceId getLastUsedInputDeviceId() override { return reader->getLastUsedInputDeviceId(); } + private: std::unique_ptr<InputReaderInterface> reader; }; |