Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9e14fec2 authored by Yeabkal Wubshit's avatar Yeabkal Wubshit Committed by Android (Google) Code Review
Browse files

Merge "Rate limit user activity pokes in InputDispatcher" into main

parents 4da99064 b8aadfa2
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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"
}
+49 −6
Original line number Diff line number Diff line
@@ -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>

@@ -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.
@@ -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 ---
@@ -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),
@@ -813,6 +835,8 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
    if (traceBackend) {
        // TODO: Create input tracer instance.
    }

    mLastUserActivityTimes.fill(0);
}

InputDispatcher::~InputDispatcher() {
@@ -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;
@@ -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);
@@ -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: {
@@ -3190,7 +3225,6 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
                return;
            }

            eventType = USER_ACTIVITY_EVENT_BUTTON;
            break;
        }
        default: {
@@ -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);
@@ -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.
+7 −0
Original line number Diff line number Diff line
@@ -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>
@@ -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,
@@ -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.
+3 −0
Original line number Diff line number Diff line
@@ -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).
+172 −9
Original line number Diff line number Diff line
@@ -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;
@@ -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) {
@@ -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;
@@ -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 {
@@ -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();