InputReader: Add API to get the last used input device
This will be used by the InputMethodManagerService to notify the IME of
the last input device type that was used to interact with Android when
the IME is shown. This API eliminates the need for IMMS to add another
global spy window to solve this problem.
Bug: 336615195
Test: atest inputflinger_tests
Change-Id: If594bd07bfd0a3cb542fc300854f1dd5717aeab2
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index e5c3aa0..62149c5 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -394,6 +394,12 @@
/* 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 12f52b8..69555f8 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -38,6 +38,8 @@
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 @@
* 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 @@
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 @@
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 @@
}
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 @@
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 d9ac917..92a778a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -118,6 +118,8 @@
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 @@
// 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 fcc52a8..e26cee4 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 @@
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 34ea54c..a19726a 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -169,6 +169,8 @@
reader->sysfsNodeChanged(sysfsNodePath);
}
+ DeviceId getLastUsedInputDeviceId() override { return reader->getLastUsedInputDeviceId(); }
+
private:
std::unique_ptr<InputReaderInterface> reader;
};