blob: 4023c66e52dbe1e11877b3f682b3f74c7776bd85 [file] [log] [blame]
/*
* 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/DisplayConfig.h>
#include <utils/String8.h>
#include <limits>
#include "BufferGenerator.h"
#include "utils/CallbackUtils.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 = new SurfaceComposerClient;
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, 0)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
.setFrame(mSurfaceControl, Rect(0, 0, width, height))
.setBuffer(mSurfaceControl, gb)
.setAcquireFence(mSurfaceControl, 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 = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mClient->initCheck());
mPrimaryDisplay = mClient->getInternalDisplayToken();
DisplayConfig config;
mClient->getActiveDisplayConfig(mPrimaryDisplay, &config);
mDisplayWidth = config.resolution.getWidth();
mDisplayHeight = config.resolution.getHeight();
Transaction setupTransaction;
setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
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()->getService(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, 0)
.setLayer(sc, std::numeric_limits<int32_t>::max() - 1)
.setBuffer(sc, gb)
.setAcquireFence(sc, 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