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

Commit 1c4c5599 authored by Robert Carr's avatar Robert Carr
Browse files

Add some tests for native input.

Test some important scenarios for input being driven by
SurfaceControl rather than the WindowManager.

Test: EndToEndNativeInputTest
Bug: 80101428
Bug: 113136004
Bug: 111440400
Change-Id: I46b302774a19c43d12680a8b7e2bb553dfcf4175
parent 720e506f
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -36,6 +36,9 @@ public:
    DECLARE_META_INTERFACE(InputFlinger)

    virtual void setInputWindows(const Vector<InputWindowInfo>& inputHandles) = 0;

    virtual void registerInputChannel(const sp<InputChannel>& channel) = 0;
    virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0;
};


@@ -46,6 +49,8 @@ class BnInputFlinger : public BnInterface<IInputFlinger> {
public:
    enum {
        SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
        REGISTER_INPUT_CHANNEL_TRANSACTION,
        UNREGISTER_INPUT_CHANNEL_TRANSACTION
    };

    virtual status_t onTransact(uint32_t code, const Parcel& data,
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ cc_test {
        "BufferItemConsumer_test.cpp",
        "BufferQueue_test.cpp",
        "CpuConsumer_test.cpp",
        "EndToEndNativeInputTest.cpp",
        "FillBuffer.cpp",
        "GLTest.cpp",
        "IGraphicBufferProducer_test.cpp",
+265 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <gtest/gtest.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdio.h>
#include <poll.h>

#include <memory>

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

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

#include <input/InputWindow.h>
#include <input/IInputFlinger.h>
#include <input/InputTransport.h>
#include <input/Input.h>

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


namespace android {
namespace test {

sp<IInputFlinger> getInputFlinger() {
   sp<IBinder> input(defaultServiceManager()->getService(
            String16("inputflinger")));
    if (input == nullptr) {
        ALOGE("Failed to link to input service");
    } else { ALOGE("Linked to input"); }
    return interface_cast<IInputFlinger>(input);
}

// We use the top 10 layers as a way to haphazardly place ourselves above anything else.
static const int LAYER_BASE = INT32_MAX - 10;

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

        InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
        mServerChannel->setToken(new BBinder());

        mInputFlinger = getInputFlinger();
        mInputFlinger->registerInputChannel(mServerChannel);

        populateInputInfo(width, height);

        mInputConsumer = new InputConsumer(mClientChannel);
    }

    InputEvent* consumeEvent() {
        waitForEventAvailable();

        InputEvent *ev;
        uint32_t seqId;
        status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev);
        if (consumed != OK) {
            return nullptr;
        }
        mInputConsumer->sendFinishedSignal(seqId, true);
        return ev;
    }

    void expectTap(int x, int y) {
        InputEvent* ev = consumeEvent();
        EXPECT_TRUE(ev != nullptr);
        EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
        MotionEvent* mev = static_cast<MotionEvent*>(ev);
        EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
        EXPECT_EQ(x, mev->getX(0));
        EXPECT_EQ(y, mev->getY(0));

        ev = consumeEvent();
        EXPECT_TRUE(ev != nullptr);
        EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
        mev = static_cast<MotionEvent*>(ev);
        EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
    }

    ~InputSurface() {
        mInputFlinger->unregisterInputChannel(mServerChannel);
    }

    void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
                    const sp<SurfaceControl>&)> transactionBody) {
        SurfaceComposerClient::Transaction t;
        transactionBody(t, mSurfaceControl);
        t.apply(true);
    }

    void showAt(int x, int y) {
        SurfaceComposerClient::Transaction t;
        t.show(mSurfaceControl);
        t.setInputWindowInfo(mSurfaceControl, mInputInfo);
        t.setLayer(mSurfaceControl, LAYER_BASE);
        t.setPosition(mSurfaceControl, x, y);
        t.setCrop_legacy(mSurfaceControl, Rect(0, 0, 100, 100));
        t.setAlpha(mSurfaceControl, 1);
        t.apply(true);
    }

private:
    void waitForEventAvailable() {
        struct pollfd fd;

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

    void populateInputInfo(int width, int height) {
        mInputInfo.inputChannel = mServerChannel;
        mInputInfo.name = "Test info";
        mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
        mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
        mInputInfo.dispatchingTimeout = 100000;
        mInputInfo.scaleFactor = 1.0;
        mInputInfo.canReceiveKeys = true;
        mInputInfo.hasFocus = true;
        mInputInfo.hasWallpaper = false;
        mInputInfo.paused = false;

        mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));

        // TODO: Fill in from SF?
        mInputInfo.ownerPid = 11111;
        mInputInfo.ownerUid = 11111;
        mInputInfo.inputFeatures = 0;
        mInputInfo.displayId = 0;
    }
public:
    sp<SurfaceControl> mSurfaceControl;
    sp<InputChannel> mServerChannel, mClientChannel;
    sp<IInputFlinger> mInputFlinger;

    InputWindowInfo mInputInfo;

    PreallocatedInputEventFactory mInputEventFactory;
    InputConsumer* mInputConsumer;
};

class InputSurfacesTest : public ::testing::Test {
public:
    InputSurfacesTest() {
        ProcessState::self()->startThreadPool();
    }

    void SetUp() {
        mComposerClient = new SurfaceComposerClient;
        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
    }

    void TearDown() {
        mComposerClient->dispose();
    }

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

    sp<SurfaceComposerClient> mComposerClient;
};

void injectTap(int x, int y) {
    char *buf1, *buf2;
    asprintf(&buf1, "%d", x);
    asprintf(&buf2, "%d", y);
    if (fork() == 0) {
        execlp("input", "input", "tap", buf1, buf2, NULL);
    }
}

TEST_F(InputSurfacesTest, can_receive_input) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    surface->showAt(100, 100);

    injectTap(101, 101);

    EXPECT_TRUE(surface->consumeEvent() != nullptr);
}

TEST_F(InputSurfacesTest, input_respects_positioning) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    surface->showAt(100, 100);

    std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
    surface2->showAt(200, 200);

    injectTap(201, 201);
    surface2->expectTap(1, 1);

    injectTap(101, 101);
    surface->expectTap(1, 1);

    surface2->doTransaction([](auto &t, auto &sc) {
         t.setPosition(sc, 100, 100);
    });
    surface->doTransaction([](auto &t, auto &sc) {
         t.setPosition(sc, 200, 200);
    });

    injectTap(101, 101);
    surface2->expectTap(1, 1);

    injectTap(201, 201);
    surface->expectTap(1, 1);
}

TEST_F(InputSurfacesTest, input_respects_layering) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);

    surface->showAt(10, 10);
    surface2->showAt(10, 10);

    surface->doTransaction([](auto &t, auto &sc) {
         t.setLayer(sc, LAYER_BASE + 1);
    });

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

    surface2->doTransaction([](auto &t, auto &sc) {
         t.setLayer(sc, LAYER_BASE + 1);
    });

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

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

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

}
}
+28 −0
Original line number Diff line number Diff line
@@ -40,6 +40,20 @@ public:
        }
        remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply);
    }

    virtual void registerInputChannel(const sp<InputChannel>& channel) {
        Parcel data, reply;
        data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
        channel->write(data);
        remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
    }

    virtual void unregisterInputChannel(const sp<InputChannel>& channel) {
        Parcel data, reply;
        data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
        channel->write(data);
        remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
    }
};

IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger");
@@ -61,6 +75,20 @@ status_t BnInputFlinger::onTransact(
        setInputWindows(handles);
        break;
    }
    case REGISTER_INPUT_CHANNEL_TRANSACTION: {
        CHECK_INTERFACE(IInputFlinger, data, reply);
        sp<InputChannel> channel = new InputChannel();
        channel->read(data);
        registerInputChannel(channel);
        break;
    }
    case UNREGISTER_INPUT_CHANNEL_TRANSACTION: {
        CHECK_INTERFACE(IInputFlinger, data, reply);
        sp<InputChannel> channel = new InputChannel();
        channel->read(data);
        unregisterInputChannel(channel);
        break;
    }
    default:
        return BBinder::onTransact(code, data, reply, flags);
    }
+20 −0
Original line number Diff line number Diff line
@@ -21,9 +21,13 @@
#include "InputManager.h"
#include "InputReaderFactory.h"

#include <binder/IPCThreadState.h>

#include <log/log.h>
#include <unordered_map>

#include <private/android_filesystem_config.h>

namespace android {

InputManager::InputManager(
@@ -118,4 +122,20 @@ void InputManager::setInputWindows(const Vector<InputWindowInfo>& infos) {
    }
}

// Used by tests only.
void InputManager::registerInputChannel(const sp<InputChannel>& channel) {
    IPCThreadState* ipc = IPCThreadState::self();
    const int uid = ipc->getCallingUid();
    if (uid != AID_SHELL && uid != AID_ROOT) {
        ALOGE("Invalid attempt to register input channel over IPC"
                "from non shell/root entity (PID: %d)", ipc->getCallingPid());
        return;
    }
    mDispatcher->registerInputChannel(channel, false);
}

void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
    mDispatcher->unregisterInputChannel(channel);
}

} // namespace android
Loading