/* * Copyright (C) 2024 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 using namespace std::string_literals; using android::gui::BufferReleaseChannel; namespace android { namespace { // Helper function to check if two file descriptors point to the same file. bool is_same_file(int fd1, int fd2) { struct stat stat1 {}; if (fstat(fd1, &stat1) != 0) { return false; } struct stat stat2 {}; if (fstat(fd2, &stat2) != 0) { return false; } return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino); } } // namespace class BufferReleaseChannelTest : public testing::Test { protected: std::unique_ptr mConsumer; std::shared_ptr mProducer; void SetUp() override { ASSERT_EQ(OK, BufferReleaseChannel::open("BufferReleaseChannelTest"s, mConsumer, mProducer)); } }; TEST_F(BufferReleaseChannelTest, MessageFlattenable) { ReleaseCallbackId releaseCallbackId{1, 2}; sp releaseFence = sp::make(memfd_create("fake-fence-fd", 0)); uint32_t maxAcquiredBufferCount = 5; std::vector dataBuffer; std::vector fdBuffer; // Verify that we can flatten a message { BufferReleaseChannel::Message message{releaseCallbackId, releaseFence, maxAcquiredBufferCount}; dataBuffer.resize(message.getFlattenedSize()); void* dataPtr = dataBuffer.data(); size_t dataSize = dataBuffer.size(); fdBuffer.resize(message.getFdCount()); int* fdPtr = fdBuffer.data(); size_t fdSize = fdBuffer.size(); ASSERT_EQ(OK, message.flatten(dataPtr, dataSize, fdPtr, fdSize)); // Fence's unique_fd uses fdsan to check ownership of the file descriptor. Normally the file // descriptor is passed through the Unix socket and duplicated (and sent to another process) // so there's no problem with duplicate file descriptor ownership. For this unit test, we // need to set up a duplicate file descriptor to avoid crashing due to duplicate ownership. ASSERT_EQ(releaseFence->get(), fdBuffer[0]); fdBuffer[0] = message.releaseFence->dup(); } // Verify that we can unflatten a message { BufferReleaseChannel::Message message; const void* dataPtr = dataBuffer.data(); size_t dataSize = dataBuffer.size(); const int* fdPtr = fdBuffer.data(); size_t fdSize = fdBuffer.size(); ASSERT_EQ(OK, message.unflatten(dataPtr, dataSize, fdPtr, fdSize)); ASSERT_EQ(releaseCallbackId, message.releaseCallbackId); ASSERT_TRUE(is_same_file(releaseFence->get(), message.releaseFence->get())); ASSERT_EQ(maxAcquiredBufferCount, message.maxAcquiredBufferCount); } } // Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message // available. TEST_F(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { ReleaseCallbackId releaseCallbackId; sp releaseFence; uint32_t maxAcquiredBufferCount; ASSERT_EQ(WOULD_BLOCK, mConsumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount)); } // Verify that we can write a message to the BufferReleaseChannel producer and read that message // using the BufferReleaseChannel consumer. TEST_F(BufferReleaseChannelTest, ProduceAndConsume) { sp fence = sp::make(memfd_create("fake-fence-fd", 0)); for (uint64_t i = 0; i < 64; i++) { ReleaseCallbackId producerId{i, i + 1}; uint32_t maxAcquiredBufferCount = i + 2; ASSERT_EQ(OK, mProducer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount)); } for (uint64_t i = 0; i < 64; i++) { ReleaseCallbackId expectedId{i, i + 1}; uint32_t expectedMaxAcquiredBufferCount = i + 2; ReleaseCallbackId consumerId; sp consumerFence; uint32_t maxAcquiredBufferCount; ASSERT_EQ(OK, mConsumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); ASSERT_EQ(expectedId, consumerId); ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get())); ASSERT_EQ(expectedMaxAcquiredBufferCount, maxAcquiredBufferCount); } } // Verify that BufferReleaseChannel::ConsumerEndpoint's socket can't be written to. TEST_F(BufferReleaseChannelTest, ConsumerSocketReadOnly) { uint64_t data = 0; ASSERT_EQ(-1, write(mConsumer->getFd().get(), &data, sizeof(uint64_t))); ASSERT_EQ(errno, EPIPE); } // Verify that BufferReleaseChannel::ProducerEndpoint's socket can't be read from. TEST_F(BufferReleaseChannelTest, ProducerSocketWriteOnly) { ASSERT_EQ(0, read(mProducer->getFd().get(), nullptr, sizeof(uint64_t))); } } // namespace android