From 2ee6d0ba7d362ed956b5e10a6809059757bc0a41 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Thu, 3 Mar 2022 20:19:38 +0800 Subject: Reset intercept key wake time when receving up event The policy need to handle the key shortcuts via policy callbacks, and it may have a pending timeout in order to detect the combination keys. If the pending key event has already known an up event has been queued, we could reset the wake time to speed up the processing for it may be handled in policy or not. Bug: 220669913 Test: atest inputflinger_tests Change-Id: Ic002b770632e15506fd2f581fc8716b26ea3626c --- .../inputflinger/dispatcher/InputDispatcher.cpp | 15 +++++ .../inputflinger/tests/InputDispatcher_test.cpp | 65 +++++++++++++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 06ad6a8f61..6bfac6cb5f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1032,6 +1032,21 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr newE } } } + + // If a new up event comes in, and the pending event with same key code has been asked + // to try again later because of the policy. We have to reset the intercept key wake up + // time for it may have been handled in the policy and could be dropped. + if (keyEntry.action == AKEY_EVENT_ACTION_UP && mPendingEvent && + mPendingEvent->type == EventEntry::Type::KEY) { + KeyEntry& pendingKey = static_cast(*mPendingEvent); + if (pendingKey.keyCode == keyEntry.keyCode && + pendingKey.interceptKeyResult == + KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) { + pendingKey.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; + pendingKey.interceptKeyWakeupTime = 0; + needWake = true; + } + } break; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 9633932e75..838e6aa785 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -289,6 +289,13 @@ public: ASSERT_EQ(token, *receivedToken); } + /** + * Set policy timeout. A value of zero means next key will not be intercepted. + */ + void setInterceptKeyTimeout(std::chrono::milliseconds timeout) { + mInterceptKeyTimeout = timeout; + } + private: std::mutex mLock; std::unique_ptr mFilteredEvent GUARDED_BY(mLock); @@ -311,6 +318,8 @@ private: sp mDropTargetWindowToken GUARDED_BY(mLock); bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false; + std::chrono::milliseconds mInterceptKeyTimeout = 0ms; + // All three ANR-related callbacks behave the same way, so we use this generic function to wait // for a specific container to become non-empty. When the container is non-empty, return the // first entry from the container and erase it. @@ -429,12 +438,20 @@ private: return true; } - void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {} + void interceptKeyBeforeQueueing(const KeyEvent* inputEvent, uint32_t&) override { + if (inputEvent->getAction() == AKEY_EVENT_ACTION_UP) { + // Clear intercept state when we handled the event. + mInterceptKeyTimeout = 0ms; + } + } void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {} nsecs_t interceptKeyBeforeDispatching(const sp&, const KeyEvent*, uint32_t) override { - return 0; + nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count(); + // Clear intercept state so we could dispatch the event in next wake. + mInterceptKeyTimeout = 0ms; + return delay; } bool dispatchUnhandledKey(const sp&, const KeyEvent*, uint32_t, KeyEvent*) override { @@ -2182,6 +2199,50 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { 0 /*expectedFlags*/); } +TEST_F(InputDispatcherTest, InterceptKeyByPolicy) { + std::shared_ptr application = std::make_shared(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + window->setFocusable(true); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); + + window->consumeFocusEvent(true); + + NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + const std::chrono::milliseconds interceptKeyTimeout = 50ms; + const nsecs_t injectTime = keyArgs.eventTime; + mFakePolicy->setInterceptKeyTimeout(interceptKeyTimeout); + mDispatcher->notifyKey(&keyArgs); + // The dispatching time should be always greater than or equal to intercept key timeout. + window->consumeKeyDown(ADISPLAY_ID_DEFAULT); + ASSERT_TRUE((systemTime(SYSTEM_TIME_MONOTONIC) - injectTime) >= + std::chrono::nanoseconds(interceptKeyTimeout).count()); +} + +TEST_F(InputDispatcherTest, InterceptKeyIfKeyUp) { + std::shared_ptr application = std::make_shared(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + window->setFocusable(true); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); + + window->consumeFocusEvent(true); + + NotifyKeyArgs keyDown = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + NotifyKeyArgs keyUp = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); + mFakePolicy->setInterceptKeyTimeout(150ms); + mDispatcher->notifyKey(&keyDown); + mDispatcher->notifyKey(&keyUp); + + // Window should receive key event immediately when same key up. + window->consumeKeyDown(ADISPLAY_ID_DEFAULT); + window->consumeKeyUp(ADISPLAY_ID_DEFAULT); +} + /** * Ensure the correct coordinate spaces are used by InputDispatcher. * -- cgit v1.2.3-59-g8ed1b