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

Commit 060f82be authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Keep track of pilfered pointer explicitly

The pointers that are getting pilfered were not being tracked explicitly
before. The behaviour relied on the window going away in order to reset
the "isPilfering" value to "false".

Now, however, some windows are getting persisted longer, because they
might receive a hover gesture that doesn't end (like a mouse that
someone stops using).

Therefore, we need to explicitly keep track of which pointers are being
pilfered. This is also in line with our eventual goal of explicit
per-window pointer management.

In the failing case, the windows was considered to be "pilfering". Since
the gesture monitors were receiving all of the pointers, the pointers
would no longer get dispatched to any windows underneath.

With this CL, we maintain the existing intended behaviours of pilfering,
while also expanding the support for multiple pilfered pointers.

In the added test case, there is a mouse interaction first, which
persists the gesture monitor window. Next, we pilfer a touch event,
which sets the "isPilfering" flag to ON. And finally, we inject the new
touch gesture. This gesture was (incorrectly) getting only sent to the
gesture monitor, because it kept maintaining the "isPilfering" status.

Bug: 266705421
Test: m inputflinger_tests && $ANDROID_HOST_OUT/nativetest64/inputflinger_tests/inputflinger_tests --gtest_filter="*MouseAndTouchWithSpyWindows*"
Change-Id: I6b72dc3e4de0e0ef09ccc469df0f703762c76fa6
parent 1ae72f16
Loading
Loading
Loading
Loading
+20 −9
Original line number Original line Diff line number Diff line
@@ -2333,14 +2333,22 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
            }
            }
        }
        }


        // If any existing window is pilfering pointers from newly added window, remove it
        // If a window is already pilfering some pointers, give it this new pointer as well and
        BitSet32 canceledPointers = BitSet32(0);
        // make it pilfering. This will prevent other non-spy windows from getting this pointer,
        for (const TouchedWindow& window : tempTouchState.windows) {
        // which is a specific behaviour that we want.
            if (window.isPilferingPointers) {
        const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
                canceledPointers |= window.pointerIds;
        for (TouchedWindow& touchedWindow : tempTouchState.windows) {
            }
            if (touchedWindow.pointerIds.hasBit(pointerId) &&
        }
                touchedWindow.pilferedPointerIds.count() > 0) {
        tempTouchState.cancelPointersForNonPilferingWindows(canceledPointers);
                // This window is already pilfering some pointers, and this new pointer is also
                // going to it. Therefore, take over this pointer and don't give it to anyone
                // else.
                touchedWindow.pilferedPointerIds.set(pointerId);
            }
        }

        // Restrict all pilfered pointers to the pilfering windows.
        tempTouchState.cancelPointersForNonPilferingWindows();
    } else {
    } else {
        /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
        /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */


@@ -3942,8 +3950,8 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
    if (action == AMOTION_EVENT_ACTION_DOWN) {
    if (action == AMOTION_EVENT_ACTION_DOWN) {
        LOG_ALWAYS_FATAL_IF(splitDownTime != originalMotionEntry.eventTime,
        LOG_ALWAYS_FATAL_IF(splitDownTime != originalMotionEntry.eventTime,
                            "Split motion event has mismatching downTime and eventTime for "
                            "Split motion event has mismatching downTime and eventTime for "
                            "ACTION_DOWN, motionEntry=%s, splitDownTime=%" PRId64 "ms",
                            "ACTION_DOWN, motionEntry=%s, splitDownTime=%" PRId64,
                            originalMotionEntry.getDescription().c_str(), ns2ms(splitDownTime));
                            originalMotionEntry.getDescription().c_str(), splitDownTime);
    }
    }


    int32_t newId = mIdGenerator.nextId();
    int32_t newId = mIdGenerator.nextId();
@@ -5778,7 +5786,10 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) {


    // Prevent the gesture from being sent to any other windows.
    // Prevent the gesture from being sent to any other windows.
    // This only blocks relevant pointers to be sent to other windows
    // This only blocks relevant pointers to be sent to other windows
    window.isPilferingPointers = true;
    for (BitSet32 idBits(window.pointerIds); !idBits.isEmpty();) {
        uint32_t id = idBits.clearFirstMarkedBit();
        window.pilferedPointerIds.set(id);
    }


    state.cancelPointersForWindowsExcept(window.pointerIds, token);
    state.cancelPointersForWindowsExcept(window.pointerIds, token);
    return OK;
    return OK;
+36 −5
Original line number Original line Diff line number Diff line
@@ -34,6 +34,7 @@ void TouchState::reset() {
void TouchState::removeTouchedPointer(int32_t pointerId) {
void TouchState::removeTouchedPointer(int32_t pointerId) {
    for (TouchedWindow& touchedWindow : windows) {
    for (TouchedWindow& touchedWindow : windows) {
        touchedWindow.pointerIds.clearBit(pointerId);
        touchedWindow.pointerIds.clearBit(pointerId);
        touchedWindow.pilferedPointerIds.reset(pointerId);
    }
    }
}
}


@@ -42,6 +43,7 @@ void TouchState::removeTouchedPointerFromWindow(
    for (TouchedWindow& touchedWindow : windows) {
    for (TouchedWindow& touchedWindow : windows) {
        if (touchedWindow.windowHandle == windowHandle) {
        if (touchedWindow.windowHandle == windowHandle) {
            touchedWindow.pointerIds.clearBit(pointerId);
            touchedWindow.pointerIds.clearBit(pointerId);
            touchedWindow.pilferedPointerIds.reset(pointerId);
            return;
            return;
        }
        }
    }
    }
@@ -137,11 +139,40 @@ void TouchState::cancelPointersForWindowsExcept(const BitSet32 pointerIds,
    std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
    std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
}
}


void TouchState::cancelPointersForNonPilferingWindows(const BitSet32 pointerIds) {
/**
    if (pointerIds.isEmpty()) return;
 * For any pointer that's being pilfered, remove it from all of the other windows that currently
    std::for_each(windows.begin(), windows.end(), [&pointerIds](TouchedWindow& w) {
 * aren't pilfering it. For example, if we determined that pointer 1 is going to both window A and
        if (!w.isPilferingPointers) {
 * window B, but window A is currently pilfering pointer 1, then pointer 1 should not go to window
            w.pointerIds &= BitSet32(~pointerIds.value);
 * B.
 */
void TouchState::cancelPointersForNonPilferingWindows() {
    // First, find all pointers that are being pilfered, across all windows
    std::bitset<MAX_POINTERS> allPilferedPointerIds;
    std::for_each(windows.begin(), windows.end(), [&allPilferedPointerIds](const TouchedWindow& w) {
        allPilferedPointerIds |= w.pilferedPointerIds;
    });

    // Optimization: most of the time, pilfering does not occur
    if (allPilferedPointerIds.none()) return;

    // Now, remove all pointers from every window that's being pilfered by other windows.
    // For example, if window A is pilfering pointer 1 (only), and window B is pilfering pointer 2
    // (only), the remove pointer 2 from window A and pointer 1 from window B. Usually, the set of
    // pilfered pointers will be disjoint across all windows, but there's no reason to cause that
    // limitation here.
    std::for_each(windows.begin(), windows.end(), [&allPilferedPointerIds](TouchedWindow& w) {
        std::bitset<MAX_POINTERS> pilferedByOtherWindows =
                w.pilferedPointerIds ^ allPilferedPointerIds;
        // TODO(b/211379801) : convert pointerIds to use std::bitset, which would allow us to
        // replace the loop below with a bitwise operation. Currently, the XOR operation above is
        // redundant, but is done to make the code more explicit / easier to convert later.
        for (std::size_t i = 0; i < pilferedByOtherWindows.size(); i++) {
            if (pilferedByOtherWindows.test(i) && !w.pilferedPointerIds.test(i)) {
                // Pointer is pilfered by other windows, but not by this one! Remove it from here.
                // We could call 'removeTouchedPointerFromWindow' here, but it's faster to directly
                // manipulate it.
                w.pointerIds.clearBit(i);
            }
        }
        }
    });
    });
    std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
    std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
+1 −1
Original line number Original line Diff line number Diff line
@@ -59,7 +59,7 @@ struct TouchState {
    void cancelPointersForWindowsExcept(const BitSet32 pointerIds, const sp<IBinder>& token);
    void cancelPointersForWindowsExcept(const BitSet32 pointerIds, const sp<IBinder>& token);
    // Cancel pointers for current set of non-pilfering windows i.e. windows with isPilferingWindow
    // Cancel pointers for current set of non-pilfering windows i.e. windows with isPilferingWindow
    // set to false.
    // set to false.
    void cancelPointersForNonPilferingWindows(const BitSet32 pointerIds);
    void cancelPointersForNonPilferingWindows();


    sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
    sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
    bool isSlippery() const;
    bool isSlippery() const;
+2 −2
Original line number Original line Diff line number Diff line
@@ -63,10 +63,10 @@ std::string TouchedWindow::dump() const {
    std::string hoveringPointers =
    std::string hoveringPointers =
            dumpMap(mHoveringPointerIdsByDevice, constToString, bitsetToString);
            dumpMap(mHoveringPointerIdsByDevice, constToString, bitsetToString);
    out += StringPrintf("name='%s', pointerIds=0x%0x, targetFlags=%s, firstDownTimeInTarget=%s, "
    out += StringPrintf("name='%s', pointerIds=0x%0x, targetFlags=%s, firstDownTimeInTarget=%s, "
                        "mHoveringPointerIdsByDevice=%s\n",
                        "mHoveringPointerIdsByDevice=%s, pilferedPointerIds=%s\n",
                        windowHandle->getName().c_str(), pointerIds.value,
                        windowHandle->getName().c_str(), pointerIds.value,
                        targetFlags.string().c_str(), toString(firstDownTimeInTarget).c_str(),
                        targetFlags.string().c_str(), toString(firstDownTimeInTarget).c_str(),
                        hoveringPointers.c_str());
                        hoveringPointers.c_str(), bitsetToString(pilferedPointerIds).c_str());
    return out;
    return out;
}
}


+2 −1
Original line number Original line Diff line number Diff line
@@ -31,7 +31,8 @@ struct TouchedWindow {
    sp<gui::WindowInfoHandle> windowHandle;
    sp<gui::WindowInfoHandle> windowHandle;
    ftl::Flags<InputTarget::Flags> targetFlags;
    ftl::Flags<InputTarget::Flags> targetFlags;
    BitSet32 pointerIds;
    BitSet32 pointerIds;
    bool isPilferingPointers = false;
    // The pointer ids of the pointers that this window is currently pilfering
    std::bitset<MAX_POINTERS> pilferedPointerIds;
    // Time at which the first action down occurred on this window.
    // Time at which the first action down occurred on this window.
    // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE scenario.
    // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE scenario.
    std::optional<nsecs_t> firstDownTimeInTarget;
    std::optional<nsecs_t> firstDownTimeInTarget;
Loading