/* * 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 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(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(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::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& 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& expectedApplication) { std::unique_lock lock(mLock); android::base::ScopedLockAssertion assumeLocked(mLock); std::shared_ptr application; ASSERT_NO_FATAL_FAILURE( application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock)); ASSERT_EQ(expectedApplication, application); } void FakeInputDispatcherPolicy::assertNotifyWindowUnresponsiveWasCalled( std::chrono::nanoseconds timeout, const sp& 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& expectedToken, std::optional 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 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& expectedToken, std::optional 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 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& 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& targetToken) { dispatcher.waitForIdle(); std::scoped_lock lock(mLock); ASSERT_TRUE(mNotifyDropWindowWasCalled); ASSERT_EQ(targetToken, mDropTargetWindowToken); mNotifyDropWindowWasCalled = false; } void FakeInputDispatcherPolicy::assertNotifyInputChannelBrokenWasCalled(const sp& token) { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); std::optional> 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::setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching) { mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching; } void FakeInputDispatcherPolicy::assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay) { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); if (!mFocusedDisplayNotifiedCondition.wait_for(lock, 100ms, [this, expectedDisplay]() REQUIRES(mLock) { if (!mNotifiedFocusedDisplay.has_value() || mNotifiedFocusedDisplay.value() != expectedDisplay) { return false; } return true; })) { ADD_FAILURE() << "Timed out waiting for notifyFocusedDisplayChanged(" << expectedDisplay << ") to be called."; } } void FakeInputDispatcherPolicy::assertUserActivityNotPoked() { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); std::optional pokeEvent = getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock, mNotifyUserActivity); ASSERT_FALSE(pokeEvent) << "Expected user activity not to have been poked"; } void FakeInputDispatcherPolicy::assertUserActivityPoked( std::optional expectedPokeEvent) { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); std::optional 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 uids) { ASSERT_EQ(std::make_pair(deviceId, uids), mNotifiedInteractions.popWithTimeout(100ms)); } void FakeInputDispatcherPolicy::assertNotifyDeviceInteractionWasNotCalled() { ASSERT_FALSE(mNotifiedInteractions.popWithTimeout(10ms)); } void FakeInputDispatcherPolicy::setUnhandledKeyHandler( std::function(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 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 unhandledKeycode = getItemFromStorageLockedInterruptible(10ms, mReportedUnhandledKeycodes, lock, mNotifyUnhandledKey); ASSERT_FALSE(unhandledKeycode) << "Expected unhandled key NOT to be reported"; } template T FakeInputDispatcherPolicy::getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue& storage, std::unique_lock& 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 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(timeout).count() << "ms, but waited " << std::chrono::duration_cast(waited).count() << "ms instead"; } return *token; } template std::optional FakeInputDispatcherPolicy::getItemFromStorageLockedInterruptible( std::chrono::nanoseconds timeout, std::queue& storage, std::unique_lock& 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::notifyWindowUnresponsive(const sp& connectionToken, std::optional pid, const std::string&) { std::scoped_lock lock(mLock); mAnrWindows.push({connectionToken, pid}); mNotifyAnr.notify_all(); } void FakeInputDispatcherPolicy::notifyWindowResponsive(const sp& connectionToken, std::optional pid) { std::scoped_lock lock(mLock); mResponsiveWindows.push({connectionToken, pid}); mNotifyAnr.notify_all(); } void FakeInputDispatcherPolicy::notifyNoFocusedWindowAnr( const std::shared_ptr& applicationHandle) { std::scoped_lock lock(mLock); mAnrApplications.push(applicationHandle); mNotifyAnr.notify_all(); } void FakeInputDispatcherPolicy::notifyInputChannelBroken(const sp& connectionToken) { std::scoped_lock lock(mLock); mBrokenInputChannels.push(connectionToken); mNotifyInputChannelBroken.notify_all(); } void FakeInputDispatcherPolicy::notifyFocusChanged(const sp&, const sp&) {} void FakeInputDispatcherPolicy::notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy, nsecs_t timestamp, const std::vector& 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(inputEvent); mFilteredEvent = std::make_unique(keyEvent); break; } case InputEventType::MOTION: { const MotionEvent& motionEvent = static_cast(inputEvent); mFilteredEvent = std::make_unique(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(ui::LogicalDisplayId, uint32_t, int32_t, nsecs_t, uint32_t&) {} nsecs_t FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp&, const KeyEvent&, uint32_t) { if (mConsumeKeyBeforeDispatching) { return -1; } 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 FakeInputDispatcherPolicy::dispatchUnhandledKey(const sp&, 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, ui::LogicalDisplayId 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& 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& 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& uids) { ASSERT_TRUE(mNotifiedInteractions.emplace(deviceId, uids)); } void FakeInputDispatcherPolicy::assertFilterInputEventWasCalledInternal( const std::function& verify) { std::scoped_lock lock(mLock); ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called."; verify(*mFilteredEvent); mFilteredEvent = nullptr; } void FakeInputDispatcherPolicy::notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) { std::scoped_lock lock(mLock); mNotifiedFocusedDisplay = displayId; mFocusedDisplayNotifiedCondition.notify_all(); } } // namespace android