diff options
| author | 2024-03-28 13:03:23 +0000 | |
|---|---|---|
| committer | 2024-03-28 13:03:23 +0000 | |
| commit | 199532b492288fc0c91df363cc87ef4e2eb97bff (patch) | |
| tree | dd4737c3753276b3d1b785078414b374ac072573 | |
| parent | a394761fa1511992deb2106aa6b7460b7b850174 (diff) | |
| parent | c5340735ca80473efe685b31e355ff4fd7392a25 (diff) | |
Merge changes I2dfc9f2d,I36d6b5ae into main
* changes:
Extract FakeInputReceiver and FakeInputWindow into a separate cpp file
Extract FakeInputDispatcherPolicy into a separate cpp file
| -rw-r--r-- | services/inputflinger/benchmarks/Android.bp | 3 | ||||
| -rw-r--r-- | services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp | 58 | ||||
| -rw-r--r-- | services/inputflinger/tests/Android.bp | 10 | ||||
| -rw-r--r-- | services/inputflinger/tests/FakeInputDispatcherPolicy.cpp | 473 | ||||
| -rw-r--r-- | services/inputflinger/tests/FakeInputDispatcherPolicy.h | 228 | ||||
| -rw-r--r-- | services/inputflinger/tests/FakeWindowHandle.h | 275 | ||||
| -rw-r--r-- | services/inputflinger/tests/FakeWindows.cpp | 354 | ||||
| -rw-r--r-- | services/inputflinger/tests/FakeWindows.h | 387 | ||||
| -rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 1172 | ||||
| -rw-r--r-- | services/inputflinger/tests/fuzzers/Android.bp | 5 | ||||
| -rw-r--r-- | services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp | 19 |
11 files changed, 1474 insertions, 1510 deletions
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp index 2d1257478d..438507229b 100644 --- a/services/inputflinger/benchmarks/Android.bp +++ b/services/inputflinger/benchmarks/Android.bp @@ -11,6 +11,7 @@ package { cc_benchmark { name: "inputflinger_benchmarks", srcs: [ + ":inputdispatcher_common_test_sources", "InputDispatcher_benchmarks.cpp", ], defaults: [ @@ -31,6 +32,8 @@ cc_benchmark { ], static_libs: [ "libattestation", + "libgmock", + "libgtest", "libinputdispatcher", ], } diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 5ae3715f2f..5f955908c5 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -22,7 +22,7 @@ #include "../dispatcher/InputDispatcher.h" #include "../tests/FakeApplicationHandle.h" #include "../tests/FakeInputDispatcherPolicy.h" -#include "../tests/FakeWindowHandle.h" +#include "../tests/FakeWindows.h" using android::base::Result; using android::gui::WindowInfo; @@ -104,16 +104,16 @@ static NotifyMotionArgs generateMotionArgs() { static void benchmarkNotifyMotion(benchmark::State& state) { // Create dispatcher FakeInputDispatcherPolicy fakePolicy; - InputDispatcher dispatcher(fakePolicy); - dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); - dispatcher.start(); + auto dispatcher = std::make_unique<InputDispatcher>(fakePolicy); + dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); + dispatcher->start(); // Create a window that will receive motion events std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID); - dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + dispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); NotifyMotionArgs motionArgs = generateMotionArgs(); @@ -122,60 +122,60 @@ static void benchmarkNotifyMotion(benchmark::State& state) { motionArgs.action = AMOTION_EVENT_ACTION_DOWN; motionArgs.downTime = now(); motionArgs.eventTime = motionArgs.downTime; - dispatcher.notifyMotion(motionArgs); + dispatcher->notifyMotion(motionArgs); // Send ACTION_UP motionArgs.action = AMOTION_EVENT_ACTION_UP; motionArgs.eventTime = now(); - dispatcher.notifyMotion(motionArgs); + dispatcher->notifyMotion(motionArgs); - window->consumeMotion(); - window->consumeMotion(); + window->consumeMotionEvent(); + window->consumeMotionEvent(); } - dispatcher.stop(); + dispatcher->stop(); } static void benchmarkInjectMotion(benchmark::State& state) { // Create dispatcher FakeInputDispatcherPolicy fakePolicy; - InputDispatcher dispatcher(fakePolicy); - dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); - dispatcher.start(); + auto dispatcher = std::make_unique<InputDispatcher>(fakePolicy); + dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); + dispatcher->start(); // Create a window that will receive motion events std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID); - dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + dispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); for (auto _ : state) { MotionEvent event = generateMotionEvent(); // Send ACTION_DOWN - dispatcher.injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE, - INJECT_EVENT_TIMEOUT, - POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + dispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE, + INJECT_EVENT_TIMEOUT, + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); // Send ACTION_UP event.setAction(AMOTION_EVENT_ACTION_UP); - dispatcher.injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE, - INJECT_EVENT_TIMEOUT, - POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + dispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE, + INJECT_EVENT_TIMEOUT, + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); - window->consumeMotion(); - window->consumeMotion(); + window->consumeMotionEvent(); + window->consumeMotionEvent(); } - dispatcher.stop(); + dispatcher->stop(); } static void benchmarkOnWindowInfosChanged(benchmark::State& state) { // Create dispatcher FakeInputDispatcherPolicy fakePolicy; - InputDispatcher dispatcher(fakePolicy); - dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); - dispatcher.start(); + auto dispatcher = std::make_unique<InputDispatcher>(fakePolicy); + dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); + dispatcher->start(); // Create a window std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -188,12 +188,12 @@ static void benchmarkOnWindowInfosChanged(benchmark::State& state) { std::vector<gui::DisplayInfo> displayInfos{info}; for (auto _ : state) { - dispatcher.onWindowInfosChanged( + dispatcher->onWindowInfosChanged( {windowInfos, displayInfos, /*vsyncId=*/0, /*timestamp=*/0}); - dispatcher.onWindowInfosChanged( + dispatcher->onWindowInfosChanged( {/*windowInfos=*/{}, /*displayInfos=*/{}, /*vsyncId=*/{}, /*timestamp=*/0}); } - dispatcher.stop(); + dispatcher->stop(); } } // namespace diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 09ae6dd28c..6ae9790aac 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -22,6 +22,15 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +// Source files shared with InputDispatcher's benchmarks and fuzzers +filegroup { + name: "inputdispatcher_common_test_sources", + srcs: [ + "FakeInputDispatcherPolicy.cpp", + "FakeWindows.cpp", + ], +} + cc_test { name: "inputflinger_tests", host_supported: true, @@ -38,6 +47,7 @@ cc_test { "libinputflinger_defaults", ], srcs: [ + ":inputdispatcher_common_test_sources", "AnrTracker_test.cpp", "CapturedTouchpadEventConverter_test.cpp", "CursorInputMapper_test.cpp", diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp new file mode 100644 index 0000000000..e231bccb79 --- /dev/null +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp @@ -0,0 +1,473 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FakeInputDispatcherPolicy.h" + +#include <gtest/gtest.h> + +namespace android { + +// --- FakeInputDispatcherPolicy --- + +void FakeInputDispatcherPolicy::assertFilterInputEventWasCalled(const NotifyKeyArgs& args) { + assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) { + ASSERT_EQ(event.getType(), InputEventType::KEY); + EXPECT_EQ(event.getDisplayId(), args.displayId); + + const auto& keyEvent = static_cast<const KeyEvent&>(event); + EXPECT_EQ(keyEvent.getEventTime(), args.eventTime); + EXPECT_EQ(keyEvent.getAction(), args.action); + }); +} + +void FakeInputDispatcherPolicy::assertFilterInputEventWasCalled(const NotifyMotionArgs& args, + vec2 point) { + assertFilterInputEventWasCalledInternal([&](const InputEvent& event) { + ASSERT_EQ(event.getType(), InputEventType::MOTION); + EXPECT_EQ(event.getDisplayId(), args.displayId); + + const auto& motionEvent = static_cast<const MotionEvent&>(event); + EXPECT_EQ(motionEvent.getEventTime(), args.eventTime); + EXPECT_EQ(motionEvent.getAction(), args.action); + EXPECT_NEAR(motionEvent.getX(0), point.x, MotionEvent::ROUNDING_PRECISION); + EXPECT_NEAR(motionEvent.getY(0), point.y, MotionEvent::ROUNDING_PRECISION); + EXPECT_NEAR(motionEvent.getRawX(0), point.x, MotionEvent::ROUNDING_PRECISION); + EXPECT_NEAR(motionEvent.getRawY(0), point.y, MotionEvent::ROUNDING_PRECISION); + }); +} + +void FakeInputDispatcherPolicy::assertFilterInputEventWasNotCalled() { + std::scoped_lock lock(mLock); + ASSERT_EQ(nullptr, mFilteredEvent); +} + +void FakeInputDispatcherPolicy::assertNotifyConfigurationChangedWasCalled(nsecs_t when) { + std::scoped_lock lock(mLock); + ASSERT_TRUE(mConfigurationChangedTime) << "Timed out waiting for configuration changed call"; + ASSERT_EQ(*mConfigurationChangedTime, when); + mConfigurationChangedTime = std::nullopt; +} + +void FakeInputDispatcherPolicy::assertNotifySwitchWasCalled(const NotifySwitchArgs& args) { + std::scoped_lock lock(mLock); + ASSERT_TRUE(mLastNotifySwitch); + // We do not check id because it is not exposed to the policy + EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime); + EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags); + EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues); + EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask); + mLastNotifySwitch = std::nullopt; +} + +void FakeInputDispatcherPolicy::assertOnPointerDownEquals(const sp<IBinder>& touchedToken) { + std::scoped_lock lock(mLock); + ASSERT_EQ(touchedToken, mOnPointerDownToken); + mOnPointerDownToken.clear(); +} + +void FakeInputDispatcherPolicy::assertOnPointerDownWasNotCalled() { + std::scoped_lock lock(mLock); + ASSERT_TRUE(mOnPointerDownToken == nullptr) + << "Expected onPointerDownOutsideFocus to not have been called"; +} + +void FakeInputDispatcherPolicy::assertNotifyNoFocusedWindowAnrWasCalled( + std::chrono::nanoseconds timeout, + const std::shared_ptr<InputApplicationHandle>& expectedApplication) { + std::unique_lock lock(mLock); + android::base::ScopedLockAssertion assumeLocked(mLock); + std::shared_ptr<InputApplicationHandle> application; + ASSERT_NO_FATAL_FAILURE( + application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock)); + ASSERT_EQ(expectedApplication, application); +} + +void FakeInputDispatcherPolicy::assertNotifyWindowUnresponsiveWasCalled( + std::chrono::nanoseconds timeout, const sp<gui::WindowInfoHandle>& window) { + LOG_ALWAYS_FATAL_IF(window == nullptr, "window should not be null"); + assertNotifyWindowUnresponsiveWasCalled(timeout, window->getToken(), + window->getInfo()->ownerPid); +} + +void FakeInputDispatcherPolicy::assertNotifyWindowUnresponsiveWasCalled( + std::chrono::nanoseconds timeout, const sp<IBinder>& expectedToken, + std::optional<gui::Pid> expectedPid) { + std::unique_lock lock(mLock); + android::base::ScopedLockAssertion assumeLocked(mLock); + AnrResult result; + ASSERT_NO_FATAL_FAILURE(result = getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock)); + ASSERT_EQ(expectedToken, result.token); + ASSERT_EQ(expectedPid, result.pid); +} + +sp<IBinder> FakeInputDispatcherPolicy::getUnresponsiveWindowToken( + std::chrono::nanoseconds timeout) { + std::unique_lock lock(mLock); + android::base::ScopedLockAssertion assumeLocked(mLock); + AnrResult result = getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock); + const auto& [token, _] = result; + return token; +} + +void FakeInputDispatcherPolicy::assertNotifyWindowResponsiveWasCalled( + const sp<IBinder>& expectedToken, std::optional<gui::Pid> expectedPid) { + std::unique_lock lock(mLock); + android::base::ScopedLockAssertion assumeLocked(mLock); + AnrResult result; + ASSERT_NO_FATAL_FAILURE(result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock)); + ASSERT_EQ(expectedToken, result.token); + ASSERT_EQ(expectedPid, result.pid); +} + +sp<IBinder> FakeInputDispatcherPolicy::getResponsiveWindowToken() { + std::unique_lock lock(mLock); + android::base::ScopedLockAssertion assumeLocked(mLock); + AnrResult result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock); + const auto& [token, _] = result; + return token; +} + +void FakeInputDispatcherPolicy::assertNotifyAnrWasNotCalled() { + std::scoped_lock lock(mLock); + ASSERT_TRUE(mAnrApplications.empty()); + ASSERT_TRUE(mAnrWindows.empty()); + ASSERT_TRUE(mResponsiveWindows.empty()) + << "ANR was not called, but please also consume the 'connection is responsive' " + "signal"; +} + +PointerCaptureRequest FakeInputDispatcherPolicy::assertSetPointerCaptureCalled( + const sp<gui::WindowInfoHandle>& window, bool enabled) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + if (!mPointerCaptureChangedCondition + .wait_for(lock, 100ms, [this, enabled, window]() REQUIRES(mLock) { + if (enabled) { + return mPointerCaptureRequest->isEnable() && + mPointerCaptureRequest->window == window->getToken(); + } else { + return !mPointerCaptureRequest->isEnable(); + } + })) { + ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << window->getName() << ", " + << enabled << ") to be called."; + return {}; + } + auto request = *mPointerCaptureRequest; + mPointerCaptureRequest.reset(); + return request; +} + +void FakeInputDispatcherPolicy::assertSetPointerCaptureNotCalled() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) { + FAIL() << "Expected setPointerCapture(request) to not be called, but was called. " + "enabled = " + << std::to_string(mPointerCaptureRequest->isEnable()); + } + mPointerCaptureRequest.reset(); +} + +void FakeInputDispatcherPolicy::assertDropTargetEquals(const InputDispatcherInterface& dispatcher, + const sp<IBinder>& targetToken) { + dispatcher.waitForIdle(); + std::scoped_lock lock(mLock); + ASSERT_TRUE(mNotifyDropWindowWasCalled); + ASSERT_EQ(targetToken, mDropTargetWindowToken); + mNotifyDropWindowWasCalled = false; +} + +void FakeInputDispatcherPolicy::assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + std::optional<sp<IBinder>> receivedToken = + getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock, + mNotifyInputChannelBroken); + ASSERT_TRUE(receivedToken.has_value()) << "Did not receive the broken channel token"; + ASSERT_EQ(token, *receivedToken); +} + +void FakeInputDispatcherPolicy::setInterceptKeyTimeout(std::chrono::milliseconds timeout) { + mInterceptKeyTimeout = timeout; +} + +std::chrono::nanoseconds FakeInputDispatcherPolicy::getKeyWaitingForEventsTimeout() { + return 500ms; +} + +void FakeInputDispatcherPolicy::setStaleEventTimeout(std::chrono::nanoseconds timeout) { + mStaleEventTimeout = timeout; +} + +void FakeInputDispatcherPolicy::assertUserActivityNotPoked() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + std::optional<UserActivityPokeEvent> pokeEvent = + getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock, + mNotifyUserActivity); + + ASSERT_FALSE(pokeEvent) << "Expected user activity not to have been poked"; +} + +void FakeInputDispatcherPolicy::assertUserActivityPoked( + std::optional<UserActivityPokeEvent> expectedPokeEvent) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + std::optional<UserActivityPokeEvent> pokeEvent = + getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock, + mNotifyUserActivity); + ASSERT_TRUE(pokeEvent) << "Expected a user poke event"; + + if (expectedPokeEvent) { + ASSERT_EQ(expectedPokeEvent, *pokeEvent); + } +} + +void FakeInputDispatcherPolicy::assertNotifyDeviceInteractionWasCalled(int32_t deviceId, + std::set<gui::Uid> uids) { + ASSERT_EQ(std::make_pair(deviceId, uids), mNotifiedInteractions.popWithTimeout(100ms)); +} + +void FakeInputDispatcherPolicy::assertNotifyDeviceInteractionWasNotCalled() { + ASSERT_FALSE(mNotifiedInteractions.popWithTimeout(10ms)); +} + +void FakeInputDispatcherPolicy::setUnhandledKeyHandler( + std::function<std::optional<KeyEvent>(const KeyEvent&)> handler) { + std::scoped_lock lock(mLock); + mUnhandledKeyHandler = handler; +} + +void FakeInputDispatcherPolicy::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 FakeInputDispatcherPolicy::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"; +} + +template <class T> +T FakeInputDispatcherPolicy::getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, + std::queue<T>& storage, + std::unique_lock<std::mutex>& lock) + REQUIRES(mLock) { + // If there is an ANR, Dispatcher won't be idle because there are still events + // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle + // before checking if ANR was called. + // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need + // to provide it some time to act. 100ms seems reasonable. + std::chrono::duration timeToWait = timeout + 100ms; // provide some slack + const std::chrono::time_point start = std::chrono::steady_clock::now(); + std::optional<T> token = + getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr); + if (!token.has_value()) { + ADD_FAILURE() << "Did not receive the ANR callback"; + return {}; + } + + const std::chrono::duration waited = std::chrono::steady_clock::now() - start; + // Ensure that the ANR didn't get raised too early. We can't be too strict here because + // the dispatcher started counting before this function was called + if (std::chrono::abs(timeout - waited) > 100ms) { + ADD_FAILURE() << "ANR was raised too early or too late. Expected " + << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count() + << "ms, but waited " + << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count() + << "ms instead"; + } + return *token; +} + +template <class T> +std::optional<T> FakeInputDispatcherPolicy::getItemFromStorageLockedInterruptible( + std::chrono::nanoseconds timeout, std::queue<T>& storage, + std::unique_lock<std::mutex>& lock, std::condition_variable& condition) REQUIRES(mLock) { + condition.wait_for(lock, timeout, [&storage]() REQUIRES(mLock) { return !storage.empty(); }); + if (storage.empty()) { + return std::nullopt; + } + T item = storage.front(); + storage.pop(); + return std::make_optional(item); +} + +void FakeInputDispatcherPolicy::notifyConfigurationChanged(nsecs_t when) { + std::scoped_lock lock(mLock); + mConfigurationChangedTime = when; +} + +void FakeInputDispatcherPolicy::notifyWindowUnresponsive(const sp<IBinder>& connectionToken, + std::optional<gui::Pid> pid, + const std::string&) { + std::scoped_lock lock(mLock); + mAnrWindows.push({connectionToken, pid}); + mNotifyAnr.notify_all(); +} + +void FakeInputDispatcherPolicy::notifyWindowResponsive(const sp<IBinder>& connectionToken, + std::optional<gui::Pid> pid) { + std::scoped_lock lock(mLock); + mResponsiveWindows.push({connectionToken, pid}); + mNotifyAnr.notify_all(); +} + +void FakeInputDispatcherPolicy::notifyNoFocusedWindowAnr( + const std::shared_ptr<InputApplicationHandle>& applicationHandle) { + std::scoped_lock lock(mLock); + mAnrApplications.push(applicationHandle); + mNotifyAnr.notify_all(); +} + +void FakeInputDispatcherPolicy::notifyInputChannelBroken(const sp<IBinder>& connectionToken) { + std::scoped_lock lock(mLock); + mBrokenInputChannels.push(connectionToken); + mNotifyInputChannelBroken.notify_all(); +} + +void FakeInputDispatcherPolicy::notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) {} + +void FakeInputDispatcherPolicy::notifySensorEvent(int32_t deviceId, + InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy, + nsecs_t timestamp, + const std::vector<float>& values) {} + +void FakeInputDispatcherPolicy::notifySensorAccuracy(int deviceId, InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy) {} + +void FakeInputDispatcherPolicy::notifyVibratorState(int32_t deviceId, bool isOn) {} + +bool FakeInputDispatcherPolicy::filterInputEvent(const InputEvent& inputEvent, + uint32_t policyFlags) { + std::scoped_lock lock(mLock); + switch (inputEvent.getType()) { + case InputEventType::KEY: { + const KeyEvent& keyEvent = static_cast<const KeyEvent&>(inputEvent); + mFilteredEvent = std::make_unique<KeyEvent>(keyEvent); + break; + } + + case InputEventType::MOTION: { + const MotionEvent& motionEvent = static_cast<const MotionEvent&>(inputEvent); + mFilteredEvent = std::make_unique<MotionEvent>(motionEvent); + break; + } + default: { + ADD_FAILURE() << "Should only filter keys or motions"; + break; + } + } + return true; +} + +void FakeInputDispatcherPolicy::interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) { + if (inputEvent.getAction() == AKEY_EVENT_ACTION_UP) { + // Clear intercept state when we handled the event. + mInterceptKeyTimeout = 0ms; + } +} + +void FakeInputDispatcherPolicy::interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, + uint32_t&) {} + +nsecs_t FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&, + const KeyEvent&, uint32_t) { + nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count(); + // Clear intercept state so we could dispatch the event in next wake. + mInterceptKeyTimeout = 0ms; + return delay; +} + +std::optional<KeyEvent> FakeInputDispatcherPolicy::dispatchUnhandledKey(const sp<IBinder>&, + const KeyEvent& event, + uint32_t) { + std::scoped_lock lock(mLock); + mReportedUnhandledKeycodes.emplace(event.getKeyCode()); + mNotifyUnhandledKey.notify_all(); + return mUnhandledKeyHandler != nullptr ? mUnhandledKeyHandler(event) : std::nullopt; +} + +void FakeInputDispatcherPolicy::notifySwitch(nsecs_t when, uint32_t switchValues, + uint32_t switchMask, uint32_t policyFlags) { + std::scoped_lock lock(mLock); + // We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is + // essentially a passthrough for notifySwitch. + mLastNotifySwitch = + NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask); +} + +void FakeInputDispatcherPolicy::pokeUserActivity(nsecs_t eventTime, int32_t eventType, + int32_t displayId) { + std::scoped_lock lock(mLock); + mNotifyUserActivity.notify_all(); + mUserActivityPokeEvents.push({eventTime, eventType, displayId}); +} + +bool FakeInputDispatcherPolicy::isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) { + return std::chrono::nanoseconds(currentTime - eventTime) >= mStaleEventTimeout; +} + +void FakeInputDispatcherPolicy::onPointerDownOutsideFocus(const sp<IBinder>& newToken) { + std::scoped_lock lock(mLock); + mOnPointerDownToken = newToken; +} + +void FakeInputDispatcherPolicy::setPointerCapture(const PointerCaptureRequest& request) { + std::scoped_lock lock(mLock); + mPointerCaptureRequest = {request}; + mPointerCaptureChangedCondition.notify_all(); +} + +void FakeInputDispatcherPolicy::notifyDropWindow(const sp<IBinder>& token, float x, float y) { + std::scoped_lock lock(mLock); + mNotifyDropWindowWasCalled = true; + mDropTargetWindowToken = token; +} + +void FakeInputDispatcherPolicy::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, + const std::set<gui::Uid>& uids) { + ASSERT_TRUE(mNotifiedInteractions.emplace(deviceId, uids)); +} + +void FakeInputDispatcherPolicy::assertFilterInputEventWasCalledInternal( + const std::function<void(const InputEvent&)>& verify) { + std::scoped_lock lock(mLock); + ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called."; + verify(*mFilteredEvent); + mFilteredEvent = nullptr; +} + +gui::Uid FakeInputDispatcherPolicy::getPackageUid(std::string) { + return gui::Uid::INVALID; +} + +} // namespace android diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h index e0a73247eb..d83924f202 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h @@ -16,78 +16,190 @@ #pragma once -#include <android-base/logging.h> #include "InputDispatcherPolicyInterface.h" -namespace android { +#include "InputDispatcherInterface.h" +#include "NotifyArgs.h" + +#include <condition_variable> +#include <functional> +#include <memory> +#include <mutex> +#include <optional> +#include <queue> +#include <string> +#include <vector> + +#include <android-base/logging.h> +#include <android-base/thread_annotations.h> +#include <binder/IBinder.h> +#include <gui/PidUid.h> +#include <gui/WindowInfo.h> +#include <input/BlockingQueue.h> +#include <input/Input.h> -// --- FakeInputDispatcherPolicy --- +namespace android { class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { public: FakeInputDispatcherPolicy() = default; virtual ~FakeInputDispatcherPolicy() = default; -private: - void notifyConfigurationChanged(nsecs_t) override {} - - void notifyNoFocusedWindowAnr( - const std::shared_ptr<InputApplicationHandle>& applicationHandle) override { - LOG(ERROR) << "There is no focused window for " << applicationHandle->getName(); - } + struct AnrResult { + sp<IBinder> token{}; + std::optional<gui::Pid> pid{}; + }; + + struct UserActivityPokeEvent { + nsecs_t eventTime; + int32_t eventType; + int32_t displayId; + + bool operator==(const UserActivityPokeEvent& rhs) const = default; + inline friend std::ostream& operator<<(std::ostream& os, const UserActivityPokeEvent& ev) { + os << "UserActivityPokeEvent[time=" << ev.eventTime << ", eventType=" << ev.eventType + << ", displayId=" << ev.displayId << "]"; + return os; + } + }; + + void assertFilterInputEventWasCalled(const NotifyKeyArgs& args); + void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point); + void assertFilterInputEventWasNotCalled(); + void assertNotifyConfigurationChangedWasCalled(nsecs_t when); + void assertNotifySwitchWasCalled(const NotifySwitchArgs& args); + void assertOnPointerDownEquals(const sp<IBinder>& touchedToken); + void assertOnPointerDownWasNotCalled(); + /** + * This function must be called soon after the expected ANR timer starts, + * because we are also checking how much time has passed. + */ + void assertNotifyNoFocusedWindowAnrWasCalled( + std::chrono::nanoseconds timeout, + const std::shared_ptr<InputApplicationHandle>& expectedApplication); + void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout, + const sp<gui::WindowInfoHandle>& window); + void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout, + const sp<IBinder>& expectedToken, + std::optional<gui::Pid> expectedPid); + /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */ + sp<IBinder> getUnresponsiveWindowToken(std::chrono::nanoseconds timeout); + void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken, + std::optional<gui::Pid> expectedPid); + /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */ + sp<IBinder> getResponsiveWindowToken(); + void assertNotifyAnrWasNotCalled(); + PointerCaptureRequest assertSetPointerCaptureCalled(const sp<gui::WindowInfoHandle>& window, + bool enabled); + void assertSetPointerCaptureNotCalled(); + void assertDropTargetEquals(const InputDispatcherInterface& dispatcher, + const sp<IBinder>& targetToken); + void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token); + /** + * Set policy timeout. A value of zero means next key will not be intercepted. + */ + void setInterceptKeyTimeout(std::chrono::milliseconds timeout); + std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override; + void setStaleEventTimeout(std::chrono::nanoseconds timeout); + void assertUserActivityNotPoked(); + /** + * Asserts that a user activity poke has happened. The earliest recorded poke event will be + * cleared after this call. + * + * If an expected UserActivityPokeEvent is provided, asserts that the given event is the + * earliest recorded poke event. + */ + void assertUserActivityPoked(std::optional<UserActivityPokeEvent> expectedPokeEvent = {}); + void assertNotifyDeviceInteractionWasCalled(int32_t deviceId, std::set<gui::Uid> uids); + void assertNotifyDeviceInteractionWasNotCalled(); + void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler); + void assertUnhandledKeyReported(int32_t keycode); + void assertUnhandledKeyNotReported(); +private: + std::mutex mLock; + std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock); + std::optional<nsecs_t> mConfigurationChangedTime GUARDED_BY(mLock); + sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock); + std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock); + + std::condition_variable mPointerCaptureChangedCondition; + + std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock); + // ANR handling + std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock); + std::queue<AnrResult> mAnrWindows GUARDED_BY(mLock); + std::queue<AnrResult> mResponsiveWindows GUARDED_BY(mLock); + std::condition_variable mNotifyAnr; + std::queue<sp<IBinder>> mBrokenInputChannels GUARDED_BY(mLock); + std::condition_variable mNotifyInputChannelBroken; + + sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock); + bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false; + + std::condition_variable mNotifyUserActivity; + std::queue<UserActivityPokeEvent> mUserActivityPokeEvents; + + std::chrono::milliseconds mInterceptKeyTimeout = 0ms; + + std::chrono::nanoseconds mStaleEventTimeout = 1000ms; + + 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. + */ + template <class T> + T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage, + std::unique_lock<std::mutex>& lock) REQUIRES(mLock); + + template <class T> + std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout, + std::queue<T>& storage, + std::unique_lock<std::mutex>& lock, + std::condition_variable& condition) + REQUIRES(mLock); + + void notifyConfigurationChanged(nsecs_t when) override; void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid, - const std::string& reason) override { - LOG(ERROR) << "Window is not responding: " << reason; - } - + const std::string&) override; void notifyWindowResponsive(const sp<IBinder>& connectionToken, - std::optional<gui::Pid> pid) override {} - - void notifyInputChannelBroken(const sp<IBinder>&) override {} - - void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {} - + std::optional<gui::Pid> pid) override; + void notifyNoFocusedWindowAnr( + const std::shared_ptr<InputApplicationHandle>& applicationHandle) override; + void notifyInputChannelBroken(const sp<IBinder>& connectionToken) override; + void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override; void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy, nsecs_t timestamp, - const std::vector<float>& values) override {} - - void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType, - InputDeviceSensorAccuracy accuracy) override {} - - void notifyVibratorState(int32_t deviceId, bool isOn) override {} - - bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override { - return true; // dispatch event normally - } - - void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {} - - void interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, uint32_t&) override {} - - nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override { - return 0; - } - - std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&, - uint32_t) override { - return {}; - } - - void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {} - - void pokeUserActivity(nsecs_t, int32_t, int32_t) override {} - - void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {} - - void setPointerCapture(const PointerCaptureRequest&) override {} - - void notifyDropWindow(const sp<IBinder>&, float x, float y) override {} - - void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp, - const std::set<gui::Uid>& uids) override {} - - gui::Uid getPackageUid(std::string) override { return gui::Uid::INVALID; } + const std::vector<float>& values) override; + void notifySensorAccuracy(int deviceId, InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy) override; + void notifyVibratorState(int32_t deviceId, bool isOn) override; + bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override; + void interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) override; + void interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, uint32_t&) override; + nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override; + std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent& event, + uint32_t) override; + void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, + uint32_t policyFlags) override; + void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override; + bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override; + void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override; + void setPointerCapture(const PointerCaptureRequest& request) override; + void notifyDropWindow(const sp<IBinder>& token, float x, float y) override; + void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, + const std::set<gui::Uid>& uids) override; + gui::Uid getPackageUid(std::string) override; + + void assertFilterInputEventWasCalledInternal( + const std::function<void(const InputEvent&)>& verify); }; } // namespace android diff --git a/services/inputflinger/tests/FakeWindowHandle.h b/services/inputflinger/tests/FakeWindowHandle.h deleted file mode 100644 index 8ce61e7800..0000000000 --- a/services/inputflinger/tests/FakeWindowHandle.h +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <android-base/logging.h> -#include <input/InputConsumer.h> -#include "../dispatcher/InputDispatcher.h" - -using android::base::Result; -using android::gui::Pid; -using android::gui::TouchOcclusionMode; -using android::gui::Uid; -using android::gui::WindowInfo; -using android::gui::WindowInfoHandle; - -namespace android { -namespace inputdispatcher { - -namespace { - -// The default pid and uid for windows created by the test. -constexpr gui::Pid WINDOW_PID{999}; -constexpr gui::Uid WINDOW_UID{1001}; - -static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms; - -} // namespace - -class FakeInputReceiver { -public: - std::unique_ptr<InputEvent> consumeEvent(std::chrono::milliseconds timeout) { - uint32_t consumeSeq = 0; - std::unique_ptr<InputEvent> event; - - std::chrono::time_point start = std::chrono::steady_clock::now(); - status_t result = WOULD_BLOCK; - while (result == WOULD_BLOCK) { - InputEvent* rawEventPtr = nullptr; - result = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, - &rawEventPtr); - event = std::unique_ptr<InputEvent>(rawEventPtr); - std::chrono::duration elapsed = std::chrono::steady_clock::now() - start; - if (elapsed > timeout) { - if (timeout != 0ms) { - LOG(ERROR) << "Waited too long for consumer to produce an event, giving up"; - } - break; - } - } - // Events produced by this factory are owned pointers. - if (result != OK) { - if (timeout == 0ms) { - // This is likely expected. No need to log. - } else { - LOG(ERROR) << "Received result = " << result << " from consume"; - } - return nullptr; - } - result = mConsumer.sendFinishedSignal(consumeSeq, true); - if (result != OK) { - LOG(ERROR) << "Received result = " << result << " from sendFinishedSignal"; - } - return event; - } - - explicit FakeInputReceiver(std::unique_ptr<InputChannel> channel, const std::string name) - : mConsumer(std::move(channel)) {} - - virtual ~FakeInputReceiver() {} - -private: - std::unique_ptr<InputChannel> mClientChannel; - InputConsumer mConsumer; - DynamicInputEventFactory mEventFactory; -}; - -class FakeWindowHandle : public WindowInfoHandle { -public: - static const int32_t WIDTH = 600; - static const int32_t HEIGHT = 800; - - FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, - InputDispatcher& dispatcher, const std::string name, int32_t displayId) - : mName(name) { - Result<std::unique_ptr<InputChannel>> channel = dispatcher.createInputChannel(name); - mInfo.token = (*channel)->getConnectionToken(); - mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name); - - inputApplicationHandle->updateInfo(); - mInfo.applicationInfo = *inputApplicationHandle->getInfo(); - - mInfo.id = sId++; - mInfo.name = name; - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; - mInfo.alpha = 1.0; - mInfo.frame.left = 0; - mInfo.frame.top = 0; - mInfo.frame.right = WIDTH; - mInfo.frame.bottom = HEIGHT; - mInfo.transform.set(0, 0); - mInfo.globalScaleFactor = 1.0; - mInfo.touchableRegion.clear(); - mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT)); - mInfo.ownerPid = WINDOW_PID; - mInfo.ownerUid = WINDOW_UID; - mInfo.displayId = displayId; - mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT; - } - - sp<FakeWindowHandle> clone(int32_t displayId) { - sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mInfo.name + "(Mirror)"); - handle->mInfo = mInfo; - handle->mInfo.displayId = displayId; - handle->mInfo.id = sId++; - handle->mInputReceiver = mInputReceiver; - return handle; - } - - void setTouchable(bool touchable) { - mInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, !touchable); - } - - void setFocusable(bool focusable) { - mInfo.setInputConfig(WindowInfo::InputConfig::NOT_FOCUSABLE, !focusable); - } - - void setVisible(bool visible) { - mInfo.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible); - } - - void setDispatchingTimeout(std::chrono::nanoseconds timeout) { - mInfo.dispatchingTimeout = timeout; - } - - void setPaused(bool paused) { - mInfo.setInputConfig(WindowInfo::InputConfig::PAUSE_DISPATCHING, paused); - } - - void setPreventSplitting(bool preventSplitting) { - mInfo.setInputConfig(WindowInfo::InputConfig::PREVENT_SPLITTING, preventSplitting); - } - - void setSlippery(bool slippery) { - mInfo.setInputConfig(WindowInfo::InputConfig::SLIPPERY, slippery); - } - - void setWatchOutsideTouch(bool watchOutside) { - mInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside); - } - - void setSpy(bool spy) { mInfo.setInputConfig(WindowInfo::InputConfig::SPY, spy); } - - void setInterceptsStylus(bool interceptsStylus) { - mInfo.setInputConfig(WindowInfo::InputConfig::INTERCEPTS_STYLUS, interceptsStylus); - } - - void setDropInput(bool dropInput) { - mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT, dropInput); - } - - void setDropInputIfObscured(bool dropInputIfObscured) { - mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured); - } - - void setNoInputChannel(bool noInputChannel) { - mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel); - } - - void setDisableUserActivity(bool disableUserActivity) { - mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity); - } - - void setAlpha(float alpha) { mInfo.alpha = alpha; } - - void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; } - - void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; } - - void setFrame(const Rect& frame, const ui::Transform& displayTransform = ui::Transform()) { - mInfo.frame.left = frame.left; - mInfo.frame.top = frame.top; - mInfo.frame.right = frame.right; - mInfo.frame.bottom = frame.bottom; - mInfo.touchableRegion.clear(); - mInfo.addTouchableRegion(frame); - - const Rect logicalDisplayFrame = displayTransform.transform(frame); - ui::Transform translate; - translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top); - mInfo.transform = translate * displayTransform; - } - - void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; } - - void setIsWallpaper(bool isWallpaper) { - mInfo.setInputConfig(WindowInfo::InputConfig::IS_WALLPAPER, isWallpaper); - } - - void setDupTouchToWallpaper(bool hasWallpaper) { - mInfo.setInputConfig(WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper); - } - - void setTrustedOverlay(bool trustedOverlay) { - mInfo.setInputConfig(WindowInfo::InputConfig::TRUSTED_OVERLAY, trustedOverlay); - } - - void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) { - mInfo.transform.set(dsdx, dtdx, dtdy, dsdy); - } - - void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); } - - void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); } - - std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout) { - if (mInputReceiver == nullptr) { - return nullptr; - } - return mInputReceiver->consumeEvent(timeout); - } - - void consumeMotion() { - std::unique_ptr<InputEvent> event = consume(100ms); - - if (event == nullptr) { - LOG(FATAL) << mName << ": expected a MotionEvent, but didn't get one."; - return; - } - - if (event->getType() != InputEventType::MOTION) { - LOG(FATAL) << mName << " expected a MotionEvent, got " << *event; - return; - } - } - - sp<IBinder> getToken() { return mInfo.token; } - - const std::string& getName() { return mName; } - - void setOwnerInfo(Pid ownerPid, Uid ownerUid) { - mInfo.ownerPid = ownerPid; - mInfo.ownerUid = ownerUid; - } - - Pid getPid() const { return mInfo.ownerPid; } - - void destroyReceiver() { mInputReceiver = nullptr; } - -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}; - -} // namespace inputdispatcher - -} // namespace android diff --git a/services/inputflinger/tests/FakeWindows.cpp b/services/inputflinger/tests/FakeWindows.cpp new file mode 100644 index 0000000000..0ac2f0f7e6 --- /dev/null +++ b/services/inputflinger/tests/FakeWindows.cpp @@ -0,0 +1,354 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FakeWindows.h" + +#include <gtest/gtest.h> + +namespace android { + +// --- FakeInputReceiver --- + +FakeInputReceiver::FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, + const std::string name) + : mConsumer(std::move(clientChannel)), mName(name) {} + +std::unique_ptr<InputEvent> FakeInputReceiver::consume(std::chrono::milliseconds timeout, + bool handled) { + auto [consumeSeq, event] = receiveEvent(timeout); + if (!consumeSeq) { + return nullptr; + } + finishEvent(*consumeSeq, handled); + return std::move(event); +} + +std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> FakeInputReceiver::receiveEvent( + std::chrono::milliseconds timeout) { + uint32_t consumeSeq; + std::unique_ptr<InputEvent> event; + + std::chrono::time_point start = std::chrono::steady_clock::now(); + status_t status = WOULD_BLOCK; + while (status == WOULD_BLOCK) { + InputEvent* rawEventPtr = nullptr; + status = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, + &rawEventPtr); + event = std::unique_ptr<InputEvent>(rawEventPtr); + std::chrono::duration elapsed = std::chrono::steady_clock::now() - start; + if (elapsed > timeout) { + break; + } + } + + if (status == WOULD_BLOCK) { + // Just means there's no event available. + return std::make_pair(std::nullopt, nullptr); + } + + if (status != OK) { + ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK."; + return std::make_pair(std::nullopt, nullptr); + } + if (event == nullptr) { + ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer"; + } + return std::make_pair(consumeSeq, std::move(event)); +} + +void FakeInputReceiver::finishEvent(uint32_t consumeSeq, bool handled) { + const status_t status = mConsumer.sendFinishedSignal(consumeSeq, handled); + ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK."; +} + +void FakeInputReceiver::sendTimeline(int32_t inputEventId, + std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) { + const status_t status = mConsumer.sendTimeline(inputEventId, timeline); + ASSERT_EQ(OK, status); +} + +void FakeInputReceiver::consumeEvent(InputEventType expectedEventType, int32_t expectedAction, + std::optional<int32_t> expectedDisplayId, + std::optional<int32_t> expectedFlags) { + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + + ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; + ASSERT_EQ(expectedEventType, event->getType()) + << mName.c_str() << " expected " << ftl::enum_string(expectedEventType) + << " event, got " << *event; + + if (expectedDisplayId.has_value()) { + EXPECT_EQ(expectedDisplayId, event->getDisplayId()); + } + + switch (expectedEventType) { + case InputEventType::KEY: { + const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event); + ASSERT_THAT(keyEvent, WithKeyAction(expectedAction)); + if (expectedFlags.has_value()) { + EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags()); + } + break; + } + case InputEventType::MOTION: { + const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event); + ASSERT_THAT(motionEvent, WithMotionAction(expectedAction)); + if (expectedFlags.has_value()) { + EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags()); + } + break; + } + case InputEventType::FOCUS: { + FAIL() << "Use 'consumeFocusEvent' for FOCUS events"; + } + case InputEventType::CAPTURE: { + FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events"; + } + case InputEventType::TOUCH_MODE: { + FAIL() << "Use 'consumeTouchModeEvent' for TOUCH_MODE events"; + } + case InputEventType::DRAG: { + FAIL() << "Use 'consumeDragEvent' for DRAG events"; + } + } +} + +std::unique_ptr<MotionEvent> FakeInputReceiver::consumeMotion() { + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + + if (event == nullptr) { + ADD_FAILURE() << mName << ": expected a MotionEvent, but didn't get one."; + return nullptr; + } + + if (event->getType() != InputEventType::MOTION) { + ADD_FAILURE() << mName << " expected a MotionEvent, got " << *event; + return nullptr; + } + return std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release())); +} + +void FakeInputReceiver::consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) { + std::unique_ptr<MotionEvent> motionEvent = consumeMotion(); + ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher; + ASSERT_THAT(*motionEvent, matcher); +} + +void FakeInputReceiver::consumeFocusEvent(bool hasFocus, bool inTouchMode) { + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; + ASSERT_EQ(InputEventType::FOCUS, event->getType()) << "Instead of FocusEvent, got " << *event; + + ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) + << mName.c_str() << ": event displayId should always be NONE."; + + FocusEvent& focusEvent = static_cast<FocusEvent&>(*event); + EXPECT_EQ(hasFocus, focusEvent.getHasFocus()); +} + +void FakeInputReceiver::consumeCaptureEvent(bool hasCapture) { + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; + ASSERT_EQ(InputEventType::CAPTURE, event->getType()) + << "Instead of CaptureEvent, got " << *event; + + ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) + << mName.c_str() << ": event displayId should always be NONE."; + + const auto& captureEvent = static_cast<const CaptureEvent&>(*event); + EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled()); +} + +void FakeInputReceiver::consumeDragEvent(bool isExiting, float x, float y) { + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; + ASSERT_EQ(InputEventType::DRAG, event->getType()) << "Instead of DragEvent, got " << *event; + + EXPECT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) + << mName.c_str() << ": event displayId should always be NONE."; + + const auto& dragEvent = static_cast<const DragEvent&>(*event); + EXPECT_EQ(isExiting, dragEvent.isExiting()); + EXPECT_EQ(x, dragEvent.getX()); + EXPECT_EQ(y, dragEvent.getY()); +} + +void FakeInputReceiver::consumeTouchModeEvent(bool inTouchMode) { + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event."; + ASSERT_EQ(InputEventType::TOUCH_MODE, event->getType()) + << "Instead of TouchModeEvent, got " << *event; + + ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) + << mName.c_str() << ": event displayId should always be NONE."; + const auto& touchModeEvent = static_cast<const TouchModeEvent&>(*event); + EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode()); +} + +void FakeInputReceiver::assertNoEvents(std::chrono::milliseconds timeout) { + std::unique_ptr<InputEvent> event = consume(timeout); + if (event == nullptr) { + return; + } + if (event->getType() == InputEventType::KEY) { + KeyEvent& keyEvent = static_cast<KeyEvent&>(*event); + ADD_FAILURE() << "Received key event " << keyEvent; + } else if (event->getType() == InputEventType::MOTION) { + MotionEvent& motionEvent = static_cast<MotionEvent&>(*event); + ADD_FAILURE() << "Received motion event " << motionEvent; + } else if (event->getType() == InputEventType::FOCUS) { + FocusEvent& focusEvent = static_cast<FocusEvent&>(*event); + ADD_FAILURE() << "Received focus event, hasFocus = " + << (focusEvent.getHasFocus() ? "true" : "false"); + } else if (event->getType() == InputEventType::CAPTURE) { + const auto& captureEvent = static_cast<CaptureEvent&>(*event); + ADD_FAILURE() << "Received capture event, pointerCaptureEnabled = " + << (captureEvent.getPointerCaptureEnabled() ? "true" : "false"); + } else if (event->getType() == InputEventType::TOUCH_MODE) { + const auto& touchModeEvent = static_cast<TouchModeEvent&>(*event); + ADD_FAILURE() << "Received touch mode event, inTouchMode = " + << (touchModeEvent.isInTouchMode() ? "true" : "false"); + } + FAIL() << mName.c_str() + << ": should not have received any events, so consume() should return NULL"; +} + +sp<IBinder> FakeInputReceiver::getToken() { + return mConsumer.getChannel()->getConnectionToken(); +} + +int FakeInputReceiver::getChannelFd() { + return mConsumer.getChannel()->getFd(); +} + +// --- FakeWindowHandle --- + +std::function<void(const std::unique_ptr<InputEvent>&, const gui::WindowInfo&)> + FakeWindowHandle::sOnEventReceivedCallback{}; + +std::atomic<int32_t> FakeWindowHandle::sId{1}; + +FakeWindowHandle::FakeWindowHandle( + const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, + const std::unique_ptr<inputdispatcher::InputDispatcher>& dispatcher, const std::string name, + int32_t displayId, bool createInputChannel) + : mName(name) { + sp<IBinder> token; + if (createInputChannel) { + base::Result<std::unique_ptr<InputChannel>> channel = dispatcher->createInputChannel(name); + token = (*channel)->getConnectionToken(); + mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name); + } + + inputApplicationHandle->updateInfo(); + mInfo.applicationInfo = *inputApplicationHandle->getInfo(); + + mInfo.token = token; + mInfo.id = sId++; + mInfo.name = name; + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; + mInfo.alpha = 1.0; + mInfo.frame = Rect(0, 0, WIDTH, HEIGHT); + mInfo.transform.set(0, 0); + mInfo.globalScaleFactor = 1.0; + mInfo.touchableRegion.clear(); + mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT)); + mInfo.ownerPid = WINDOW_PID; + mInfo.ownerUid = WINDOW_UID; + mInfo.displayId = displayId; + mInfo.inputConfig = InputConfig::DEFAULT; +} + +sp<FakeWindowHandle> FakeWindowHandle::clone(int32_t displayId) { + sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mInfo.name + "(Mirror)"); + handle->mInfo = mInfo; + handle->mInfo.displayId = displayId; + handle->mInfo.id = sId++; + handle->mInputReceiver = mInputReceiver; + return handle; +} + +std::unique_ptr<KeyEvent> FakeWindowHandle::consumeKey(bool handled) { + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled); + if (event == nullptr) { + ADD_FAILURE() << "No event"; + return nullptr; + } + if (event->getType() != InputEventType::KEY) { + ADD_FAILURE() << "Instead of key event, got " << event; + return nullptr; + } + return std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event.release())); +} + +std::unique_ptr<MotionEvent> FakeWindowHandle::consumeMotionEvent( + const ::testing::Matcher<MotionEvent>& matcher) { + std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + if (event == nullptr) { + ADD_FAILURE() << "No event"; + return nullptr; + } + if (event->getType() != InputEventType::MOTION) { + ADD_FAILURE() << "Instead of motion event, got " << *event; + return nullptr; + } + std::unique_ptr<MotionEvent> motionEvent = + std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release())); + EXPECT_THAT(*motionEvent, matcher); + return motionEvent; +} + +void FakeWindowHandle::assertNoEvents(std::optional<std::chrono::milliseconds> timeout) { + if (mInputReceiver == nullptr && mInfo.inputConfig.test(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(timeout.value_or(CONSUME_TIMEOUT_NO_EVENT_EXPECTED)); +} + +std::unique_ptr<InputEvent> FakeWindowHandle::consume(std::chrono::milliseconds timeout, + bool handled) { + if (mInputReceiver == nullptr) { + LOG(FATAL) << "Cannot consume event from a window with no input event receiver"; + } + std::unique_ptr<InputEvent> event = mInputReceiver->consume(timeout, handled); + if (event == nullptr) { + ADD_FAILURE() << "Consume failed: no event"; + } + + if (sOnEventReceivedCallback != nullptr) { + sOnEventReceivedCallback(event, mInfo); + } + return event; +} + +std::pair<std::optional<uint32_t /*seq*/>, std::unique_ptr<InputEvent>> +FakeWindowHandle::receive() { + if (mInputReceiver == nullptr) { + ADD_FAILURE() << "Invalid receive event on window with no receiver"; + return std::make_pair(std::nullopt, nullptr); + } + auto out = mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED); + const auto& [_, event] = out; + + if (sOnEventReceivedCallback != nullptr) { + sOnEventReceivedCallback(event, mInfo); + } + return out; +} + +} // namespace android diff --git a/services/inputflinger/tests/FakeWindows.h b/services/inputflinger/tests/FakeWindows.h new file mode 100644 index 0000000000..c0c8975e76 --- /dev/null +++ b/services/inputflinger/tests/FakeWindows.h @@ -0,0 +1,387 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "../dispatcher/InputDispatcher.h" +#include "TestEventMatchers.h" + +#include <android-base/logging.h> +#include <gtest/gtest.h> +#include <input/Input.h> +#include <input/InputConsumer.h> + +namespace android { + +/** + * If we expect to receive the event, the timeout can be made very long. When the test are running + * correctly, we will actually never wait until the end of the timeout because the wait will end + * when the event comes in. Still, this value shouldn't be infinite. During development, a local + * change may cause the test to fail. This timeout should be short enough to not annoy so that the + * developer can see the failure quickly (on human scale). + */ +static constexpr std::chrono::duration CONSUME_TIMEOUT_EVENT_EXPECTED = 1000ms; + +/** + * When no event is expected, we can have a very short timeout. A large value here would slow down + * the tests. In the unlikely event of system being too slow, the event may still be present but the + * timeout would complete before it is consumed. This would result in test flakiness. If this + * occurs, the flakiness rate would be high. Since the flakes are treated with high priority, this + * would get noticed and addressed quickly. + */ +static constexpr std::chrono::duration CONSUME_TIMEOUT_NO_EVENT_EXPECTED = 10ms; + +/** + * The default pid and uid for windows created on the primary display by the test. + */ +static constexpr gui::Pid WINDOW_PID{999}; +static constexpr gui::Uid WINDOW_UID{1001}; + +/** + * Default input dispatching timeout if there is no focused application or paused window + * from which to determine an appropriate dispatching timeout. + */ +static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::milliseconds( + android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * + android::base::HwTimeoutMultiplier()); + +// --- FakeInputReceiver --- + +class FakeInputReceiver { +public: + explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name); + + std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = false); + /** + * Receive an event without acknowledging it. + * Return the sequence number that could later be used to send finished signal. + */ + std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent( + std::chrono::milliseconds timeout); + /** + * To be used together with "receiveEvent" to complete the consumption of an event. + */ + void finishEvent(uint32_t consumeSeq, bool handled = true); + + void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); + + void consumeEvent(android::InputEventType expectedEventType, int32_t expectedAction, + std::optional<int32_t> expectedDisplayId, + std::optional<int32_t> expectedFlags); + + std::unique_ptr<MotionEvent> consumeMotion(); + void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher); + + void consumeFocusEvent(bool hasFocus, bool inTouchMode); + void consumeCaptureEvent(bool hasCapture); + void consumeDragEvent(bool isExiting, float x, float y); + void consumeTouchModeEvent(bool inTouchMode); + + void assertNoEvents(std::chrono::milliseconds timeout); + + sp<IBinder> getToken(); + int getChannelFd(); + +private: + InputConsumer mConsumer; + DynamicInputEventFactory mEventFactory; + std::string mName; +}; + +// --- FakeWindowHandle --- + +class FakeWindowHandle : public gui::WindowInfoHandle { +public: + static const int32_t WIDTH = 600; + static const int32_t HEIGHT = 800; + using InputConfig = gui::WindowInfo::InputConfig; + + // This is a callback that is fired when an event is received by the window. + // It is static to avoid having to pass it individually into all of the FakeWindowHandles + // created by tests. + // TODO(b/210460522): Update the tests to use a factory pattern so that we can avoid + // the need to make this static. + static std::function<void(const std::unique_ptr<InputEvent>&, const gui::WindowInfo&)> + sOnEventReceivedCallback; + + FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, + const std::unique_ptr<inputdispatcher::InputDispatcher>& dispatcher, + const std::string name, int32_t displayId, bool createInputChannel = true); + + sp<FakeWindowHandle> clone(int32_t displayId); + + inline void setTouchable(bool touchable) { + mInfo.setInputConfig(InputConfig::NOT_TOUCHABLE, !touchable); + } + + inline void setFocusable(bool focusable) { + mInfo.setInputConfig(InputConfig::NOT_FOCUSABLE, !focusable); + } + + inline void setVisible(bool visible) { + mInfo.setInputConfig(InputConfig::NOT_VISIBLE, !visible); + } + + inline void setDispatchingTimeout(std::chrono::nanoseconds timeout) { + mInfo.dispatchingTimeout = timeout; + } + + inline void setPaused(bool paused) { + mInfo.setInputConfig(InputConfig::PAUSE_DISPATCHING, paused); + } + + inline void setPreventSplitting(bool preventSplitting) { + mInfo.setInputConfig(InputConfig::PREVENT_SPLITTING, preventSplitting); + } + + inline void setSlippery(bool slippery) { + mInfo.setInputConfig(InputConfig::SLIPPERY, slippery); + } + + inline void setWatchOutsideTouch(bool watchOutside) { + mInfo.setInputConfig(InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside); + } + + inline void setSpy(bool spy) { mInfo.setInputConfig(InputConfig::SPY, spy); } + + inline void setInterceptsStylus(bool interceptsStylus) { + mInfo.setInputConfig(InputConfig::INTERCEPTS_STYLUS, interceptsStylus); + } + + inline void setDropInput(bool dropInput) { + mInfo.setInputConfig(InputConfig::DROP_INPUT, dropInput); + } + + inline void setDropInputIfObscured(bool dropInputIfObscured) { + mInfo.setInputConfig(InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured); + } + + inline void setNoInputChannel(bool noInputChannel) { + mInfo.setInputConfig(InputConfig::NO_INPUT_CHANNEL, noInputChannel); + } + + inline void setDisableUserActivity(bool disableUserActivity) { + mInfo.setInputConfig(InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity); + } + + inline void setGlobalStylusBlocksTouch(bool shouldGlobalStylusBlockTouch) { + mInfo.setInputConfig(InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH, shouldGlobalStylusBlockTouch); + } + + inline void setAlpha(float alpha) { mInfo.alpha = alpha; } + + inline void setTouchOcclusionMode(gui::TouchOcclusionMode mode) { + mInfo.touchOcclusionMode = mode; + } + + inline void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; } + + inline void setFrame(const Rect& frame, + const ui::Transform& displayTransform = ui::Transform()) { + mInfo.frame = frame; + mInfo.touchableRegion.clear(); + mInfo.addTouchableRegion(frame); + + const Rect logicalDisplayFrame = displayTransform.transform(frame); + ui::Transform translate; + translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top); + mInfo.transform = translate * displayTransform; + } + + inline void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; } + + inline void setIsWallpaper(bool isWallpaper) { + mInfo.setInputConfig(InputConfig::IS_WALLPAPER, isWallpaper); + } + + inline void setDupTouchToWallpaper(bool hasWallpaper) { + mInfo.setInputConfig(InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper); + } + + inline void setTrustedOverlay(bool trustedOverlay) { + mInfo.setInputConfig(InputConfig::TRUSTED_OVERLAY, trustedOverlay); + } + + inline void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) { + mInfo.transform.set(dsdx, dtdx, dtdy, dsdy); + } + + inline void setWindowScale(float xScale, float yScale) { + setWindowTransform(xScale, 0, 0, yScale); + } + + inline void setWindowOffset(float offsetX, float offsetY) { + mInfo.transform.set(offsetX, offsetY); + } + + std::unique_ptr<KeyEvent> consumeKey(bool handled = true); + + inline void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) { + std::unique_ptr<KeyEvent> keyEvent = consumeKey(); + ASSERT_NE(nullptr, keyEvent); + ASSERT_THAT(*keyEvent, matcher); + } + + inline void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + consumeKeyEvent(testing::AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), + WithDisplayId(expectedDisplayId), WithFlags(expectedFlags))); + } + + inline void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + consumeKeyEvent(testing::AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), + WithDisplayId(expectedDisplayId), WithFlags(expectedFlags))); + } + + inline void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { + consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL), + WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED))); + } + + inline void consumeMotionMove(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { + consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); + } + + inline void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { + consumeAnyMotionDown(expectedDisplayId, expectedFlags); + } + + inline void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt, + std::optional<int32_t> expectedFlags = std::nullopt) { + consumeMotionEvent( + testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + testing::Conditional(expectedDisplayId.has_value(), + WithDisplayId(*expectedDisplayId), testing::_), + testing::Conditional(expectedFlags.has_value(), + WithFlags(*expectedFlags), testing::_))); + } + + inline void consumeMotionPointerDown(int32_t pointerIdx, + int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { + const int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN | + (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + consumeMotionEvent(testing::AllOf(WithMotionAction(action), + WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); + } + + inline void consumeMotionPointerUp(int32_t pointerIdx, + int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { + const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP | + (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + consumeMotionEvent(testing::AllOf(WithMotionAction(action), + WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); + } + + inline void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { + consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); + } + + inline void consumeMotionOutside(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { + consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE), + WithDisplayId(expectedDisplayId), + WithFlags(expectedFlags))); + } + + inline void consumeMotionOutsideWithZeroedCoords() { + consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE), + WithRawCoords(0, 0))); + } + + inline void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) { + ASSERT_NE(mInputReceiver, nullptr) + << "Cannot consume events from a window with no receiver"; + mInputReceiver->consumeFocusEvent(hasFocus, inTouchMode); + } + + inline void consumeCaptureEvent(bool hasCapture) { + ASSERT_NE(mInputReceiver, nullptr) + << "Cannot consume events from a window with no receiver"; + mInputReceiver->consumeCaptureEvent(hasCapture); + } + + std::unique_ptr<MotionEvent> consumeMotionEvent( + const ::testing::Matcher<MotionEvent>& matcher = testing::_); + + inline void consumeDragEvent(bool isExiting, float x, float y) { + mInputReceiver->consumeDragEvent(isExiting, x, y); + } + + inline void consumeTouchModeEvent(bool inTouchMode) { + ASSERT_NE(mInputReceiver, nullptr) + << "Cannot consume events from a window with no receiver"; + mInputReceiver->consumeTouchModeEvent(inTouchMode); + } + + inline std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent() { + return receive(); + } + + inline void finishEvent(uint32_t sequenceNum) { + ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver"; + mInputReceiver->finishEvent(sequenceNum); + } + + inline void sendTimeline(int32_t inputEventId, + std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) { + ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver"; + mInputReceiver->sendTimeline(inputEventId, timeline); + } + + void assertNoEvents(std::optional<std::chrono::milliseconds> timeout = {}); + + inline sp<IBinder> getToken() { return mInfo.token; } + + inline const std::string& getName() { return mName; } + + inline void setOwnerInfo(gui::Pid ownerPid, gui::Uid ownerUid) { + mInfo.ownerPid = ownerPid; + mInfo.ownerUid = ownerUid; + } + + inline gui::Pid getPid() const { return mInfo.ownerPid; } + + inline void destroyReceiver() { mInputReceiver = nullptr; } + + inline int getChannelFd() { return mInputReceiver->getChannelFd(); } + + // FakeWindowHandle uses this consume method to ensure received events are added to the trace. + std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = true); + +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>; + + // FakeWindowHandle uses this receive method to ensure received events are added to the trace. + std::pair<std::optional<uint32_t /*seq*/>, std::unique_ptr<InputEvent>> receive(); +}; + +} // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 9e3a4f10ef..4bb64fc9e1 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -16,7 +16,9 @@ #include "../dispatcher/InputDispatcher.h" #include "FakeApplicationHandle.h" +#include "FakeInputDispatcherPolicy.h" #include "FakeInputTracingBackend.h" +#include "FakeWindows.h" #include "TestEventMatchers.h" #include <NotifyArgsBuilders.h> @@ -106,10 +108,6 @@ static constexpr int32_t POINTER_1_UP = static constexpr int32_t POINTER_2_UP = AMOTION_EVENT_ACTION_POINTER_UP | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); -// The default pid and uid for windows created on the primary display by the test. -static constexpr gui::Pid WINDOW_PID{999}; -static constexpr gui::Uid WINDOW_UID{1001}; - // The default pid and uid for the windows created on the secondary display by the test. static constexpr gui::Pid SECONDARY_WINDOW_PID{1010}; static constexpr gui::Uid SECONDARY_WINDOW_UID{1012}; @@ -117,23 +115,6 @@ static constexpr gui::Uid SECONDARY_WINDOW_UID{1012}; // An arbitrary pid of the gesture monitor window static constexpr gui::Pid MONITOR_PID{2001}; -/** - * If we expect to receive the event, the timeout can be made very long. When the test are running - * correctly, we will actually never wait until the end of the timeout because the wait will end - * when the event comes in. Still, this value shouldn't be infinite. During development, a local - * change may cause the test to fail. This timeout should be short enough to not annoy so that the - * developer can see the failure quickly (on human scale). - */ -static constexpr std::chrono::duration CONSUME_TIMEOUT_EVENT_EXPECTED = 1000ms; -/** - * When no event is expected, we can have a very short timeout. A large value here would slow down - * the tests. In the unlikely event of system being too slow, the event may still be present but the - * timeout would complete before it is consumed. This would result in test flakiness. If this - * occurs, the flakiness rate would be high. Since the flakes are treated with high priority, this - * would get noticed and addressed quickly. - */ -static constexpr std::chrono::duration CONSUME_TIMEOUT_NO_EVENT_EXPECTED = 10ms; - static constexpr int expectedWallpaperFlags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; @@ -151,538 +132,26 @@ static KeyEvent getTestKeyEvent() { return event; } -// --- FakeInputDispatcherPolicy --- - -class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { - struct AnrResult { - sp<IBinder> token{}; - std::optional<gui::Pid> pid{}; - }; - /* Stores data about a user-activity-poke event from the dispatcher. */ - struct UserActivityPokeEvent { - nsecs_t eventTime; - int32_t eventType; - int32_t displayId; - - bool operator==(const UserActivityPokeEvent& rhs) const = default; - - friend std::ostream& operator<<(std::ostream& os, const UserActivityPokeEvent& ev) { - os << "UserActivityPokeEvent[time=" << ev.eventTime << ", eventType=" << ev.eventType - << ", displayId=" << ev.displayId << "]"; - return os; - } - }; - -public: - FakeInputDispatcherPolicy() = default; - virtual ~FakeInputDispatcherPolicy() = default; - - void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) { - assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) { - ASSERT_EQ(event.getType(), InputEventType::KEY); - EXPECT_EQ(event.getDisplayId(), args.displayId); - - const auto& keyEvent = static_cast<const KeyEvent&>(event); - EXPECT_EQ(keyEvent.getEventTime(), args.eventTime); - EXPECT_EQ(keyEvent.getAction(), args.action); - }); - } - - void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point) { - assertFilterInputEventWasCalledInternal([&](const InputEvent& event) { - ASSERT_EQ(event.getType(), InputEventType::MOTION); - EXPECT_EQ(event.getDisplayId(), args.displayId); - - const auto& motionEvent = static_cast<const MotionEvent&>(event); - EXPECT_EQ(motionEvent.getEventTime(), args.eventTime); - EXPECT_EQ(motionEvent.getAction(), args.action); - EXPECT_NEAR(motionEvent.getX(0), point.x, MotionEvent::ROUNDING_PRECISION); - EXPECT_NEAR(motionEvent.getY(0), point.y, MotionEvent::ROUNDING_PRECISION); - EXPECT_NEAR(motionEvent.getRawX(0), point.x, MotionEvent::ROUNDING_PRECISION); - EXPECT_NEAR(motionEvent.getRawY(0), point.y, MotionEvent::ROUNDING_PRECISION); - }); - } - - void assertFilterInputEventWasNotCalled() { - std::scoped_lock lock(mLock); - ASSERT_EQ(nullptr, mFilteredEvent); - } - - void assertNotifyConfigurationChangedWasCalled(nsecs_t when) { - std::scoped_lock lock(mLock); - ASSERT_TRUE(mConfigurationChangedTime) - << "Timed out waiting for configuration changed call"; - ASSERT_EQ(*mConfigurationChangedTime, when); - mConfigurationChangedTime = std::nullopt; - } - - void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) { - std::scoped_lock lock(mLock); - ASSERT_TRUE(mLastNotifySwitch); - // We do not check id because it is not exposed to the policy - EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime); - EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags); - EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues); - EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask); - mLastNotifySwitch = std::nullopt; - } - - void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) { - std::scoped_lock lock(mLock); - ASSERT_EQ(touchedToken, mOnPointerDownToken); - mOnPointerDownToken.clear(); - } - - void assertOnPointerDownWasNotCalled() { - std::scoped_lock lock(mLock); - ASSERT_TRUE(mOnPointerDownToken == nullptr) - << "Expected onPointerDownOutsideFocus to not have been called"; - } - - // This function must be called soon after the expected ANR timer starts, - // because we are also checking how much time has passed. - void assertNotifyNoFocusedWindowAnrWasCalled( - std::chrono::nanoseconds timeout, - const std::shared_ptr<InputApplicationHandle>& expectedApplication) { - std::unique_lock lock(mLock); - android::base::ScopedLockAssertion assumeLocked(mLock); - std::shared_ptr<InputApplicationHandle> application; - ASSERT_NO_FATAL_FAILURE( - application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock)); - ASSERT_EQ(expectedApplication, application); - } - - void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout, - const sp<WindowInfoHandle>& window) { - LOG_ALWAYS_FATAL_IF(window == nullptr, "window should not be null"); - assertNotifyWindowUnresponsiveWasCalled(timeout, window->getToken(), - window->getInfo()->ownerPid); - } - - void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout, - const sp<IBinder>& expectedToken, - std::optional<gui::Pid> expectedPid) { - std::unique_lock lock(mLock); - android::base::ScopedLockAssertion assumeLocked(mLock); - AnrResult result; - ASSERT_NO_FATAL_FAILURE(result = - getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock)); - ASSERT_EQ(expectedToken, result.token); - ASSERT_EQ(expectedPid, result.pid); - } - - /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */ - sp<IBinder> getUnresponsiveWindowToken(std::chrono::nanoseconds timeout) { - std::unique_lock lock(mLock); - android::base::ScopedLockAssertion assumeLocked(mLock); - AnrResult result = getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock); - const auto& [token, _] = result; - return token; - } - - void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken, - std::optional<gui::Pid> expectedPid) { - std::unique_lock lock(mLock); - android::base::ScopedLockAssertion assumeLocked(mLock); - AnrResult result; - ASSERT_NO_FATAL_FAILURE( - result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock)); - ASSERT_EQ(expectedToken, result.token); - ASSERT_EQ(expectedPid, result.pid); - } - - /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */ - sp<IBinder> getResponsiveWindowToken() { - std::unique_lock lock(mLock); - android::base::ScopedLockAssertion assumeLocked(mLock); - AnrResult result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock); - const auto& [token, _] = result; - return token; - } - - void assertNotifyAnrWasNotCalled() { - std::scoped_lock lock(mLock); - ASSERT_TRUE(mAnrApplications.empty()); - ASSERT_TRUE(mAnrWindows.empty()); - ASSERT_TRUE(mResponsiveWindows.empty()) - << "ANR was not called, but please also consume the 'connection is responsive' " - "signal"; - } - - PointerCaptureRequest assertSetPointerCaptureCalled(const sp<WindowInfoHandle>& window, - bool enabled) { - std::unique_lock lock(mLock); - base::ScopedLockAssertion assumeLocked(mLock); - - if (!mPointerCaptureChangedCondition - .wait_for(lock, 100ms, [this, enabled, window]() REQUIRES(mLock) { - if (enabled) { - return mPointerCaptureRequest->isEnable() && - mPointerCaptureRequest->window == window->getToken(); - } else { - return !mPointerCaptureRequest->isEnable(); - } - })) { - ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << window->getName() << ", " - << enabled << ") to be called."; - return {}; - } - auto request = *mPointerCaptureRequest; - mPointerCaptureRequest.reset(); - return request; - } - - void assertSetPointerCaptureNotCalled() { - std::unique_lock lock(mLock); - base::ScopedLockAssertion assumeLocked(mLock); - - if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) { - FAIL() << "Expected setPointerCapture(request) to not be called, but was called. " - "enabled = " - << std::to_string(mPointerCaptureRequest->isEnable()); - } - mPointerCaptureRequest.reset(); - } - - void assertDropTargetEquals(const InputDispatcherInterface& dispatcher, - const sp<IBinder>& targetToken) { - dispatcher.waitForIdle(); - std::scoped_lock lock(mLock); - ASSERT_TRUE(mNotifyDropWindowWasCalled); - ASSERT_EQ(targetToken, mDropTargetWindowToken); - mNotifyDropWindowWasCalled = false; - } - - void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token) { - std::unique_lock lock(mLock); - base::ScopedLockAssertion assumeLocked(mLock); - std::optional<sp<IBinder>> receivedToken = - getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock, - mNotifyInputChannelBroken); - ASSERT_TRUE(receivedToken.has_value()) << "Did not receive the broken channel token"; - 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; - } - - std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override { return 500ms; } - - void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; } - - void assertUserActivityNotPoked() { - std::unique_lock lock(mLock); - base::ScopedLockAssertion assumeLocked(mLock); - - std::optional<UserActivityPokeEvent> pokeEvent = - getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock, - mNotifyUserActivity); - - ASSERT_FALSE(pokeEvent) << "Expected user activity not to have been poked"; - } - - /** - * Asserts that a user activity poke has happened. The earliest recorded poke event will be - * cleared after this call. - * - * If an expected UserActivityPokeEvent is provided, asserts that the given event is the - * earliest recorded poke event. - */ - void assertUserActivityPoked(std::optional<UserActivityPokeEvent> expectedPokeEvent = {}) { - std::unique_lock lock(mLock); - base::ScopedLockAssertion assumeLocked(mLock); - - std::optional<UserActivityPokeEvent> pokeEvent = - getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock, - mNotifyUserActivity); - ASSERT_TRUE(pokeEvent) << "Expected a user poke event"; - - if (expectedPokeEvent) { - ASSERT_EQ(expectedPokeEvent, *pokeEvent); - } - } - - void assertNotifyDeviceInteractionWasCalled(int32_t deviceId, std::set<gui::Uid> uids) { - ASSERT_EQ(std::make_pair(deviceId, uids), mNotifiedInteractions.popWithTimeout(100ms)); - } - - void assertNotifyDeviceInteractionWasNotCalled() { - 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); - std::optional<nsecs_t> mConfigurationChangedTime GUARDED_BY(mLock); - sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock); - std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock); - - std::condition_variable mPointerCaptureChangedCondition; - - std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock); - - // ANR handling - std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock); - std::queue<AnrResult> mAnrWindows GUARDED_BY(mLock); - std::queue<AnrResult> mResponsiveWindows GUARDED_BY(mLock); - std::condition_variable mNotifyAnr; - std::queue<sp<IBinder>> mBrokenInputChannels GUARDED_BY(mLock); - std::condition_variable mNotifyInputChannelBroken; - - sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock); - bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false; - - std::condition_variable mNotifyUserActivity; - std::queue<UserActivityPokeEvent> mUserActivityPokeEvents; - - std::chrono::milliseconds mInterceptKeyTimeout = 0ms; - - std::chrono::nanoseconds mStaleEventTimeout = 1000ms; - - 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. - template <class T> - T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage, - std::unique_lock<std::mutex>& lock) REQUIRES(mLock) { - // If there is an ANR, Dispatcher won't be idle because there are still events - // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle - // before checking if ANR was called. - // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need - // to provide it some time to act. 100ms seems reasonable. - std::chrono::duration timeToWait = timeout + 100ms; // provide some slack - const std::chrono::time_point start = std::chrono::steady_clock::now(); - std::optional<T> token = - getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr); - if (!token.has_value()) { - ADD_FAILURE() << "Did not receive the ANR callback"; - return {}; - } - - const std::chrono::duration waited = std::chrono::steady_clock::now() - start; - // Ensure that the ANR didn't get raised too early. We can't be too strict here because - // the dispatcher started counting before this function was called - if (std::chrono::abs(timeout - waited) > 100ms) { - ADD_FAILURE() << "ANR was raised too early or too late. Expected " - << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count() - << "ms, but waited " - << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count() - << "ms instead"; - } - return *token; - } - - template <class T> - std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout, - std::queue<T>& storage, - std::unique_lock<std::mutex>& lock, - std::condition_variable& condition) - REQUIRES(mLock) { - condition.wait_for(lock, timeout, - [&storage]() REQUIRES(mLock) { return !storage.empty(); }); - if (storage.empty()) { - return std::nullopt; - } - T item = storage.front(); - storage.pop(); - return std::make_optional(item); - } - - void notifyConfigurationChanged(nsecs_t when) override { - std::scoped_lock lock(mLock); - mConfigurationChangedTime = when; - } - - void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid, - const std::string&) override { - std::scoped_lock lock(mLock); - mAnrWindows.push({connectionToken, pid}); - mNotifyAnr.notify_all(); - } - - void notifyWindowResponsive(const sp<IBinder>& connectionToken, - std::optional<gui::Pid> pid) override { - std::scoped_lock lock(mLock); - mResponsiveWindows.push({connectionToken, pid}); - mNotifyAnr.notify_all(); - } - - void notifyNoFocusedWindowAnr( - const std::shared_ptr<InputApplicationHandle>& applicationHandle) override { - std::scoped_lock lock(mLock); - mAnrApplications.push(applicationHandle); - mNotifyAnr.notify_all(); - } - - void notifyInputChannelBroken(const sp<IBinder>& connectionToken) override { - std::scoped_lock lock(mLock); - mBrokenInputChannels.push(connectionToken); - mNotifyInputChannelBroken.notify_all(); - } - - void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {} - - void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType, - InputDeviceSensorAccuracy accuracy, nsecs_t timestamp, - const std::vector<float>& values) override {} - - void notifySensorAccuracy(int deviceId, InputDeviceSensorType sensorType, - InputDeviceSensorAccuracy accuracy) override {} - - void notifyVibratorState(int32_t deviceId, bool isOn) override {} - - bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override { - std::scoped_lock lock(mLock); - switch (inputEvent.getType()) { - case InputEventType::KEY: { - const KeyEvent& keyEvent = static_cast<const KeyEvent&>(inputEvent); - mFilteredEvent = std::make_unique<KeyEvent>(keyEvent); - break; - } - - case InputEventType::MOTION: { - const MotionEvent& motionEvent = static_cast<const MotionEvent&>(inputEvent); - mFilteredEvent = std::make_unique<MotionEvent>(motionEvent); - break; - } - default: { - ADD_FAILURE() << "Should only filter keys or motions"; - break; - } - } - return true; - } - - 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, uint32_t, int32_t, nsecs_t, uint32_t&) override {} - - nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override { - nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count(); - // Clear intercept state so we could dispatch the event in next wake. - mInterceptKeyTimeout = 0ms; - return delay; - } - - std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent& event, - uint32_t) override { - 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, - uint32_t policyFlags) override { - std::scoped_lock lock(mLock); - /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is - * essentially a passthrough for notifySwitch. - */ - mLastNotifySwitch = - NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask); - } - - void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override { - std::scoped_lock lock(mLock); - mNotifyUserActivity.notify_all(); - mUserActivityPokeEvents.push({eventTime, eventType, displayId}); - } - - bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override { - return std::chrono::nanoseconds(currentTime - eventTime) >= mStaleEventTimeout; - } - - void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override { - std::scoped_lock lock(mLock); - mOnPointerDownToken = newToken; - } - - void setPointerCapture(const PointerCaptureRequest& request) override { - std::scoped_lock lock(mLock); - mPointerCaptureRequest = {request}; - mPointerCaptureChangedCondition.notify_all(); - } - - void notifyDropWindow(const sp<IBinder>& token, float x, float y) override { - std::scoped_lock lock(mLock); - mNotifyDropWindowWasCalled = true; - mDropTargetWindowToken = token; - } - - void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, - const std::set<gui::Uid>& uids) override { - ASSERT_TRUE(mNotifiedInteractions.emplace(deviceId, uids)); - } - - void assertFilterInputEventWasCalledInternal( - const std::function<void(const InputEvent&)>& verify) { - std::scoped_lock lock(mLock); - ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called."; - verify(*mFilteredEvent); - mFilteredEvent = nullptr; - } - - gui::Uid getPackageUid(std::string) override { return gui::Uid::INVALID; } -}; } // namespace // --- InputDispatcherTest --- -// The trace is a global variable for now, to avoid having to pass it into all of the -// FakeWindowHandles created throughout the tests. -// TODO(b/210460522): Update the tests to avoid the need to have the trace be a global variable. -static std::shared_ptr<VerifyingTrace> gVerifyingTrace = std::make_shared<VerifyingTrace>(); - class InputDispatcherTest : public testing::Test { protected: std::unique_ptr<FakeInputDispatcherPolicy> mFakePolicy; std::unique_ptr<InputDispatcher> mDispatcher; + std::shared_ptr<VerifyingTrace> mVerifyingTrace; void SetUp() override { - gVerifyingTrace->reset(); + mVerifyingTrace = std::make_shared<VerifyingTrace>(); + FakeWindowHandle::sOnEventReceivedCallback = [this](const auto& _1, const auto& _2) { + handleEventReceivedByWindow(_1, _2); + }; + mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>(); mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, std::make_unique<FakeInputTracingBackend>( - gVerifyingTrace)); + mVerifyingTrace)); mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); // Start InputDispatcher thread @@ -690,12 +159,35 @@ protected: } void TearDown() override { - ASSERT_NO_FATAL_FAILURE(gVerifyingTrace->verifyExpectedEventsTraced()); + ASSERT_NO_FATAL_FAILURE(mVerifyingTrace->verifyExpectedEventsTraced()); + FakeWindowHandle::sOnEventReceivedCallback = nullptr; + ASSERT_EQ(OK, mDispatcher->stop()); mFakePolicy.reset(); mDispatcher.reset(); } + void handleEventReceivedByWindow(const std::unique_ptr<InputEvent>& event, + const gui::WindowInfo& info) { + if (!event) { + return; + } + + switch (event->getType()) { + case InputEventType::KEY: { + mVerifyingTrace->expectKeyDispatchTraced(static_cast<KeyEvent&>(*event), info.id); + break; + } + case InputEventType::MOTION: { + mVerifyingTrace->expectMotionDispatchTraced(static_cast<MotionEvent&>(*event), + info.id); + break; + } + default: + break; + } + } + /** * Used for debugging when writing the test */ @@ -907,604 +399,6 @@ TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) { namespace { static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms; -// Default input dispatching timeout if there is no focused application or paused window -// from which to determine an appropriate dispatching timeout. -static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::milliseconds( - android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * - android::base::HwTimeoutMultiplier()); - -class FakeInputReceiver { -public: - explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name) - : mConsumer(std::move(clientChannel)), mName(name) {} - - std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = false) { - auto [consumeSeq, event] = receiveEvent(timeout); - if (!consumeSeq) { - return nullptr; - } - finishEvent(*consumeSeq, handled); - return std::move(event); - } - - /** - * Receive an event without acknowledging it. - * Return the sequence number that could later be used to send finished signal. - */ - std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent( - std::chrono::milliseconds timeout) { - uint32_t consumeSeq; - std::unique_ptr<InputEvent> event; - - std::chrono::time_point start = std::chrono::steady_clock::now(); - status_t status = WOULD_BLOCK; - while (status == WOULD_BLOCK) { - InputEvent* rawEventPtr = nullptr; - status = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, - &rawEventPtr); - event = std::unique_ptr<InputEvent>(rawEventPtr); - std::chrono::duration elapsed = std::chrono::steady_clock::now() - start; - if (elapsed > timeout) { - break; - } - } - - if (status == WOULD_BLOCK) { - // Just means there's no event available. - return std::make_pair(std::nullopt, nullptr); - } - - if (status != OK) { - ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK."; - return std::make_pair(std::nullopt, nullptr); - } - if (event == nullptr) { - ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer"; - } - return std::make_pair(consumeSeq, std::move(event)); - } - - /** - * To be used together with "receiveEvent" to complete the consumption of an event. - */ - 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."; - } - - void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) { - const status_t status = mConsumer.sendTimeline(inputEventId, timeline); - ASSERT_EQ(OK, status); - } - - void consumeEvent(InputEventType expectedEventType, int32_t expectedAction, - std::optional<int32_t> expectedDisplayId, - std::optional<int32_t> expectedFlags) { - std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); - - ASSERT_NE(nullptr, event) << mName.c_str() - << ": consumer should have returned non-NULL event."; - ASSERT_EQ(expectedEventType, event->getType()) - << mName.c_str() << " expected " << ftl::enum_string(expectedEventType) - << " event, got " << *event; - - if (expectedDisplayId.has_value()) { - EXPECT_EQ(expectedDisplayId, event->getDisplayId()); - } - - switch (expectedEventType) { - case InputEventType::KEY: { - const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event); - ASSERT_THAT(keyEvent, WithKeyAction(expectedAction)); - if (expectedFlags.has_value()) { - EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags()); - } - break; - } - case InputEventType::MOTION: { - const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event); - ASSERT_THAT(motionEvent, WithMotionAction(expectedAction)); - if (expectedFlags.has_value()) { - EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags()); - } - break; - } - case InputEventType::FOCUS: { - FAIL() << "Use 'consumeFocusEvent' for FOCUS events"; - } - case InputEventType::CAPTURE: { - FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events"; - } - case InputEventType::TOUCH_MODE: { - FAIL() << "Use 'consumeTouchModeEvent' for TOUCH_MODE events"; - } - case InputEventType::DRAG: { - FAIL() << "Use 'consumeDragEvent' for DRAG events"; - } - } - } - - std::unique_ptr<MotionEvent> consumeMotion() { - std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); - - if (event == nullptr) { - ADD_FAILURE() << mName << ": expected a MotionEvent, but didn't get one."; - return nullptr; - } - - if (event->getType() != InputEventType::MOTION) { - ADD_FAILURE() << mName << " expected a MotionEvent, got " << *event; - return nullptr; - } - return std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release())); - } - - void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) { - std::unique_ptr<MotionEvent> motionEvent = consumeMotion(); - ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher; - ASSERT_THAT(*motionEvent, matcher); - } - - void consumeFocusEvent(bool hasFocus, bool inTouchMode) { - std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); - ASSERT_NE(nullptr, event) << mName.c_str() - << ": consumer should have returned non-NULL event."; - ASSERT_EQ(InputEventType::FOCUS, event->getType()) - << "Instead of FocusEvent, got " << *event; - - ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) - << mName.c_str() << ": event displayId should always be NONE."; - - FocusEvent& focusEvent = static_cast<FocusEvent&>(*event); - EXPECT_EQ(hasFocus, focusEvent.getHasFocus()); - } - - void consumeCaptureEvent(bool hasCapture) { - std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); - ASSERT_NE(nullptr, event) << mName.c_str() - << ": consumer should have returned non-NULL event."; - ASSERT_EQ(InputEventType::CAPTURE, event->getType()) - << "Instead of CaptureEvent, got " << *event; - - ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) - << mName.c_str() << ": event displayId should always be NONE."; - - const auto& captureEvent = static_cast<const CaptureEvent&>(*event); - EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled()); - } - - void consumeDragEvent(bool isExiting, float x, float y) { - std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); - ASSERT_NE(nullptr, event) << mName.c_str() - << ": consumer should have returned non-NULL event."; - ASSERT_EQ(InputEventType::DRAG, event->getType()) << "Instead of DragEvent, got " << *event; - - EXPECT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) - << mName.c_str() << ": event displayId should always be NONE."; - - const auto& dragEvent = static_cast<const DragEvent&>(*event); - EXPECT_EQ(isExiting, dragEvent.isExiting()); - EXPECT_EQ(x, dragEvent.getX()); - EXPECT_EQ(y, dragEvent.getY()); - } - - void consumeTouchModeEvent(bool inTouchMode) { - std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); - ASSERT_NE(nullptr, event) << mName.c_str() - << ": consumer should have returned non-NULL event."; - ASSERT_EQ(InputEventType::TOUCH_MODE, event->getType()) - << "Instead of TouchModeEvent, got " << *event; - - ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId()) - << mName.c_str() << ": event displayId should always be NONE."; - const auto& touchModeEvent = static_cast<const TouchModeEvent&>(*event); - EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode()); - } - - void assertNoEvents(std::chrono::milliseconds timeout) { - std::unique_ptr<InputEvent> event = consume(timeout); - if (event == nullptr) { - return; - } - if (event->getType() == InputEventType::KEY) { - KeyEvent& keyEvent = static_cast<KeyEvent&>(*event); - ADD_FAILURE() << "Received key event " << keyEvent; - } else if (event->getType() == InputEventType::MOTION) { - MotionEvent& motionEvent = static_cast<MotionEvent&>(*event); - ADD_FAILURE() << "Received motion event " << motionEvent; - } else if (event->getType() == InputEventType::FOCUS) { - FocusEvent& focusEvent = static_cast<FocusEvent&>(*event); - ADD_FAILURE() << "Received focus event, hasFocus = " - << (focusEvent.getHasFocus() ? "true" : "false"); - } else if (event->getType() == InputEventType::CAPTURE) { - const auto& captureEvent = static_cast<CaptureEvent&>(*event); - ADD_FAILURE() << "Received capture event, pointerCaptureEnabled = " - << (captureEvent.getPointerCaptureEnabled() ? "true" : "false"); - } else if (event->getType() == InputEventType::TOUCH_MODE) { - const auto& touchModeEvent = static_cast<TouchModeEvent&>(*event); - ADD_FAILURE() << "Received touch mode event, inTouchMode = " - << (touchModeEvent.isInTouchMode() ? "true" : "false"); - } - FAIL() << mName.c_str() - << ": should not have received any events, so consume() should return NULL"; - } - - sp<IBinder> getToken() { return mConsumer.getChannel()->getConnectionToken(); } - - int getChannelFd() { return mConsumer.getChannel()->getFd(); } - -private: - InputConsumer mConsumer; - DynamicInputEventFactory mEventFactory; - - std::string mName; -}; - -class FakeWindowHandle : public WindowInfoHandle { -public: - static const int32_t WIDTH = 600; - static const int32_t HEIGHT = 800; - - FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, - const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name, - int32_t displayId, bool createInputChannel = true) - : mName(name) { - sp<IBinder> token; - if (createInputChannel) { - base::Result<std::unique_ptr<InputChannel>> channel = - dispatcher->createInputChannel(name); - token = (*channel)->getConnectionToken(); - mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name); - } - - inputApplicationHandle->updateInfo(); - mInfo.applicationInfo = *inputApplicationHandle->getInfo(); - - mInfo.token = token; - mInfo.id = sId++; - mInfo.name = name; - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; - mInfo.alpha = 1.0; - mInfo.frame = Rect(0, 0, WIDTH, HEIGHT); - mInfo.transform.set(0, 0); - mInfo.globalScaleFactor = 1.0; - mInfo.touchableRegion.clear(); - mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT)); - mInfo.ownerPid = WINDOW_PID; - mInfo.ownerUid = WINDOW_UID; - mInfo.displayId = displayId; - mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT; - } - - sp<FakeWindowHandle> clone(int32_t displayId) { - sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mInfo.name + "(Mirror)"); - handle->mInfo = mInfo; - handle->mInfo.displayId = displayId; - handle->mInfo.id = sId++; - handle->mInputReceiver = mInputReceiver; - return handle; - } - - void setTouchable(bool touchable) { - mInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, !touchable); - } - - void setFocusable(bool focusable) { - mInfo.setInputConfig(WindowInfo::InputConfig::NOT_FOCUSABLE, !focusable); - } - - void setVisible(bool visible) { - mInfo.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible); - } - - void setDispatchingTimeout(std::chrono::nanoseconds timeout) { - mInfo.dispatchingTimeout = timeout; - } - - void setPaused(bool paused) { - mInfo.setInputConfig(WindowInfo::InputConfig::PAUSE_DISPATCHING, paused); - } - - void setPreventSplitting(bool preventSplitting) { - mInfo.setInputConfig(WindowInfo::InputConfig::PREVENT_SPLITTING, preventSplitting); - } - - void setSlippery(bool slippery) { - mInfo.setInputConfig(WindowInfo::InputConfig::SLIPPERY, slippery); - } - - void setWatchOutsideTouch(bool watchOutside) { - mInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside); - } - - void setSpy(bool spy) { mInfo.setInputConfig(WindowInfo::InputConfig::SPY, spy); } - - void setInterceptsStylus(bool interceptsStylus) { - mInfo.setInputConfig(WindowInfo::InputConfig::INTERCEPTS_STYLUS, interceptsStylus); - } - - void setDropInput(bool dropInput) { - mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT, dropInput); - } - - void setDropInputIfObscured(bool dropInputIfObscured) { - mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured); - } - - void setNoInputChannel(bool noInputChannel) { - mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel); - } - - void setDisableUserActivity(bool disableUserActivity) { - mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity); - } - - void setGlobalStylusBlocksTouch(bool shouldGlobalStylusBlockTouch) { - mInfo.setInputConfig(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH, - shouldGlobalStylusBlockTouch); - } - - void setAlpha(float alpha) { mInfo.alpha = alpha; } - - void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; } - - void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; } - - void setFrame(const Rect& frame, const ui::Transform& displayTransform = ui::Transform()) { - mInfo.frame = frame; - mInfo.touchableRegion.clear(); - mInfo.addTouchableRegion(frame); - - const Rect logicalDisplayFrame = displayTransform.transform(frame); - ui::Transform translate; - translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top); - mInfo.transform = translate * displayTransform; - } - - void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; } - - void setIsWallpaper(bool isWallpaper) { - mInfo.setInputConfig(WindowInfo::InputConfig::IS_WALLPAPER, isWallpaper); - } - - void setDupTouchToWallpaper(bool hasWallpaper) { - mInfo.setInputConfig(WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper); - } - - void setTrustedOverlay(bool trustedOverlay) { - mInfo.setInputConfig(WindowInfo::InputConfig::TRUSTED_OVERLAY, trustedOverlay); - } - - void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) { - mInfo.transform.set(dsdx, dtdx, dtdy, dsdy); - } - - void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); } - - void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); } - - std::unique_ptr<KeyEvent> consumeKey(bool handled = true) { - std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled); - if (event == nullptr) { - ADD_FAILURE() << "No event"; - return nullptr; - } - if (event->getType() != InputEventType::KEY) { - ADD_FAILURE() << "Instead of key event, got " << event; - return nullptr; - } - return std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event.release())); - } - - void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) { - std::unique_ptr<KeyEvent> keyEvent = consumeKey(); - ASSERT_NE(nullptr, keyEvent); - ASSERT_THAT(*keyEvent, matcher); - } - - void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { - consumeKeyEvent(AllOf(WithKeyAction(ACTION_DOWN), WithDisplayId(expectedDisplayId), - WithFlags(expectedFlags))); - } - - void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { - consumeKeyEvent(AllOf(WithKeyAction(ACTION_UP), WithDisplayId(expectedDisplayId), - WithFlags(expectedFlags))); - } - - void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, - int32_t expectedFlags = 0) { - consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(expectedDisplayId), - WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED))); - } - - void consumeMotionMove(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, - int32_t expectedFlags = 0) { - consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithDisplayId(expectedDisplayId), WithFlags(expectedFlags))); - } - - void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, - int32_t expectedFlags = 0) { - consumeAnyMotionDown(expectedDisplayId, expectedFlags); - } - - void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt, - std::optional<int32_t> expectedFlags = std::nullopt) { - consumeMotionEvent( - AllOf(WithMotionAction(ACTION_DOWN), - testing::Conditional(expectedDisplayId.has_value(), - WithDisplayId(*expectedDisplayId), testing::_), - testing::Conditional(expectedFlags.has_value(), WithFlags(*expectedFlags), - testing::_))); - } - - void consumeMotionPointerDown(int32_t pointerIdx, - int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, - int32_t expectedFlags = 0) { - const int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN | - (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId), - WithFlags(expectedFlags))); - } - - void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, - int32_t expectedFlags = 0) { - const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP | - (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId), - WithFlags(expectedFlags))); - } - - void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, - int32_t expectedFlags = 0) { - consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDisplayId(expectedDisplayId), - WithFlags(expectedFlags))); - } - - void consumeMotionOutside(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, - int32_t expectedFlags = 0) { - consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE), - WithDisplayId(expectedDisplayId), WithFlags(expectedFlags))); - } - - void consumeMotionOutsideWithZeroedCoords() { - consumeMotionEvent( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE), WithRawCoords(0, 0))); - } - - void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) { - ASSERT_NE(mInputReceiver, nullptr) - << "Cannot consume events from a window with no receiver"; - mInputReceiver->consumeFocusEvent(hasFocus, inTouchMode); - } - - void consumeCaptureEvent(bool hasCapture) { - ASSERT_NE(mInputReceiver, nullptr) - << "Cannot consume events from a window with no receiver"; - mInputReceiver->consumeCaptureEvent(hasCapture); - } - - std::unique_ptr<MotionEvent> consumeMotionEvent( - const ::testing::Matcher<MotionEvent>& matcher = testing::_) { - std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); - if (event == nullptr) { - ADD_FAILURE() << "No event"; - return nullptr; - } - if (event->getType() != InputEventType::MOTION) { - ADD_FAILURE() << "Instead of motion event, got " << *event; - return nullptr; - } - std::unique_ptr<MotionEvent> motionEvent = - std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release())); - EXPECT_THAT(*motionEvent, matcher); - return motionEvent; - } - - void consumeDragEvent(bool isExiting, float x, float y) { - mInputReceiver->consumeDragEvent(isExiting, x, y); - } - - void consumeTouchModeEvent(bool inTouchMode) { - ASSERT_NE(mInputReceiver, nullptr) - << "Cannot consume events from a window with no receiver"; - mInputReceiver->consumeTouchModeEvent(inTouchMode); - } - - std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent() { - return receive(); - } - - void finishEvent(uint32_t sequenceNum) { - ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver"; - mInputReceiver->finishEvent(sequenceNum); - } - - void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) { - ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver"; - mInputReceiver->sendTimeline(inputEventId, timeline); - } - - 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(timeout); - } - - sp<IBinder> getToken() { return mInfo.token; } - - const std::string& getName() { return mName; } - - void setOwnerInfo(gui::Pid ownerPid, gui::Uid ownerUid) { - mInfo.ownerPid = ownerPid; - mInfo.ownerUid = ownerUid; - } - - gui::Pid getPid() const { return mInfo.ownerPid; } - - void destroyReceiver() { mInputReceiver = nullptr; } - - int getChannelFd() { return mInputReceiver->getChannelFd(); } - - // FakeWindowHandle uses this consume method to ensure received events are added to the trace. - 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"; - } - std::unique_ptr<InputEvent> event = mInputReceiver->consume(timeout, handled); - if (event == nullptr) { - ADD_FAILURE() << "Consume failed: no event"; - } - expectReceivedEventTraced(event); - 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>; - - // FakeWindowHandle uses this receive method to ensure received events are added to the trace. - std::pair<std::optional<uint32_t /*seq*/>, std::unique_ptr<InputEvent>> receive() { - if (mInputReceiver == nullptr) { - ADD_FAILURE() << "Invalid receive event on window with no receiver"; - return std::make_pair(std::nullopt, nullptr); - } - auto out = mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED); - const auto& [_, event] = out; - expectReceivedEventTraced(event); - return std::move(out); - } - - void expectReceivedEventTraced(const std::unique_ptr<InputEvent>& event) { - if (!event) { - return; - } - - switch (event->getType()) { - case InputEventType::KEY: { - gVerifyingTrace->expectKeyDispatchTraced(static_cast<KeyEvent&>(*event), mInfo.id); - break; - } - case InputEventType::MOTION: { - gVerifyingTrace->expectMotionDispatchTraced(static_cast<MotionEvent&>(*event), - mInfo.id); - break; - } - default: - break; - } - } -}; - -std::atomic<int32_t> FakeWindowHandle::sId{1}; class FakeMonitorReceiver { public: diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp index 81c335326a..48e19546c5 100644 --- a/services/inputflinger/tests/fuzzers/Android.bp +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -178,7 +178,12 @@ cc_fuzz { shared_libs: [ "libinputreporter", ], + static_libs: [ + "libgmock", + "libgtest", + ], srcs: [ + ":inputdispatcher_common_test_sources", "InputDispatcherFuzzer.cpp", ], } diff --git a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp index dc5a2130e7..7335fb7500 100644 --- a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp @@ -18,7 +18,7 @@ #include <fuzzer/FuzzedDataProvider.h> #include "../FakeApplicationHandle.h" #include "../FakeInputDispatcherPolicy.h" -#include "../FakeWindowHandle.h" +#include "../FakeWindows.h" #include "FuzzedInputStream.h" #include "dispatcher/InputDispatcher.h" #include "input/InputVerifier.h" @@ -88,7 +88,8 @@ void scrambleWindow(FuzzedDataProvider& fdp, FakeWindowHandle& window) { } // namespace -sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, InputDispatcher& dispatcher, +sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, + std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId) { static size_t windowNumber = 0; std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -102,7 +103,7 @@ sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, InputDispatch void randomizeWindows( std::unordered_map<int32_t, std::vector<sp<FakeWindowHandle>>>& windowsPerDisplay, - FuzzedDataProvider& fdp, InputDispatcher& dispatcher) { + FuzzedDataProvider& fdp, std::unique_ptr<InputDispatcher>& dispatcher) { const int32_t displayId = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_RANDOM_DISPLAYS - 1); std::vector<sp<FakeWindowHandle>>& windows = windowsPerDisplay[displayId]; @@ -142,10 +143,10 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { NotifyStreamProvider streamProvider(fdp); FakeInputDispatcherPolicy fakePolicy; - InputDispatcher dispatcher(fakePolicy); - dispatcher.setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); + auto dispatcher = std::make_unique<InputDispatcher>(fakePolicy); + dispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); // Start InputDispatcher thread - dispatcher.start(); + dispatcher->start(); std::unordered_map<int32_t, std::vector<sp<FakeWindowHandle>>> windowsPerDisplay; @@ -155,7 +156,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { [&]() -> void { std::optional<NotifyMotionArgs> motion = streamProvider.nextMotion(); if (motion) { - dispatcher.notifyMotion(*motion); + dispatcher->notifyMotion(*motion); } }, [&]() -> void { @@ -169,7 +170,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { } } - dispatcher.onWindowInfosChanged( + dispatcher->onWindowInfosChanged( {windowInfos, {}, /*vsyncId=*/0, /*timestamp=*/0}); }, // Consume on all the windows @@ -187,7 +188,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { })(); } - dispatcher.stop(); + dispatcher->stop(); return 0; } |