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