diff options
author | 2018-09-24 13:18:43 -0700 | |
---|---|---|
committer | 2018-11-27 12:39:33 -0800 | |
commit | 1c4c5599696d049b112c40d629f8c74bb612b01f (patch) | |
tree | d0da6e66f155d1d5e3573e5f4c99d2781d97eb88 | |
parent | 720e506fcab38d2f73b84630744ad100c5b6609d (diff) |
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
-rw-r--r-- | include/input/IInputFlinger.h | 5 | ||||
-rw-r--r-- | libs/gui/tests/Android.bp | 1 | ||||
-rw-r--r-- | libs/gui/tests/EndToEndNativeInputTest.cpp | 265 | ||||
-rw-r--r-- | libs/input/IInputFlinger.cpp | 28 | ||||
-rw-r--r-- | services/inputflinger/InputManager.cpp | 20 | ||||
-rw-r--r-- | services/inputflinger/InputManager.h | 4 | ||||
-rw-r--r-- | services/inputflinger/host/InputFlinger.h | 2 |
7 files changed, 325 insertions, 0 deletions
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h index 1ef8986267..610834d23e 100644 --- a/include/input/IInputFlinger.h +++ b/include/input/IInputFlinger.h @@ -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, diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 7ecadf836c..a6295e0387 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -16,6 +16,7 @@ cc_test { "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "CpuConsumer_test.cpp", + "EndToEndNativeInputTest.cpp", "FillBuffer.cpp", "GLTest.cpp", "IGraphicBufferProducer_test.cpp", diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp new file mode 100644 index 0000000000..2f165c4131 --- /dev/null +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -0,0 +1,265 @@ +/* + * 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); +} + +} +} diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp index 47a2c0c347..477e54e708 100644 --- a/libs/input/IInputFlinger.cpp +++ b/libs/input/IInputFlinger.cpp @@ -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); } diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 388423ca20..cf3ca42d93 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -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 diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 1173fa165f..8f7551e5b2 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -35,6 +35,7 @@ #include <utils/RefBase.h> namespace android { +class InputChannel; /* * The input manager is the core of the system event processing. @@ -91,6 +92,9 @@ public: virtual void setInputWindows(const Vector<InputWindowInfo>& handles); + virtual void registerInputChannel(const sp<InputChannel>& channel); + virtual void unregisterInputChannel(const sp<InputChannel>& channel); + private: sp<InputReaderInterface> mReader; sp<InputReaderThread> mReaderThread; diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h index 15ca7b3c24..82ff089e89 100644 --- a/services/inputflinger/host/InputFlinger.h +++ b/services/inputflinger/host/InputFlinger.h @@ -40,6 +40,8 @@ public: virtual status_t dump(int fd, const Vector<String16>& args); void setInputWindows(const Vector<InputWindowInfo>&) {} + void registerInputChannel(const sp<InputChannel>&) {} + void unregisterInputChannel(const sp<InputChannel>&) {} private: virtual ~InputFlinger(); |