1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
/*
* 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 <string>
#include <vector>
#include <gtest/gtest.h>
#include <gui/BufferReleaseChannel.h>
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<BufferReleaseChannel::ConsumerEndpoint> mConsumer;
std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> mProducer;
void SetUp() override {
ASSERT_EQ(OK,
BufferReleaseChannel::open("BufferReleaseChannelTest"s, mConsumer, mProducer));
}
};
TEST_F(BufferReleaseChannelTest, MessageFlattenable) {
ReleaseCallbackId releaseCallbackId{1, 2};
sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
uint32_t maxAcquiredBufferCount = 5;
std::vector<uint8_t> dataBuffer;
std::vector<int> 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<Fence> 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> fence = sp<Fence>::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<Fence> 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
|