/* * Copyright (C) 2020 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TAG "InputFlingerServiceTest" using android::os::BnInputFlinger; using android::os::BnSetInputWindowsListener; using android::os::IInputFlinger; using android::os::ISetInputWindowsListener; using std::chrono_literals::operator""ms; using std::chrono_literals::operator""s; namespace android { static const sp TestInfoToken = new BBinder(); static const sp FocusedTestInfoToken = new BBinder(); static constexpr int32_t TestInfoId = 1; static const std::string TestInfoName = "InputFlingerServiceTestInputWindowInfo"; static constexpr Flags TestInfoFlags = InputWindowInfo::Flag::NOT_FOCUSABLE; static constexpr InputWindowInfo::Type TestInfoType = InputWindowInfo::Type::INPUT_METHOD; static constexpr std::chrono::duration TestInfoDispatchingTimeout = 2532ms; static constexpr int32_t TestInfoFrameLeft = 93; static constexpr int32_t TestInfoFrameTop = 34; static constexpr int32_t TestInfoFrameRight = 16; static constexpr int32_t TestInfoFrameBottom = 19; static constexpr int32_t TestInfoSurfaceInset = 17; static constexpr float TestInfoGlobalScaleFactor = 0.3; static constexpr float TestInfoWindowXScale = 0.4; static constexpr float TestInfoWindowYScale = 0.5; static const Rect TestInfoTouchableRegionRect = {100 /* left */, 150 /* top */, 400 /* right */, 450 /* bottom */}; static const Region TestInfoTouchableRegion(TestInfoTouchableRegionRect); static constexpr bool TestInfoVisible = false; static constexpr bool TestInfoTrustedOverlay = true; static constexpr bool TestInfoFocusable = false; static constexpr bool TestInfoHasWallpaper = false; static constexpr bool TestInfoPaused = false; static constexpr int32_t TestInfoOwnerPid = 19; static constexpr int32_t TestInfoOwnerUid = 24; static constexpr InputWindowInfo::Feature TestInfoInputFeatures = InputWindowInfo::Feature::NO_INPUT_CHANNEL; static constexpr int32_t TestInfoDisplayId = 34; static constexpr int32_t TestInfoPortalToDisplayId = 2; static constexpr bool TestInfoReplaceTouchableRegionWithCrop = true; static const sp TestInfoTouchableRegionCropHandle = new BBinder(); static const std::string TestAppInfoName = "InputFlingerServiceTestInputApplicationInfo"; static const sp TestAppInfoToken = new BBinder(); static constexpr std::chrono::duration TestAppInfoDispatchingTimeout = 12345678ms; static const String16 kTestServiceName = String16("InputFlingerService"); static const String16 kQueryServiceName = String16("InputFlingerQueryService"); struct SetInputWindowsListener; // --- InputFlingerServiceTest --- class InputFlingerServiceTest : public testing::Test { public: void SetUp() override; void TearDown() override; protected: void InitializeInputFlinger(); void setInputWindowsByInfos(const std::vector& infos); void setFocusedWindow(const sp token, const sp focusedToken, nsecs_t timestampNanos); void setInputWindowsFinished(); void verifyInputWindowInfo(const InputWindowInfo& info) const; InputWindowInfo& getInfo() const { return const_cast(mInfo); } sp mService; sp mQuery; private: sp mSetInputWindowsListener; std::unique_ptr mServerChannel, mClientChannel; InputWindowInfo mInfo; std::mutex mLock; std::condition_variable mSetInputWindowsFinishedCondition; }; struct SetInputWindowsListener : BnSetInputWindowsListener { explicit SetInputWindowsListener(std::function cbFunc) : mCbFunc(cbFunc) {} binder::Status onSetInputWindowsFinished() override; std::function mCbFunc; }; class TestInputManager : public BnInputFlinger { protected: virtual ~TestInputManager(){}; public: TestInputManager(){}; binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles); binder::Status getInputChannels(std::vector<::android::InputChannel>* channels); binder::Status getLastFocusRequest(FocusRequest*); status_t dump(int fd, const Vector& args) override; binder::Status setInputWindows( const std::vector& handles, const sp& setInputWindowsListener) override; binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; binder::Status removeInputChannel(const sp& connectionToken) override; binder::Status setFocusedWindow(const FocusRequest&) override; void reset(); private: mutable Mutex mLock; std::unordered_map>> mHandlesPerDisplay; std::vector> mInputChannels; FocusRequest mFocusRequest; }; class TestInputQuery : public BnInputFlingerQuery { public: TestInputQuery(sp manager) : mManager(manager){}; binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override; binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override; binder::Status getLastFocusRequest(FocusRequest*) override; binder::Status resetInputManager() override; private: sp mManager; }; binder::Status TestInputQuery::getInputWindows( std::vector<::android::InputWindowInfo>* inputHandles) { return mManager->getInputWindows(inputHandles); } binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) { return mManager->getInputChannels(channels); } binder::Status TestInputQuery::getLastFocusRequest(FocusRequest* request) { return mManager->getLastFocusRequest(request); } binder::Status TestInputQuery::resetInputManager() { mManager->reset(); return binder::Status::ok(); } binder::Status SetInputWindowsListener::onSetInputWindowsFinished() { if (mCbFunc != nullptr) { mCbFunc(); } return binder::Status::ok(); } binder::Status TestInputManager::setInputWindows( const std::vector& infos, const sp& setInputWindowsListener) { AutoMutex _l(mLock); for (const auto& info : infos) { mHandlesPerDisplay.emplace(info.displayId, std::vector>()); mHandlesPerDisplay[info.displayId].push_back(new InputWindowHandle(info)); } if (setInputWindowsListener) { setInputWindowsListener->onSetInputWindowsFinished(); } return binder::Status::ok(); } binder::Status TestInputManager::createInputChannel(const std::string& name, InputChannel* outChannel) { AutoMutex _l(mLock); std::unique_ptr serverChannel; std::unique_ptr clientChannel; InputChannel::openInputChannelPair(name, serverChannel, clientChannel); clientChannel->copyTo(*outChannel); mInputChannels.emplace_back(std::move(serverChannel)); return binder::Status::ok(); } binder::Status TestInputManager::removeInputChannel(const sp& connectionToken) { AutoMutex _l(mLock); auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(), [&](std::shared_ptr& c) { return c->getConnectionToken() == connectionToken; }); if (it != mInputChannels.end()) { mInputChannels.erase(it); } return binder::Status::ok(); } status_t TestInputManager::dump(int fd, const Vector& args) { std::string dump; dump += " InputFlinger dump\n"; ::write(fd, dump.c_str(), dump.size()); return NO_ERROR; } binder::Status TestInputManager::getInputWindows( std::vector<::android::InputWindowInfo>* inputInfos) { for (auto& [displayId, inputHandles] : mHandlesPerDisplay) { for (auto& inputHandle : inputHandles) { inputInfos->push_back(*inputHandle->getInfo()); } } return binder::Status::ok(); } binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) { channels->clear(); for (std::shared_ptr& channel : mInputChannels) { channels->push_back(*channel); } return binder::Status::ok(); } binder::Status TestInputManager::getLastFocusRequest(FocusRequest* request) { *request = mFocusRequest; return binder::Status::ok(); } binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) { mFocusRequest = request; return binder::Status::ok(); } void TestInputManager::reset() { mHandlesPerDisplay.clear(); mInputChannels.clear(); mFocusRequest = FocusRequest(); } void InputFlingerServiceTest::SetUp() { mSetInputWindowsListener = new SetInputWindowsListener([&]() { std::unique_lock lock(mLock); mSetInputWindowsFinishedCondition.notify_all(); }); InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel); mInfo.token = TestInfoToken; mInfo.id = TestInfoId; mInfo.name = TestInfoName; mInfo.flags = TestInfoFlags; mInfo.type = TestInfoType; mInfo.dispatchingTimeout = TestInfoDispatchingTimeout; mInfo.frameLeft = TestInfoFrameLeft; mInfo.frameTop = TestInfoFrameTop; mInfo.frameRight = TestInfoFrameRight; mInfo.frameBottom = TestInfoFrameBottom; mInfo.surfaceInset = TestInfoSurfaceInset; mInfo.globalScaleFactor = TestInfoGlobalScaleFactor; mInfo.transform.set({TestInfoWindowXScale, 0, TestInfoFrameLeft, 0, TestInfoWindowYScale, TestInfoFrameTop, 0, 0, 1}); mInfo.touchableRegion = TestInfoTouchableRegion; mInfo.visible = TestInfoVisible; mInfo.trustedOverlay = TestInfoTrustedOverlay; mInfo.focusable = TestInfoFocusable; mInfo.hasWallpaper = TestInfoHasWallpaper; mInfo.paused = TestInfoPaused; mInfo.ownerPid = TestInfoOwnerPid; mInfo.ownerUid = TestInfoOwnerUid; mInfo.inputFeatures = TestInfoInputFeatures; mInfo.displayId = TestInfoDisplayId; mInfo.portalToDisplayId = TestInfoPortalToDisplayId; mInfo.replaceTouchableRegionWithCrop = TestInfoReplaceTouchableRegionWithCrop; mInfo.touchableRegionCropHandle = TestInfoTouchableRegionCropHandle; mInfo.applicationInfo.name = TestAppInfoName; mInfo.applicationInfo.token = TestAppInfoToken; mInfo.applicationInfo.dispatchingTimeoutMillis = std::chrono::duration_cast(TestAppInfoDispatchingTimeout) .count(); InitializeInputFlinger(); } void InputFlingerServiceTest::TearDown() { mQuery->resetInputManager(); } void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const { EXPECT_EQ(mInfo, info); } void InputFlingerServiceTest::InitializeInputFlinger() { sp input(defaultServiceManager()->waitForService(kTestServiceName)); ASSERT_TRUE(input != nullptr); mService = interface_cast(input); input = defaultServiceManager()->waitForService(kQueryServiceName); ASSERT_TRUE(input != nullptr); mQuery = interface_cast(input); } void InputFlingerServiceTest::setInputWindowsByInfos(const std::vector& infos) { std::unique_lock lock(mLock); mService->setInputWindows(infos, mSetInputWindowsListener); // Verify listener call EXPECT_NE(mSetInputWindowsFinishedCondition.wait_for(lock, 1s), std::cv_status::timeout); } void InputFlingerServiceTest::setFocusedWindow(const sp token, const sp focusedToken, nsecs_t timestampNanos) { FocusRequest request; request.token = TestInfoToken; request.focusedToken = focusedToken; request.timestamp = timestampNanos; mService->setFocusedWindow(request); // call set input windows and wait for the callback to drain the queue. setInputWindowsByInfos(std::vector()); } /** * Test InputFlinger service interface SetInputWindows */ TEST_F(InputFlingerServiceTest, InputWindow_SetInputWindows) { std::vector infos = {getInfo()}; setInputWindowsByInfos(infos); // Verify input windows from service std::vector<::android::InputWindowInfo> windowInfos; mQuery->getInputWindows(&windowInfos); for (const ::android::InputWindowInfo& windowInfo : windowInfos) { verifyInputWindowInfo(windowInfo); } } /** * Test InputFlinger service interface createInputChannel */ TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) { // Test that the unblocked file descriptor flag is kept across processes over binder // transactions. InputChannel channel; ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk()); const base::unique_fd& fd = channel.getFd(); ASSERT_TRUE(fd.ok()); const int result = fcntl(fd, F_GETFL); EXPECT_NE(result, -1); EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK); } TEST_F(InputFlingerServiceTest, InputWindow_CreateInputChannel) { InputChannel channel; ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk()); std::vector<::android::InputChannel> channels; mQuery->getInputChannels(&channels); ASSERT_EQ(channels.size(), 1UL); EXPECT_EQ(channels[0].getConnectionToken(), channel.getConnectionToken()); mService->removeInputChannel(channel.getConnectionToken()); mQuery->getInputChannels(&channels); EXPECT_EQ(channels.size(), 0UL); } TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindow) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); setFocusedWindow(TestInfoToken, nullptr /* focusedToken */, now); FocusRequest request; mQuery->getLastFocusRequest(&request); EXPECT_EQ(request.token, TestInfoToken); EXPECT_EQ(request.focusedToken, nullptr); EXPECT_EQ(request.timestamp, now); } TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindowWithFocusedToken) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); setFocusedWindow(TestInfoToken, FocusedTestInfoToken, now); FocusRequest request; mQuery->getLastFocusRequest(&request); EXPECT_EQ(request.token, TestInfoToken); EXPECT_EQ(request.focusedToken, FocusedTestInfoToken); EXPECT_EQ(request.timestamp, now); } } // namespace android int main(int argc, char** argv) { pid_t forkPid = fork(); if (forkPid == 0) { // Server process android::sp manager = new android::TestInputManager(); android::sp query = new android::TestInputQuery(manager); android::defaultServiceManager()->addService(android::kTestServiceName, manager, false /*allowIsolated*/); android::defaultServiceManager()->addService(android::kQueryServiceName, query, false /*allowIsolated*/); android::ProcessState::self()->startThreadPool(); android::IPCThreadState::self()->joinThreadPool(); } else { android::ProcessState::self()->startThreadPool(); ::testing::InitGoogleTest(&argc, argv); int result = RUN_ALL_TESTS(); kill(forkPid, SIGKILL); return result; } return 0; }