Loading include/input/Input.h +9 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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 Loading include/input/InputEventBuilders.h +14 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; } Loading @@ -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; }; Loading libs/input/Input.cpp +100 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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, Loading libs/input/tests/InputEvent_test.cpp +152 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <gtest/gtest.h> #include <gui/constants.h> #include <input/Input.h> #include <input/InputEventBuilders.h> namespace android { Loading @@ -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, Loading Loading @@ -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); Loading services/inputflinger/dispatcher/InputDispatcher.cpp +7 −66 Original line number Diff line number Diff line Loading @@ -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 " Loading Loading @@ -4361,7 +4302,7 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( originalMotionEntry.yPrecision, originalMotionEntry.xCursorPosition, originalMotionEntry.yCursorPosition, splitDownTime, splitPointerProperties, splitPointerCoords); pointerProperties, pointerCoords); return splitMotionEntry; } Loading Loading
include/input/Input.h +9 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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 Loading
include/input/InputEventBuilders.h +14 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; } Loading @@ -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; }; Loading
libs/input/Input.cpp +100 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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, Loading
libs/input/tests/InputEvent_test.cpp +152 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <gtest/gtest.h> #include <gui/constants.h> #include <input/Input.h> #include <input/InputEventBuilders.h> namespace android { Loading @@ -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, Loading Loading @@ -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); Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +7 −66 Original line number Diff line number Diff line Loading @@ -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 " Loading Loading @@ -4361,7 +4302,7 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( originalMotionEntry.yPrecision, originalMotionEntry.xCursorPosition, originalMotionEntry.yCursorPosition, splitDownTime, splitPointerProperties, splitPointerCoords); pointerProperties, pointerCoords); return splitMotionEntry; } Loading