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

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

Input: Update input window info when the layer receives its first buffer

Currently input window info is populated when a surface flinger transaction is applied. When
populating input window info for a layer that is yet to receive its first buffer, the window
will be invisible and not receive any input until another transaction is applied. This can
result in input being dispatched to a window beneath the target window.

To fix, populate the input window info when we latch a new buffer as well.

Test: sf unit tests+go/wm-smoke
Bug: 120839715
Change-Id: I42f9dc3a64a7af2f3359f0dfeefb57f7a3e75306
parent 4233fe00
Loading
Loading
Loading
Loading
+176 −4
Original line number Original line Diff line number Diff line
@@ -24,11 +24,15 @@


#include <memory>
#include <memory>


#include <android/native_window.h>

#include <binder/Binder.h>
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
#include <binder/ProcessState.h>


#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
#include <gui/SurfaceControl.h>


@@ -37,6 +41,7 @@
#include <input/InputTransport.h>
#include <input/InputTransport.h>
#include <input/Input.h>
#include <input/Input.h>


#include <ui/DisplayInfo.h>
#include <ui/Rect.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Region.h>


@@ -44,6 +49,8 @@
namespace android {
namespace android {
namespace test {
namespace test {


using Transaction = SurfaceComposerClient::Transaction;

sp<IInputFlinger> getInputFlinger() {
sp<IInputFlinger> getInputFlinger() {
   sp<IBinder> input(defaultServiceManager()->getService(
   sp<IBinder> input(defaultServiceManager()->getService(
            String16("inputflinger")));
            String16("inputflinger")));
@@ -58,9 +65,8 @@ static const int LAYER_BASE = INT32_MAX - 10;


class InputSurface {
class InputSurface {
public:
public:
    InputSurface(const sp<SurfaceComposerClient>& scc, int width, int height) {
    InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
        mSurfaceControl = scc->createSurface(String8("Test Surface"), 0, 0, PIXEL_FORMAT_RGBA_8888,
        mSurfaceControl = sc;
                                             ISurfaceComposerClient::eFXSurfaceColor);


        InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
        InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
        mServerChannel->setToken(new BBinder());
        mServerChannel->setToken(new BBinder());
@@ -73,6 +79,31 @@ public:
        mInputConsumer = new InputConsumer(mClientChannel);
        mInputConsumer = new InputConsumer(mClientChannel);
    }
    }


    static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient> &scc,
                                                               int width, int height) {
        sp<SurfaceControl> surfaceControl =
                scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */,
                                   PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
        return std::make_unique<InputSurface>(surfaceControl, width, height);
    }

    static std::unique_ptr<InputSurface> makeBufferInputSurface(
            const sp<SurfaceComposerClient> &scc, int width, int height) {
        sp<SurfaceControl> surfaceControl =
                scc->createSurface(String8("Test Buffer Surface"), width, height,
                                   PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
        return std::make_unique<InputSurface>(surfaceControl, width, height);
    }

    static std::unique_ptr<InputSurface> makeContainerInputSurface(
            const sp<SurfaceComposerClient> &scc, int width, int height) {
        sp<SurfaceControl> surfaceControl =
                scc->createSurface(String8("Test Container Surface"), 0 /* bufHeight */,
                                   0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
                                   ISurfaceComposerClient::eFXSurfaceContainer);
        return std::make_unique<InputSurface>(surfaceControl, width, height);
    }

    InputEvent* consumeEvent() {
    InputEvent* consumeEvent() {
        waitForEventAvailable();
        waitForEventAvailable();


@@ -180,6 +211,15 @@ public:
    void SetUp() {
    void SetUp() {
        mComposerClient = new SurfaceComposerClient;
        mComposerClient = new SurfaceComposerClient;
        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());

        DisplayInfo info;
        auto display = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
        SurfaceComposerClient::getDisplayInfo(display, &info);

        // After a new buffer is queued, SurfaceFlinger is notified and will
        // latch the new buffer on next vsync.  Let's heuristically wait for 3
        // vsyncs.
        mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
    }
    }


    void TearDown() {
    void TearDown() {
@@ -187,10 +227,23 @@ public:
    }
    }


    std::unique_ptr<InputSurface> makeSurface(int width, int height) {
    std::unique_ptr<InputSurface> makeSurface(int width, int height) {
        return std::make_unique<InputSurface>(mComposerClient, width, height);
        return InputSurface::makeColorInputSurface(mComposerClient, width, height);
    }

    void postBuffer(const sp<SurfaceControl> &layer) {
        // wait for previous transactions (such as setSize) to complete
        Transaction().apply(true);
        ANativeWindow_Buffer buffer = {};
        EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
        ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
        // Request an empty transaction to get applied synchronously to ensure the buffer is
        // latched.
        Transaction().apply(true);
        usleep(mBufferPostDelay);
    }
    }


    sp<SurfaceComposerClient> mComposerClient;
    sp<SurfaceComposerClient> mComposerClient;
    int32_t mBufferPostDelay;
};
};


void injectTap(int x, int y) {
void injectTap(int x, int y) {
@@ -267,5 +320,124 @@ TEST_F(InputSurfacesTest, input_respects_layering) {
    surface->expectTap(1, 1);
    surface->expectTap(1, 1);
}
}


// Surface Insets are set to offset the client content and draw a border around the client surface
// (such as shadows in dialogs). Inputs sent to the client are offset such that 0,0 is the start
// of the client content.
TEST_F(InputSurfacesTest, input_respects_surface_insets) {
    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
    bgSurface->showAt(100, 100);

    fgSurface->mInputInfo.surfaceInset = 5;
    fgSurface->showAt(100, 100);

    injectTap(106, 106);
    fgSurface->expectTap(1, 1);

    injectTap(101, 101);
    bgSurface->expectTap(1, 1);
}

// Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463
TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) {
    std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
    std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
    parentSurface->showAt(100, 100);

    childSurface->mInputInfo.surfaceInset = 10;
    childSurface->showAt(100, 100);

    childSurface->doTransaction([&](auto &t, auto &sc) {
        t.setPosition(sc, -5, -5);
        t.reparent(sc, parentSurface->mSurfaceControl->getHandle());
    });

    injectTap(106, 106);
    childSurface->expectTap(1, 1);

    injectTap(101, 101);
    parentSurface->expectTap(1, 1);
}

// Ensure we ignore transparent region when getting screen bounds when positioning input frame.
TEST_F(InputSurfacesTest, input_ignores_transparent_region) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    surface->doTransaction([](auto &t, auto &sc) {
        Region transparentRegion(Rect(0, 0, 10, 10));
        t.setTransparentRegionHint(sc, transparentRegion);
    });
    surface->showAt(100, 100);
    injectTap(101, 101);
    surface->expectTap(1, 1);
}

// Ensure we send the input to the right surface when the surface visibility changes due to the
// first buffer being submitted. ref: b/120839715
TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) {
    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
    std::unique_ptr<InputSurface> bufferSurface =
            InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);

    bgSurface->showAt(10, 10);
    bufferSurface->showAt(10, 10);

    injectTap(11, 11);
    bgSurface->expectTap(1, 1);

    postBuffer(bufferSurface->mSurfaceControl);
    injectTap(11, 11);
    bufferSurface->expectTap(1, 1);
}

TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) {
    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
    std::unique_ptr<InputSurface> bufferSurface =
            InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
    postBuffer(bufferSurface->mSurfaceControl);

    bgSurface->showAt(10, 10);
    bufferSurface->showAt(10, 10);

    injectTap(11, 11);
    bufferSurface->expectTap(1, 1);

    bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });

    injectTap(11, 11);
    bgSurface->expectTap(1, 1);
}

TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) {
    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);

    bgSurface->showAt(10, 10);
    fgSurface->showAt(10, 10);

    injectTap(11, 11);
    fgSurface->expectTap(1, 1);

    fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });

    injectTap(11, 11);
    bgSurface->expectTap(1, 1);
}

TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) {
    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
    std::unique_ptr<InputSurface> containerSurface =
            InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);

    bgSurface->showAt(10, 10);
    containerSurface->showAt(10, 10);

    injectTap(11, 11);
    containerSurface->expectTap(1, 1);

    containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });

    injectTap(11, 11);
    bgSurface->expectTap(1, 1);
}
}
}
}
}
+10 −1
Original line number Original line Diff line number Diff line
@@ -2736,7 +2736,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)


    commitTransaction();
    commitTransaction();


    if ((inputChanged || mVisibleRegionsDirty) && mInputFlinger) {
    if (inputChanged || mVisibleRegionsDirty) {
        updateInputWindows();
        updateInputWindows();
    }
    }


@@ -2746,6 +2746,10 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
void SurfaceFlinger::updateInputWindows() {
void SurfaceFlinger::updateInputWindows() {
    ATRACE_CALL();
    ATRACE_CALL();


    if (mInputFlinger == nullptr) {
        return;
    }

    Vector<InputWindowInfo> inputHandles;
    Vector<InputWindowInfo> inputHandles;


    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
@@ -3015,6 +3019,11 @@ bool SurfaceFlinger::handlePageFlip()


    mVisibleRegionsDirty |= visibleRegions;
    mVisibleRegionsDirty |= visibleRegions;


    if (visibleRegions) {
        // Update input window info if the layer receives its first buffer.
        updateInputWindows();
    }

    // If we will need to wake up at some time in the future to deal with a
    // If we will need to wake up at some time in the future to deal with a
    // queued frame that shouldn't be displayed during this vsync period, wake
    // queued frame that shouldn't be displayed during this vsync period, wake
    // up during the next vsync period to check again.
    // up during the next vsync period to check again.