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

Commit 81420cc4 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

InputDispatcherPolicy: Transform incoming/outgoing events

The InputDispatcherPolicy expects events to be in the logical display
space. When per-window-input-rotation is enabled, the dispatcher works
in the display space, so we need to transform events accordingly.

1. MotionEvents that are injected from the policy are in logical display
coordinates. We need to transform them to display space before
proceeding with injection.

2. When sending events to the policy to be filtered by the InputFilter,
we need to include the display transform so that the events are in the
logical display space.

This also removes the flag gurad for the per-window-input-rotation
feature in Input.cpp, which is required for the tests to pass when the
flag is disabled. This guard does not do anything anymore because the
RawTransforms are blocked by the flag in SF.

Bug: 179274888
Test: manual: adb shell input swipe 200 200 500 500 1000
Test: atest InputShellCommandTest // CTS test for input injeciton
Test: atest inputflinger_tests
Change-Id: I122e511039ca629ab8982ed27d3d35f9e7b37d70
parent 5632d62f
Loading
Loading
Loading
Loading
+0 −13
Original line number Diff line number Diff line
@@ -20,10 +20,8 @@
#include <attestation/HmacKeyManager.h>
#include <cutils/compiler.h>
#include <inttypes.h>
#include <limits.h>
#include <string.h>

#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <gui/constants.h>
#include <input/Input.h>
@@ -43,15 +41,6 @@ namespace android {

namespace {

// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
// coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
bool isPerWindowInputRotationEnabled() {
    static const bool PER_WINDOW_INPUT_ROTATION =
            base::GetBoolProperty("persist.debug.per_window_input_rotation", false);

    return PER_WINDOW_INPUT_ROTATION;
}

float transformAngle(const ui::Transform& transform, float angleRadians) {
    // Construct and transform a vector oriented at the specified clockwise angle from vertical.
    // Coordinate system: down is increasing Y, right is increasing X.
@@ -511,8 +500,6 @@ float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
                                             size_t historicalIndex) const {
    const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);

    if (!isPerWindowInputRotationEnabled()) return coords->getAxisValue(axis);

    if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
        // For compatibility, convert raw coordinates into logical display space.
        const vec2 xy = shouldDisregardTranslation(mSource)
+0 −22
Original line number Diff line number Diff line
@@ -231,36 +231,14 @@ protected:
    static constexpr float RAW_X_OFFSET = 12;
    static constexpr float RAW_Y_OFFSET = -41.1;

    static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;

    int32_t mId;
    ui::Transform mTransform;
    ui::Transform mRawTransform;

    void SetUp() override;
    void TearDown() override;

    void initializeEventWithHistory(MotionEvent* event);
    void assertEqualsEventWithHistory(const MotionEvent* event);
};

const std::optional<bool> MotionEventTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
        !base::GetProperty("persist.debug.per_window_input_rotation", "").empty()
        ? std::optional(base::GetBoolProperty("persist.debug.per_window_input_rotation", false))
        : std::nullopt;

void MotionEventTest::SetUp() {
    // Ensure per_window_input_rotation is enabled.
    base::SetProperty("persist.debug.per_window_input_rotation", "true");
}

void MotionEventTest::TearDown() {
    const auto val = INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE.has_value()
            ? (*INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE ? "true" : "false")
            : "";
    base::SetProperty("persist.debug.per_window_input_rotation", val);
}

void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
    mId = InputEvent::nextId();
    mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
+51 −3
Original line number Diff line number Diff line
@@ -524,6 +524,16 @@ bool isConnectionResponsive(const Connection& connection) {
    return true;
}

bool isFromSource(uint32_t source, uint32_t test) {
    return (source & test) == test;
}

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;
}

} // namespace

// --- InputDispatcher ---
@@ -3962,15 +3972,19 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
        mLock.lock();

        if (shouldSendMotionToInputFilterLocked(args)) {
            ui::Transform displayTransform;
            if (const auto it = mDisplayInfos.find(args->displayId); it != mDisplayInfos.end()) {
                displayTransform = it->second.transform;
            }

            mLock.unlock();

            MotionEvent event;
            ui::Transform identityTransform;
            event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
                             args->action, args->actionButton, args->flags, args->edgeFlags,
                             args->metaState, args->buttonState, args->classification,
                             identityTransform, args->xPrecision, args->yPrecision,
                             args->xCursorPosition, args->yCursorPosition, identityTransform,
                             displayTransform, args->xPrecision, args->yPrecision,
                             args->xCursorPosition, args->yCursorPosition, displayTransform,
                             args->downTime, args->eventTime, args->pointerCount,
                             args->pointerProperties, args->pointerCoords);

@@ -4220,6 +4234,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(
                                                  pointerProperties, samplePointerCoords,
                                                  motionEvent.getXOffset(),
                                                  motionEvent.getYOffset());
            transformMotionEntryForInjectionLocked(*injectedEntry);
            injectedEntries.push(std::move(injectedEntry));
            for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
                sampleEventTimes += 1;
@@ -4241,6 +4256,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(
                                                      uint32_t(pointerCount), pointerProperties,
                                                      samplePointerCoords, motionEvent.getXOffset(),
                                                      motionEvent.getYOffset());
                transformMotionEntryForInjectionLocked(*nextInjectedEntry);
                injectedEntries.push(std::move(nextInjectedEntry));
            }
            break;
@@ -4404,6 +4420,38 @@ 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;
    }

    // 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();

    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);
    }
}

void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
    InjectionState* injectionState = entry.injectionState;
    if (injectionState) {
+1 −0
Original line number Diff line number Diff line
@@ -280,6 +280,7 @@ private:
    bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
    void setInjectionResult(EventEntry& entry,
                            android::os::InputEventInjectionResult injectionResult);
    void transformMotionEntryForInjectionLocked(MotionEntry&) const REQUIRES(mLock);

    std::condition_variable mInjectionSyncFinished;
    void incrementPendingForegroundDispatches(EventEntry& entry);
+52 −25
Original line number Diff line number Diff line
@@ -92,13 +92,29 @@ public:
    FakeInputDispatcherPolicy() {}

    void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
                                        args.displayId);
        assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) {
            ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_KEY);
            EXPECT_EQ(event.getDisplayId(), args.displayId);

            const auto& keyEvent = static_cast<const KeyEvent&>(event);
            EXPECT_EQ(keyEvent.getEventTime(), args.eventTime);
            EXPECT_EQ(keyEvent.getAction(), args.action);
        });
    }

    void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action,
                                        args.displayId);
    void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point) {
        assertFilterInputEventWasCalledInternal([&](const InputEvent& event) {
            ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_MOTION);
            EXPECT_EQ(event.getDisplayId(), args.displayId);

            const auto& motionEvent = static_cast<const MotionEvent&>(event);
            EXPECT_EQ(motionEvent.getEventTime(), args.eventTime);
            EXPECT_EQ(motionEvent.getAction(), args.action);
            EXPECT_EQ(motionEvent.getX(0), point.x);
            EXPECT_EQ(motionEvent.getY(0), point.y);
            EXPECT_EQ(motionEvent.getRawX(0), point.x);
            EXPECT_EQ(motionEvent.getRawY(0), point.y);
        });
    }

    void assertFilterInputEventWasNotCalled() {
@@ -425,26 +441,11 @@ private:
        mDropTargetWindowToken = token;
    }

    void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
                                         int32_t displayId) {
    void assertFilterInputEventWasCalledInternal(
            const std::function<void(const InputEvent&)>& verify) {
        std::scoped_lock lock(mLock);
        ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
        ASSERT_EQ(mFilteredEvent->getType(), type);

        if (type == AINPUT_EVENT_TYPE_KEY) {
            const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
            EXPECT_EQ(keyEvent.getEventTime(), eventTime);
            EXPECT_EQ(keyEvent.getAction(), action);
            EXPECT_EQ(keyEvent.getDisplayId(), displayId);
        } else if (type == AINPUT_EVENT_TYPE_MOTION) {
            const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
            EXPECT_EQ(motionEvent.getEventTime(), eventTime);
            EXPECT_EQ(motionEvent.getAction(), action);
            EXPECT_EQ(motionEvent.getDisplayId(), displayId);
        } else {
            FAIL() << "Unknown type: " << type;
        }

        verify(*mFilteredEvent);
        mFilteredEvent = nullptr;
    }
};
@@ -3481,7 +3482,8 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay)

class InputFilterTest : public InputDispatcherTest {
protected:
    void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
    void testNotifyMotion(int32_t displayId, bool expectToBeFiltered,
                          const ui::Transform& transform = ui::Transform()) {
        NotifyMotionArgs motionArgs;

        motionArgs =
@@ -3492,7 +3494,8 @@ protected:
        mDispatcher->notifyMotion(&motionArgs);
        ASSERT_TRUE(mDispatcher->waitForIdle());
        if (expectToBeFiltered) {
            mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
            const auto xy = transform.transform(motionArgs.pointerCoords->getXYValue());
            mFakePolicy->assertFilterInputEventWasCalled(motionArgs, xy);
        } else {
            mFakePolicy->assertFilterInputEventWasNotCalled();
        }
@@ -3550,6 +3553,30 @@ TEST_F(InputFilterTest, KeyEvent_InputFilter) {
    testNotifyKey(/*expectToBeFiltered*/ false);
}

// Ensure that MotionEvents sent to the InputFilter through InputListener are converted to the
// logical display coordinate space.
TEST_F(InputFilterTest, MotionEvent_UsesLogicalDisplayCoordinates_notifyMotion) {
    ui::Transform firstDisplayTransform;
    firstDisplayTransform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1});
    ui::Transform secondDisplayTransform;
    secondDisplayTransform.set({-6.6, -5.5, -4.4, -3.3, -2.2, -1.1, 0, 0, 1});

    std::vector<gui::DisplayInfo> displayInfos(2);
    displayInfos[0].displayId = ADISPLAY_ID_DEFAULT;
    displayInfos[0].transform = firstDisplayTransform;
    displayInfos[1].displayId = SECOND_DISPLAY_ID;
    displayInfos[1].transform = secondDisplayTransform;

    mDispatcher->onWindowInfosChanged({}, displayInfos);

    // Enable InputFilter
    mDispatcher->setInputFilterEnabled(true);

    // Ensure the correct transforms are used for the displays.
    testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true, firstDisplayTransform);
    testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true, secondDisplayTransform);
}

class InputFilterInjectionPolicyTest : public InputDispatcherTest {
protected:
    virtual void SetUp() override {