diff options
| author | 2024-02-05 23:54:38 +0000 | |
|---|---|---|
| committer | 2024-02-05 23:54:38 +0000 | |
| commit | 94b4ec823d95b56e17e200ca7ab96adc70df462c (patch) | |
| tree | a39e70368e029a303cf0f60b2d73d4dd25c6c2c6 | |
| parent | 8b159e5effb5a16aa990d7cce70ad94226cab448 (diff) | |
| parent | ef2b4502cc2d1079e2e86ab8966ca9d4857c06d0 (diff) | |
Merge "Update mKeyIsWaitingForEventsTimeout separately from the prune check" into main
4 files changed, 76 insertions, 58 deletions
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index af72eb9bcb..77c222290e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -121,11 +121,6 @@ constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2 // Log a warning when an interception call takes longer than this to process. constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; -// Additional key latency in case a connection is still processing some motion events. -// This will help with the case when a user touched a button that opens a new window, -// and gives us the chance to dispatch the key to this new window. -constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms; - // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; @@ -1192,7 +1187,7 @@ bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) * Return true if the events preceding this incoming motion event should be dropped * Return false otherwise (the default behaviour) */ -bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) { +bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) const { const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER); @@ -1234,16 +1229,6 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt } } - // Prevent getting stuck: if we have a pending key event, and some motion events that have not - // yet been processed by some connections, the dispatcher will wait for these motion - // events to be processed before dispatching the key event. This is because these motion events - // may cause a new window to be launched, which the user might expect to receive focus. - // To prevent waiting forever for such events, just send the key to the currently focused window - if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) { - ALOGD("Received a new pointer down event, stop waiting for events to process and " - "just send the pending key event to the focused window."); - mKeyIsWaitingForEventsTimeout = now(); - } return false; } @@ -1291,6 +1276,20 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE mNextUnblockedEvent = mInboundQueue.back(); needWake = true; } + + const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && + isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER); + if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) { + // Prevent waiting too long for unprocessed events: if we have a pending key event, + // and some other events have not yet been processed, the dispatcher will wait for + // these events to be processed before dispatching the key event. This is because + // the unprocessed events may cause the focus to change (for example, by launching a + // new window or tapping a different window). To prevent waiting too long, we force + // the key to be sent to the currently focused window when a new tap comes in. + ALOGD("Received a new pointer down event, stop waiting for events to process and " + "just send the pending key event to the currently focused window."); + mKeyIsWaitingForEventsTimeout = now(); + } break; } case EventEntry::Type::FOCUS: { @@ -2137,7 +2136,8 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, // Start the timer // Wait to send key because there are unprocessed events that may cause focus to change mKeyIsWaitingForEventsTimeout = currentTime + - std::chrono::duration_cast<std::chrono::nanoseconds>(KEY_WAITING_FOR_EVENTS_TIMEOUT) + std::chrono::duration_cast<std::chrono::nanoseconds>( + mPolicy.getKeyWaitingForEventsTimeout()) .count(); return true; } @@ -4879,7 +4879,7 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu break; } default: { - ALOGE("Cannot verify events of type %" PRId32, event.getType()); + LOG(ERROR) << "Cannot verify events of type " << ftl::enum_string(event.getType()); return nullptr; } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 155d485ae4..dcd156675e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -470,7 +470,7 @@ private: bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry); - bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock); + bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) const REQUIRES(mLock); /** * Time to stop waiting for the events to be processed while trying to dispatch a key. diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 9e6209b3d9..62c2b02967 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -131,6 +131,18 @@ public: return std::chrono::nanoseconds(currentTime - eventTime) >= STALE_EVENT_TIMEOUT; } + /** + * Get the additional latency to add while waiting for other input events to process before + * dispatching the pending key. + * If there are unprocessed events, the pending key will not be dispatched immediately. Instead, + * the dispatcher will wait for this timeout, to account for the possibility that the focus + * might change due to touch or other events (such as another app getting launched by keys). + * This would give the pending key the opportunity to go to a newly focused window instead. + */ + virtual std::chrono::nanoseconds getKeyWaitingForEventsTimeout() { + return KEY_WAITING_FOR_EVENTS_TIMEOUT; + } + /* Notifies the policy that a pointer down event has occurred outside the current focused * window. * @@ -150,6 +162,13 @@ public: /* Notifies the policy that there was an input device interaction with apps. */ virtual void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp, const std::set<gui::Uid>& uids) = 0; + +private: + // Additional key latency in case a connection is still processing some motion events. + // This will help with the case when a user touched a button that opens a new window, + // and gives us the chance to dispatch the key to this new window. + static constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = + std::chrono::milliseconds(500); }; } // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 163eab078e..ab667d01fc 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -363,6 +363,8 @@ public: mInterceptKeyTimeout = timeout; } + std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override { return 500ms; } + void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; } void assertUserActivityNotPoked() { @@ -1083,8 +1085,8 @@ public: EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode()); } - void assertNoEvents() { - std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_NO_EVENT_EXPECTED); + void assertNoEvents(std::chrono::milliseconds timeout) { + std::unique_ptr<InputEvent> event = consume(timeout); if (event == nullptr) { return; } @@ -1412,14 +1414,14 @@ public: mInputReceiver->sendTimeline(inputEventId, timeline); } - void assertNoEvents() { + void assertNoEvents(std::chrono::milliseconds timeout = CONSUME_TIMEOUT_NO_EVENT_EXPECTED) { if (mInputReceiver == nullptr && mInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) { return; // Can't receive events if the window does not have input channel } ASSERT_NE(nullptr, mInputReceiver) << "Window without InputReceiver must specify feature NO_INPUT_CHANNEL"; - mInputReceiver->assertNoEvents(); + mInputReceiver->assertNoEvents(timeout); } sp<IBinder> getToken() { return mInfo.token; } @@ -1437,13 +1439,6 @@ public: int getChannelFd() { return mInputReceiver->getChannelFd(); } -private: - FakeWindowHandle(std::string name) : mName(name){}; - const std::string mName; - std::shared_ptr<FakeInputReceiver> mInputReceiver; - static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger - friend class sp<FakeWindowHandle>; - std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = true) { if (mInputReceiver == nullptr) { LOG(FATAL) << "Cannot consume event from a window with no input event receiver"; @@ -1454,6 +1449,13 @@ private: } return event; } + +private: + FakeWindowHandle(std::string name) : mName(name){}; + const std::string mName; + std::shared_ptr<FakeInputReceiver> mInputReceiver; + static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger + friend class sp<FakeWindowHandle>; }; std::atomic<int32_t> FakeWindowHandle::sId{1}; @@ -1512,7 +1514,7 @@ public: std::unique_ptr<MotionEvent> consumeMotion() { return mInputReceiver.consumeMotion(); } - void assertNoEvents() { mInputReceiver.assertNoEvents(); } + void assertNoEvents() { mInputReceiver.assertNoEvents(CONSUME_TIMEOUT_NO_EVENT_EXPECTED); } private: FakeInputReceiver mInputReceiver; @@ -8824,16 +8826,11 @@ TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) { /** * If a window is processing a motion event, and then a key event comes in, the key event should * not get delivered to the focused window until the motion is processed. - * - * Warning!!! - * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT - * and the injection timeout that we specify when injecting the key. - * We must have the injection timeout (100ms) be smaller than - * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms). - * - * If that value changes, this test should also change. */ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { + // The timeouts in this test are established by relying on the fact that the "key waiting for + // events timeout" is equal to 500ms. + ASSERT_EQ(mFakePolicy->getKeyWaitingForEventsTimeout(), 500ms); mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); @@ -8842,23 +8839,18 @@ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { ASSERT_TRUE(downSequenceNum); const auto& [upSequenceNum, upEvent] = mWindow->receiveEvent(); ASSERT_TRUE(upSequenceNum); - // Don't finish the events yet, and send a key - // Injection will "succeed" because we will eventually give up and send the key to the focused - // window even if motions are still being processed. But because the injection timeout is short, - // we will receive INJECTION_TIMED_OUT as the result. - InputEventInjectionResult result = - injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT, - InputEventInjectionSync::WAIT_FOR_RESULT, 100ms); - ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result); + // Don't finish the events yet, and send a key + mDispatcher->notifyKey( + KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) + .build()); // Key will not be sent to the window, yet, because the window is still processing events // and the key remains pending, waiting for the touch events to be processed // Make sure that `assertNoEvents` doesn't wait too long, because it could cause an ANR. - // Rely here on the fact that it uses CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood. - static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms); - mWindow->assertNoEvents(); + mWindow->assertNoEvents(100ms); - std::this_thread::sleep_for(500ms); + std::this_thread::sleep_for(400ms); // if we wait long enough though, dispatcher will give up, and still send the key // to the focused window, even though we have not yet finished the motion event mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); @@ -8873,7 +8865,10 @@ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { * focused window right away. */ TEST_F(InputDispatcherSingleWindowAnr, - PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) { + PendingKey_IsDeliveredWhileMotionIsProcessingAndNewTouchComesIn) { + // The timeouts in this test are established by relying on the fact that the "key waiting for + // events timeout" is equal to 500ms. + ASSERT_EQ(mFakePolicy->getKeyWaitingForEventsTimeout(), 500ms); mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); @@ -8888,15 +8883,19 @@ TEST_F(InputDispatcherSingleWindowAnr, .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) .build()); // At this point, key is still pending, and should not be sent to the application yet. - // Make sure the `assertNoEvents` check doesn't take too long. It uses - // CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood. - static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms); - mWindow->assertNoEvents(); + mWindow->assertNoEvents(100ms); // Now tap down again. It should cause the pending key to go to the focused window right away. tapOnWindow(); - mWindow->consumeKeyEvent(WithKeyAction(AKEY_EVENT_ACTION_DOWN)); // it doesn't matter that we - // haven't ack'd the other events yet. We can finish events in any order. + // Now that we tapped, we should receive the key immediately. + // Since there's still room for slowness, we use 200ms, which is much less than + // the "key waiting for events' timeout of 500ms minus the already waited 100ms duration. + std::unique_ptr<InputEvent> keyEvent = mWindow->consume(200ms); + ASSERT_NE(nullptr, keyEvent); + ASSERT_EQ(InputEventType::KEY, keyEvent->getType()); + ASSERT_THAT(static_cast<KeyEvent&>(*keyEvent), WithKeyAction(AKEY_EVENT_ACTION_DOWN)); + // it doesn't matter that we haven't ack'd the other events yet. We can finish events in any + // order. mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN mWindow->finishEvent(*upSequenceNum); // first tap's ACTION_UP mWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); |