Add test cases for fallback key generation from InputDispatcher
Bug: 210460522
Bug: 309154937
Test: atest inputflinger_tests
Change-Id: If07a2d1dd0043e4959ed20b086fa5afef26bd751
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 9c0c10e..2d23b97 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -160,4 +160,90 @@
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 2509c60..3c2b31d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -335,7 +335,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/**
* 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 @@
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 @@
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 @@
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 ee6ff53..a0a0f5a 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -464,9 +464,32 @@
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") {