| /* |
| * Copyright (C) 2019 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 <binder/IInterface.h> |
| #include <binder/IPCThreadState.h> |
| #include <binder/IServiceManager.h> |
| #include <binder/ProcessState.h> |
| #include <gtest/gtest.h> |
| #include <gui/ISurfaceComposer.h> |
| #include <gui/LayerState.h> |
| #include <gui/Surface.h> |
| #include <gui/SurfaceComposerClient.h> |
| #include <ui/DisplayMode.h> |
| #include <utils/String8.h> |
| |
| #include <limits> |
| |
| #include <gui/test/CallbackUtils.h> |
| #include "BufferGenerator.h" |
| #include "utils/ColorUtils.h" |
| #include "utils/TransactionUtils.h" |
| |
| namespace android { |
| |
| namespace test { |
| |
| using Transaction = SurfaceComposerClient::Transaction; |
| using CallbackInfo = SurfaceComposerClient::CallbackInfo; |
| using TCLHash = SurfaceComposerClient::TCLHash; |
| using android::hardware::graphics::common::V1_1::BufferUsage; |
| |
| class TransactionHelper : public Transaction { |
| public: |
| size_t getNumListeners() { return mListenerCallbacks.size(); } |
| |
| std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> |
| getListenerCallbacks() { |
| return mListenerCallbacks; |
| } |
| }; |
| |
| class IPCTestUtils { |
| public: |
| static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult, |
| bool finalState = false); |
| static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence); |
| }; |
| |
| class IIPCTest : public IInterface { |
| public: |
| DECLARE_META_INTERFACE(IPCTest) |
| enum class Tag : uint32_t { |
| SetDeathToken = IBinder::FIRST_CALL_TRANSACTION, |
| InitClient, |
| CreateTransaction, |
| MergeAndApply, |
| VerifyCallbacks, |
| CleanUp, |
| Last, |
| }; |
| |
| virtual status_t setDeathToken(sp<IBinder>& token) = 0; |
| |
| virtual status_t initClient() = 0; |
| |
| virtual status_t createTransaction(TransactionHelper* outTransaction, uint32_t width, |
| uint32_t height) = 0; |
| |
| virtual status_t mergeAndApply(TransactionHelper transaction) = 0; |
| |
| virtual status_t verifyCallbacks() = 0; |
| |
| virtual status_t cleanUp() = 0; |
| }; |
| |
| class BpIPCTest : public SafeBpInterface<IIPCTest> { |
| public: |
| explicit BpIPCTest(const sp<IBinder>& impl) : SafeBpInterface<IIPCTest>(impl, "BpIPCTest") {} |
| |
| status_t setDeathToken(sp<IBinder>& token) { |
| return callRemote<decltype(&IIPCTest::setDeathToken)>(Tag::SetDeathToken, token); |
| } |
| |
| status_t initClient() { return callRemote<decltype(&IIPCTest::initClient)>(Tag::InitClient); } |
| |
| status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) { |
| return callRemote<decltype(&IIPCTest::createTransaction)>(Tag::CreateTransaction, |
| transaction, width, height); |
| } |
| |
| status_t mergeAndApply(TransactionHelper transaction) { |
| return callRemote<decltype(&IIPCTest::mergeAndApply)>(Tag::MergeAndApply, transaction); |
| } |
| |
| status_t verifyCallbacks() { |
| return callRemote<decltype(&IIPCTest::verifyCallbacks)>(Tag::VerifyCallbacks); |
| } |
| |
| status_t cleanUp() { return callRemote<decltype(&IIPCTest::cleanUp)>(Tag::CleanUp); } |
| }; |
| |
| IMPLEMENT_META_INTERFACE(IPCTest, "android.gfx.tests.IIPCTest") |
| |
| class onTestDeath : public IBinder::DeathRecipient { |
| public: |
| void binderDied(const wp<IBinder>& /*who*/) override { |
| ALOGE("onTestDeath::binderDied, exiting"); |
| exit(0); |
| } |
| }; |
| |
| sp<onTestDeath> getDeathToken() { |
| static sp<onTestDeath> token = new onTestDeath; |
| return token; |
| } |
| |
| class BnIPCTest : public SafeBnInterface<IIPCTest> { |
| public: |
| BnIPCTest() : SafeBnInterface("BnIPCTest") {} |
| |
| status_t setDeathToken(sp<IBinder>& token) override { |
| return token->linkToDeath(getDeathToken()); |
| } |
| |
| status_t initClient() override { |
| mClient = sp<SurfaceComposerClient>::make(); |
| auto err = mClient->initCheck(); |
| return err; |
| } |
| |
| status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) { |
| if (transaction == nullptr) { |
| ALOGE("Error in createTransaction: transaction is nullptr"); |
| return BAD_VALUE; |
| } |
| mSurfaceControl = mClient->createSurface(String8("parentProcessSurface"), 0, 0, |
| PIXEL_FORMAT_RGBA_8888, |
| ISurfaceComposerClient::eFXSurfaceBufferState, |
| /*parent*/ nullptr); |
| sp<GraphicBuffer> gb; |
| sp<Fence> fence; |
| int err = IPCTestUtils::getBuffer(&gb, &fence); |
| if (err != NO_ERROR) return err; |
| |
| TransactionUtils::fillGraphicBufferColor(gb, |
| {0, 0, static_cast<int32_t>(width), |
| static_cast<int32_t>(height)}, |
| Color::RED); |
| transaction->setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK) |
| .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max()) |
| .setBuffer(mSurfaceControl, gb, fence) |
| .show(mSurfaceControl) |
| .addTransactionCompletedCallback(mCallbackHelper.function, |
| mCallbackHelper.getContext()); |
| return NO_ERROR; |
| } |
| |
| status_t mergeAndApply(TransactionHelper /*transaction*/) { |
| // transaction.apply(); |
| return NO_ERROR; |
| } |
| |
| status_t verifyCallbacks() { |
| ExpectedResult expected; |
| expected.addSurface(ExpectedResult::Transaction::PRESENTED, mSurfaceControl); |
| EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(mCallbackHelper, expected, true)); |
| return NO_ERROR; |
| } |
| |
| status_t cleanUp() { |
| if (mClient) mClient->dispose(); |
| mSurfaceControl = nullptr; |
| IPCThreadState::self()->stopProcess(); |
| return NO_ERROR; |
| } |
| |
| status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, |
| uint32_t /*flags*/) override { |
| EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION); |
| EXPECT_LT(code, static_cast<uint32_t>(IIPCTest::Tag::Last)); |
| switch (static_cast<IIPCTest::Tag>(code)) { |
| case IIPCTest::Tag::SetDeathToken: |
| return callLocal(data, reply, &IIPCTest::setDeathToken); |
| case IIPCTest::Tag::InitClient: |
| return callLocal(data, reply, &IIPCTest::initClient); |
| case IIPCTest::Tag::CreateTransaction: |
| return callLocal(data, reply, &IIPCTest::createTransaction); |
| case IIPCTest::Tag::MergeAndApply: |
| return callLocal(data, reply, &IIPCTest::mergeAndApply); |
| case IIPCTest::Tag::VerifyCallbacks: |
| return callLocal(data, reply, &IIPCTest::verifyCallbacks); |
| case IIPCTest::Tag::CleanUp: |
| return callLocal(data, reply, &IIPCTest::cleanUp); |
| default: |
| return UNKNOWN_ERROR; |
| } |
| } |
| |
| private: |
| sp<SurfaceComposerClient> mClient; |
| sp<SurfaceControl> mSurfaceControl; |
| CallbackHelper mCallbackHelper; |
| }; |
| |
| class IPCTest : public ::testing::Test { |
| public: |
| IPCTest() : mDeathRecipient(new BBinder), mRemote(initRemoteService()) { |
| ProcessState::self()->startThreadPool(); |
| } |
| void SetUp() { |
| mClient = sp<SurfaceComposerClient>::make(); |
| ASSERT_EQ(NO_ERROR, mClient->initCheck()); |
| |
| const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); |
| ASSERT_FALSE(ids.empty()); |
| mPrimaryDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); |
| ui::DisplayMode mode; |
| mClient->getActiveDisplayMode(mPrimaryDisplay, &mode); |
| mDisplayWidth = mode.resolution.getWidth(); |
| mDisplayHeight = mode.resolution.getHeight(); |
| |
| Transaction setupTransaction; |
| setupTransaction.setDisplayLayerStack(mPrimaryDisplay, ui::DEFAULT_LAYER_STACK); |
| setupTransaction.apply(); |
| } |
| |
| protected: |
| sp<IIPCTest> initRemoteService(); |
| |
| sp<IBinder> mDeathRecipient; |
| sp<IIPCTest> mRemote; |
| sp<SurfaceComposerClient> mClient; |
| sp<IBinder> mPrimaryDisplay; |
| uint32_t mDisplayWidth; |
| uint32_t mDisplayHeight; |
| sp<SurfaceControl> sc; |
| }; |
| |
| status_t IPCTestUtils::getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { |
| static BufferGenerator bufferGenerator; |
| return bufferGenerator.get(outBuffer, outFence); |
| } |
| |
| void IPCTestUtils::waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult, |
| bool finalState) { |
| CallbackData callbackData; |
| ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData)); |
| EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData)); |
| |
| if (finalState) { |
| ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState()); |
| } |
| } |
| |
| sp<IIPCTest> IPCTest::initRemoteService() { |
| static std::mutex mMutex; |
| static sp<IIPCTest> remote; |
| const String16 serviceName("IPCTest"); |
| |
| std::unique_lock<decltype(mMutex)> lock; |
| if (remote == nullptr) { |
| pid_t forkPid = fork(); |
| EXPECT_NE(forkPid, -1); |
| |
| if (forkPid == 0) { |
| sp<IIPCTest> nativeService = new BnIPCTest; |
| if (!nativeService) { |
| ALOGE("null service..."); |
| } |
| status_t err = defaultServiceManager()->addService(serviceName, |
| IInterface::asBinder(nativeService)); |
| if (err != NO_ERROR) { |
| ALOGE("failed to add service: %d", err); |
| } |
| ProcessState::self()->startThreadPool(); |
| IPCThreadState::self()->joinThreadPool(); |
| [&]() { exit(0); }(); |
| } |
| sp<IBinder> binder = defaultServiceManager()->waitForService(serviceName); |
| remote = interface_cast<IIPCTest>(binder); |
| remote->setDeathToken(mDeathRecipient); |
| } |
| return remote; |
| } |
| |
| TEST_F(IPCTest, MergeBasic) { |
| CallbackHelper helper1; |
| sc = mClient->createSurface(String8("parentProcessSurface"), 0, 0, PIXEL_FORMAT_RGBA_8888, |
| ISurfaceComposerClient::eFXSurfaceBufferState, |
| /*parent*/ nullptr); |
| sp<GraphicBuffer> gb; |
| sp<Fence> fence; |
| int err = IPCTestUtils::getBuffer(&gb, &fence); |
| ASSERT_EQ(NO_ERROR, err); |
| TransactionUtils::fillGraphicBufferColor(gb, |
| {0, 0, static_cast<int32_t>(mDisplayWidth), |
| static_cast<int32_t>(mDisplayHeight)}, |
| Color::RED); |
| |
| Transaction transaction; |
| transaction.setLayerStack(sc, ui::DEFAULT_LAYER_STACK) |
| .setLayer(sc, std::numeric_limits<int32_t>::max() - 1) |
| .setBuffer(sc, gb, fence) |
| .show(sc) |
| .addTransactionCompletedCallback(helper1.function, helper1.getContext()); |
| |
| TransactionHelper remote; |
| mRemote->initClient(); |
| mRemote->createTransaction(&remote, mDisplayWidth / 2, mDisplayHeight / 2); |
| ASSERT_EQ(1, remote.getNumListeners()); |
| auto remoteListenerCallbacks = remote.getListenerCallbacks(); |
| auto remoteCallback = remoteListenerCallbacks.begin(); |
| auto remoteCallbackInfo = remoteCallback->second; |
| auto remoteListenerScs = remoteCallbackInfo.surfaceControls; |
| ASSERT_EQ(1, remoteCallbackInfo.callbackIds.size()); |
| ASSERT_EQ(1, remoteListenerScs.size()); |
| |
| sp<SurfaceControl> remoteSc = *(remoteListenerScs.begin()); |
| transaction.merge(std::move(remote)); |
| transaction.apply(); |
| |
| sleep(1); |
| ExpectedResult expected; |
| expected.addSurface(ExpectedResult::Transaction::PRESENTED, sc); |
| expected.addSurface(ExpectedResult::Transaction::PRESENTED, remoteSc); |
| EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(helper1, expected, true)); |
| |
| mRemote->verifyCallbacks(); |
| mRemote->cleanUp(); |
| } |
| |
| } // namespace test |
| } // namespace android |