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

Commit 0b0374d3 authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Erase touch state if empty

Before this CL, touch state would remain stored even if it had no
windows inside. After a recent refactor, this started causing events
that have different sources to trigger the "conflicting pointer actions"
block.

The TooltipTest relied on this behaviour.

To fix this, erase touch state whenever there aren't any windows in it.
In the future, we may have to revise this test to send the correct
stream of events, which would include HOVER_EXIT.

Bug: 258845980
Test: m inputflinger_tests && out/host/linux-x86/nativetest/inputflinger_tests/inputflinger_tests
Test: atest android.view.cts.TooltipTest
Change-Id: I1cbe800793ba8cde55b81a3e1d1d4b0fe25f8c77
parent e060ce13
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@
          "include-filter": "android.view.cts.input",
          "include-filter": "android.view.cts.MotionEventTest",
          "include-filter": "android.view.cts.PointerCaptureTest",
          "include-filter": "android.view.cts.TooltipTest",
          "include-filter": "android.view.cts.VerifyInputEventTest"
        }
      ]
@@ -128,6 +129,7 @@
        {
          "include-filter": "android.view.cts.MotionEventTest",
          "include-filter": "android.view.cts.PointerCaptureTest",
          "include-filter": "android.view.cts.TooltipTest",
          "include-filter": "android.view.cts.VerifyInputEventTest"
        }
      ]
+10 −5
Original line number Diff line number Diff line
@@ -1769,15 +1769,16 @@ void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<Dr

void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
        ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
        ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=%s, displayId=%" PRId32
              ", policyFlags=0x%x, "
              "action=%s, actionButton=0x%x, flags=0x%x, "
              "metaState=0x%x, buttonState=0x%x,"
              "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
              prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
              entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(),
              entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags,
              entry.xPrecision, entry.yPrecision, entry.downTime);
              prefix, entry.eventTime, entry.deviceId,
              inputEventSourceToString(entry.source).c_str(), entry.displayId, entry.policyFlags,
              MotionEvent::actionToString(entry.action).c_str(), entry.actionButton, entry.flags,
              entry.metaState, entry.buttonState, entry.edgeFlags, entry.xPrecision,
              entry.yPrecision, entry.downTime);

        for (uint32_t i = 0; i < entry.pointerCount; i++) {
            ALOGD("  Pointer %d: id=%d, toolType=%d, "
@@ -2502,6 +2503,10 @@ Failed:
        }
    }

    if (tempTouchState.windows.empty()) {
        mTouchStatesByDisplay.erase(displayId);
    }

    // Update hover state.
    mLastHoverWindowHandle = newHoverWindowHandle;

+2 −1
Original line number Diff line number Diff line
@@ -148,7 +148,8 @@ bool TouchState::isDown() const {

std::string TouchState::dump() const {
    std::string out;
    out += StringPrintf("deviceId=%d, source=0x%08x\n", deviceId, source);
    out += StringPrintf("deviceId=%d, source=%s\n", deviceId,
                        inputEventSourceToString(source).c_str());
    if (!windows.empty()) {
        out += "  Windows:\n";
        for (size_t i = 0; i < windows.size(); i++) {
+72 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <android-base/thread_annotations.h>
#include <binder/Binder.h>
#include <fcntl.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/Input.h>
#include <linux/input.h>
@@ -43,6 +44,7 @@ using android::os::InputEventInjectionSync;
namespace android::inputdispatcher {

using namespace ftl::flag_operators;
using testing::AllOf;

// An arbitrary time value.
static constexpr nsecs_t ARBITRARY_TIME = 1234;
@@ -102,6 +104,28 @@ static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) {
            << MotionEvent::actionToString(receivedAction);
}

MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
    bool matches = action == arg.getAction();
    if (!matches) {
        *result_listener << "expected action " << MotionEvent::actionToString(action)
                         << ", but got " << MotionEvent::actionToString(arg.getAction());
    }
    if (action == AMOTION_EVENT_ACTION_CANCEL) {
        if (!matches) {
            *result_listener << "; ";
        }
        *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
        matches &= (arg.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
    }
    return matches;
}

MATCHER_P(WithSource, source, "InputEvent with specified source") {
    *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
                     << inputEventSourceToString(arg.getSource());
    return arg.getSource() == source;
}

// --- FakeInputDispatcherPolicy ---

class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -1205,6 +1229,12 @@ public:
        mInputReceiver->consumeCaptureEvent(hasCapture);
    }

    void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
        MotionEvent* motionEvent = consumeMotion();
        ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event";
        ASSERT_THAT(*motionEvent, matcher);
    }

    void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
                      std::optional<int32_t> expectedDisplayId,
                      std::optional<int32_t> expectedFlags) {
@@ -2207,6 +2237,48 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
                         ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
}

/**
 * Inject a mouse hover event followed by a tap from touchscreen.
 * In the current implementation, the tap does not cause a HOVER_EXIT event.
 */
TEST_F(InputDispatcherTest, MouseHoverAndTouchTap) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
    window->setFrame(Rect(0, 0, 100, 100));

    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});

    // Inject a hover_move from mouse.
    NotifyMotionArgs motionArgs =
            generateMotionArgs(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE,
                               ADISPLAY_ID_DEFAULT, {{50, 50}});
    motionArgs.xCursorPosition = 50;
    motionArgs.yCursorPosition = 50;
    mDispatcher->notifyMotion(&motionArgs);
    ASSERT_NO_FATAL_FAILURE(
            window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
                                             WithSource(AINPUT_SOURCE_MOUSE))));
    ASSERT_NO_FATAL_FAILURE(
            window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
                                             WithSource(AINPUT_SOURCE_MOUSE))));

    // Tap on the window
    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                    ADISPLAY_ID_DEFAULT, {{10, 10}});
    mDispatcher->notifyMotion(&motionArgs);
    ASSERT_NO_FATAL_FAILURE(
            window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
                                             WithSource(AINPUT_SOURCE_TOUCHSCREEN))));

    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
                                    ADISPLAY_ID_DEFAULT, {{10, 10}});
    mDispatcher->notifyMotion(&motionArgs);
    ASSERT_NO_FATAL_FAILURE(
            window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
                                             WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
}

TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();

+2 −1
Original line number Diff line number Diff line
@@ -46,7 +46,8 @@ MATCHER_P(WithKeyAction, action, "KeyEvent with specified action") {
}

MATCHER_P(WithSource, source, "InputEvent with specified source") {
    *result_listener << "expected source " << source << ", but got " << arg.source;
    *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
                     << inputEventSourceToString(arg.source);
    return arg.source == source;
}