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

Commit f155b3ed authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

Input injection: Assume transformed values are in logical display space

Previously, we assumed that, for injected MotionEvents, the values in
the PointerCoords were in logical display space. This is not always
true, as the PointerCoords for events generated by dispatcher are in the
display space, and are only in the logical display space once their
transform is applied.

In this CL we assume that the transformed PointerCoords values are in
the logical display space before converting it to the display space.

Additionally, we set the offset values to 0, because they are now
already included in the transform.

Bug: 206842332
Test: atest inputflinger_tests
Test: manual with accessibility over (e.g. Magnification) in different
orientations

Change-Id: I65c284e5e00ed7c1b60b31269e16ba6f045071c2
parent 20ca6625
Loading
Loading
Loading
Loading
+20 −35
Original line number Diff line number Diff line
@@ -506,12 +506,6 @@ bool isConnectionResponsive(const Connection& connection) {
    return true;
}

vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) {
    const vec2 transformedXy = transform.transform(x, y);
    const vec2 transformedOrigin = transform.transform(0, 0);
    return transformedXy - transformedOrigin;
}

// Returns true if the event type passed as argument represents a user activity.
bool isUserActivityEvent(const EventEntry& eventEntry) {
    switch (eventEntry.type) {
@@ -4214,10 +4208,8 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(
                                                  motionEvent.getRawXCursorPosition(),
                                                  motionEvent.getRawYCursorPosition(),
                                                  motionEvent.getDownTime(), uint32_t(pointerCount),
                                                  pointerProperties, samplePointerCoords,
                                                  motionEvent.getXOffset(),
                                                  motionEvent.getYOffset());
            transformMotionEntryForInjectionLocked(*injectedEntry);
                                                  pointerProperties, samplePointerCoords, 0, 0);
            transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform());
            injectedEntries.push(std::move(injectedEntry));
            for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
                sampleEventTimes += 1;
@@ -4236,9 +4228,9 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(
                                                      motionEvent.getRawYCursorPosition(),
                                                      motionEvent.getDownTime(),
                                                      uint32_t(pointerCount), pointerProperties,
                                                      samplePointerCoords, motionEvent.getXOffset(),
                                                      motionEvent.getYOffset());
                transformMotionEntryForInjectionLocked(*nextInjectedEntry);
                                                      samplePointerCoords, 0, 0);
                transformMotionEntryForInjectionLocked(*nextInjectedEntry,
                                                       motionEvent.getTransform());
                injectedEntries.push(std::move(nextInjectedEntry));
            }
            break;
@@ -4402,35 +4394,28 @@ void InputDispatcher::setInjectionResult(EventEntry& entry,
    }
}

void InputDispatcher::transformMotionEntryForInjectionLocked(MotionEntry& entry) const {
    const bool isRelativeMouseEvent = isFromSource(entry.source, AINPUT_SOURCE_MOUSE_RELATIVE);
    if (!isRelativeMouseEvent && !isFromSource(entry.source, AINPUT_SOURCE_CLASS_POINTER)) {
        return;
    }

void InputDispatcher::transformMotionEntryForInjectionLocked(
        MotionEntry& entry, const ui::Transform& injectedTransform) const {
    // Input injection works in the logical display coordinate space, but the input pipeline works
    // display space, so we need to transform the injected events accordingly.
    const auto it = mDisplayInfos.find(entry.displayId);
    if (it == mDisplayInfos.end()) return;
    const auto& transformToDisplay = it->second.transform.inverse();
    const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform;

    for (uint32_t i = 0; i < entry.pointerCount; i++) {
        PointerCoords& pc = entry.pointerCoords[i];
        const auto xy = isRelativeMouseEvent
                ? transformWithoutTranslation(transformToDisplay, pc.getX(), pc.getY())
                : transformToDisplay.transform(pc.getXYValue());
        pc.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
        pc.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);

        // Axes with relative values never represent points on a screen, so they should never have
        // translation applied. If a device does not report relative values, these values are always
        // 0, and will remain unaffected by the following operation.
        const auto rel =
                transformWithoutTranslation(transformToDisplay,
                                            pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
                                            pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
        pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, rel.x);
        pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, rel.y);
        // Make a copy of the injected coords. We cannot change them in place because some of them
        // are interdependent (for example, X coordinate might depend on the Y coordinate).
        PointerCoords injectedCoords = entry.pointerCoords[i];

        BitSet64 bits(injectedCoords.bits);
        while (!bits.isEmpty()) {
            const auto axis = static_cast<int32_t>(bits.clearFirstMarkedBit());
            const float value =
                    MotionEvent::calculateTransformedAxisValue(axis, entry.source,
                                                               transformToDisplay, injectedCoords);
            pc.setAxisValue(axis, value);
        }
    }
}

+3 −1
Original line number Diff line number Diff line
@@ -280,7 +280,9 @@ private:
    bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
    void setInjectionResult(EventEntry& entry,
                            android::os::InputEventInjectionResult injectionResult);
    void transformMotionEntryForInjectionLocked(MotionEntry&) const REQUIRES(mLock);
    void transformMotionEntryForInjectionLocked(MotionEntry&,
                                                const ui::Transform& injectedTransform) const
            REQUIRES(mLock);

    std::condition_variable mInjectionSyncFinished;
    void incrementPendingForegroundDispatches(EventEntry& entry);
+27 −0
Original line number Diff line number Diff line
@@ -2079,6 +2079,33 @@ TEST_F(InputDispatcherDisplayProjectionTest, InjectionInLogicalDisplaySpace) {
    secondWindow->assertNoEvents();
}

// Ensure that when a MotionEvent that has a custom transform is injected, the post-transformed
// event should be treated as being in the logical display space.
TEST_F(InputDispatcherDisplayProjectionTest, InjectionWithTransformInLogicalDisplaySpace) {
    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();

    const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
    ui::Transform injectedEventTransform;
    injectedEventTransform.set(matrix);
    const vec2 expectedPoint{75, 55}; // The injected point in the logical display space.
    const vec2 untransformedPoint = injectedEventTransform.inverse().transform(expectedPoint);

    MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                                .displayId(ADISPLAY_ID_DEFAULT)
                                .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
                                .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
                                                 .x(untransformedPoint.x)
                                                 .y(untransformedPoint.y))
                                .build();
    event.transform(matrix);

    injectMotionEvent(mDispatcher, event, INJECT_EVENT_TIMEOUT,
                      InputEventInjectionSync::WAIT_FOR_RESULT);

    firstWindow->consumeMotionDown();
    secondWindow->assertNoEvents();
}

TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinateSpace) {
    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();