diff options
-rw-r--r-- | include/input/InputEventBuilders.h | 86 | ||||
-rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 261 | ||||
-rw-r--r-- | services/inputflinger/tests/TestEventMatchers.h | 29 |
3 files changed, 361 insertions, 15 deletions
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h index 9c0c10e603..2d23b97386 100644 --- a/include/input/InputEventBuilders.h +++ b/include/input/InputEventBuilders.h @@ -160,4 +160,90 @@ private: std::vector<PointerBuilder> mPointers; }; +class KeyEventBuilder { +public: + KeyEventBuilder(int32_t action, int32_t source) { + mAction = action; + mSource = source; + mEventTime = systemTime(SYSTEM_TIME_MONOTONIC); + mDownTime = mEventTime; + } + + KeyEventBuilder(const KeyEvent& event) { + mAction = event.getAction(); + mDeviceId = event.getDeviceId(); + mSource = event.getSource(); + mDownTime = event.getDownTime(); + mEventTime = event.getEventTime(); + mDisplayId = event.getDisplayId(); + mFlags = event.getFlags(); + mKeyCode = event.getKeyCode(); + mScanCode = event.getScanCode(); + mMetaState = event.getMetaState(); + mRepeatCount = event.getRepeatCount(); + } + + KeyEventBuilder& deviceId(int32_t deviceId) { + mDeviceId = deviceId; + return *this; + } + + KeyEventBuilder& downTime(nsecs_t downTime) { + mDownTime = downTime; + return *this; + } + + KeyEventBuilder& eventTime(nsecs_t eventTime) { + mEventTime = eventTime; + return *this; + } + + KeyEventBuilder& displayId(int32_t displayId) { + mDisplayId = displayId; + return *this; + } + + KeyEventBuilder& policyFlags(int32_t policyFlags) { + mPolicyFlags = policyFlags; + return *this; + } + + KeyEventBuilder& addFlag(uint32_t flags) { + mFlags |= flags; + return *this; + } + + KeyEventBuilder& keyCode(int32_t keyCode) { + mKeyCode = keyCode; + return *this; + } + + KeyEventBuilder& repeatCount(int32_t repeatCount) { + mRepeatCount = repeatCount; + return *this; + } + + KeyEvent build() const { + KeyEvent event{}; + event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC, + mAction, mFlags, mKeyCode, mScanCode, mMetaState, mRepeatCount, mDownTime, + mEventTime); + return event; + } + +private: + int32_t mAction; + int32_t mDeviceId = DEFAULT_DEVICE_ID; + uint32_t mSource; + nsecs_t mDownTime; + nsecs_t mEventTime; + int32_t mDisplayId{ADISPLAY_ID_DEFAULT}; + uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS; + int32_t mFlags{0}; + int32_t mKeyCode{AKEYCODE_UNKNOWN}; + int32_t mScanCode{0}; + int32_t mMetaState{AMETA_NONE}; + int32_t mRepeatCount{0}; +}; + } // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 2509c60573..3c2b31d770 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -335,7 +335,7 @@ public: std::optional<sp<IBinder>> receivedToken = getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock, mNotifyInputChannelBroken); - ASSERT_TRUE(receivedToken.has_value()); + ASSERT_TRUE(receivedToken.has_value()) << "Did not receive the broken channel token"; ASSERT_EQ(token, *receivedToken); } @@ -366,6 +366,30 @@ public: ASSERT_FALSE(mNotifiedInteractions.popWithTimeout(10ms)); } + void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler) { + std::scoped_lock lock(mLock); + mUnhandledKeyHandler = handler; + } + + void assertUnhandledKeyReported(int32_t keycode) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + std::optional<int32_t> unhandledKeycode = + getItemFromStorageLockedInterruptible(100ms, mReportedUnhandledKeycodes, lock, + mNotifyUnhandledKey); + ASSERT_TRUE(unhandledKeycode) << "Expected unhandled key to be reported"; + ASSERT_EQ(unhandledKeycode, keycode); + } + + void assertUnhandledKeyNotReported() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + std::optional<int32_t> unhandledKeycode = + getItemFromStorageLockedInterruptible(10ms, mReportedUnhandledKeycodes, lock, + mNotifyUnhandledKey); + ASSERT_FALSE(unhandledKeycode) << "Expected unhandled key NOT to be reported"; + } + private: std::mutex mLock; std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock); @@ -395,6 +419,10 @@ private: BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions; + std::condition_variable mNotifyUnhandledKey; + std::queue<int32_t> mReportedUnhandledKeycodes GUARDED_BY(mLock); + std::function<std::optional<KeyEvent>(const KeyEvent&)> mUnhandledKeyHandler GUARDED_BY(mLock); + // 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. @@ -437,7 +465,6 @@ private: condition.wait_for(lock, timeout, [&storage]() REQUIRES(mLock) { return !storage.empty(); }); if (storage.empty()) { - ADD_FAILURE() << "Did not receive the expected callback"; return std::nullopt; } T item = storage.front(); @@ -528,9 +555,12 @@ private: return delay; } - std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&, + std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent& event, uint32_t) override { - return {}; + std::scoped_lock lock(mLock); + mReportedUnhandledKeycodes.emplace(event.getKeyCode()); + mNotifyUnhandledKey.notify_all(); + return mUnhandledKeyHandler != nullptr ? mUnhandledKeyHandler(event) : std::nullopt; } void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, @@ -845,13 +875,13 @@ public: mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel)); } - InputEvent* consume(std::chrono::milliseconds timeout) { + InputEvent* consume(std::chrono::milliseconds timeout, bool handled = false) { InputEvent* event; std::optional<uint32_t> consumeSeq = receiveEvent(timeout, &event); if (!consumeSeq) { return nullptr; } - finishEvent(*consumeSeq); + finishEvent(*consumeSeq, handled); return event; } @@ -897,8 +927,8 @@ public: /** * To be used together with "receiveEvent" to complete the consumption of an event. */ - void finishEvent(uint32_t consumeSeq) { - const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true); + void finishEvent(uint32_t consumeSeq, bool handled = true) { + const status_t status = mConsumer->sendFinishedSignal(consumeSeq, handled); ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK."; } @@ -1209,8 +1239,8 @@ public: void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); } - KeyEvent* consumeKey() { - InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + KeyEvent* consumeKey(bool handled = true) { + InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled); if (event == nullptr) { ADD_FAILURE() << "Consume failed : no event"; return nullptr; @@ -1349,11 +1379,11 @@ public: mInputReceiver->sendTimeline(inputEventId, timeline); } - InputEvent* consume(std::chrono::milliseconds timeout) { + InputEvent* consume(std::chrono::milliseconds timeout, bool handled = true) { if (mInputReceiver == nullptr) { return nullptr; } - return mInputReceiver->consume(timeout); + return mInputReceiver->consume(timeout, handled); } MotionEvent* consumeMotion() { @@ -6534,6 +6564,213 @@ TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithKeys) { ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasNotCalled()); } +class InputDispatcherFallbackKeyTest : public InputDispatcherTest { +protected: + std::shared_ptr<FakeApplicationHandle> mApp; + sp<FakeWindowHandle> mWindow; + + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + mApp = std::make_shared<FakeApplicationHandle>(); + + mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + mWindow->setFrame(Rect(0, 0, 100, 100)); + + mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); + setFocusedWindow(mWindow); + ASSERT_NO_FATAL_FAILURE(mWindow->consumeFocusEvent(/*hasFocus=*/true)); + } + + void setFallback(int32_t keycode) { + mFakePolicy->setUnhandledKeyHandler([keycode](const KeyEvent& event) { + return KeyEventBuilder(event).keyCode(keycode).build(); + }); + } + + void consumeKey(bool handled, const ::testing::Matcher<KeyEvent>& matcher) { + KeyEvent* event = mWindow->consumeKey(handled); + ASSERT_NE(event, nullptr) << "Did not receive key event"; + ASSERT_THAT(*event, matcher); + } +}; + +TEST_F(InputDispatcherFallbackKeyTest, PolicyNotNotifiedForHandledKey) { + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/true, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); +} + +TEST_F(InputDispatcherFallbackKeyTest, PolicyNotifiedForUnhandledKey) { + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/false, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); +} + +TEST_F(InputDispatcherFallbackKeyTest, NoFallbackRequestedByPolicy) { + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + + // Since the policy did not request any fallback to be generated, ensure there are no events. + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); +} + +TEST_F(InputDispatcherFallbackKeyTest, FallbackDispatchForUnhandledKey) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + + // Since the key was not handled, ensure the fallback event was dispatched instead. + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // Release the original key, and ensure the fallback key is also released. + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherFallbackKeyTest, AppHandlesPreviouslyUnhandledKey) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event, but handle the fallback. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // Release the original key, and ensure the fallback key is also released. + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + // But this time, the app handles the original key. + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + // Ensure the fallback key is canceled. + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherFallbackKeyTest, AppDoesNotHandleFallback) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + // App does not handle the fallback either, so ensure another fallback is not generated. + setFallback(AKEYCODE_C); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // Release the original key, and ensure the fallback key is also released. + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherFallbackKeyTest, InconsistentPolicyCancelsFallback) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event, so fallback is generated. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // Release the original key, but assume the policy is misbehaving and it + // generates an inconsistent fallback to the one from the DOWN event. + setFallback(AKEYCODE_C); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + // Ensure the fallback key reported before as DOWN is canceled due to the inconsistency. + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event, so fallback is generated. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // The original key is canceled. + mDispatcher->notifyKey(KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD) + .keyCode(AKEYCODE_A) + .addFlag(AKEY_EVENT_FLAG_CANCELED) + .build()); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), + WithFlags(AKEY_EVENT_FLAG_CANCELED))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + // Ensure the fallback key is also canceled due to the original key being canceled. + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); + mWindow->assertNoEvents(); +} + class InputDispatcherKeyRepeatTest : public InputDispatcherTest { protected: static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h index ee6ff53d8c..a0a0f5a3de 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -464,9 +464,32 @@ inline WithPointersMatcher WithPointers( return WithPointersMatcher(pointers); } -MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") { - *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode; - return arg.keyCode == keyCode; +/// Key code +class WithKeyCodeMatcher { +public: + using is_gtest_matcher = void; + explicit WithKeyCodeMatcher(int32_t keyCode) : mKeyCode(keyCode) {} + + bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const { + return mKeyCode == args.keyCode; + } + + bool MatchAndExplain(const KeyEvent& event, std::ostream*) const { + return mKeyCode == event.getKeyCode(); + } + + void DescribeTo(std::ostream* os) const { + *os << "with key code " << KeyEvent::getLabel(mKeyCode); + } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong key code"; } + +private: + const int32_t mKeyCode; +}; + +inline WithKeyCodeMatcher WithKeyCode(int32_t keyCode) { + return WithKeyCodeMatcher(keyCode); } MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") { |