diff options
-rw-r--r-- | include/input/Input.h | 9 | ||||
-rw-r--r-- | include/input/InputEventBuilders.h | 17 | ||||
-rw-r--r-- | libs/input/Input.cpp | 100 | ||||
-rw-r--r-- | libs/input/tests/InputEvent_test.cpp | 152 | ||||
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 73 |
5 files changed, 282 insertions, 69 deletions
diff --git a/include/input/Input.h b/include/input/Input.h index a84dcfc63c..374254fc84 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -870,6 +870,10 @@ public: void copyFrom(const MotionEvent* other, bool keepHistory); + // Initialize this event by keeping only the pointers from "other" that are in splitPointerIds. + void splitFrom(const MotionEvent& other, std::bitset<MAX_POINTER_ID + 1> splitPointerIds, + int32_t newEventId); + void addSample( nsecs_t eventTime, const PointerCoords* pointerCoords); @@ -910,6 +914,11 @@ public: static std::string actionToString(int32_t action); + static std::tuple<int32_t /*action*/, std::vector<PointerProperties>, + std::vector<PointerCoords>> + split(int32_t action, int32_t flags, int32_t historySize, const std::vector<PointerProperties>&, + const std::vector<PointerCoords>&, std::bitset<MAX_POINTER_ID + 1> splitPointerIds); + // MotionEvent will transform various axes in different ways, based on the source. For // example, the x and y axes will not have any offsets/translations applied if it comes from a // relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h index 2d23b97386..c0c5e2412d 100644 --- a/include/input/InputEventBuilders.h +++ b/include/input/InputEventBuilders.h @@ -118,6 +118,16 @@ public: return *this; } + MotionEventBuilder& transform(ui::Transform t) { + mTransform = t; + return *this; + } + + MotionEventBuilder& rawTransform(ui::Transform t) { + mRawTransform = t; + return *this; + } + MotionEvent build() { std::vector<PointerProperties> pointerProperties; std::vector<PointerCoords> pointerCoords; @@ -134,12 +144,11 @@ public: } MotionEvent event; - static const ui::Transform kIdentityTransform; event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC, mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState, - MotionClassification::NONE, kIdentityTransform, + MotionClassification::NONE, mTransform, /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition, - mRawYCursorPosition, kIdentityTransform, mDownTime, mEventTime, + mRawYCursorPosition, mRawTransform, mDownTime, mEventTime, mPointers.size(), pointerProperties.data(), pointerCoords.data()); return event; } @@ -156,6 +165,8 @@ private: int32_t mFlags{0}; float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; + ui::Transform mTransform; + ui::Transform mRawTransform; std::vector<PointerBuilder> mPointers; }; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 9e0ce1db33..d58fb42f05 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -60,6 +60,45 @@ bool shouldDisregardOffset(uint32_t source) { return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER); } +int32_t resolveActionForSplitMotionEvent( + int32_t action, int32_t flags, const std::vector<PointerProperties>& pointerProperties, + const std::vector<PointerProperties>& splitPointerProperties) { + LOG_ALWAYS_FATAL_IF(splitPointerProperties.empty()); + const auto maskedAction = MotionEvent::getActionMasked(action); + if (maskedAction != AMOTION_EVENT_ACTION_POINTER_DOWN && + maskedAction != AMOTION_EVENT_ACTION_POINTER_UP) { + // The action is unaffected by splitting this motion event. + return action; + } + const auto actionIndex = MotionEvent::getActionIndex(action); + if (CC_UNLIKELY(actionIndex >= pointerProperties.size())) { + LOG(FATAL) << "Action index is out of bounds, index: " << actionIndex; + } + + const auto affectedPointerId = pointerProperties[actionIndex].id; + std::optional<uint32_t> splitActionIndex; + for (uint32_t i = 0; i < splitPointerProperties.size(); i++) { + if (affectedPointerId == splitPointerProperties[i].id) { + splitActionIndex = i; + break; + } + } + if (!splitActionIndex.has_value()) { + // The affected pointer is not part of the split motion event. + return AMOTION_EVENT_ACTION_MOVE; + } + + if (splitPointerProperties.size() > 1) { + return maskedAction | (*splitActionIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + } + + if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { + return ((flags & AMOTION_EVENT_FLAG_CANCELED) != 0) ? AMOTION_EVENT_ACTION_CANCEL + : AMOTION_EVENT_ACTION_UP; + } + return AMOTION_EVENT_ACTION_DOWN; +} + } // namespace const char* motionClassificationToString(MotionClassification classification) { @@ -584,6 +623,28 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { } } +void MotionEvent::splitFrom(const android::MotionEvent& other, + std::bitset<MAX_POINTER_ID + 1> splitPointerIds, int32_t newEventId) { + // TODO(b/327503168): The down time should be a parameter to the split function, because only + // the caller can know when the first event went down on the target. + const nsecs_t splitDownTime = other.mDownTime; + + auto [action, pointerProperties, pointerCoords] = + split(other.getAction(), other.getFlags(), other.getHistorySize(), + other.mPointerProperties, other.mSamplePointerCoords, splitPointerIds); + + // Initialize the event with zero pointers, and manually set the split pointers. + initialize(newEventId, other.mDeviceId, other.mSource, other.mDisplayId, /*hmac=*/{}, action, + other.mActionButton, other.mFlags, other.mEdgeFlags, other.mMetaState, + other.mButtonState, other.mClassification, other.mTransform, other.mXPrecision, + other.mYPrecision, other.mRawXCursorPosition, other.mRawYCursorPosition, + other.mRawTransform, splitDownTime, other.getEventTime(), /*pointerCount=*/0, + pointerProperties.data(), pointerCoords.data()); + mPointerProperties = std::move(pointerProperties); + mSamplePointerCoords = std::move(pointerCoords); + mSampleEventTimes = other.mSampleEventTimes; +} + void MotionEvent::addSample( int64_t eventTime, const PointerCoords* pointerCoords) { @@ -934,6 +995,45 @@ std::string MotionEvent::actionToString(int32_t action) { return android::base::StringPrintf("%" PRId32, action); } +std::tuple<int32_t, std::vector<PointerProperties>, std::vector<PointerCoords>> MotionEvent::split( + int32_t action, int32_t flags, int32_t historySize, + const std::vector<PointerProperties>& pointerProperties, + const std::vector<PointerCoords>& pointerCoords, + std::bitset<MAX_POINTER_ID + 1> splitPointerIds) { + LOG_ALWAYS_FATAL_IF(!splitPointerIds.any()); + const auto pointerCount = pointerProperties.size(); + LOG_ALWAYS_FATAL_IF(pointerCoords.size() != (pointerCount * (historySize + 1))); + const auto splitCount = splitPointerIds.count(); + + std::vector<PointerProperties> splitPointerProperties; + std::vector<PointerCoords> splitPointerCoords; + + for (uint32_t i = 0; i < pointerCount; i++) { + if (splitPointerIds.test(pointerProperties[i].id)) { + splitPointerProperties.emplace_back(pointerProperties[i]); + } + } + for (uint32_t i = 0; i < pointerCoords.size(); i++) { + if (splitPointerIds.test(pointerProperties[i % pointerCount].id)) { + splitPointerCoords.emplace_back(pointerCoords[i]); + } + } + LOG_ALWAYS_FATAL_IF(splitPointerCoords.size() != + (splitPointerProperties.size() * (historySize + 1))); + + if (CC_UNLIKELY(splitPointerProperties.size() != splitCount)) { + LOG(FATAL) << "Cannot split MotionEvent: Requested splitting " << splitCount + << " pointers from the original event, but the original event only contained " + << splitPointerProperties.size() << " of those pointers."; + } + + // TODO(b/327503168): Verify the splitDownTime here once it is used correctly. + + const auto splitAction = resolveActionForSplitMotionEvent(action, flags, pointerProperties, + splitPointerProperties); + return {splitAction, splitPointerProperties, splitPointerCoords}; +} + // Apply the given transformation to the point without checking whether the entire transform // should be disregarded altogether for the provided source. static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform, diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index a9655730fc..540766d66c 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -23,6 +23,7 @@ #include <gtest/gtest.h> #include <gui/constants.h> #include <input/Input.h> +#include <input/InputEventBuilders.h> namespace android { @@ -31,6 +32,18 @@ static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; +static constexpr auto POINTER_0_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + +static constexpr auto POINTER_1_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + +static constexpr auto POINTER_0_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + +static constexpr auto POINTER_1_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + class BaseTest : public testing::Test { protected: static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, @@ -554,6 +567,145 @@ TEST_F(MotionEventTest, CopyFrom_DoNotKeepHistory) { ASSERT_EQ(event.getX(0), copy.getX(0)); } +TEST_F(MotionEventTest, SplitPointerDown) { + MotionEvent event = MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6)) + .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8)) + .build(); + + MotionEvent splitDown; + std::bitset<MAX_POINTER_ID + 1> splitDownIds{}; + splitDownIds.set(6, true); + splitDown.splitFrom(event, splitDownIds, /*eventId=*/42); + ASSERT_EQ(splitDown.getAction(), AMOTION_EVENT_ACTION_DOWN); + ASSERT_EQ(splitDown.getPointerCount(), 1u); + ASSERT_EQ(splitDown.getPointerId(0), 6); + ASSERT_EQ(splitDown.getX(0), 6); + ASSERT_EQ(splitDown.getY(0), 6); + + MotionEvent splitPointerDown; + std::bitset<MAX_POINTER_ID + 1> splitPointerDownIds{}; + splitPointerDownIds.set(6, true); + splitPointerDownIds.set(8, true); + splitPointerDown.splitFrom(event, splitPointerDownIds, /*eventId=*/42); + ASSERT_EQ(splitPointerDown.getAction(), POINTER_0_DOWN); + ASSERT_EQ(splitPointerDown.getPointerCount(), 2u); + ASSERT_EQ(splitPointerDown.getPointerId(0), 6); + ASSERT_EQ(splitPointerDown.getX(0), 6); + ASSERT_EQ(splitPointerDown.getY(0), 6); + ASSERT_EQ(splitPointerDown.getPointerId(1), 8); + ASSERT_EQ(splitPointerDown.getX(1), 8); + ASSERT_EQ(splitPointerDown.getY(1), 8); + + MotionEvent splitMove; + std::bitset<MAX_POINTER_ID + 1> splitMoveIds{}; + splitMoveIds.set(4, true); + splitMove.splitFrom(event, splitMoveIds, /*eventId=*/43); + ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE); + ASSERT_EQ(splitMove.getPointerCount(), 1u); + ASSERT_EQ(splitMove.getPointerId(0), 4); + ASSERT_EQ(splitMove.getX(0), 4); + ASSERT_EQ(splitMove.getY(0), 4); +} + +TEST_F(MotionEventTest, SplitPointerUp) { + MotionEvent event = MotionEventBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6)) + .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8)) + .build(); + + MotionEvent splitUp; + std::bitset<MAX_POINTER_ID + 1> splitUpIds{}; + splitUpIds.set(4, true); + splitUp.splitFrom(event, splitUpIds, /*eventId=*/42); + ASSERT_EQ(splitUp.getAction(), AMOTION_EVENT_ACTION_UP); + ASSERT_EQ(splitUp.getPointerCount(), 1u); + ASSERT_EQ(splitUp.getPointerId(0), 4); + ASSERT_EQ(splitUp.getX(0), 4); + ASSERT_EQ(splitUp.getY(0), 4); + + MotionEvent splitPointerUp; + std::bitset<MAX_POINTER_ID + 1> splitPointerUpIds{}; + splitPointerUpIds.set(4, true); + splitPointerUpIds.set(8, true); + splitPointerUp.splitFrom(event, splitPointerUpIds, /*eventId=*/42); + ASSERT_EQ(splitPointerUp.getAction(), POINTER_0_UP); + ASSERT_EQ(splitPointerUp.getPointerCount(), 2u); + ASSERT_EQ(splitPointerUp.getPointerId(0), 4); + ASSERT_EQ(splitPointerUp.getX(0), 4); + ASSERT_EQ(splitPointerUp.getY(0), 4); + ASSERT_EQ(splitPointerUp.getPointerId(1), 8); + ASSERT_EQ(splitPointerUp.getX(1), 8); + ASSERT_EQ(splitPointerUp.getY(1), 8); + + MotionEvent splitMove; + std::bitset<MAX_POINTER_ID + 1> splitMoveIds{}; + splitMoveIds.set(6, true); + splitMoveIds.set(8, true); + splitMove.splitFrom(event, splitMoveIds, /*eventId=*/43); + ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE); + ASSERT_EQ(splitMove.getPointerCount(), 2u); + ASSERT_EQ(splitMove.getPointerId(0), 6); + ASSERT_EQ(splitMove.getX(0), 6); + ASSERT_EQ(splitMove.getY(0), 6); + ASSERT_EQ(splitMove.getPointerId(1), 8); + ASSERT_EQ(splitMove.getX(1), 8); + ASSERT_EQ(splitMove.getY(1), 8); +} + +TEST_F(MotionEventTest, SplitPointerUpCancel) { + MotionEvent event = MotionEventBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6)) + .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8)) + .addFlag(AMOTION_EVENT_FLAG_CANCELED) + .build(); + + MotionEvent splitUp; + std::bitset<MAX_POINTER_ID + 1> splitUpIds{}; + splitUpIds.set(6, true); + splitUp.splitFrom(event, splitUpIds, /*eventId=*/42); + ASSERT_EQ(splitUp.getAction(), AMOTION_EVENT_ACTION_CANCEL); + ASSERT_EQ(splitUp.getPointerCount(), 1u); + ASSERT_EQ(splitUp.getPointerId(0), 6); + ASSERT_EQ(splitUp.getX(0), 6); + ASSERT_EQ(splitUp.getY(0), 6); +} + +TEST_F(MotionEventTest, SplitPointerMove) { + MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6)) + .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8)) + .transform(ui::Transform(ui::Transform::ROT_90, 100, 100)) + .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50)) + .build(); + + MotionEvent splitMove; + std::bitset<MAX_POINTER_ID + 1> splitMoveIds{}; + splitMoveIds.set(4, true); + splitMoveIds.set(8, true); + splitMove.splitFrom(event, splitMoveIds, /*eventId=*/42); + ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE); + ASSERT_EQ(splitMove.getPointerCount(), 2u); + ASSERT_EQ(splitMove.getPointerId(0), 4); + ASSERT_EQ(splitMove.getX(0), event.getX(0)); + ASSERT_EQ(splitMove.getY(0), event.getY(0)); + ASSERT_EQ(splitMove.getRawX(0), event.getRawX(0)); + ASSERT_EQ(splitMove.getRawY(0), event.getRawY(0)); + ASSERT_EQ(splitMove.getPointerId(1), 8); + ASSERT_EQ(splitMove.getX(1), event.getX(2)); + ASSERT_EQ(splitMove.getY(1), event.getY(2)); + ASSERT_EQ(splitMove.getRawX(1), event.getRawX(2)); + ASSERT_EQ(splitMove.getRawY(1), event.getRawY(2)); +} + TEST_F(MotionEventTest, OffsetLocation) { MotionEvent event; initializeEventWithHistory(&event); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 8858f0caec..bedb681366 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -4268,72 +4268,13 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( const MotionEntry& originalMotionEntry, std::bitset<MAX_POINTER_ID + 1> pointerIds, nsecs_t splitDownTime) { - ALOG_ASSERT(pointerIds.any()); - - uint32_t splitPointerIndexMap[MAX_POINTERS]; - std::vector<PointerProperties> splitPointerProperties; - std::vector<PointerCoords> splitPointerCoords; - - uint32_t originalPointerCount = originalMotionEntry.getPointerCount(); - uint32_t splitPointerCount = 0; - - for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount; - originalPointerIndex++) { - const PointerProperties& pointerProperties = - originalMotionEntry.pointerProperties[originalPointerIndex]; - uint32_t pointerId = uint32_t(pointerProperties.id); - if (pointerIds.test(pointerId)) { - splitPointerIndexMap[splitPointerCount] = originalPointerIndex; - splitPointerProperties.push_back(pointerProperties); - splitPointerCoords.push_back(originalMotionEntry.pointerCoords[originalPointerIndex]); - splitPointerCount += 1; - } - } - - if (splitPointerCount != pointerIds.count()) { - // This is bad. We are missing some of the pointers that we expected to deliver. - // Most likely this indicates that we received an ACTION_MOVE events that has - // different pointer ids than we expected based on the previous ACTION_DOWN - // or ACTION_POINTER_DOWN events that caused us to decide to split the pointers - // in this way. - ALOGW("Dropping split motion event because the pointer count is %d but " - "we expected there to be %zu pointers. This probably means we received " - "a broken sequence of pointer ids from the input device: %s", - splitPointerCount, pointerIds.count(), originalMotionEntry.getDescription().c_str()); - return nullptr; - } - - int32_t action = originalMotionEntry.action; - int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; - if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN || - maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { - int32_t originalPointerIndex = MotionEvent::getActionIndex(action); - const PointerProperties& pointerProperties = - originalMotionEntry.pointerProperties[originalPointerIndex]; - uint32_t pointerId = uint32_t(pointerProperties.id); - if (pointerIds.test(pointerId)) { - if (pointerIds.count() == 1) { - // The first/last pointer went down/up. - action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN - ? AMOTION_EVENT_ACTION_DOWN - : (originalMotionEntry.flags & AMOTION_EVENT_FLAG_CANCELED) != 0 - ? AMOTION_EVENT_ACTION_CANCEL - : AMOTION_EVENT_ACTION_UP; - } else { - // A secondary pointer went down/up. - uint32_t splitPointerIndex = 0; - while (pointerId != uint32_t(splitPointerProperties[splitPointerIndex].id)) { - splitPointerIndex += 1; - } - action = maskedAction | - (splitPointerIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - } - } else { - // An unrelated pointer changed. - action = AMOTION_EVENT_ACTION_MOVE; - } - } + const auto& [action, pointerProperties, pointerCoords] = + MotionEvent::split(originalMotionEntry.action, originalMotionEntry.flags, + /*historySize=*/0, originalMotionEntry.pointerProperties, + originalMotionEntry.pointerCoords, pointerIds); + // TODO(b/327503168): Move this check inside MotionEvent::split once all callers handle it + // correctly. if (action == AMOTION_EVENT_ACTION_DOWN && splitDownTime != originalMotionEntry.eventTime) { logDispatchStateLocked(); LOG_ALWAYS_FATAL("Split motion event has mismatching downTime and eventTime for " @@ -4361,7 +4302,7 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( originalMotionEntry.yPrecision, originalMotionEntry.xCursorPosition, originalMotionEntry.yCursorPosition, splitDownTime, - splitPointerProperties, splitPointerCoords); + pointerProperties, pointerCoords); return splitMotionEntry; } |