xref: /aosp_15_r20/frameworks/native/libs/gui/BufferReleaseChannel.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1 /*
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 
31 using android::base::Result;
32 
33 namespace android::gui {
34 
35 namespace {
36 
37 template <typename T>
readAligned(const void * & buffer,size_t & size,T & value)38 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 
43 template <typename T>
writeAligned(void * & buffer,size_t & size,T value)44 void writeAligned(void*& buffer, size_t& size, T value) {
45     size -= FlattenableUtils::align<alignof(T)>(buffer);
46     FlattenableUtils::write(buffer, size, value);
47 }
48 
49 template <typename T>
addAligned(size_t & size,T)50 void addAligned(size_t& size, T /* value */) {
51     size = FlattenableUtils::align<sizeof(T)>(size);
52     size += sizeof(T);
53 }
54 
55 template <typename T>
low32(const T n)56 inline constexpr uint32_t low32(const T n) {
57     return static_cast<uint32_t>(static_cast<uint64_t>(n));
58 }
59 
60 template <typename T>
high32(const T n)61 inline constexpr uint32_t high32(const T n) {
62     return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32);
63 }
64 
65 template <typename T>
to64(const uint32_t lo,const uint32_t hi)66 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 
getPodSize() const72 size_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 
getFlattenedSize() const82 size_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 
flatten(void * & buffer,size_t & size,int * & fds,size_t & count) const89 status_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 
unflatten(void const * & buffer,size_t & size,int const * & fds,size_t & count)109 status_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 
readReleaseFence(ReleaseCallbackId & outReleaseCallbackId,sp<Fence> & outReleaseFence,uint32_t & outMaxAcquiredBufferCount)136 status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence(
137         ReleaseCallbackId& outReleaseCallbackId, sp<Fence>& outReleaseFence,
138         uint32_t& outMaxAcquiredBufferCount) {
139     std::lock_guard lock{mMutex};
140     Message message;
141     mFlattenedBuffer.resize(message.getFlattenedSize());
142     std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{};
143 
144     iovec iov{
145             .iov_base = mFlattenedBuffer.data(),
146             .iov_len = mFlattenedBuffer.size(),
147     };
148 
149     msghdr msg{};
150     msg.msg_iov = &iov;
151     msg.msg_iovlen = 1;
152     msg.msg_control = controlMessageBuffer.data();
153     msg.msg_controllen = controlMessageBuffer.size();
154 
155     ssize_t 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 %d (%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 
writeReleaseFence(const ReleaseCallbackId & callbackId,const sp<Fence> & fence,uint32_t maxAcquiredBufferCount)202 status_t BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(
203         const ReleaseCallbackId& callbackId, 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 status = message.flatten(flattenedBufferPtr, flattenedBufferSize,
216                                               flattenedFdPtr, flattenedFdCount);
217             status != OK) {
218             return status;
219         }
220     }
221 
222     iovec iov{};
223     iov.iov_base = mFlattenedBuffer.data();
224     iov.iov_len = mFlattenedBuffer.size();
225 
226     msghdr msg{};
227     msg.msg_iov = &iov;
228     msg.msg_iovlen = 1;
229 
230     std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{};
231     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 
242     ssize_t result;
243     do {
244         result = sendmsg(mFd, &msg, 0);
245     } while (result == -1 && errno == EINTR);
246     if (result == -1) {
247         return -errno;
248     }
249 
250     return OK;
251 }
252 
readFromParcel(const android::Parcel * parcel)253 status_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 
writeToParcel(android::Parcel * parcel) const260 status_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 
open(std::string name,std::unique_ptr<ConsumerEndpoint> & outConsumer,std::shared_ptr<ProducerEndpoint> & outProducer)267 status_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 
342     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