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

Commit 112b1ad7 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

InputDispatcher: Use correct coordinate space when canceling motions

Input targets for synthesized cancellations were not created using the
same pipeline as normal dispatching. Due to this, things like window and
display transforms were not being applied to the events. This means
ACTION_CANCEL events did not have the correct coordinates in many cases.

Here, we fix that by attempting to use the same pipeline as normal
dispatching for creating the input target. When it's not possible to do
due to there being no window (e.g. for global monitors), we fall back to
creating the target ourselves.

Bug: 287908447
Test: atest inputflinger_tests
Change-Id: Ic82f87c55e4eaf3e87b07986a14933fbdb69e042
parent 36f5994c
Loading
Loading
Loading
Loading
+33 −13
Original line number Diff line number Diff line
@@ -3944,7 +3944,6 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
    android_log_event_list(LOGTAG_INPUT_CANCEL)
            << connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;

    InputTarget target;
    sp<WindowInfoHandle> windowHandle;
    if (options.displayId) {
        windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken(),
@@ -3952,27 +3951,47 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
    } else {
        windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken());
    }
    if (windowHandle != nullptr) {
        const WindowInfo* windowInfo = windowHandle->getInfo();
        target.setDefaultPointerTransform(windowInfo->transform);
        target.globalScaleFactor = windowInfo->globalScaleFactor;
    }
    target.inputChannel = connection->inputChannel;
    target.flags = InputTarget::Flags::DISPATCH_AS_IS;

    const bool wasEmpty = connection->outboundQueue.empty();

    for (size_t i = 0; i < cancelationEvents.size(); i++) {
        std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]);
        std::vector<InputTarget> targets{};
        // The target to use if we don't find a window associated with the channel.
        const InputTarget fallbackTarget{.inputChannel = connection->inputChannel,
                                         .flags = InputTarget::Flags::DISPATCH_AS_IS};

        switch (cancelationEventEntry->type) {
            case EventEntry::Type::KEY: {
                logOutboundKeyDetails("cancel - ",
                                      static_cast<const KeyEntry&>(*cancelationEventEntry));
                const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry);
                if (windowHandle != nullptr) {
                    addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
                                          /*pointerIds=*/{}, keyEntry.downTime, targets);
                } else {
                    targets.emplace_back(fallbackTarget);
                }
                logOutboundKeyDetails("cancel - ", keyEntry);
                break;
            }
            case EventEntry::Type::MOTION: {
                logOutboundMotionDetails("cancel - ",
                                         static_cast<const MotionEntry&>(*cancelationEventEntry));
                const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry);
                if (windowHandle != nullptr) {
                    std::bitset<MAX_POINTER_ID + 1> pointerIds;
                    for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount;
                         pointerIndex++) {
                        pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
                    }
                    addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
                                          pointerIds, motionEntry.downTime, targets);
                } else {
                    targets.emplace_back(fallbackTarget);
                    const auto it = mDisplayInfos.find(motionEntry.displayId);
                    if (it != mDisplayInfos.end()) {
                        targets.back().displayTransform = it->second.transform;
                        targets.back().setDefaultPointerTransform(it->second.transform);
                    }
                }
                logOutboundMotionDetails("cancel - ", motionEntry);
                break;
            }
            case EventEntry::Type::FOCUS:
@@ -3992,7 +4011,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
            }
        }

        enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), target,
        if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created";
        enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), targets[0],
                                   InputTarget::Flags::DISPATCH_AS_IS);
    }

+93 −59
Original line number Diff line number Diff line
@@ -1451,6 +1451,69 @@ private:

std::atomic<int32_t> FakeWindowHandle::sId{1};

class FakeMonitorReceiver {
public:
    FakeMonitorReceiver(const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name,
                        int32_t displayId) {
        base::Result<std::unique_ptr<InputChannel>> channel =
                dispatcher->createInputMonitor(displayId, name, MONITOR_PID);
        mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
    }

    sp<IBinder> getToken() { return mInputReceiver->getToken(); }

    void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
        mInputReceiver->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
                                     expectedFlags);
    }

    std::optional<int32_t> receiveEvent() {
        return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
    }

    void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }

    void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
                                     expectedDisplayId, expectedFlags);
    }

    void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
                                     expectedDisplayId, expectedFlags);
    }

    void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
                                     expectedDisplayId, expectedFlags);
    }

    void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
        mInputReceiver->consumeMotionEvent(
                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
                      WithDisplayId(expectedDisplayId),
                      WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
    }

    void consumeMotionPointerDown(int32_t pointerIdx) {
        int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
                (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
        mInputReceiver->consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
                                     /*expectedFlags=*/0);
    }

    void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
        mInputReceiver->consumeMotionEvent(matcher);
    }

    MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }

    void assertNoEvents() { mInputReceiver->assertNoEvents(); }

private:
    std::unique_ptr<FakeInputReceiver> mInputReceiver;
};

static InputEventInjectionResult injectKey(
        InputDispatcher& dispatcher, int32_t action, int32_t repeatCount,
        int32_t displayId = ADISPLAY_ID_NONE,
@@ -4607,6 +4670,36 @@ TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinate
    EXPECT_EQ(80, event->getY(0));
}

TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {
    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
    // The monitor will always receive events in the logical display's coordinate space, because
    // it does not have a window.
    FakeMonitorReceiver monitor{mDispatcher, "Monitor", ADISPLAY_ID_DEFAULT};

    // Send down to the first window.
    mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                                 ADISPLAY_ID_DEFAULT, {PointF{50, 100}}));
    firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
    monitor.consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));

    // Second pointer goes down on second window.
    mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                                 ADISPLAY_ID_DEFAULT,
                                                 {PointF{50, 100}, PointF{150, 220}}));
    secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 80)));
    const std::map<int32_t, PointF> expectedMonitorPointers{{0, PointF{100, 400}},
                                                            {1, PointF{300, 880}}};
    monitor.consumeMotionEvent(
            AllOf(WithMotionAction(POINTER_1_DOWN), WithPointers(expectedMonitorPointers)));

    mDispatcher->cancelCurrentTouch();

    firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400)));
    secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 80)));
    monitor.consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_CANCEL), WithPointers(expectedMonitorPointers)));
}

/** Ensure consistent behavior of InputDispatcher in all orientations. */
class InputDispatcherDisplayOrientationFixture
      : public InputDispatcherDisplayProjectionTest,
@@ -5389,65 +5482,6 @@ TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) {
    mDispatcher->waitForIdle();
}

class FakeMonitorReceiver {
public:
    FakeMonitorReceiver(const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name,
                        int32_t displayId) {
        base::Result<std::unique_ptr<InputChannel>> channel =
                dispatcher->createInputMonitor(displayId, name, MONITOR_PID);
        mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
    }

    sp<IBinder> getToken() { return mInputReceiver->getToken(); }

    void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
        mInputReceiver->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
                                     expectedFlags);
    }

    std::optional<int32_t> receiveEvent() {
        return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
    }

    void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }

    void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
                                     expectedDisplayId, expectedFlags);
    }

    void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
                                     expectedDisplayId, expectedFlags);
    }

    void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
                                     expectedDisplayId, expectedFlags);
    }

    void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
        mInputReceiver->consumeMotionEvent(
                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
                      WithDisplayId(expectedDisplayId),
                      WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
    }

    void consumeMotionPointerDown(int32_t pointerIdx) {
        int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
                (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
        mInputReceiver->consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
                                     /*expectedFlags=*/0);
    }

    MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }

    void assertNoEvents() { mInputReceiver->assertNoEvents(); }

private:
    std::unique_ptr<FakeInputReceiver> mInputReceiver;
};

using InputDispatcherMonitorTest = InputDispatcherTest;

/**