/* * Copyright (C) 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. */ #define LOG_TAG "buffferpool_unit_test" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "allocator.h" using android::hardware::configureRpcThreadpool; using android::hardware::hidl_handle; using android::hardware::media::bufferpool::V2_0::IClientManager; using android::hardware::media::bufferpool::V2_0::ResultStatus; using android::hardware::media::bufferpool::V2_0::implementation::BufferId; using android::hardware::media::bufferpool::V2_0::implementation::ClientManager; using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId; using android::hardware::media::bufferpool::V2_0::implementation::TransactionId; using android::hardware::media::bufferpool::BufferPoolData; namespace { // communication message types between processes. enum PipeCommand : int32_t { INIT_OK = 0, INIT_ERROR, SEND, RECEIVE_OK, RECEIVE_ERROR, }; // communication message between processes. union PipeMessage { struct { int32_t command; BufferId bufferId; ConnectionId connectionId; TransactionId transactionId; int64_t timestampUs; } data; char array[0]; }; // media.bufferpool test setup class BufferpoolMultiTest : public ::testing::Test { public: virtual void SetUp() override { ResultStatus status; mReceiverPid = -1; mConnectionValid = false; ASSERT_TRUE(pipe(mCommandPipeFds) == 0); ASSERT_TRUE(pipe(mResultPipeFds) == 0); mReceiverPid = fork(); ASSERT_TRUE(mReceiverPid >= 0); if (mReceiverPid == 0) { doReceiver(); // In order to ignore gtest behaviour, wait for being killed from // tearDown pause(); } mManager = ClientManager::getInstance(); ASSERT_NE(mManager, nullptr); mAllocator = std::make_shared(); ASSERT_TRUE((bool)mAllocator); status = mManager->create(mAllocator, &mConnectionId); ASSERT_TRUE(status == ResultStatus::OK); mConnectionValid = true; } virtual void TearDown() override { if (mReceiverPid > 0) { kill(mReceiverPid, SIGKILL); int wstatus; wait(&wstatus); } if (mConnectionValid) { mManager->close(mConnectionId); } } protected: static void description(const std::string& description) { RecordProperty("description", description); } android::sp mManager; std::shared_ptr mAllocator; bool mConnectionValid; ConnectionId mConnectionId; pid_t mReceiverPid; int mCommandPipeFds[2]; int mResultPipeFds[2]; bool sendMessage(int *pipes, const PipeMessage &message) { int ret = write(pipes[1], message.array, sizeof(PipeMessage)); return ret == sizeof(PipeMessage); } bool receiveMessage(int *pipes, PipeMessage *message) { int ret = read(pipes[0], message->array, sizeof(PipeMessage)); return ret == sizeof(PipeMessage); } void doReceiver() { configureRpcThreadpool(1, false); PipeMessage message; mManager = ClientManager::getInstance(); if (!mManager) { message.data.command = PipeCommand::INIT_ERROR; sendMessage(mResultPipeFds, message); return; } android::status_t status = mManager->registerAsService(); if (status != android::OK) { message.data.command = PipeCommand::INIT_ERROR; sendMessage(mResultPipeFds, message); return; } message.data.command = PipeCommand::INIT_OK; sendMessage(mResultPipeFds, message); receiveMessage(mCommandPipeFds, &message); { native_handle_t *rhandle = nullptr; std::shared_ptr rbuffer; ResultStatus status = mManager->receive( message.data.connectionId, message.data.transactionId, message.data.bufferId, message.data.timestampUs, &rhandle, &rbuffer); mManager->close(message.data.connectionId); if (status != ResultStatus::OK) { message.data.command = PipeCommand::RECEIVE_ERROR; sendMessage(mResultPipeFds, message); return; } if (!TestBufferPoolAllocator::Verify(rhandle, 0x77)) { message.data.command = PipeCommand::RECEIVE_ERROR; sendMessage(mResultPipeFds, message); return; } if (rhandle) { native_handle_close(rhandle); native_handle_delete(rhandle); } } message.data.command = PipeCommand::RECEIVE_OK; sendMessage(mResultPipeFds, message); } }; // Buffer transfer test between processes. TEST_F(BufferpoolMultiTest, TransferBuffer) { ResultStatus status; PipeMessage message; ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)); android::sp receiver = IClientManager::getService(); ConnectionId receiverId; ASSERT_TRUE((bool)receiver); status = mManager->registerSender(receiver, mConnectionId, &receiverId); ASSERT_TRUE(status == ResultStatus::OK); { native_handle_t *shandle = nullptr; std::shared_ptr sbuffer; TransactionId transactionId; int64_t postUs; std::vector vecParams; getTestAllocatorParams(&vecParams); status = mManager->allocate(mConnectionId, vecParams, &shandle, &sbuffer); ASSERT_TRUE(status == ResultStatus::OK); ASSERT_TRUE(TestBufferPoolAllocator::Fill(shandle, 0x77)); if (shandle) { native_handle_close(shandle); native_handle_delete(shandle); } status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs); ASSERT_TRUE(status == ResultStatus::OK); message.data.command = PipeCommand::SEND; message.data.bufferId = sbuffer->mId; message.data.connectionId = receiverId; message.data.transactionId = transactionId; message.data.timestampUs = postUs; sendMessage(mCommandPipeFds, message); } EXPECT_TRUE(receiveMessage(mResultPipeFds, &message)); EXPECT_TRUE(message.data.command == PipeCommand::RECEIVE_OK); } } // anonymous namespace int main(int argc, char** argv) { android::hardware::details::setTrebleTestingOverride(true); ::testing::InitGoogleTest(&argc, argv); int status = RUN_ALL_TESTS(); LOG(INFO) << "Test result = " << status; return status; }