blob: e9cb013baf6ee8ae0dabeedfb5e30d0128a50514 [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>
Patrick Williamsbb504472024-10-25 13:20:32 -050038void readAligned(const void*& buffer, size_t& size, T& value) {
Patrick Williamsf693bcf2024-08-02 09:55:23 -050039 size -= FlattenableUtils::align<alignof(T)>(buffer);
40 FlattenableUtils::read(buffer, size, value);
41}
42
43template <typename T>
Patrick Williamsbb504472024-10-25 13:20:32 -050044void writeAligned(void*& buffer, size_t& size, T value) {
Patrick Williamsf693bcf2024-08-02 09:55:23 -050045 size -= FlattenableUtils::align<alignof(T)>(buffer);
46 FlattenableUtils::write(buffer, size, value);
47}
48
49template <typename T>
Patrick Williamsbb504472024-10-25 13:20:32 -050050void addAligned(size_t& size, T /* value */) {
Patrick Williamsf693bcf2024-08-02 09:55:23 -050051 size = FlattenableUtils::align<sizeof(T)>(size);
52 size += sizeof(T);
53}
54
55template <typename T>
Patrick Williamsbb504472024-10-25 13:20:32 -050056inline constexpr uint32_t low32(const T n) {
Patrick Williamsf693bcf2024-08-02 09:55:23 -050057 return static_cast<uint32_t>(static_cast<uint64_t>(n));
58}
59
60template <typename T>
Patrick Williamsbb504472024-10-25 13:20:32 -050061inline constexpr uint32_t high32(const T n) {
Patrick Williamsf693bcf2024-08-02 09:55:23 -050062 return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32);
63}
64
65template <typename T>
Patrick Williamsbb504472024-10-25 13:20:32 -050066inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
Patrick Williamsf693bcf2024-08-02 09:55:23 -050067 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) {
Patrick Williams078d7362024-08-27 10:20:39 -0500139 std::lock_guard lock{mMutex};
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500140 Message message;
141 mFlattenedBuffer.resize(message.getFlattenedSize());
Patrick Williamsbb504472024-10-25 13:20:32 -0500142 std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{};
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500143
144 iovec iov{
145 .iov_base = mFlattenedBuffer.data(),
146 .iov_len = mFlattenedBuffer.size(),
147 };
148
Patrick Williamsbb504472024-10-25 13:20:32 -0500149 msghdr msg{};
150 msg.msg_iov = &iov;
151 msg.msg_iovlen = 1;
152 msg.msg_control = controlMessageBuffer.data();
153 msg.msg_controllen = controlMessageBuffer.size();
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500154
Patrick Williams078d7362024-08-27 10:20:39 -0500155 ssize_t result;
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500156 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 }
Patrick Williamsbb504472024-10-25 13:20:32 -0500163 ALOGE("Error reading release fence from socket: error %d (%s)", errno, strerror(errno));
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500164 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
Patrick Williamsbb504472024-10-25 13:20:32 -0500202status_t BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(
203 const ReleaseCallbackId& callbackId, const sp<Fence>& fence,
204 uint32_t maxAcquiredBufferCount) {
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500205 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;
Patrick Williamsbb504472024-10-25 13:20:32 -0500215 if (status_t status = message.flatten(flattenedBufferPtr, flattenedBufferSize,
216 flattenedFdPtr, flattenedFdCount);
217 status != OK) {
218 return status;
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500219 }
220 }
221
Patrick Williamsbb504472024-10-25 13:20:32 -0500222 iovec iov{};
223 iov.iov_base = mFlattenedBuffer.data();
224 iov.iov_len = mFlattenedBuffer.size();
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500225
Patrick Williamsbb504472024-10-25 13:20:32 -0500226 msghdr msg{};
227 msg.msg_iov = &iov;
228 msg.msg_iovlen = 1;
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500229
Patrick Williamsbb504472024-10-25 13:20:32 -0500230 std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{};
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500231 if (fence && fence->isValid()) {
232 msg.msg_control = controlMessageBuffer.data();
233 msg.msg_controllen = controlMessageBuffer.size();
234
235 cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
236 cmsg->cmsg_level = SOL_SOCKET;
237 cmsg->cmsg_type = SCM_RIGHTS;
238 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
239 memcpy(CMSG_DATA(cmsg), &flattenedFd, sizeof(int));
240 }
241
Patrick Williams078d7362024-08-27 10:20:39 -0500242 ssize_t result;
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500243 do {
244 result = sendmsg(mFd, &msg, 0);
245 } while (result == -1 && errno == EINTR);
246 if (result == -1) {
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500247 return -errno;
248 }
249
250 return OK;
251}
252
253status_t BufferReleaseChannel::ProducerEndpoint::readFromParcel(const android::Parcel* parcel) {
254 if (!parcel) return STATUS_BAD_VALUE;
255 SAFE_PARCEL(parcel->readUtf8FromUtf16, &mName);
256 SAFE_PARCEL(parcel->readUniqueFileDescriptor, &mFd);
257 return STATUS_OK;
258}
259
260status_t BufferReleaseChannel::ProducerEndpoint::writeToParcel(android::Parcel* parcel) const {
261 if (!parcel) return STATUS_BAD_VALUE;
262 SAFE_PARCEL(parcel->writeUtf8AsUtf16, mName);
263 SAFE_PARCEL(parcel->writeUniqueFileDescriptor, mFd);
264 return STATUS_OK;
265}
266
267status_t BufferReleaseChannel::open(std::string name,
268 std::unique_ptr<ConsumerEndpoint>& outConsumer,
269 std::shared_ptr<ProducerEndpoint>& outProducer) {
270 outConsumer.reset();
271 outProducer.reset();
272
273 int sockets[2];
274 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
275 ALOGE("[%s] Failed to create socket pair. errorno=%d message='%s'", name.c_str(), errno,
276 strerror(errno));
277 return -errno;
278 }
279
280 android::base::unique_fd consumerFd(sockets[0]);
281 android::base::unique_fd producerFd(sockets[1]);
282
283 // Socket buffer size. The default is typically about 128KB, which is much larger than
284 // we really need.
285 size_t bufferSize = 32 * 1024;
286 if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
287 -1) {
288 ALOGE("[%s] Failed to set consumer socket send buffer size. errno=%d message='%s'",
289 name.c_str(), errno, strerror(errno));
290 return -errno;
291 }
292 if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
293 -1) {
294 ALOGE("[%s] Failed to set consumer socket receive buffer size. errno=%d "
295 "message='%s'",
296 name.c_str(), errno, strerror(errno));
297 return -errno;
298 }
299 if (setsockopt(producerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
300 -1) {
301 ALOGE("[%s] Failed to set producer socket send buffer size. errno=%d message='%s'",
302 name.c_str(), errno, strerror(errno));
303 return -errno;
304 }
305 if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
306 -1) {
307 ALOGE("[%s] Failed to set producer socket receive buffer size. errno=%d "
308 "message='%s'",
309 name.c_str(), errno, strerror(errno));
310 return -errno;
311 }
312
313 // Configure the consumer socket to be non-blocking.
314 int flags = fcntl(consumerFd.get(), F_GETFL, 0);
315 if (flags == -1) {
316 ALOGE("[%s] Failed to get consumer socket flags. errno=%d message='%s'", name.c_str(),
317 errno, strerror(errno));
318 return -errno;
319 }
320 if (fcntl(consumerFd.get(), F_SETFL, flags | O_NONBLOCK) == -1) {
321 ALOGE("[%s] Failed to set consumer socket to non-blocking mode. errno=%d "
322 "message='%s'",
323 name.c_str(), errno, strerror(errno));
324 return -errno;
325 }
326
327 // Configure a timeout for the producer socket.
328 const timeval timeout{.tv_sec = 1, .tv_usec = 0};
329 if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval)) == -1) {
330 ALOGE("[%s] Failed to set producer socket timeout. errno=%d message='%s'", name.c_str(),
331 errno, strerror(errno));
332 return -errno;
333 }
334
335 // Make the consumer read-only
336 if (shutdown(consumerFd.get(), SHUT_WR) == -1) {
337 ALOGE("[%s] Failed to shutdown writing on consumer socket. errno=%d message='%s'",
338 name.c_str(), errno, strerror(errno));
339 return -errno;
340 }
341
Patrick Williamsf693bcf2024-08-02 09:55:23 -0500342 outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd));
343 outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd));
344 return STATUS_OK;
345}
346
347} // namespace android::gui