Loading libs/input/input_flags.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -97,3 +97,10 @@ flag { description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones" bug: "315313622" } flag { name: "rate_limit_user_activity_poke_in_dispatcher" namespace: "input" description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher." bug: "320499729" } services/inputflinger/dispatcher/InputDispatcher.cpp +49 −6 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ #include <input/PrintTools.h> #include <input/TraceTools.h> #include <openssl/mem.h> #include <powermanager/PowerManager.h> #include <unistd.h> #include <utils/Trace.h> Loading Loading @@ -100,6 +99,9 @@ const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::mil android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * HwTimeoutMultiplier()); // The default minimum time gap between two user activity poke events. const std::chrono::milliseconds DEFAULT_USER_ACTIVITY_POKE_INTERVAL = 100ms; const std::chrono::duration STALE_EVENT_TIMEOUT = std::chrono::seconds(10) * HwTimeoutMultiplier(); // Log a warning when an event takes longer than this to process, even if an ANR does not occur. Loading Loading @@ -778,6 +780,25 @@ Result<void> validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) { return {}; } int32_t getUserActivityEventType(const EventEntry& eventEntry) { switch (eventEntry.type) { case EventEntry::Type::KEY: { return USER_ACTIVITY_EVENT_BUTTON; } case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) { return USER_ACTIVITY_EVENT_TOUCH; } return USER_ACTIVITY_EVENT_OTHER; } default: { LOG_ALWAYS_FATAL("%s events are not user activity", ftl::enum_string(eventEntry.type).c_str()); } } } } // namespace // --- InputDispatcher --- Loading @@ -791,6 +812,7 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, mPendingEvent(nullptr), mLastDropReason(DropReason::NOT_DROPPED), mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER), mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL), mNextUnblockedEvent(nullptr), mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT), mDispatchEnabled(false), Loading @@ -813,6 +835,8 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, if (traceBackend) { // TODO: Create input tracer instance. } mLastUserActivityTimes.fill(0); } InputDispatcher::~InputDispatcher() { Loading Loading @@ -3140,6 +3164,21 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { // Not poking user activity if the event type does not represent a user activity return; } const int32_t eventType = getUserActivityEventType(eventEntry); if (input_flags::rate_limit_user_activity_poke_in_dispatcher()) { // Note that we're directly getting the time diff between the current event and the previous // event. This is assuming that the first user event always happens at a timestamp that is // greater than `mMinTimeBetweenUserActivityPokes` (otherwise, the first user event will // wrongly be dropped). In real life, `mMinTimeBetweenUserActivityPokes` is a much smaller // value than the potential first user activity event time, so this is ok. std::chrono::nanoseconds timeSinceLastEvent = std::chrono::nanoseconds(eventEntry.eventTime - mLastUserActivityTimes[eventType]); if (timeSinceLastEvent < mMinTimeBetweenUserActivityPokes) { return; } } int32_t displayId = getTargetDisplayId(eventEntry); sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); const WindowInfo* windowDisablingUserActivityInfo = nullptr; Loading @@ -3150,7 +3189,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } } int32_t eventType = USER_ACTIVITY_EVENT_OTHER; switch (eventEntry.type) { case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); Loading @@ -3164,9 +3202,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } return; } if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) { eventType = USER_ACTIVITY_EVENT_TOUCH; } break; } case EventEntry::Type::KEY: { Loading @@ -3190,7 +3225,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { return; } eventType = USER_ACTIVITY_EVENT_BUTTON; break; } default: { Loading @@ -3200,6 +3234,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } } mLastUserActivityTimes[eventType] = eventEntry.eventTime; auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]() REQUIRES(mLock) { scoped_unlock unlock(mLock); Loading Loading @@ -5292,6 +5327,14 @@ void InputDispatcher::setFocusedApplicationLocked( resetNoFocusedWindowTimeoutLocked(); } void InputDispatcher::setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) { if (interval.count() < 0) { LOG_ALWAYS_FATAL("Minimum time between user activity pokes should be >= 0"); } std::scoped_lock _l(mLock); mMinTimeBetweenUserActivityPokes = interval; } /** * Sets the focused display, which is responsible for receiving focus-dispatched input events where * the display not specified. Loading services/inputflinger/dispatcher/InputDispatcher.h +7 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ #include <input/Input.h> #include <input/InputTransport.h> #include <limits.h> #include <powermanager/PowerManager.h> #include <stddef.h> #include <unistd.h> #include <utils/BitSet.h> Loading Loading @@ -116,6 +117,7 @@ public: int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override; void setFocusedDisplay(int32_t displayId) override; void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) override; void setInputDispatchMode(bool enabled, bool frozen) override; void setInputFilterEnabled(bool enabled) override; bool setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid, bool hasPermission, Loading Loading @@ -211,6 +213,11 @@ private: int64_t mWindowInfosVsyncId GUARDED_BY(mLock); std::chrono::milliseconds mMinTimeBetweenUserActivityPokes GUARDED_BY(mLock); /** Stores the latest user-activity poke event times per user activity types. */ std::array<nsecs_t, USER_ACTIVITY_EVENT_LAST + 1> mLastUserActivityTimes GUARDED_BY(mLock); // With each iteration, InputDispatcher nominally processes one queued event, // a timeout, or a response from an input consumer. // This method should only be called on the input dispatcher's own thread. Loading services/inputflinger/dispatcher/include/InputDispatcherInterface.h +3 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,9 @@ public: */ virtual void setFocusedDisplay(int32_t displayId) = 0; /** Sets the minimum time between user activity pokes. */ virtual void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) = 0; /* Sets the input dispatching mode. * * This method may be called on any thread (usually by the input manager). Loading services/inputflinger/tests/InputDispatcher_test.cpp +172 −9 Original line number Diff line number Diff line Loading @@ -156,6 +156,20 @@ class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { sp<IBinder> token{}; gui::Pid pid{gui::Pid::INVALID}; }; /* 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; Loading Loading @@ -351,14 +365,36 @@ public: void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; } void assertUserActivityPoked() { std::scoped_lock lock(mLock); ASSERT_TRUE(mPokedUserActivity) << "Expected user activity to have been poked"; 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"; } void assertUserActivityNotPoked() { std::scoped_lock lock(mLock); ASSERT_FALSE(mPokedUserActivity) << "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) { Loading Loading @@ -414,7 +450,9 @@ private: sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock); bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false; bool mPokedUserActivity GUARDED_BY(mLock) = false; std::condition_variable mNotifyUserActivity; std::queue<UserActivityPokeEvent> mUserActivityPokeEvents; std::chrono::milliseconds mInterceptKeyTimeout = 0ms; Loading Loading @@ -576,9 +614,10 @@ private: NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask); } void pokeUserActivity(nsecs_t, int32_t, int32_t) override { void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override { std::scoped_lock lock(mLock); mPokedUserActivity = true; mNotifyUserActivity.notify_all(); mUserActivityPokeEvents.push({eventTime, eventType, displayId}); } bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override { Loading Loading @@ -7690,6 +7729,130 @@ TEST_F(InputFilterInjectionPolicyTest, RegularInjectedEvents_ReceiveVirtualDevic /*resolvedDeviceId=*/VIRTUAL_KEYBOARD_ID, /*flags=*/0); } class InputDispatcherUserActivityPokeTests : public InputDispatcherTest { protected: virtual void SetUp() override { InputDispatcherTest::SetUp(); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); application->setDispatchingTimeout(100ms); mWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); mWindow->setFrame(Rect(0, 0, 30, 30)); mWindow->setDispatchingTimeout(100ms); mWindow->setFocusable(true); // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); setFocusedWindow(mWindow); mWindow->consumeFocusEvent(true); } void notifyAndConsumeMotion(int32_t action, uint32_t source, int32_t displayId, nsecs_t eventTime) { mDispatcher->notifyMotion(MotionArgsBuilder(action, source) .displayId(displayId) .eventTime(eventTime) .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) .build()); mWindow->consumeMotionEvent(WithMotionAction(action)); } private: sp<FakeWindowHandle> mWindow; }; TEST_F_WITH_FLAGS( InputDispatcherUserActivityPokeTests, MinPokeTimeObserved, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, rate_limit_user_activity_poke_in_dispatcher))) { mDispatcher->setMinTimeBetweenUserActivityPokes(50ms); // First event of type TOUCH. Should poke. notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(50)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); // 80ns > 50ns has passed since previous TOUCH event. Should poke. notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(130)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); // First event of type OTHER. Should poke (despite being within 50ns of previous TOUCH event). notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(135)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}}); // Within 50ns of previous TOUCH event. Should NOT poke. notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(140)); mFakePolicy->assertUserActivityNotPoked(); // Within 50ns of previous OTHER event. Should NOT poke. notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(150)); mFakePolicy->assertUserActivityNotPoked(); // Within 50ns of previous TOUCH event (which was at time 130). Should NOT poke. // Note that STYLUS is mapped to TOUCH user activity, since it's a pointer-type source. notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(160)); mFakePolicy->assertUserActivityNotPoked(); // 65ns > 50ns has passed since previous OTHER event. Should poke. notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(200)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}}); // 170ns > 50ns has passed since previous TOUCH event. Should poke. notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(300)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); // Assert that there's no more user activity poke event. mFakePolicy->assertUserActivityNotPoked(); } TEST_F_WITH_FLAGS( InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, rate_limit_user_activity_poke_in_dispatcher))) { notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(200)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(280)); mFakePolicy->assertUserActivityNotPoked(); notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(340)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); } TEST_F_WITH_FLAGS( InputDispatcherUserActivityPokeTests, ZeroMinPokeTimeDisablesRateLimiting, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, rate_limit_user_activity_poke_in_dispatcher))) { mDispatcher->setMinTimeBetweenUserActivityPokes(0ms); notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20); mFakePolicy->assertUserActivityPoked(); notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 30); mFakePolicy->assertUserActivityPoked(); } class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); Loading Loading
libs/input/input_flags.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -97,3 +97,10 @@ flag { description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones" bug: "315313622" } flag { name: "rate_limit_user_activity_poke_in_dispatcher" namespace: "input" description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher." bug: "320499729" }
services/inputflinger/dispatcher/InputDispatcher.cpp +49 −6 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ #include <input/PrintTools.h> #include <input/TraceTools.h> #include <openssl/mem.h> #include <powermanager/PowerManager.h> #include <unistd.h> #include <utils/Trace.h> Loading Loading @@ -100,6 +99,9 @@ const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::mil android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * HwTimeoutMultiplier()); // The default minimum time gap between two user activity poke events. const std::chrono::milliseconds DEFAULT_USER_ACTIVITY_POKE_INTERVAL = 100ms; const std::chrono::duration STALE_EVENT_TIMEOUT = std::chrono::seconds(10) * HwTimeoutMultiplier(); // Log a warning when an event takes longer than this to process, even if an ANR does not occur. Loading Loading @@ -778,6 +780,25 @@ Result<void> validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) { return {}; } int32_t getUserActivityEventType(const EventEntry& eventEntry) { switch (eventEntry.type) { case EventEntry::Type::KEY: { return USER_ACTIVITY_EVENT_BUTTON; } case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) { return USER_ACTIVITY_EVENT_TOUCH; } return USER_ACTIVITY_EVENT_OTHER; } default: { LOG_ALWAYS_FATAL("%s events are not user activity", ftl::enum_string(eventEntry.type).c_str()); } } } } // namespace // --- InputDispatcher --- Loading @@ -791,6 +812,7 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, mPendingEvent(nullptr), mLastDropReason(DropReason::NOT_DROPPED), mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER), mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL), mNextUnblockedEvent(nullptr), mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT), mDispatchEnabled(false), Loading @@ -813,6 +835,8 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, if (traceBackend) { // TODO: Create input tracer instance. } mLastUserActivityTimes.fill(0); } InputDispatcher::~InputDispatcher() { Loading Loading @@ -3140,6 +3164,21 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { // Not poking user activity if the event type does not represent a user activity return; } const int32_t eventType = getUserActivityEventType(eventEntry); if (input_flags::rate_limit_user_activity_poke_in_dispatcher()) { // Note that we're directly getting the time diff between the current event and the previous // event. This is assuming that the first user event always happens at a timestamp that is // greater than `mMinTimeBetweenUserActivityPokes` (otherwise, the first user event will // wrongly be dropped). In real life, `mMinTimeBetweenUserActivityPokes` is a much smaller // value than the potential first user activity event time, so this is ok. std::chrono::nanoseconds timeSinceLastEvent = std::chrono::nanoseconds(eventEntry.eventTime - mLastUserActivityTimes[eventType]); if (timeSinceLastEvent < mMinTimeBetweenUserActivityPokes) { return; } } int32_t displayId = getTargetDisplayId(eventEntry); sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); const WindowInfo* windowDisablingUserActivityInfo = nullptr; Loading @@ -3150,7 +3189,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } } int32_t eventType = USER_ACTIVITY_EVENT_OTHER; switch (eventEntry.type) { case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); Loading @@ -3164,9 +3202,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } return; } if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) { eventType = USER_ACTIVITY_EVENT_TOUCH; } break; } case EventEntry::Type::KEY: { Loading @@ -3190,7 +3225,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { return; } eventType = USER_ACTIVITY_EVENT_BUTTON; break; } default: { Loading @@ -3200,6 +3234,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { } } mLastUserActivityTimes[eventType] = eventEntry.eventTime; auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]() REQUIRES(mLock) { scoped_unlock unlock(mLock); Loading Loading @@ -5292,6 +5327,14 @@ void InputDispatcher::setFocusedApplicationLocked( resetNoFocusedWindowTimeoutLocked(); } void InputDispatcher::setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) { if (interval.count() < 0) { LOG_ALWAYS_FATAL("Minimum time between user activity pokes should be >= 0"); } std::scoped_lock _l(mLock); mMinTimeBetweenUserActivityPokes = interval; } /** * Sets the focused display, which is responsible for receiving focus-dispatched input events where * the display not specified. Loading
services/inputflinger/dispatcher/InputDispatcher.h +7 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ #include <input/Input.h> #include <input/InputTransport.h> #include <limits.h> #include <powermanager/PowerManager.h> #include <stddef.h> #include <unistd.h> #include <utils/BitSet.h> Loading Loading @@ -116,6 +117,7 @@ public: int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override; void setFocusedDisplay(int32_t displayId) override; void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) override; void setInputDispatchMode(bool enabled, bool frozen) override; void setInputFilterEnabled(bool enabled) override; bool setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid, bool hasPermission, Loading Loading @@ -211,6 +213,11 @@ private: int64_t mWindowInfosVsyncId GUARDED_BY(mLock); std::chrono::milliseconds mMinTimeBetweenUserActivityPokes GUARDED_BY(mLock); /** Stores the latest user-activity poke event times per user activity types. */ std::array<nsecs_t, USER_ACTIVITY_EVENT_LAST + 1> mLastUserActivityTimes GUARDED_BY(mLock); // With each iteration, InputDispatcher nominally processes one queued event, // a timeout, or a response from an input consumer. // This method should only be called on the input dispatcher's own thread. Loading
services/inputflinger/dispatcher/include/InputDispatcherInterface.h +3 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,9 @@ public: */ virtual void setFocusedDisplay(int32_t displayId) = 0; /** Sets the minimum time between user activity pokes. */ virtual void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) = 0; /* Sets the input dispatching mode. * * This method may be called on any thread (usually by the input manager). Loading
services/inputflinger/tests/InputDispatcher_test.cpp +172 −9 Original line number Diff line number Diff line Loading @@ -156,6 +156,20 @@ class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { sp<IBinder> token{}; gui::Pid pid{gui::Pid::INVALID}; }; /* 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; Loading Loading @@ -351,14 +365,36 @@ public: void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; } void assertUserActivityPoked() { std::scoped_lock lock(mLock); ASSERT_TRUE(mPokedUserActivity) << "Expected user activity to have been poked"; 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"; } void assertUserActivityNotPoked() { std::scoped_lock lock(mLock); ASSERT_FALSE(mPokedUserActivity) << "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) { Loading Loading @@ -414,7 +450,9 @@ private: sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock); bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false; bool mPokedUserActivity GUARDED_BY(mLock) = false; std::condition_variable mNotifyUserActivity; std::queue<UserActivityPokeEvent> mUserActivityPokeEvents; std::chrono::milliseconds mInterceptKeyTimeout = 0ms; Loading Loading @@ -576,9 +614,10 @@ private: NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask); } void pokeUserActivity(nsecs_t, int32_t, int32_t) override { void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override { std::scoped_lock lock(mLock); mPokedUserActivity = true; mNotifyUserActivity.notify_all(); mUserActivityPokeEvents.push({eventTime, eventType, displayId}); } bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override { Loading Loading @@ -7690,6 +7729,130 @@ TEST_F(InputFilterInjectionPolicyTest, RegularInjectedEvents_ReceiveVirtualDevic /*resolvedDeviceId=*/VIRTUAL_KEYBOARD_ID, /*flags=*/0); } class InputDispatcherUserActivityPokeTests : public InputDispatcherTest { protected: virtual void SetUp() override { InputDispatcherTest::SetUp(); std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); application->setDispatchingTimeout(100ms); mWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); mWindow->setFrame(Rect(0, 0, 30, 30)); mWindow->setDispatchingTimeout(100ms); mWindow->setFocusable(true); // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); setFocusedWindow(mWindow); mWindow->consumeFocusEvent(true); } void notifyAndConsumeMotion(int32_t action, uint32_t source, int32_t displayId, nsecs_t eventTime) { mDispatcher->notifyMotion(MotionArgsBuilder(action, source) .displayId(displayId) .eventTime(eventTime) .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) .build()); mWindow->consumeMotionEvent(WithMotionAction(action)); } private: sp<FakeWindowHandle> mWindow; }; TEST_F_WITH_FLAGS( InputDispatcherUserActivityPokeTests, MinPokeTimeObserved, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, rate_limit_user_activity_poke_in_dispatcher))) { mDispatcher->setMinTimeBetweenUserActivityPokes(50ms); // First event of type TOUCH. Should poke. notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(50)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); // 80ns > 50ns has passed since previous TOUCH event. Should poke. notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(130)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); // First event of type OTHER. Should poke (despite being within 50ns of previous TOUCH event). notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(135)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}}); // Within 50ns of previous TOUCH event. Should NOT poke. notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(140)); mFakePolicy->assertUserActivityNotPoked(); // Within 50ns of previous OTHER event. Should NOT poke. notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(150)); mFakePolicy->assertUserActivityNotPoked(); // Within 50ns of previous TOUCH event (which was at time 130). Should NOT poke. // Note that STYLUS is mapped to TOUCH user activity, since it's a pointer-type source. notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(160)); mFakePolicy->assertUserActivityNotPoked(); // 65ns > 50ns has passed since previous OTHER event. Should poke. notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(200)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}}); // 170ns > 50ns has passed since previous TOUCH event. Should poke. notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(300)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); // Assert that there's no more user activity poke event. mFakePolicy->assertUserActivityNotPoked(); } TEST_F_WITH_FLAGS( InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, rate_limit_user_activity_poke_in_dispatcher))) { notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(200)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(280)); mFakePolicy->assertUserActivityNotPoked(); notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, milliseconds_to_nanoseconds(340)); mFakePolicy->assertUserActivityPoked( {{milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}}); } TEST_F_WITH_FLAGS( InputDispatcherUserActivityPokeTests, ZeroMinPokeTimeDisablesRateLimiting, REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, rate_limit_user_activity_poke_in_dispatcher))) { mDispatcher->setMinTimeBetweenUserActivityPokes(0ms); notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20); mFakePolicy->assertUserActivityPoked(); notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 30); mFakePolicy->assertUserActivityPoked(); } class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); Loading