blob: 27367aa83f3089ec47338b2858cd111f557e4599 [file] [log] [blame]
Patrick Williamsf693bcf2024-08-02 09:55:23 -05001/*
2 * Copyright (C) 2024 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_TAG "BufferReleaseChannel"
18
19#include <fcntl.h>
20#include <sys/socket.h>
21#include <sys/uio.h>
22
23#include <android-base/result.h>
24#include <android/binder_status.h>
25#include <binder/Parcel.h>
26#include <utils/Flattenable.h>
27
28#include <gui/BufferReleaseChannel.h>
29#include <private/gui/ParcelUtils.h>
30
31using android::base::Result;
32
33namespace android::gui {
34
35namespace {
36
37template <typename T>
38static void readAligned(const void*& buffer, size_t& size, T& value) {
39 size -= FlattenableUtils::align<alignof(T)>(buffer);
40 FlattenableUtils::read(buffer, size, value);
41}
42
43template <typename T>
44static void writeAligned(void*& buffer, size_t& size, T value) {
45 size -= FlattenableUtils::align<alignof(T)>(buffer);
46 FlattenableUtils::write(buffer, size, value);
47}
48
49template <typename T>
50static void addAligned(size_t& size, T /* value */) {
51 size = FlattenableUtils::align<sizeof(T)>(size);
52 size += sizeof(T);
53}
54
55template <typename T>
56static inline constexpr uint32_t low32(const T n) {
57 return static_cast<uint32_t>(static_cast<uint64_t>(n));
58}
59
60template <typename T>
61static inline constexpr uint32_t high32(const T n) {
62 return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32);
63}
64
65template <typename T>
66static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
67 return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo);
68}
69
70} // namespace
71
72size_t BufferReleaseChannel::Message::getPodSize() const {
73 size_t size = 0;
74 addAligned(size, low32(releaseCallbackId.bufferId));
75 addAligned(size, high32(releaseCallbackId.bufferId));
76 addAligned(size, low32(releaseCallbackId.framenumber));
77 addAligned(size, high32(releaseCallbackId.framenumber));
78 addAligned(size, maxAcquiredBufferCount);
79 return size;
80}
81
82size_t BufferReleaseChannel::Message::getFlattenedSize() const {
83 size_t size = releaseFence->getFlattenedSize();
84 size = FlattenableUtils::align<4>(size);
85 size += getPodSize();
86 return size;
87}
88
89status_t BufferReleaseChannel::Message::flatten(void*& buffer, size_t& size, int*& fds,
90 size_t& count) const {
91 if (status_t err = releaseFence->flatten(buffer, size, fds, count); err != OK) {
92 return err;
93 }
94 size -= FlattenableUtils::align<4>(buffer);
95
96 // Check we still have enough space
97 if (size < getPodSize()) {
98 return NO_MEMORY;
99 }
100
101 writeAligned(buffer, size, low32(releaseCallbackId.bufferId));
102 writeAligned(buffer, size, high32(releaseCallbackId.bufferId));
103 writeAligned(buffer, size, low32(releaseCallbackId.framenumber));
104 writeAligned(buffer, size, high32(releaseCallbackId.framenumber));
105 writeAligned(buffer, size, maxAcquiredBufferCount);
106 return OK;
107}
108
109status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size,
110 int const*& fds, size_t& count) {
111 releaseFence = new Fence();
112 if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) {
113 return err;
114 }
115 size -= FlattenableUtils::align<4>(buffer);
116
117 // Check we still have enough space
118 if (size < getPodSize()) {
119 return OK;
120 }
121
122 uint32_t bufferIdLo = 0, bufferIdHi = 0;
123 uint32_t frameNumberLo = 0, frameNumberHi = 0;
124
125 readAligned(buffer, size, bufferIdLo);
126 readAligned(buffer, size, bufferIdHi);
127 releaseCallbackId.bufferId = to64<int64_t>(bufferIdLo, bufferIdHi);
128 readAligned(buffer, size, frameNumberLo);
129 readAligned(buffer, size, frameNumberHi);
130 releaseCallbackId.framenumber = to64<uint64_t>(frameNumberLo, frameNumberHi);
131 readAligned(buffer, size, maxAcquiredBufferCount);
132
133 return OK;
134}
135
136status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence(
137 ReleaseCallbackId& outReleaseCallbackId, sp<Fence>& outReleaseFence,
138 uint32_t& outMaxAcquiredBufferCount) {
139 Message message;
140 mFlattenedBuffer.resize(message.getFlattenedSize());
141 std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
142
143 iovec iov{
144 .iov_base = mFlattenedBuffer.data(),
145 .iov_len = mFlattenedBuffer.size(),
146 };
147
148 msghdr msg{
149 .msg_iov = &iov,
150 .msg_iovlen = 1,
151 .msg_control = controlMessageBuffer.data(),
152 .msg_controllen = controlMessageBuffer.size(),
153 };
154
155 int result;
156 do {
157 result = recvmsg(mFd, &msg, 0);
158 } while (result == -1 && errno == EINTR);
159 if (result == -1) {
160 if (errno == EWOULDBLOCK || errno == EAGAIN) {
161 return WOULD_BLOCK;
162 }
163 ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno));
164 return UNKNOWN_ERROR;
165 }
166
167 if (msg.msg_iovlen != 1) {
168 ALOGE("Error reading release fence from socket: bad data length");
169 return UNKNOWN_ERROR;
170 }
171
172 if (msg.msg_controllen % sizeof(int) != 0) {
173 ALOGE("Error reading release fence from socket: bad fd length");
174 return UNKNOWN_ERROR;
175 }
176
177 size_t dataLen = msg.msg_iov->iov_len;
178 const void* data = static_cast<const void*>(msg.msg_iov->iov_base);
179 if (!data) {
180 ALOGE("Error reading release fence from socket: no buffer data");
181 return UNKNOWN_ERROR;
182 }
183
184 size_t fdCount = 0;
185 const int* fdData = nullptr;
186 if (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg)) {
187 fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
188 fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
189 }
190
191 if (status_t err = message.unflatten(data, dataLen, fdData, fdCount); err != OK) {
192 return err;
193 }
194
195 outReleaseCallbackId = message.releaseCallbackId;
196 outReleaseFence = std::move(message.releaseFence);
197 outMaxAcquiredBufferCount = message.maxAcquiredBufferCount;
198
199 return OK;
200}
201
202int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId,
203 const sp<Fence>& fence,
204 uint32_t maxAcquiredBufferCount) {
205 Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount};
206 mFlattenedBuffer.resize(message.getFlattenedSize());
207 int flattenedFd;
208 {
209 // Make copies of needed items since flatten modifies them, and we don't
210 // want to send anything if there's an error during flatten.
211 void* flattenedBufferPtr = mFlattenedBuffer.data();
212 size_t flattenedBufferSize = mFlattenedBuffer.size();
213 int* flattenedFdPtr = &flattenedFd;
214 size_t flattenedFdCount = 1;
215 if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr,
216 flattenedFdCount);
217 err != OK) {
218 ALOGE("Failed to flatten BufferReleaseChannel message.");
219 return err;
220 }
221 }
222
223 iovec iov{
224 .iov_base = mFlattenedBuffer.data(),
225 .iov_len = mFlattenedBuffer.size(),
226 };
227
228 msghdr msg{
229 .msg_iov = &iov,
230 .msg_iovlen = 1,
231 };
232
233 std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
234 if (fence && fence->isValid()) {
235 msg.msg_control = controlMessageBuffer.data();
236 msg.msg_controllen = controlMessageBuffer.size();
237
238 cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
239 cmsg->cmsg_level = SOL_SOCKET;
240 cmsg->cmsg_type = SCM_RIGHTS;
241 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
242 memcpy(CMSG_DATA(cmsg), &flattenedFd, sizeof(int));
243 }
244
245 int result;
246 do {
247 result = sendmsg(mFd, &msg, 0);
248 } while (result == -1 && errno == EINTR);
249 if (result == -1) {
250 ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno));
251 return -errno;
252 }
253
254 return OK;
255}
256
257status_t BufferReleaseChannel::ProducerEndpoint::readFromParcel(const android::Parcel* parcel) {
258 if (!parcel) return STATUS_BAD_VALUE;
259 SAFE_PARCEL(parcel->readUtf8FromUtf16, &mName);
260 SAFE_PARCEL(parcel->readUniqueFileDescriptor, &mFd);
261 return STATUS_OK;
262}
263
264status_t BufferReleaseChannel::ProducerEndpoint::writeToParcel(android::Parcel* parcel) const {
265 if (!parcel) return STATUS_BAD_VALUE;
266 SAFE_PARCEL(parcel->writeUtf8AsUtf16, mName);
267 SAFE_PARCEL(parcel->writeUniqueFileDescriptor, mFd);
268 return STATUS_OK;
269}
270
271status_t BufferReleaseChannel::open(std::string name,
272 std::unique_ptr<ConsumerEndpoint>& outConsumer,
273 std::shared_ptr<ProducerEndpoint>& outProducer) {
274 outConsumer.reset();
275 outProducer.reset();
276
277 int sockets[2];
278 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
279 ALOGE("[%s] Failed to create socket pair. errorno=%d message='%s'", name.c_str(), errno,
280 strerror(errno));
281 return -errno;
282 }
283
284 android::base::unique_fd consumerFd(sockets[0]);
285 android::base::unique_fd producerFd(sockets[1]);
286
287 // Socket buffer size. The default is typically about 128KB, which is much larger than
288 // we really need.
289 size_t bufferSize = 32 * 1024;
290 if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
291 -1) {
292 ALOGE("[%s] Failed to set consumer socket send buffer size. errno=%d message='%s'",
293 name.c_str(), errno, strerror(errno));
294 return -errno;
295 }
296 if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
297 -1) {
298 ALOGE("[%s] Failed to set consumer socket receive buffer size. errno=%d "
299 "message='%s'",
300 name.c_str(), errno, strerror(errno));
301 return -errno;
302 }
303 if (setsockopt(producerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
304 -1) {
305 ALOGE("[%s] Failed to set producer socket send buffer size. errno=%d message='%s'",
306 name.c_str(), errno, strerror(errno));
307 return -errno;
308 }
309 if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
310 -1) {
311 ALOGE("[%s] Failed to set producer socket receive buffer size. errno=%d "
312 "message='%s'",
313 name.c_str(), errno, strerror(errno));
314 return -errno;
315 }
316
317 // Configure the consumer socket to be non-blocking.
318 int flags = fcntl(consumerFd.get(), F_GETFL, 0);
319 if (flags == -1) {
320 ALOGE("[%s] Failed to get consumer socket flags. errno=%d message='%s'", name.c_str(),
321 errno, strerror(errno));
322 return -errno;
323 }
324 if (fcntl(consumerFd.get(), F_SETFL, flags | O_NONBLOCK) == -1) {
325 ALOGE("[%s] Failed to set consumer socket to non-blocking mode. errno=%d "
326 "message='%s'",
327 name.c_str(), errno, strerror(errno));
328 return -errno;
329 }
330
331 // Configure a timeout for the producer socket.
332 const timeval timeout{.tv_sec = 1, .tv_usec = 0};
333 if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval)) == -1) {
334 ALOGE("[%s] Failed to set producer socket timeout. errno=%d message='%s'", name.c_str(),
335 errno, strerror(errno));
336 return -errno;
337 }
338
339 // Make the consumer read-only
340 if (shutdown(consumerFd.get(), SHUT_WR) == -1) {
341 ALOGE("[%s] Failed to shutdown writing on consumer socket. errno=%d message='%s'",
342 name.c_str(), errno, strerror(errno));
343 return -errno;
344 }
345
346 // Make the producer write-only
347 if (shutdown(producerFd.get(), SHUT_RD) == -1) {
348 ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'",
349 name.c_str(), errno, strerror(errno));
350 return -errno;
351 }
352
353 outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd));
354 outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd));
355 return STATUS_OK;
356}
357
358} // namespace android::gui