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

Commit ff798f3a authored by Vaibhav Devmurari's avatar Vaibhav Devmurari
Browse files

Modify pilferpointers API to selectively cancel pointers

Behavior changes:
- Pilfer pointers only cancels relevant pointers (If a pointer
is used by the window calling pilferPointers(), only then,
we need to send ACTION_CANCEL/POINTER_UP to other windows using
that particular pointer)
- Any pointers not used by the window calling pilferPointers()
will remain unaffected and the windows using them will keep
receiving the events
- Any future Pointer down events will go to other windows if and
only if the "the pilfering window" that called pilferPointers() do
not need that pointer for its gesture detection (the pointer down
occurs outside the touchable bounds of the pilfering window)

Bug: 220109830
Test: atest inputflinger_tests

Change-Id: Idc59ae06352560fefd7274bc043bd6e72d5e7710
parent aca9f3c0
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -17,9 +17,11 @@
#ifndef _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
#define _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H

#include <utils/BitSet.h>
#include <optional>

namespace android::inputdispatcher {
namespace android {
namespace inputdispatcher {

/* Specifies which events are to be canceled and why. */
struct CancelationOptions {
@@ -45,9 +47,13 @@ struct CancelationOptions {
    // The specific display id of events to cancel, or nullopt to cancel events on any display.
    std::optional<int32_t> displayId = std::nullopt;

    // The specific pointers to cancel, or nullopt to cancel all pointer events
    std::optional<BitSet32> pointerIds = std::nullopt;

    CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) {}
};

} // namespace android::inputdispatcher
} // namespace inputdispatcher
} // namespace android

#endif // _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H
+24 −5
Original line number Diff line number Diff line
@@ -2195,6 +2195,15 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(

            tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds);
        }

        // If any existing window is pilfering pointers from newly added window, remove it
        BitSet32 canceledPointers = BitSet32(0);
        for (const TouchedWindow& window : tempTouchState.windows) {
            if (window.isPilferingPointers) {
                canceledPointers |= window.pointerIds;
            }
        }
        tempTouchState.cancelPointersForNonPilferingWindows(canceledPointers);
    } else {
        /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */

@@ -5579,16 +5588,20 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) {
    }

    TouchState& state = *statePtr;

    TouchedWindow& window = *windowPtr;
    // Send cancel events to all the input channels we're stealing from.
    CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                               "input channel stole pointer stream");
    options.deviceId = state.deviceId;
    options.displayId = state.displayId;
    if (state.split) {
        // If split pointers then selectively cancel pointers otherwise cancel all pointers
        options.pointerIds = window.pointerIds;
    }
    std::string canceledWindows;
    for (const TouchedWindow& window : state.windows) {
    for (const TouchedWindow& w : state.windows) {
        const std::shared_ptr<InputChannel> channel =
                getInputChannelLocked(window.windowHandle->getToken());
                getInputChannelLocked(w.windowHandle->getToken());
        if (channel != nullptr && channel->getConnectionToken() != token) {
            synthesizeCancelationEventsForInputChannelLocked(channel, options);
            canceledWindows += canceledWindows.empty() ? "[" : ", ";
@@ -5600,8 +5613,14 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) {
          canceledWindows.c_str());

    // Prevent the gesture from being sent to any other windows.
    // This only blocks relevant pointers to be sent to other windows
    window.isPilferingPointers = true;

    if (state.split) {
        state.cancelPointersForWindowsExcept(window.pointerIds, token);
    } else {
        state.filterWindowsExcept(token);
    state.preventNewTargets = true;
    }
    return OK;
}

+91 −13
Original line number Diff line number Diff line
@@ -286,19 +286,30 @@ std::vector<std::unique_ptr<EventEntry>> InputState::synthesizeCancelationEvents

    for (const MotionMemento& memento : mMotionMementos) {
        if (shouldCancelMotion(memento, options)) {
            if (options.pointerIds == std::nullopt) {
                const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
                                                        : AMOTION_EVENT_ACTION_CANCEL;
                events.push_back(
                        std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
                                                      memento.deviceId, memento.source,
                                                  memento.displayId, memento.policyFlags, action,
                                                  0 /*actionButton*/, memento.flags, AMETA_NONE,
                                                  0 /*buttonState*/, MotionClassification::NONE,
                                                  AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
                                                  memento.yPrecision, memento.xCursorPosition,
                                                      memento.displayId, memento.policyFlags,
                                                      action, 0 /*actionButton*/, memento.flags,
                                                      AMETA_NONE, 0 /*buttonState*/,
                                                      MotionClassification::NONE,
                                                      AMOTION_EVENT_EDGE_FLAG_NONE,
                                                      memento.xPrecision, memento.yPrecision,
                                                      memento.xCursorPosition,
                                                      memento.yCursorPosition, memento.downTime,
                                                  memento.pointerCount, memento.pointerProperties,
                                                      memento.pointerCount,
                                                      memento.pointerProperties,
                                                      memento.pointerCoords));
            } else {
                std::vector<std::unique_ptr<MotionEntry>> pointerCancelEvents =
                        synthesizeCancelationEventsForPointers(memento, options.pointerIds.value(),
                                                               currentTime);
                events.insert(events.end(), std::make_move_iterator(pointerCancelEvents.begin()),
                              std::make_move_iterator(pointerCancelEvents.end()));
            }
        }
    }
    return events;
@@ -359,6 +370,73 @@ std::vector<std::unique_ptr<EventEntry>> InputState::synthesizePointerDownEvents
    return events;
}

std::vector<std::unique_ptr<MotionEntry>> InputState::synthesizeCancelationEventsForPointers(
        const MotionMemento& memento, const BitSet32 pointerIds, nsecs_t currentTime) {
    std::vector<std::unique_ptr<MotionEntry>> events;
    std::vector<uint32_t> canceledPointerIndices;
    std::vector<PointerProperties> pointerProperties(MAX_POINTERS);
    std::vector<PointerCoords> pointerCoords(MAX_POINTERS);
    for (uint32_t pointerIdx = 0; pointerIdx < memento.pointerCount; pointerIdx++) {
        uint32_t pointerId = uint32_t(memento.pointerProperties[pointerIdx].id);
        pointerProperties[pointerIdx].copyFrom(memento.pointerProperties[pointerIdx]);
        pointerCoords[pointerIdx].copyFrom(memento.pointerCoords[pointerIdx]);
        if (pointerIds.hasBit(pointerId)) {
            canceledPointerIndices.push_back(pointerIdx);
        }
    }

    if (canceledPointerIndices.size() == memento.pointerCount) {
        const int32_t action =
                memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL;
        events.push_back(
                std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId,
                                              memento.source, memento.displayId,
                                              memento.policyFlags, action, 0 /*actionButton*/,
                                              memento.flags, AMETA_NONE, 0 /*buttonState*/,
                                              MotionClassification::NONE,
                                              AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
                                              memento.yPrecision, memento.xCursorPosition,
                                              memento.yCursorPosition, memento.downTime,
                                              memento.pointerCount, memento.pointerProperties,
                                              memento.pointerCoords));
    } else {
        // If we aren't canceling all pointers, we need to generated ACTION_POINTER_UP with
        // FLAG_CANCELED for each of the canceled pointers. For each event, we must remove the
        // previously canceled pointers from PointerProperties and PointerCoords, and update
        // pointerCount appropriately. For convenience, sort the canceled pointer indices so that we
        // can just slide the remaining pointers to the beginning of the array when a pointer is
        // canceled.
        std::sort(canceledPointerIndices.begin(), canceledPointerIndices.end(),
                  std::greater<uint32_t>());

        uint32_t pointerCount = memento.pointerCount;
        for (const uint32_t pointerIdx : canceledPointerIndices) {
            const int32_t action = pointerCount == 1 ? AMOTION_EVENT_ACTION_CANCEL
                                                     : AMOTION_EVENT_ACTION_POINTER_UP |
                            (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
            events.push_back(
                    std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
                                                  memento.deviceId, memento.source,
                                                  memento.displayId, memento.policyFlags, action,
                                                  0 /*actionButton*/,
                                                  memento.flags | AMOTION_EVENT_FLAG_CANCELED,
                                                  AMETA_NONE, 0 /*buttonState*/,
                                                  MotionClassification::NONE,
                                                  AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
                                                  memento.yPrecision, memento.xCursorPosition,
                                                  memento.yCursorPosition, memento.downTime,
                                                  pointerCount, pointerProperties.data(),
                                                  pointerCoords.data()));

            // Cleanup pointer information
            pointerProperties.erase(pointerProperties.begin() + pointerIdx);
            pointerCoords.erase(pointerCoords.begin() + pointerIdx);
            pointerCount--;
        }
    }
    return events;
}

void InputState::clear() {
    mKeyMementos.clear();
    mMotionMementos.clear();
+8 −2
Original line number Diff line number Diff line
@@ -22,7 +22,8 @@

#include <utils/Timers.h>

namespace android::inputdispatcher {
namespace android {
namespace inputdispatcher {

static constexpr int32_t INVALID_POINTER_INDEX = -1;

@@ -125,8 +126,13 @@ private:

    static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options);
    static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options);

    // Synthesizes pointer cancel events for a particular set of pointers.
    std::vector<std::unique_ptr<MotionEntry>> synthesizeCancelationEventsForPointers(
            const MotionMemento& memento, const BitSet32 pointerIds, nsecs_t currentTime);
};

} // namespace android::inputdispatcher
} // namespace inputdispatcher
} // namespace android

#endif // _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H
+21 −2
Original line number Diff line number Diff line
@@ -47,8 +47,6 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int
        }
    }

    if (preventNewTargets) return; // Don't add new TouchedWindows.

    TouchedWindow touchedWindow;
    touchedWindow.windowHandle = windowHandle;
    touchedWindow.targetFlags = targetFlags;
@@ -79,6 +77,27 @@ void TouchState::filterNonAsIsTouchWindows() {
    }
}

void TouchState::cancelPointersForWindowsExcept(const BitSet32 pointerIds,
                                                const sp<IBinder>& token) {
    if (pointerIds.isEmpty()) return;
    std::for_each(windows.begin(), windows.end(), [&pointerIds, &token](TouchedWindow& w) {
        if (w.windowHandle->getToken() != token) {
            w.pointerIds &= BitSet32(~pointerIds.value);
        }
    });
    std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
}

void TouchState::cancelPointersForNonPilferingWindows(const BitSet32 pointerIds) {
    if (pointerIds.isEmpty()) return;
    std::for_each(windows.begin(), windows.end(), [&pointerIds](TouchedWindow& w) {
        if (!w.isPilferingPointers) {
            w.pointerIds &= BitSet32(~pointerIds.value);
        }
    });
    std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
}

void TouchState::filterWindowsExcept(const sp<IBinder>& token) {
    std::erase_if(windows,
                  [&token](const TouchedWindow& w) { return w.windowHandle->getToken() != token; });
Loading