diff options
| author | 2024-11-12 01:31:04 +0000 | |
|---|---|---|
| committer | 2025-03-17 21:49:34 +0000 | |
| commit | 523b461f296f6841e60ef43eb9577ec0833b6e12 (patch) | |
| tree | d6165f3f2387963114ed2f28930b1e68110fe2b4 | |
| parent | 4c54b762b946f733841c34d79884ccae7747ffde (diff) | |
Add support for search modifier fallbacks
Fixes a bug in where kcm fallbacks with search based modifiers could
not activate since PhoneWindowManager would consume all search events.
This prevented the InputDispatcher from generating the fallback.
This change modifies the behavior so InputDispatcher handles whether
the search-based event will attempt to generate a fallback.
Bug: 384113980
Test: atest InputDispatcherTest
Flag: com.android.hardware.input.fix_search_modifier_fallbacks
Change-Id: Ib7eca10f00ec93fd46a93396b7908ff925dd705b
8 files changed, 169 insertions, 20 deletions
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index becfb05f67..9cd76c7d11 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -125,10 +125,17 @@ struct KeyEntry : EventEntry { bool syntheticRepeat; // set to true for synthetic key repeats enum class InterceptKeyResult { + // The interception result is unknown. UNKNOWN, + // The event should be skipped and not sent to the application. SKIP, + // The event should be sent to the application. CONTINUE, + // The event should eventually be sent to the application, after a delay. TRY_AGAIN_LATER, + // The event should not be initially sent to the application, but instead go through + // post-processing to generate a fallback key event and then sent to the application. + FALLBACK, }; // These are special fields that may need to be modified while the event is being dispatched. mutable InterceptKeyResult interceptKeyResult; // set based on the interception result diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 95e1c06615..391703d506 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -952,7 +952,7 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, new LatencyAggregatorWithHistograms())) : std::move(std::unique_ptr<InputEventTimelineProcessor>( new LatencyAggregator()))), - mLatencyTracker(*mInputEventTimelineProcessor) { + mLatencyTracker(*mInputEventTimelineProcessor, mInputDevices) { mReporter = createInputReporter(); mWindowInfoListener = sp<DispatcherWindowListener>::make(*this); @@ -1961,11 +1961,74 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con } } + if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::FALLBACK) { + findAndDispatchFallbackEvent(currentTime, entry, inputTargets); + // Drop the key. + return true; + } + // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true; } +void InputDispatcher::findAndDispatchFallbackEvent(nsecs_t currentTime, + std::shared_ptr<const KeyEntry> entry, + std::vector<InputTarget>& inputTargets) { + // Find the fallback associated with the incoming key event and dispatch it. + KeyEvent event = createKeyEvent(*entry); + const int32_t originalKeyCode = entry->keyCode; + + // Fetch the fallback event. + KeyCharacterMap::FallbackAction fallback; + for (const InputDeviceInfo& deviceInfo : mInputDevices) { + if (deviceInfo.getId() == entry->deviceId) { + const KeyCharacterMap* map = deviceInfo.getKeyCharacterMap(); + + LOG_ALWAYS_FATAL_IF(map == nullptr, "No KeyCharacterMap for device %d", + entry->deviceId); + map->getFallbackAction(entry->keyCode, entry->metaState, &fallback); + break; + } + } + + if (fallback.keyCode == AKEYCODE_UNKNOWN) { + // No fallback detected. + return; + } + + std::unique_ptr<KeyEntry> fallbackKeyEntry = + std::make_unique<KeyEntry>(mIdGenerator.nextId(), entry->injectionState, + event.getEventTime(), event.getDeviceId(), event.getSource(), + event.getDisplayId(), entry->policyFlags, entry->action, + event.getFlags() | AKEY_EVENT_FLAG_FALLBACK, + fallback.keyCode, event.getScanCode(), /*metaState=*/0, + event.getRepeatCount(), event.getDownTime()); + + if (mTracer) { + fallbackKeyEntry->traceTracker = + mTracer->traceDerivedEvent(*fallbackKeyEntry, *entry->traceTracker); + } + + for (const InputTarget& inputTarget : inputTargets) { + std::shared_ptr<Connection> connection = inputTarget.connection; + if (!connection->responsive || (connection->status != Connection::Status::NORMAL)) { + return; + } + + connection->inputState.setFallbackKey(originalKeyCode, fallback.keyCode); + if (entry->action == AKEY_EVENT_ACTION_UP) { + connection->inputState.removeFallbackKey(originalKeyCode); + } + + if (mTracer) { + mTracer->dispatchToTargetHint(*fallbackKeyEntry->traceTracker, inputTarget); + } + enqueueDispatchEntryAndStartDispatchCycleLocked(currentTime, connection, + std::move(fallbackKeyEntry), inputTarget); + } +} + void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) { LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS) << prefix << "eventTime=" << entry.eventTime << ", deviceId=" << entry.deviceId @@ -4346,7 +4409,7 @@ void InputDispatcher::notifyInputDevicesChanged(const NotifyInputDevicesChangedA std::scoped_lock _l(mLock); // Reset key repeating in case a keyboard device was added or removed or something. resetKeyRepeatLocked(); - mLatencyTracker.setInputDevices(args.inputDeviceInfos); + mInputDevices = args.inputDeviceInfos; } void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 38f782573a..ad7e87e192 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -918,10 +918,14 @@ private: std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable( const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry, bool handled) REQUIRES(mLock); + void findAndDispatchFallbackEvent(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry, + std::vector<InputTarget>& inputTargets) REQUIRES(mLock); // Statistics gathering. nsecs_t mLastStatisticPushTime = 0; std::unique_ptr<InputEventTimelineProcessor> mInputEventTimelineProcessor GUARDED_BY(mLock); + // Must outlive `mLatencyTracker`. + std::vector<InputDeviceInfo> mInputDevices; LatencyTracker mLatencyTracker GUARDED_BY(mLock); void traceInboundQueueLengthLocked() REQUIRES(mLock); void traceOutboundQueueLength(const Connection& connection); diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp index 0921e37d03..7c23694fbd 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.cpp +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -67,8 +67,9 @@ static void eraseByValue(std::multimap<K, V>& map, const V& value) { } // namespace -LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor) - : mTimelineProcessor(&processor) {} +LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor, + std::vector<InputDeviceInfo>& inputDevices) + : mTimelineProcessor(&processor), mInputDevices(inputDevices) {} void LatencyTracker::trackListener(const NotifyArgs& args) { if (const NotifyKeyArgs* keyArgs = std::get_if<NotifyKeyArgs>(&args)) { @@ -248,8 +249,4 @@ std::string LatencyTracker::dump(const char* prefix) const { StringPrintf("%s mEventTimes.size() = %zu\n", prefix, mEventTimes.size()); } -void LatencyTracker::setInputDevices(const std::vector<InputDeviceInfo>& inputDevices) { - mInputDevices = inputDevices; -} - } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h index 79ea14c4fb..7e2cc79249 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -20,9 +20,11 @@ #include <map> #include <unordered_map> +#include <vector> #include <binder/IBinder.h> #include <input/Input.h> +#include <input/InputDevice.h> #include "InputEventTimeline.h" #include "NotifyArgs.h" @@ -41,8 +43,10 @@ public: /** * Create a LatencyTracker. * param reportingFunction: the function that will be called in order to report full latency. + * param inputDevices: input devices relevant for tracking. */ - LatencyTracker(InputEventTimelineProcessor& processor); + LatencyTracker(InputEventTimelineProcessor& processor, + std::vector<InputDeviceInfo>& inputDevices); /** * Start keeping track of an event identified by the args. This must be called first. * If duplicate events are encountered (events that have the same eventId), none of them will be @@ -60,7 +64,6 @@ public: std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); std::string dump(const char* prefix) const; - void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices); private: /** @@ -81,7 +84,7 @@ private: std::multimap<nsecs_t /*eventTime*/, int32_t /*inputEventId*/> mEventTimes; InputEventTimelineProcessor* mTimelineProcessor; - std::vector<InputDeviceInfo> mInputDevices; + std::vector<InputDeviceInfo>& mInputDevices; void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId, const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction, diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 835b677e1e..f7dcd6c6e3 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -37,6 +37,7 @@ #include <input/BlockingQueue.h> #include <input/Input.h> #include <input/InputConsumer.h> +#include <input/KeyCharacterMap.h> #include <input/PrintTools.h> #include <linux/input.h> #include <sys/epoll.h> @@ -139,6 +140,30 @@ static KeyEvent getTestKeyEvent() { return event; } +InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t productId, DeviceId deviceId) { + InputDeviceIdentifier identifier; + identifier.vendor = vendorId; + identifier.product = productId; + auto info = InputDeviceInfo(); + info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "Test Device", + /*isExternal=*/false, /*hasMic=*/false, ui::LogicalDisplayId::INVALID); + return info; +} + +std::unique_ptr<KeyCharacterMap> loadKeyCharacterMap(const char* name) { + InputDeviceIdentifier identifier; + identifier.name = name; + std::string path = getInputDeviceConfigurationFilePathByName(identifier.getCanonicalName(), + InputDeviceConfigurationFileType:: + KEY_CHARACTER_MAP); + + if (path.empty()) { + return nullptr; + } + + return *KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE); +} + } // namespace // --- InputDispatcherTest --- @@ -7480,6 +7505,50 @@ TEST_F(InputDispatcherTest, FocusedWindow_PolicyConsumedKeyIgnoresDisableUserAct mFakePolicy->assertUserActivityPoked(); } +TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceivePolicyFallbackKey) { +#if !defined(__ANDROID__) + GTEST_SKIP() << "b/253299089 Generic files are currently read directly from device."; +#endif + InputDeviceInfo testDevice = generateTestDeviceInfo(/*vendorId=*/0, + /*productId=*/0, /*deviceId=*/1); + std::unique_ptr<KeyCharacterMap> kcm = loadKeyCharacterMap("Generic"); + ASSERT_NE(nullptr, kcm); + + testDevice.setKeyCharacterMap(std::move(kcm)); + mDispatcher->notifyInputDevicesChanged(NotifyInputDevicesChangedArgs(/*id=*/1, {testDevice})); + + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", + ui::LogicalDisplayId::DEFAULT); + + window->setFocusable(true); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + setFocusedWindow(window); + + window->consumeFocusEvent(true); + + mFakePolicy->setInterceptKeyBeforeDispatchingResult( + inputdispatcher::KeyEntry::InterceptKeyResult::FALLBACK); + + // In the Generic KCM fallbacks, Meta + Space => SEARCH. + mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .keyCode(AKEYCODE_SPACE) + .metaState(AMETA_META_ON) + .build()); + mDispatcher->waitForIdle(); + + // Should have poked user activity + mFakePolicy->assertUserActivityPoked(); + + // Fallback is generated and sent instead. + std::unique_ptr<KeyEvent> consumedEvent = window->consumeKey(/*handled=*/false); + ASSERT_NE(nullptr, consumedEvent); + ASSERT_THAT(*consumedEvent, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_SEARCH), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); +} + class DisableUserActivityInputDispatcherTest : public InputDispatcherTest, public ::testing::WithParamInterface<bool> {}; diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index d8c5eac3c4..b7ca24bb61 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -19,10 +19,13 @@ #include "NotifyArgsBuilders.h" #include "android/input.h" +#include <vector> + #include <android-base/logging.h> #include <android-base/properties.h> #include <binder/Binder.h> #include <gtest/gtest.h> +#include <input/InputDevice.h> #include <input/PrintTools.h> #include <inttypes.h> #include <linux/input.h> @@ -51,11 +54,6 @@ static InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t produc return info; } -void setDefaultInputDeviceInfo(LatencyTracker& tracker) { - InputDeviceInfo deviceInfo = generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID); - tracker.setInputDevices({deviceInfo}); -} - const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200); const auto FIRST_MOUSE_POINTER = PointerBuilder(/*id=*/1, ToolType::MOUSE); @@ -120,13 +118,14 @@ protected: std::unique_ptr<LatencyTracker> mTracker; sp<IBinder> connection1; sp<IBinder> connection2; + std::vector<InputDeviceInfo> inputDevices; void SetUp() override { connection1 = sp<BBinder>::make(); connection2 = sp<BBinder>::make(); - mTracker = std::make_unique<LatencyTracker>(*this); - setDefaultInputDeviceInfo(*mTracker); + inputDevices.push_back(generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID)); + mTracker = std::make_unique<LatencyTracker>(*this, inputDevices); } void TearDown() override {} @@ -140,6 +139,10 @@ protected: */ void assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines); + void updateInputDevices(const std::vector<InputDeviceInfo>& inputDevicesUpdated) { + inputDevices = inputDevicesUpdated; + } + private: void processTimeline(const InputEventTimeline& timeline) override { mReceivedTimelines.push_back(timeline); @@ -448,7 +451,7 @@ TEST_F(LatencyTrackerTest, TrackListenerCheck_DeviceInfoFieldsInputEventTimeline deviceInfo2.addSource(AINPUT_SOURCE_TOUCHSCREEN); deviceInfo2.addSource(AINPUT_SOURCE_STYLUS); - mTracker->setInputDevices({deviceInfo1, deviceInfo2}); + updateInputDevices({deviceInfo1, deviceInfo2}); mTracker->trackListener( MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, inputEventId) diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp index 157a3338da..9c027fa00c 100644 --- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp @@ -17,6 +17,8 @@ #include <fuzzer/FuzzedDataProvider.h> #include <linux/input.h> +#include <vector> + #include "../../InputDeviceMetricsSource.h" #include "../InputEventTimeline.h" #include "NotifyArgsBuilders.h" @@ -58,7 +60,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { FuzzedDataProvider fdp(data, size); EmptyProcessor emptyProcessor; - LatencyTracker tracker(emptyProcessor); + std::vector<InputDeviceInfo> emptyDevices; + LatencyTracker tracker(emptyProcessor, emptyDevices); // Make some pre-defined tokens to ensure that some timelines are complete. std::array<sp<IBinder> /*token*/, 10> predefinedTokens; |