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

Commit fb06059f authored by Vishnu Nair's avatar Vishnu Nair
Browse files

SurfaceFlinger: Implement drop input modes

ALL: If this mode is set on a layer, set the DROP_INPUT
feature flag on the layer and its children if it has a valid input
channel. This will ensure these layers will not be able to receive any
input.

OBSCURED: If this mode is set, set the DROP_INPUT
feature flag on the layer and its children if they have a valid input
channel and they are considered occluded. This can happen if the layer
has a alpha set by it's parent OR if its buffer has been cropped by its
parent. Otherwise, the input feature flag DROP_INPUT_IF_OBSCURED flag
will be set so inputflinger can decide to drop input based on
occlusion.

Test: atest libgui_test InputDispatcherDropInputFeatureTest
Bug: 197364677
Merged-In: Ibce11ee7df5b5c8c226ebfab29574a99cd656f6d
Change-Id: Ibce11ee7df5b5c8c226ebfab29574a99cd656f6d
parent c469011b
Loading
Loading
Loading
Loading
+169 −4
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@

#include <memory>

#include <android/keycodes.h>
#include <android/native_window.h>

#include <binder/Binder.h>
@@ -120,8 +121,8 @@ public:
        return std::make_unique<InputSurface>(surfaceControl, width, height);
    }

    InputEvent* consumeEvent() {
        waitForEventAvailable();
    InputEvent *consumeEvent(int timeoutMs = 3000) {
        waitForEventAvailable(timeoutMs);

        InputEvent *ev;
        uint32_t seqId;
@@ -178,6 +179,24 @@ public:
        EXPECT_EQ(flags, mev->getFlags() & flags);
    }

    void expectKey(uint32_t keycode) {
        InputEvent *ev = consumeEvent();
        ASSERT_NE(ev, nullptr);
        ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, ev->getType());
        KeyEvent *keyEvent = static_cast<KeyEvent *>(ev);
        EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, keyEvent->getAction());
        EXPECT_EQ(keycode, keyEvent->getKeyCode());
        EXPECT_EQ(0, keyEvent->getFlags() & VERIFIED_KEY_EVENT_FLAGS);

        ev = consumeEvent();
        ASSERT_NE(ev, nullptr);
        ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, ev->getType());
        keyEvent = static_cast<KeyEvent *>(ev);
        EXPECT_EQ(AMOTION_EVENT_ACTION_UP, keyEvent->getAction());
        EXPECT_EQ(keycode, keyEvent->getKeyCode());
        EXPECT_EQ(0, keyEvent->getFlags() & VERIFIED_KEY_EVENT_FLAGS);
    }

    virtual ~InputSurface() {
        mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken());
    }
@@ -215,12 +234,12 @@ public:
    }

private:
    void waitForEventAvailable() {
    void waitForEventAvailable(int timeoutMs) {
        struct pollfd fd;

        fd.fd = mClientChannel->getFd();
        fd.events = POLLIN;
        poll(&fd, 1, 3000);
        poll(&fd, 1, timeoutMs);
    }

    void populateInputInfo(int width, int height) {
@@ -363,6 +382,14 @@ void injectTap(int x, int y) {
    }
}

void injectKey(uint32_t keycode) {
    char *buf1;
    asprintf(&buf1, "%d", keycode);
    if (fork() == 0) {
        execlp("input", "input", "keyevent", buf1, NULL);
    }
}

TEST_F(InputSurfacesTest, can_receive_input) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    surface->showAt(100, 100);
@@ -614,6 +641,9 @@ TEST_F(InputSurfacesTest, can_be_focused) {
    surface->requestFocus();

    surface->assertFocusChange(true);

    injectKey(AKEYCODE_V);
    surface->expectKey(AKEYCODE_V);
}

TEST_F(InputSurfacesTest, rotate_surface) {
@@ -781,4 +811,139 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) {
    surface->expectTap(1, 1);
}

TEST_F(InputSurfacesTest, strict_unobscured_input_unobscured_window) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    surface->doTransaction(
            [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
    surface->showAt(100, 100);

    injectTap(101, 101);

    EXPECT_NE(surface->consumeEvent(), nullptr);
    EXPECT_NE(surface->consumeEvent(), nullptr);

    surface->requestFocus();
    surface->assertFocusChange(true);
    injectKey(AKEYCODE_V);
    surface->expectKey(AKEYCODE_V);
}

TEST_F(InputSurfacesTest, strict_unobscured_input_scaled_without_crop_window) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    surface->doTransaction([&](auto &t, auto &sc) {
        t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
        t.setMatrix(sc, 2.0, 0, 0, 2.0);
    });
    surface->showAt(100, 100);

    injectTap(101, 101);

    EXPECT_NE(surface->consumeEvent(), nullptr);
    EXPECT_NE(surface->consumeEvent(), nullptr);

    surface->requestFocus();
    surface->assertFocusChange(true);
    injectKey(AKEYCODE_V);
    surface->expectKey(AKEYCODE_V);
}

TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    surface->mInputInfo.ownerUid = 11111;
    surface->doTransaction(
            [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
    surface->showAt(100, 100);
    std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
    obscuringSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
    obscuringSurface->mInputInfo.ownerUid = 22222;
    obscuringSurface->showAt(100, 100);
    injectTap(101, 101);
    EXPECT_EQ(surface->consumeEvent(100), nullptr);

    surface->requestFocus();
    surface->assertFocusChange(true);
    injectKey(AKEYCODE_V);
    EXPECT_EQ(surface->consumeEvent(100), nullptr);
}

TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    surface->mInputInfo.ownerUid = 11111;
    surface->doTransaction(
            [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
    surface->showAt(100, 100);
    std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
    obscuringSurface->mInputInfo.flags = WindowInfo::Flag::NOT_TOUCHABLE;
    obscuringSurface->mInputInfo.ownerUid = 22222;
    obscuringSurface->showAt(190, 190);

    injectTap(101, 101);

    EXPECT_EQ(surface->consumeEvent(100), nullptr);

    surface->requestFocus();
    surface->assertFocusChange(true);
    injectKey(AKEYCODE_V);
    EXPECT_EQ(surface->consumeEvent(100), nullptr);
}

TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) {
    std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300);
    parentSurface->showAt(0, 0, Rect(0, 0, 300, 300));

    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    surface->showAt(100, 100);
    surface->doTransaction([&](auto &t, auto &sc) {
        t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
        t.reparent(sc, parentSurface->mSurfaceControl);
        t.setAlpha(parentSurface->mSurfaceControl, 0.9f);
    });

    injectTap(101, 101);

    EXPECT_EQ(surface->consumeEvent(100), nullptr);

    surface->requestFocus();
    surface->assertFocusChange(true);
    injectKey(AKEYCODE_V);
    EXPECT_EQ(surface->consumeEvent(100), nullptr);
}

TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) {
    std::unique_ptr<InputSurface> parentSurface = makeSurface(300, 300);
    parentSurface->showAt(0, 0, Rect(0, 0, 300, 300));

    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    surface->doTransaction([&](auto &t, auto &sc) {
        t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
        t.reparent(sc, parentSurface->mSurfaceControl);
        t.setCrop(parentSurface->mSurfaceControl, Rect(10, 10, 100, 100));
    });
    surface->showAt(100, 100);

    injectTap(111, 111);

    EXPECT_EQ(surface->consumeEvent(100), nullptr);

    surface->requestFocus();
    surface->assertFocusChange(true);
    injectKey(AKEYCODE_V);
    EXPECT_EQ(surface->consumeEvent(100), nullptr);
}

TEST_F(InputSurfacesTest, drop_input_policy) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    surface->doTransaction(
            [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::ALL); });
    surface->showAt(100, 100);

    injectTap(101, 101);

    EXPECT_EQ(surface->consumeEvent(100), nullptr);

    surface->requestFocus();
    surface->assertFocusChange(true);
    injectKey(AKEYCODE_V);
    EXPECT_EQ(surface->consumeEvent(100), nullptr);
}
} // namespace android::test
+69 −0
Original line number Diff line number Diff line
@@ -2291,6 +2291,74 @@ void Layer::fillTouchOcclusionMode(WindowInfo& info) {
    }
}

gui::DropInputMode Layer::getDropInputMode() const {
    gui::DropInputMode mode = mDrawingState.dropInputMode;
    if (mode == gui::DropInputMode::ALL) {
        return mode;
    }
    sp<Layer> parent = mDrawingParent.promote();
    if (parent) {
        gui::DropInputMode parentMode = parent->getDropInputMode();
        if (parentMode != gui::DropInputMode::NONE) {
            return parentMode;
        }
    }
    return mode;
}

void Layer::handleDropInputMode(gui::WindowInfo& info) const {
    if (mDrawingState.inputInfo.inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL)) {
        return;
    }

    // Check if we need to drop input unconditionally
    gui::DropInputMode dropInputMode = getDropInputMode();
    if (dropInputMode == gui::DropInputMode::ALL) {
        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
        ALOGV("Dropping input for %s as requested by policy.", getDebugName());
        return;
    }

    // Check if we need to check if the window is obscured by parent
    if (dropInputMode != gui::DropInputMode::OBSCURED) {
        return;
    }

    // Check if the parent has set an alpha on the layer
    sp<Layer> parent = mDrawingParent.promote();
    if (parent && parent->getAlpha() != 1.0_hf) {
        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
        ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(),
              static_cast<float>(getAlpha()));
    }

    // Check if the parent has cropped the buffer
    Rect bufferSize = getCroppedBufferSize(getDrawingState());
    if (!bufferSize.isValid()) {
        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
        return;
    }

    // Screenbounds are the layer bounds cropped by parents, transformed to screenspace.
    // To check if the layer has been cropped, we take the buffer bounds, apply the local
    // layer crop and apply the same set of transforms to move to screenspace. If the bounds
    // match then the layer has not been cropped by its parents.
    Rect bufferInScreenSpace(getTransform().transform(bufferSize));
    bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds};

    if (croppedByParent) {
        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
        ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent",
              getDebugName());
    } else {
        // If the layer is not obscured by its parents (by setting an alpha or crop), then only drop
        // input if the window is obscured. This check should be done in surfaceflinger but the
        // logic currently resides in inputflinger. So pass the if_obscured check to input to only
        // drop input events if the window is obscured.
        info.inputFeatures |= WindowInfo::Feature::DROP_INPUT_IF_OBSCURED;
    }
}

WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
    if (!hasInputInfo()) {
        mDrawingState.inputInfo.name = getName();
@@ -2344,6 +2412,7 @@ WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
    info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
    info.alpha = getAlpha();
    fillTouchOcclusionMode(info);
    handleDropInputMode(info);

    auto cropLayer = mDrawingState.touchableRegionCrop.promote();
    if (info.replaceTouchableRegionWithCrop) {
+2 −0
Original line number Diff line number Diff line
@@ -1062,6 +1062,8 @@ private:
    bool setFrameRateForLayerTree(FrameRate);
    void setZOrderRelativeOf(const wp<Layer>& relativeOf);
    bool isTrustedOverlay() const;
    gui::DropInputMode getDropInputMode() const;
    void handleDropInputMode(gui::WindowInfo& info) const;

    // Find the root of the cloned hierarchy, this means the first non cloned parent.
    // This will return null if first non cloned parent is not found.