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

Commit 837fab15 authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Allow new gestures to cancel current gestures

In a previous patch, we chose to ignore new gestures whenever there's
currently an active gesture. Generally this is fine to do, but there are
some concerns around doing that:
1) This is different from the previous behaviour, and some tests were
   relying on the previous behaviour.
2) If a test injects an ACTION_DOWN event (globally) and never lifts up
   the pointer, this would cause all real subsequent events to be
   rejected. That means a bad test can cause device to get into a bad
   state.
Rather than adding a special case to deal with 2), let's revert to the
previous behaviour.

Since we are now allowing the new device to take over, and only 1 device
can be active at a time (for now), we must reset the touching pointers
whenever we have a new gesture starting. That's because the function
synthesizeCancelationEventsForAllConnectionsLocked does not modify
TouchState.

We should also be canceling any of the currently hovering pointers by
sending an ACTION_HOVER_EXIT if a touch down occurs. This behaviour
was previously inconsistent in the mouse case.

Once per-device functionality is enabled, this behaviour will be
revisited.

Bug: 268683979
Test: m inputflinger_tests && $ANDROID_HOST_OUT/nativetest64/inputflinger_tests/inputflinger_tests
Change-Id: I10c7ebde7c108baecb67a865f541253fa6e5f7ef
parent ab67ebfd
Loading
Loading
Loading
Loading
+23 −19
Original line number Diff line number Diff line
@@ -2184,18 +2184,20 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
    const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction;
    const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);

    if (newGesture) {
    // If pointers are already down, let's finish the current gesture and ignore the new events
        // from another device.
        if (switchedDevice && wasDown) {
            ALOGI("Dropping event because a pointer for a different device is already down "
                  "in display %" PRId32,
                  displayId);
    // from another device. However, if the new event is a down event, let's cancel the current
    // touch and let the new one take over.
    if (switchedDevice && wasDown && !isDown) {
        LOG(INFO) << "Dropping event because a pointer for device " << oldState->deviceId
                  << " is already down in display " << displayId << ": " << entry.getDescription();
        // TODO(b/211379801): test multiple simultaneous input streams.
        outInjectionResult = InputEventInjectionResult::FAILED;
        return {}; // wrong device
    }
        tempTouchState.clearWindowsWithoutPointers();

    if (newGesture) {
        // If a new gesture is starting, clear the touch state completely.
        tempTouchState.reset();
        tempTouchState.deviceId = entry.deviceId;
        tempTouchState.source = entry.source;
        isSplit = false;
@@ -2317,6 +2319,10 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
            const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
                    maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN;

            // TODO(b/211379801): Currently, even if pointerIds are empty (hover case), we would
            // still add a window to the touch state. We should avoid doing that, but some of the
            // later checks ("at least one foreground window") rely on this in order to dispatch
            // the event properly, so that needs to be updated, possibly by looking at InputTargets.
            tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds,
                                             isDownOrPointerDown
                                                     ? std::make_optional(entry.eventTime)
@@ -2369,10 +2375,9 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(

        // If the pointer is not currently down, then ignore the event.
        if (!tempTouchState.isDown()) {
            ALOGD_IF(DEBUG_FOCUS,
                     "Dropping event because the pointer is not down or we previously "
                     "dropped the pointer down event in display %" PRId32 ": %s",
                     displayId, entry.getDescription().c_str());
            LOG(INFO) << "Dropping event because the pointer is not down or we previously "
                         "dropped the pointer down event in display "
                      << displayId << ": " << entry.getDescription();
            outInjectionResult = InputEventInjectionResult::FAILED;
            return {};
        }
@@ -2530,7 +2535,6 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
    }

    // Success!  Output targets from the touch state.
    tempTouchState.clearWindowsWithoutPointers();
    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
        if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
            // Windows with hovering pointers are getting persisted inside TouchState.
@@ -2570,14 +2574,13 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
    } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
        // Pointer went up.
        tempTouchState.removeTouchedPointer(entry.pointerProperties[0].id);
        tempTouchState.clearWindowsWithoutPointers();
    } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
        // All pointers up or canceled.
        tempTouchState.reset();
    } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
        // First pointer went down.
        if (oldState && oldState->isDown()) {
            ALOGD("Conflicting pointer actions: Down received while already down.");
        if (oldState && (oldState->isDown() || oldState->hasHoveringPointers())) {
            ALOGD("Conflicting pointer actions: Down received while already down or hovering.");
            *outConflictingPointerActions = true;
        }
    } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
@@ -2600,6 +2603,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
    // state was only valid for this one action.
    if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
        if (displayId >= 0) {
            tempTouchState.clearWindowsWithoutPointers();
            mTouchStatesByDisplay[displayId] = tempTouchState;
        } else {
            mTouchStatesByDisplay.erase(displayId);
+11 −5
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ void TouchState::removeTouchedPointer(int32_t pointerId) {
    for (TouchedWindow& touchedWindow : windows) {
        touchedWindow.removeTouchingPointer(pointerId);
    }
    clearWindowsWithoutPointers();
}

void TouchState::removeTouchedPointerFromWindow(
@@ -42,6 +43,7 @@ void TouchState::removeTouchedPointerFromWindow(
    for (TouchedWindow& touchedWindow : windows) {
        if (touchedWindow.windowHandle == windowHandle) {
            touchedWindow.removeTouchingPointer(pointerId);
            clearWindowsWithoutPointers();
            return;
        }
    }
@@ -51,6 +53,7 @@ void TouchState::clearHoveringPointers() {
    for (TouchedWindow& touchedWindow : windows) {
        touchedWindow.clearHoveringPointers();
    }
    clearWindowsWithoutPointers();
}

void TouchState::clearWindowsWithoutPointers() {
@@ -135,7 +138,7 @@ void TouchState::cancelPointersForWindowsExcept(std::bitset<MAX_POINTER_ID + 1>
            w.pointerIds &= ~pointerIds;
        }
    });
    std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.none(); });
    clearWindowsWithoutPointers();
}

/**
@@ -164,7 +167,7 @@ void TouchState::cancelPointersForNonPilferingWindows() {
                w.pilferedPointerIds ^ allPilferedPointerIds;
        w.pointerIds &= ~pilferedByOtherWindows;
    });
    std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.none(); });
    clearWindowsWithoutPointers();
}

sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
@@ -216,6 +219,11 @@ bool TouchState::isDown() const {
                       [](const TouchedWindow& window) { return window.pointerIds.any(); });
}

bool TouchState::hasHoveringPointers() const {
    return std::any_of(windows.begin(), windows.end(),
                       [](const TouchedWindow& window) { return window.hasHoveringPointers(); });
}

std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(int32_t hoveringDeviceId,
                                                                         int32_t pointerId) const {
    std::set<sp<WindowInfoHandle>> out;
@@ -231,9 +239,7 @@ void TouchState::removeHoveringPointer(int32_t hoveringDeviceId, int32_t hoverin
    for (TouchedWindow& window : windows) {
        window.removeHoveringPointer(hoveringDeviceId, hoveringPointerId);
    }
    std::erase_if(windows, [](const TouchedWindow& w) {
        return w.pointerIds.none() && !w.hasHoveringPointers();
    });
    clearWindowsWithoutPointers();
}

std::string TouchState::dump() const {
+1 −0
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ struct TouchState {
            const sp<android::gui::WindowInfoHandle>& windowHandle) const;
    // Whether any of the windows are currently being touched
    bool isDown() const;
    bool hasHoveringPointers() const;

    std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer(
            int32_t deviceId, int32_t pointerId) const;
+427 −4

File changed.

Preview size limit exceeded, changes collapsed.