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

Commit cfcb5c94 authored by Bernardo Rufino's avatar Bernardo Rufino Committed by Android (Google) Code Review
Browse files

Merge "Add InputDispatcher unit tests for untrusted touches [1/n]" into sc-dev

parents 2804272f c3b7b8ce
Loading
Loading
Loading
Loading
+136 −7
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
using android::base::StringPrintf;
using android::os::InputEventInjectionResult;
using android::os::InputEventInjectionSync;
using android::os::TouchOcclusionMode;
using namespace android::flag_operators;

namespace android::inputdispatcher {
@@ -727,8 +728,9 @@ public:
        ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
    }

    void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
                      int32_t expectedFlags) {
    void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
                      std::optional<int32_t> expectedDisplayId,
                      std::optional<int32_t> expectedFlags) {
        InputEvent* event = consume();

        ASSERT_NE(nullptr, event) << mName.c_str()
@@ -737,19 +739,25 @@ public:
                << mName.c_str() << " expected " << inputEventTypeToString(expectedEventType)
                << " event, got " << inputEventTypeToString(event->getType()) << " event";

        if (expectedDisplayId.has_value()) {
            EXPECT_EQ(expectedDisplayId, event->getDisplayId());
        }

        switch (expectedEventType) {
            case AINPUT_EVENT_TYPE_KEY: {
                const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
                EXPECT_EQ(expectedAction, keyEvent.getAction());
                EXPECT_EQ(expectedFlags, keyEvent.getFlags());
                if (expectedFlags.has_value()) {
                    EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags());
                }
                break;
            }
            case AINPUT_EVENT_TYPE_MOTION: {
                const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
                EXPECT_EQ(expectedAction, motionEvent.getAction());
                EXPECT_EQ(expectedFlags, motionEvent.getFlags());
                if (expectedFlags.has_value()) {
                    EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
                }
                break;
            }
            case AINPUT_EVENT_TYPE_FOCUS: {
@@ -854,6 +862,7 @@ public:
        mInfo.name = name;
        mInfo.type = InputWindowInfo::Type::APPLICATION;
        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
        mInfo.alpha = 1.0;
        mInfo.frameLeft = 0;
        mInfo.frameTop = 0;
        mInfo.frameRight = WIDTH;
@@ -883,6 +892,12 @@ public:

    void setPaused(bool paused) { mInfo.paused = paused; }

    void setAlpha(float alpha) { mInfo.alpha = alpha; }

    void setTouchOcclusionMode(android::os::TouchOcclusionMode mode) {
        mInfo.touchOcclusionMode = mode;
    }

    void setFrame(const Rect& frame) {
        mInfo.frameLeft = frame.left;
        mInfo.frameTop = frame.top;
@@ -928,6 +943,11 @@ public:

    void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
                           int32_t expectedFlags = 0) {
        consumeAnyMotionDown(expectedDisplayId, expectedFlags);
    }

    void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt,
                              std::optional<int32_t> expectedFlags = std::nullopt) {
        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
                     expectedFlags);
    }
@@ -971,8 +991,9 @@ public:
        mInputReceiver->consumeCaptureEvent(hasCapture);
    }

    void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
                      int32_t expectedFlags) {
    void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
                      std::optional<int32_t> expectedDisplayId,
                      std::optional<int32_t> expectedFlags) {
        ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
        mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId,
                                     expectedFlags);
@@ -4240,4 +4261,112 @@ TEST_F(InputDispatcherPointerCaptureTests, OutOfOrderRequests) {
    mSecondWindow->consumeCaptureEvent(true);
}

class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
protected:
    constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
    static const int32_t APP_A_UID = 10001;
    static const int32_t APP_B_UID = 10002;

    sp<FakeWindowHandle> mTouchWindow;

    virtual void SetUp() override {
        InputDispatcherTest::SetUp();
        mTouchWindow = getWindow(APP_A_UID, "Touched");
        mDispatcher->setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode::BLOCK);
        mDispatcher->setMaximumObscuringOpacityForTouch(MAXIMUM_OBSCURING_OPACITY);
    }

    virtual void TearDown() override {
        InputDispatcherTest::TearDown();
        mTouchWindow.clear();
    }

    sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name,
                                            os::TouchOcclusionMode mode) {
        sp<FakeWindowHandle> window = getWindow(uid, name);
        window->setFlags(InputWindowInfo::Flag::NOT_TOUCHABLE);
        window->setTouchOcclusionMode(mode);
        return window;
    }

    sp<FakeWindowHandle> getWindow(int32_t uid, std::string name) {
        std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
        sp<FakeWindowHandle> window =
                new FakeWindowHandle(app, mDispatcher, name, ADISPLAY_ID_DEFAULT);
        // Generate an arbitrary PID based on the UID
        window->setOwnerInfo(1777 + (uid % 10000), uid);
        return window;
    }

    void touch(const std::vector<PointF>& points = {PointF{100, 200}}) {
        NotifyMotionArgs args =
                generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                                   ADISPLAY_ID_DEFAULT, points);
        mDispatcher->notifyMotion(&args);
    }
};

TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithBlockUntrustedOcclusionMode_BlocksTouch) {
    const sp<FakeWindowHandle>& window =
            getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});

    touch();

    mTouchWindow->assertNoEvents();
}

TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithAllowOcclusionMode_AllowsTouch) {
    const sp<FakeWindowHandle>& window =
            getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::ALLOW);
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});

    touch();

    mTouchWindow->consumeAnyMotionDown();
}

TEST_F(InputDispatcherUntrustedTouchesTest, TouchOutsideOccludingWindow_AllowsTouch) {
    const sp<FakeWindowHandle>& window =
            getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
    window->setFrame(Rect(0, 0, 50, 50));
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});

    touch({PointF{100, 100}});

    mTouchWindow->consumeAnyMotionDown();
}

TEST_F(InputDispatcherUntrustedTouchesTest, WindowFromSameUid_AllowsTouch) {
    const sp<FakeWindowHandle>& window =
            getOccludingWindow(APP_A_UID, "A", TouchOcclusionMode::BLOCK_UNTRUSTED);
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});

    touch();

    mTouchWindow->consumeAnyMotionDown();
}

TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityBelowThreshold_AllowsTouch) {
    const sp<FakeWindowHandle>& window =
            getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY);
    window->setAlpha(0.7);
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});

    touch();

    mTouchWindow->consumeAnyMotionDown();
}

TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityAboveThreshold_BlocksTouch) {
    const sp<FakeWindowHandle>& window =
            getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY);
    window->setAlpha(0.9);
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});

    touch();

    mTouchWindow->assertNoEvents();
}

} // namespace android::inputdispatcher