1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "BufferpoolUnitTest"
19 #include <utils/Log.h>
20
21 #include <binder/ProcessState.h>
22 #include <bufferpool/ClientManager.h>
23 #include <gtest/gtest.h>
24 #include <hidl/LegacySupport.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <unordered_set>
28 #include <vector>
29 #include "allocator.h"
30
31 using android::hardware::configureRpcThreadpool;
32 using android::hardware::media::bufferpool::BufferPoolData;
33 using android::hardware::media::bufferpool::V2_0::IClientManager;
34 using android::hardware::media::bufferpool::V2_0::ResultStatus;
35 using android::hardware::media::bufferpool::V2_0::implementation::BufferId;
36 using android::hardware::media::bufferpool::V2_0::implementation::ClientManager;
37 using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId;
38 using android::hardware::media::bufferpool::V2_0::implementation::TransactionId;
39
40 using namespace android;
41
42 // communication message types between processes.
43 enum PipeCommand : int32_t {
44 INIT,
45 TRANSFER,
46 STOP,
47
48 INIT_OK,
49 INIT_ERROR,
50 TRANSFER_OK,
51 TRANSFER_ERROR,
52 STOP_OK,
53 STOP_ERROR,
54 };
55
56 // communication message between processes.
57 union PipeMessage {
58 struct {
59 int32_t command;
60 int32_t memsetValue;
61 BufferId bufferId;
62 ConnectionId connectionId;
63 TransactionId transactionId;
64 int64_t timestampUs;
65 } data;
66 char array[0];
67 };
68
69 static int32_t kNumIterationCount = 10;
70
71 class BufferpoolTest {
72 public:
BufferpoolTest()73 BufferpoolTest() : mConnectionValid(false), mManager(nullptr), mAllocator(nullptr) {
74 mConnectionId = -1;
75 mReceiverId = -1;
76 }
77
~BufferpoolTest()78 ~BufferpoolTest() {
79 if (mConnectionValid) {
80 mManager->close(mConnectionId);
81 }
82 }
83
84 protected:
85 bool mConnectionValid;
86 ConnectionId mConnectionId;
87 ConnectionId mReceiverId;
88
89 android::sp<ClientManager> mManager;
90 std::shared_ptr<BufferPoolAllocator> mAllocator;
91
92 void setupBufferpoolManager();
93 };
94
setupBufferpoolManager()95 void BufferpoolTest::setupBufferpoolManager() {
96 // retrieving per process bufferpool object sp<ClientManager>
97 mManager = ClientManager::getInstance();
98 ASSERT_NE(mManager, nullptr) << "unable to get ClientManager\n";
99
100 mAllocator = std::make_shared<TestBufferPoolAllocator>();
101 ASSERT_NE(mAllocator, nullptr) << "unable to create TestBufferPoolAllocator\n";
102
103 // set-up local bufferpool connection for sender
104 ResultStatus status = mManager->create(mAllocator, &mConnectionId);
105 ASSERT_EQ(status, ResultStatus::OK)
106 << "unable to set-up local bufferpool connection for sender\n";
107 mConnectionValid = true;
108 }
109
110 class BufferpoolUnitTest : public BufferpoolTest, public ::testing::Test {
111 public:
SetUp()112 virtual void SetUp() override { setupBufferpoolManager(); }
113
TearDown()114 virtual void TearDown() override {}
115 };
116
117 class BufferpoolFunctionalityTest : public BufferpoolTest, public ::testing::Test {
118 public:
SetUp()119 virtual void SetUp() override {
120 mReceiverPid = -1;
121
122 ASSERT_TRUE(pipe(mCommandPipeFds) == 0) << "pipe connection failed for commandPipe\n";
123 ASSERT_TRUE(pipe(mResultPipeFds) == 0) << "pipe connection failed for resultPipe\n";
124
125 mReceiverPid = fork();
126 ASSERT_TRUE(mReceiverPid >= 0) << "fork failed\n";
127
128 if (mReceiverPid == 0) {
129 doReceiver();
130 // In order to ignore gtest behaviour, wait for being killed from tearDown
131 pause();
132 }
133 setupBufferpoolManager();
134 }
135
TearDown()136 virtual void TearDown() override {
137 if (mReceiverPid > 0) {
138 kill(mReceiverPid, SIGKILL);
139 int wstatus;
140 wait(&wstatus);
141 }
142 }
143
144 protected:
145 pid_t mReceiverPid;
146 int mCommandPipeFds[2];
147 int mResultPipeFds[2];
148
sendMessage(int * pipes,const PipeMessage & message)149 bool sendMessage(int* pipes, const PipeMessage& message) {
150 int ret = write(pipes[1], message.array, sizeof(PipeMessage));
151 return ret == sizeof(PipeMessage);
152 }
153
receiveMessage(int * pipes,PipeMessage * message)154 bool receiveMessage(int* pipes, PipeMessage* message) {
155 int ret = read(pipes[0], message->array, sizeof(PipeMessage));
156 return ret == sizeof(PipeMessage);
157 }
158
159 void doReceiver();
160 };
161
doReceiver()162 void BufferpoolFunctionalityTest::doReceiver() {
163 // Configures the threadpool used for handling incoming RPC calls in this process.
164 configureRpcThreadpool(1 /*threads*/, false /*willJoin*/);
165 bool receiverRunning = true;
166 while (receiverRunning) {
167 PipeMessage message;
168 receiveMessage(mCommandPipeFds, &message);
169 ResultStatus err = ResultStatus::OK;
170 switch (message.data.command) {
171 case PipeCommand::INIT: {
172 // receiver manager creation
173 mManager = ClientManager::getInstance();
174 if (!mManager) {
175 message.data.command = PipeCommand::INIT_ERROR;
176 sendMessage(mResultPipeFds, message);
177 return;
178 }
179
180 android::status_t status = mManager->registerAsService();
181 if (status != android::OK) {
182 message.data.command = PipeCommand::INIT_ERROR;
183 sendMessage(mResultPipeFds, message);
184 return;
185 }
186 message.data.command = PipeCommand::INIT_OK;
187 sendMessage(mResultPipeFds, message);
188 break;
189 }
190 case PipeCommand::TRANSFER: {
191 native_handle_t* receiveHandle = nullptr;
192 std::shared_ptr<BufferPoolData> receiveBuffer;
193 err = mManager->receive(message.data.connectionId, message.data.transactionId,
194 message.data.bufferId, message.data.timestampUs,
195 &receiveHandle, &receiveBuffer);
196 if (err != ResultStatus::OK) {
197 message.data.command = PipeCommand::TRANSFER_ERROR;
198 sendMessage(mResultPipeFds, message);
199 return;
200 }
201 if (!TestBufferPoolAllocator::Verify(receiveHandle, message.data.memsetValue)) {
202 message.data.command = PipeCommand::TRANSFER_ERROR;
203 sendMessage(mResultPipeFds, message);
204 return;
205 }
206 if (receiveHandle) {
207 native_handle_close(receiveHandle);
208 native_handle_delete(receiveHandle);
209 }
210 receiveHandle = nullptr;
211 receiveBuffer.reset();
212 message.data.command = PipeCommand::TRANSFER_OK;
213 sendMessage(mResultPipeFds, message);
214 break;
215 }
216 case PipeCommand::STOP: {
217 err = mManager->close(message.data.connectionId);
218 if (err != ResultStatus::OK) {
219 message.data.command = PipeCommand::STOP_ERROR;
220 sendMessage(mResultPipeFds, message);
221 return;
222 }
223 message.data.command = PipeCommand::STOP_OK;
224 sendMessage(mResultPipeFds, message);
225 receiverRunning = false;
226 break;
227 }
228 default:
229 ALOGE("unknown command. try again");
230 break;
231 }
232 }
233 }
234
235 // Buffer allocation test.
236 // Check whether each buffer allocation is done successfully with unique buffer id.
TEST_F(BufferpoolUnitTest,AllocateBuffer)237 TEST_F(BufferpoolUnitTest, AllocateBuffer) {
238 std::vector<uint8_t> vecParams;
239 getTestAllocatorParams(&vecParams);
240
241 std::vector<std::shared_ptr<BufferPoolData>> buffers{};
242 std::vector<native_handle_t*> allocHandle{};
243 ResultStatus status;
244 for (int i = 0; i < kNumIterationCount; ++i) {
245 native_handle_t* handle = nullptr;
246 std::shared_ptr<BufferPoolData> buffer{};
247 status = mManager->allocate(mConnectionId, vecParams, &handle, &buffer);
248 ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for " << i << "iteration";
249
250 buffers.push_back(std::move(buffer));
251 if (handle) {
252 allocHandle.push_back(std::move(handle));
253 }
254 }
255
256 for (int i = 0; i < kNumIterationCount; ++i) {
257 for (int j = i + 1; j < kNumIterationCount; ++j) {
258 ASSERT_TRUE(buffers[i]->mId != buffers[j]->mId) << "allocated buffers are not unique";
259 }
260 }
261 // delete the buffer handles
262 for (auto handle : allocHandle) {
263 native_handle_close(handle);
264 native_handle_delete(handle);
265 }
266 // clear the vectors
267 buffers.clear();
268 allocHandle.clear();
269 }
270
271 // Buffer recycle test.
272 // Check whether de-allocated buffers are recycled.
TEST_F(BufferpoolUnitTest,RecycleBuffer)273 TEST_F(BufferpoolUnitTest, RecycleBuffer) {
274 std::vector<uint8_t> vecParams;
275 getTestAllocatorParams(&vecParams);
276
277 ResultStatus status;
278 std::vector<BufferId> bid{};
279 std::vector<native_handle_t*> allocHandle{};
280 for (int i = 0; i < kNumIterationCount; ++i) {
281 native_handle_t* handle = nullptr;
282 std::shared_ptr<BufferPoolData> buffer;
283 status = mManager->allocate(mConnectionId, vecParams, &handle, &buffer);
284 ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for " << i << "iteration";
285
286 bid.push_back(buffer->mId);
287 if (handle) {
288 allocHandle.push_back(std::move(handle));
289 }
290 buffer.reset();
291 }
292
293 std::unordered_set<BufferId> set(bid.begin(), bid.end());
294 ASSERT_EQ(set.size(), 1) << "buffers are not recycled properly";
295
296 // delete the buffer handles
297 for (auto handle : allocHandle) {
298 native_handle_close(handle);
299 native_handle_delete(handle);
300 }
301 allocHandle.clear();
302 }
303
304 // Validate cache evict and invalidate APIs.
TEST_F(BufferpoolUnitTest,FlushTest)305 TEST_F(BufferpoolUnitTest, FlushTest) {
306 std::vector<uint8_t> vecParams;
307 getTestAllocatorParams(&vecParams);
308
309 ResultStatus status = mManager->registerSender(mManager, mConnectionId, &mReceiverId);
310 ASSERT_TRUE(status == ResultStatus::ALREADY_EXISTS && mReceiverId == mConnectionId);
311
312 // testing empty flush
313 status = mManager->flush(mConnectionId);
314 ASSERT_EQ(status, ResultStatus::OK) << "failed to flush connection : " << mConnectionId;
315
316 std::vector<std::shared_ptr<BufferPoolData>> senderBuffer{};
317 std::vector<native_handle_t*> allocHandle{};
318 std::vector<TransactionId> tid{};
319 std::vector<int64_t> timestampUs{};
320
321 std::map<TransactionId, BufferId> bufferMap{};
322
323 for (int i = 0; i < kNumIterationCount; i++) {
324 int64_t postUs;
325 TransactionId transactionId;
326 native_handle_t* handle = nullptr;
327 std::shared_ptr<BufferPoolData> buffer{};
328 status = mManager->allocate(mConnectionId, vecParams, &handle, &buffer);
329 ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for " << i << " iteration";
330
331 ASSERT_TRUE(TestBufferPoolAllocator::Fill(handle, i));
332
333 status = mManager->postSend(mReceiverId, buffer, &transactionId, &postUs);
334 ASSERT_EQ(status, ResultStatus::OK) << "unable to post send transaction on bufferpool";
335
336 timestampUs.push_back(postUs);
337 tid.push_back(transactionId);
338 bufferMap.insert({transactionId, buffer->mId});
339
340 senderBuffer.push_back(std::move(buffer));
341 if (handle) {
342 allocHandle.push_back(std::move(handle));
343 }
344 buffer.reset();
345 }
346
347 status = mManager->flush(mConnectionId);
348 ASSERT_EQ(status, ResultStatus::OK) << "failed to flush connection : " << mConnectionId;
349
350 std::shared_ptr<BufferPoolData> receiverBuffer{};
351 native_handle_t* recvHandle = nullptr;
352 for (int i = 0; i < kNumIterationCount; i++) {
353 status = mManager->receive(mReceiverId, tid[i], senderBuffer[i]->mId, timestampUs[i],
354 &recvHandle, &receiverBuffer);
355 ASSERT_EQ(status, ResultStatus::OK) << "receive failed for buffer " << senderBuffer[i]->mId;
356
357 // find the buffer id from transaction id
358 auto findIt = bufferMap.find(tid[i]);
359 ASSERT_NE(findIt, bufferMap.end()) << "inconsistent buffer mapping";
360
361 // buffer id received must be same as the buffer id sent
362 ASSERT_EQ(findIt->second, receiverBuffer->mId) << "invalid buffer received";
363
364 ASSERT_TRUE(TestBufferPoolAllocator::Verify(recvHandle, i))
365 << "Message received not same as that sent";
366
367 bufferMap.erase(findIt);
368 if (recvHandle) {
369 native_handle_close(recvHandle);
370 native_handle_delete(recvHandle);
371 }
372 recvHandle = nullptr;
373 receiverBuffer.reset();
374 }
375
376 ASSERT_EQ(bufferMap.size(), 0) << "buffers received is less than the number of buffers sent";
377
378 for (auto handle : allocHandle) {
379 native_handle_close(handle);
380 native_handle_delete(handle);
381 }
382 allocHandle.clear();
383 senderBuffer.clear();
384 timestampUs.clear();
385 }
386
387 // Buffer transfer test between processes.
TEST_F(BufferpoolFunctionalityTest,TransferBuffer)388 TEST_F(BufferpoolFunctionalityTest, TransferBuffer) {
389 // initialize the receiver
390 PipeMessage message;
391 message.data.command = PipeCommand::INIT;
392 sendMessage(mCommandPipeFds, message);
393 ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
394 ASSERT_EQ(message.data.command, PipeCommand::INIT_OK) << "receiver init failed";
395
396 android::sp<IClientManager> receiver = IClientManager::getService();
397 ASSERT_NE(receiver, nullptr) << "getService failed for receiver\n";
398
399 ConnectionId receiverId;
400 ResultStatus status = mManager->registerSender(receiver, mConnectionId, &receiverId);
401 ASSERT_EQ(status, ResultStatus::OK)
402 << "registerSender failed for connection id " << mConnectionId << "\n";
403
404 std::vector<uint8_t> vecParams;
405 getTestAllocatorParams(&vecParams);
406
407 for (int i = 0; i < kNumIterationCount; ++i) {
408 native_handle_t* handle = nullptr;
409 std::shared_ptr<BufferPoolData> buffer;
410 status = mManager->allocate(mConnectionId, vecParams, &handle, &buffer);
411 ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for " << i << "iteration";
412
413 ASSERT_TRUE(TestBufferPoolAllocator::Fill(handle, i))
414 << "Fill fail for buffer handle " << handle << "\n";
415
416 // send the buffer to the receiver
417 int64_t postUs;
418 TransactionId transactionId;
419 status = mManager->postSend(receiverId, buffer, &transactionId, &postUs);
420 ASSERT_EQ(status, ResultStatus::OK)
421 << "postSend failed for receiver " << receiverId << "\n";
422
423 // PipeMessage message;
424 message.data.command = PipeCommand::TRANSFER;
425 message.data.memsetValue = i;
426 message.data.bufferId = buffer->mId;
427 message.data.connectionId = receiverId;
428 message.data.transactionId = transactionId;
429 message.data.timestampUs = postUs;
430 sendMessage(mCommandPipeFds, message);
431 // delete buffer handle
432 if (handle) {
433 native_handle_close(handle);
434 native_handle_delete(handle);
435 }
436 ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
437 ASSERT_EQ(message.data.command, PipeCommand::TRANSFER_OK)
438 << "received error during buffer transfer\n";
439 }
440 message.data.command = PipeCommand::STOP;
441 sendMessage(mCommandPipeFds, message);
442 ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
443 ASSERT_EQ(message.data.command, PipeCommand::STOP_OK)
444 << "received error during buffer transfer\n";
445 }
446
447 /* Validate bufferpool for following corner cases:
448 1. invalid connectionID
449 2. invalid receiver
450 3. when sender is not registered
451 4. when connection is closed
452 */
453 // TODO: Enable when the issue in b/212196495 is fixed
TEST_F(BufferpoolFunctionalityTest,DISABLED_ValidityTest)454 TEST_F(BufferpoolFunctionalityTest, DISABLED_ValidityTest) {
455 std::vector<uint8_t> vecParams;
456 getTestAllocatorParams(&vecParams);
457
458 std::shared_ptr<BufferPoolData> senderBuffer;
459 native_handle_t* allocHandle = nullptr;
460
461 // call allocate() on a random connection id
462 ConnectionId randomId = rand();
463 ResultStatus status = mManager->allocate(randomId, vecParams, &allocHandle, &senderBuffer);
464 EXPECT_TRUE(status == ResultStatus::NOT_FOUND);
465
466 // initialize the receiver
467 PipeMessage message;
468 message.data.command = PipeCommand::INIT;
469 sendMessage(mCommandPipeFds, message);
470 ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
471 ASSERT_EQ(message.data.command, PipeCommand::INIT_OK) << "receiver init failed";
472
473 allocHandle = nullptr;
474 senderBuffer.reset();
475 status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &senderBuffer);
476
477 ASSERT_TRUE(TestBufferPoolAllocator::Fill(allocHandle, 0x77));
478
479 // send buffers w/o registering sender
480 int64_t postUs;
481 TransactionId transactionId;
482
483 // random receiver
484 status = mManager->postSend(randomId, senderBuffer, &transactionId, &postUs);
485 ASSERT_NE(status, ResultStatus::OK) << "bufferpool shouldn't allow send on random receiver";
486
487 // establish connection
488 android::sp<IClientManager> receiver = IClientManager::getService();
489 ASSERT_NE(receiver, nullptr) << "getService failed for receiver\n";
490
491 ConnectionId receiverId;
492 status = mManager->registerSender(receiver, mConnectionId, &receiverId);
493 ASSERT_EQ(status, ResultStatus::OK)
494 << "registerSender failed for connection id " << mConnectionId << "\n";
495
496 allocHandle = nullptr;
497 senderBuffer.reset();
498 status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &senderBuffer);
499 ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for connection " << mConnectionId;
500
501 ASSERT_TRUE(TestBufferPoolAllocator::Fill(allocHandle, 0x88));
502
503 // send the buffer to the receiver
504 status = mManager->postSend(receiverId, senderBuffer, &transactionId, &postUs);
505 ASSERT_EQ(status, ResultStatus::OK) << "postSend failed for receiver " << receiverId << "\n";
506
507 // PipeMessage message;
508 message.data.command = PipeCommand::TRANSFER;
509 message.data.memsetValue = 0x88;
510 message.data.bufferId = senderBuffer->mId;
511 message.data.connectionId = receiverId;
512 message.data.transactionId = transactionId;
513 message.data.timestampUs = postUs;
514 sendMessage(mCommandPipeFds, message);
515 ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
516 ASSERT_EQ(message.data.command, PipeCommand::TRANSFER_OK)
517 << "received error during buffer transfer\n";
518
519 if (allocHandle) {
520 native_handle_close(allocHandle);
521 native_handle_delete(allocHandle);
522 }
523
524 message.data.command = PipeCommand::STOP;
525 sendMessage(mCommandPipeFds, message);
526 ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
527 ASSERT_EQ(message.data.command, PipeCommand::STOP_OK)
528 << "received error during buffer transfer\n";
529
530 // try to send msg to closed connection
531 status = mManager->postSend(receiverId, senderBuffer, &transactionId, &postUs);
532 ASSERT_NE(status, ResultStatus::OK) << "bufferpool shouldn't allow send on closed connection";
533 }
534
main(int argc,char ** argv)535 int main(int argc, char** argv) {
536 android::hardware::details::setTrebleTestingOverride(true);
537 ::testing::InitGoogleTest(&argc, argv);
538 int status = RUN_ALL_TESTS();
539 ALOGV("Test result = %d\n", status);
540 return status;
541 }
542