Loading services/inputflinger/dispatcher/CancelationOptions.h +8 −2 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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 services/inputflinger/dispatcher/InputDispatcher.cpp +24 −5 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -5581,16 +5590,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() ? "[" : ", "; Loading @@ -5602,8 +5615,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; } Loading services/inputflinger/dispatcher/InputState.cpp +91 −13 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading services/inputflinger/dispatcher/InputState.h +8 −2 Original line number Diff line number Diff line Loading @@ -22,7 +22,8 @@ #include <utils/Timers.h> namespace android::inputdispatcher { namespace android { namespace inputdispatcher { static constexpr int32_t INVALID_POINTER_INDEX = -1; Loading Loading @@ -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 services/inputflinger/dispatcher/TouchState.cpp +21 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading
services/inputflinger/dispatcher/CancelationOptions.h +8 −2 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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
services/inputflinger/dispatcher/InputDispatcher.cpp +24 −5 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -5581,16 +5590,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() ? "[" : ", "; Loading @@ -5602,8 +5615,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; } Loading
services/inputflinger/dispatcher/InputState.cpp +91 −13 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading
services/inputflinger/dispatcher/InputState.h +8 −2 Original line number Diff line number Diff line Loading @@ -22,7 +22,8 @@ #include <utils/Timers.h> namespace android::inputdispatcher { namespace android { namespace inputdispatcher { static constexpr int32_t INVALID_POINTER_INDEX = -1; Loading Loading @@ -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
services/inputflinger/dispatcher/TouchState.cpp +21 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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