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

Commit 7eb8cb82 authored by Prabir Pradhan's avatar Prabir Pradhan Committed by Android (Google) Code Review
Browse files

Merge "Move MotionEvent#split implementation to native" into main

parents 6f39b79a bf9b0a85
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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
+14 −3
Original line number Diff line number Diff line
@@ -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;
};
+100 −0
Original line number Diff line number Diff line
@@ -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,
+152 −0
Original line number Diff line number Diff line
@@ -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);
+7 −66
Original line number Diff line number Diff line
@@ -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;
}