xref: /aosp_15_r20/hardware/interfaces/automotive/evs/aidl/impl/default/src/EvsCamera.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
1 /*
2  * Copyright (C) 2023 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 #include "EvsCamera.h"
18 
19 #include <aidl/android/hardware/automotive/evs/EvsResult.h>
20 #include <aidlcommonsupport/NativeHandle.h>
21 #include <android-base/logging.h>
22 #include <android/hardware_buffer.h>
23 #include <ui/GraphicBufferAllocator.h>
24 #include <ui/GraphicBufferMapper.h>
25 
26 #include <cstddef>
27 #include <mutex>
28 
29 namespace aidl::android::hardware::automotive::evs::implementation {
30 
31 // Arbitrary limit on number of graphics buffers allowed to be allocated
32 // Safeguards against unreasonable resource consumption and provides a testable limit
33 constexpr std::size_t kMaxBuffersInFlight = 100;
34 
35 // Minimum number of buffers to run a video stream
36 constexpr int kMinimumBuffersInFlight = 1;
37 
~EvsCamera()38 EvsCamera::~EvsCamera() {
39     shutdown();
40 }
41 
doneWithFrame(const std::vector<evs::BufferDesc> & buffers)42 ndk::ScopedAStatus EvsCamera::doneWithFrame(const std::vector<evs::BufferDesc>& buffers) {
43     std::lock_guard lck(mMutex);
44     for (const auto& desc : buffers) {
45         returnBuffer_unsafe(desc.bufferId);
46     }
47     return ndk::ScopedAStatus::ok();
48 }
49 
importExternalBuffers(const std::vector<evs::BufferDesc> & buffers,int32_t * _aidl_return)50 ndk::ScopedAStatus EvsCamera::importExternalBuffers(const std::vector<evs::BufferDesc>& buffers,
51                                                     int32_t* _aidl_return) {
52     if (buffers.empty()) {
53         LOG(DEBUG) << __func__
54                    << ": Ignoring a request to import external buffers with an empty list.";
55         return ndk::ScopedAStatus::ok();
56     }
57     static auto& mapper = ::android::GraphicBufferMapper::get();
58     std::lock_guard lck(mMutex);
59     std::size_t numBuffersToAdd = std::min(buffers.size(), kMaxBuffersInFlight - mAvailableFrames);
60     if (numBuffersToAdd == 0) {
61         LOG(WARNING) << __func__ << ": The number of buffers has hit the upper limit ("
62                      << kMaxBuffersInFlight << "). Stop importing.";
63         return ndk::ScopedAStatus::ok();
64     } else if (numBuffersToAdd < buffers.size()) {
65         LOG(WARNING) << "Exceeds the limit on the number of buffers. Only " << numBuffersToAdd
66                      << " buffers will be imported. " << buffers.size() << " are asked.";
67     }
68     const size_t before = mAvailableFrames;
69     for (std::size_t idx = 0; idx < numBuffersToAdd; ++idx) {
70         auto& buffer = buffers[idx];
71         const AHardwareBuffer_Desc* pDesc =
72                 reinterpret_cast<const AHardwareBuffer_Desc*>(&buffer.buffer.description);
73 
74         buffer_handle_t handleToImport = ::android::dupFromAidl(buffer.buffer.handle);
75         buffer_handle_t handleToStore = nullptr;
76         if (handleToImport == nullptr) {
77             LOG(WARNING) << "Failed to duplicate a memory handle. Ignoring a buffer "
78                          << buffer.bufferId;
79             continue;
80         }
81 
82         ::android::status_t result =
83                 mapper.importBuffer(handleToImport, pDesc->width, pDesc->height, pDesc->layers,
84                                     pDesc->format, pDesc->usage, pDesc->stride, &handleToStore);
85         if (result != ::android::NO_ERROR || handleToStore == nullptr ||
86             !increaseAvailableFrames_unsafe(handleToStore)) {
87             LOG(WARNING) << "Failed to import a buffer " << buffer.bufferId;
88         }
89     }
90     *_aidl_return = mAvailableFrames - before;
91     return ndk::ScopedAStatus::ok();
92 }
93 
setMaxFramesInFlight(int32_t bufferCount)94 ndk::ScopedAStatus EvsCamera::setMaxFramesInFlight(int32_t bufferCount) {
95     std::lock_guard lock(mMutex);
96     if (bufferCount < 1) {
97         LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested.";
98         return ndk::ScopedAStatus::fromServiceSpecificError(
99                 static_cast<int>(EvsResult::INVALID_ARG));
100     }
101     if (!setAvailableFrames_unsafe(bufferCount)) {
102         LOG(ERROR) << "Failed to adjust the maximum number of frames in flight.";
103         return ndk::ScopedAStatus::fromServiceSpecificError(
104                 static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
105     }
106     return ndk::ScopedAStatus::ok();
107 }
108 
freeOneFrame(const buffer_handle_t handle)109 void EvsCamera::freeOneFrame(const buffer_handle_t handle) {
110     static auto& alloc = ::android::GraphicBufferAllocator::get();
111     alloc.free(handle);
112 }
113 
preVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream> & receiver,ndk::ScopedAStatus & status,std::unique_lock<std::mutex> &)114 bool EvsCamera::preVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
115                                            ndk::ScopedAStatus& status,
116                                            std::unique_lock<std::mutex>& /* lck */) {
117     if (!receiver) {
118         LOG(ERROR) << __func__ << ": Null receiver.";
119         status = ndk::ScopedAStatus::fromServiceSpecificError(
120                 static_cast<int>(EvsResult::INVALID_ARG));
121         return false;
122     }
123 
124     // If we've been displaced by another owner of the camera, then we can't do anything else
125     if (mStreamState == StreamState::DEAD) {
126         LOG(ERROR) << __func__ << ": Ignoring when camera has been lost.";
127         status = ndk::ScopedAStatus::fromServiceSpecificError(
128                 static_cast<int>(EvsResult::OWNERSHIP_LOST));
129         return false;
130     }
131 
132     if (mStreamState != StreamState::STOPPED) {
133         LOG(ERROR) << __func__ << ": Ignoring when a stream is already running.";
134         status = ndk::ScopedAStatus::fromServiceSpecificError(
135                 static_cast<int>(EvsResult::STREAM_ALREADY_RUNNING));
136         return false;
137     }
138 
139     // If the client never indicated otherwise, configure ourselves for a single streaming buffer
140     if (mAvailableFrames < kMinimumBuffersInFlight &&
141         !setAvailableFrames_unsafe(kMinimumBuffersInFlight)) {
142         LOG(ERROR) << __func__ << "Failed to because we could not get a graphics buffer.";
143         status = ndk::ScopedAStatus::fromServiceSpecificError(
144                 static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
145         return false;
146     }
147     mStreamState = StreamState::RUNNING;
148     return true;
149 }
150 
postVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream> &,ndk::ScopedAStatus &,std::unique_lock<std::mutex> &)151 bool EvsCamera::postVideoStreamStart_locked(
152         const std::shared_ptr<evs::IEvsCameraStream>& /* receiver */,
153         ndk::ScopedAStatus& /* status */, std::unique_lock<std::mutex>& /* lck */) {
154     return true;
155 }
156 
preVideoStreamStop_locked(ndk::ScopedAStatus & status,std::unique_lock<std::mutex> &)157 bool EvsCamera::preVideoStreamStop_locked(ndk::ScopedAStatus& status,
158                                           std::unique_lock<std::mutex>& /* lck */) {
159     if (mStreamState != StreamState::RUNNING) {
160         // Terminate the stop process because a stream is not running.
161         status = ndk::ScopedAStatus::ok();
162         return false;
163     }
164     mStreamState = StreamState::STOPPING;
165     return true;
166 }
167 
postVideoStreamStop_locked(ndk::ScopedAStatus &,std::unique_lock<std::mutex> &)168 bool EvsCamera::postVideoStreamStop_locked(ndk::ScopedAStatus& /* status */,
169                                            std::unique_lock<std::mutex>& /* lck */) {
170     mStreamState = StreamState::STOPPED;
171     return true;
172 }
173 
startVideoStream(const std::shared_ptr<evs::IEvsCameraStream> & receiver)174 ndk::ScopedAStatus EvsCamera::startVideoStream(
175         const std::shared_ptr<evs::IEvsCameraStream>& receiver) {
176     bool needShutdown = false;
177     auto status = ndk::ScopedAStatus::ok();
178     {
179         std::unique_lock lck(mMutex);
180         if (!preVideoStreamStart_locked(receiver, status, lck)) {
181             return status;
182         }
183 
184         if ((!startVideoStreamImpl_locked(receiver, status, lck) ||
185              !postVideoStreamStart_locked(receiver, status, lck)) &&
186             !status.isOk()) {
187             needShutdown = true;
188         }
189     }
190     if (needShutdown) {
191         shutdown();
192     }
193     return status;
194 }
195 
stopVideoStream()196 ndk::ScopedAStatus EvsCamera::stopVideoStream() {
197     bool needShutdown = false;
198     auto status = ndk::ScopedAStatus::ok();
199     {
200         std::unique_lock lck(mMutex);
201         if (mStreamState != StreamState::RUNNING) {
202             // We're already in the middle of the procedure to stop current data
203             // stream.
204             return status;
205         }
206 
207         if ((!preVideoStreamStop_locked(status, lck) || !stopVideoStreamImpl_locked(status, lck) ||
208              !postVideoStreamStop_locked(status, lck)) &&
209             !status.isOk()) {
210             needShutdown = true;
211         }
212     }
213     if (needShutdown) {
214         shutdown();
215     }
216     return status;
217 }
218 
pauseVideoStream()219 ndk::ScopedAStatus EvsCamera::pauseVideoStream() {
220     return ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
221 }
222 
resumeVideoStream()223 ndk::ScopedAStatus EvsCamera::resumeVideoStream() {
224     return ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
225 }
226 
setAvailableFrames_unsafe(const std::size_t bufferCount)227 bool EvsCamera::setAvailableFrames_unsafe(const std::size_t bufferCount) {
228     if (bufferCount < 1) {
229         LOG(ERROR) << "Ignoring request to set buffer count to zero.";
230         return false;
231     }
232     if (bufferCount > kMaxBuffersInFlight) {
233         LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
234         return false;
235     }
236 
237     if (bufferCount > mAvailableFrames) {
238         bool success = true;
239         const std::size_t numBufferBeforeAlloc = mAvailableFrames;
240         for (int numBufferToAllocate = bufferCount - mAvailableFrames;
241              success && numBufferToAllocate > 0; --numBufferToAllocate) {
242             buffer_handle_t handle = nullptr;
243             const auto result = allocateOneFrame(&handle);
244             if (result != ::android::NO_ERROR || !handle) {
245                 LOG(ERROR) << __func__ << ": Failed to allocate a graphics buffer. Error " << result
246                            << ", handle: " << handle;
247                 success = false;
248                 break;
249             }
250             success &= increaseAvailableFrames_unsafe(handle);
251         }
252         if (!success) {
253             // Rollback when failure.
254             for (int numBufferToRelease = mAvailableFrames - numBufferBeforeAlloc;
255                  numBufferToRelease > 0; --numBufferToRelease) {
256                 decreaseAvailableFrames_unsafe();
257             }
258             return false;
259         }
260     } else {
261         for (int numBufferToRelease = mAvailableFrames - std::max(bufferCount, mFramesInUse);
262              numBufferToRelease > 0; --numBufferToRelease) {
263             decreaseAvailableFrames_unsafe();
264         }
265         if (mAvailableFrames > bufferCount) {
266             // This shouldn't happen with a properly behaving client because the client
267             // should only make this call after returning sufficient outstanding buffers
268             // to allow a clean resize.
269             LOG(ERROR) << "Buffer queue shrink failed, asked: " << bufferCount
270                        << ", actual: " << mAvailableFrames
271                        << " -- too many buffers currently in use?";
272         }
273     }
274     return true;
275 }
276 
shutdown()277 void EvsCamera::shutdown() {
278     stopVideoStream();
279     std::lock_guard lck(mMutex);
280     closeAllBuffers_unsafe();
281     mStreamState = StreamState::DEAD;
282 }
283 
closeAllBuffers_unsafe()284 void EvsCamera::closeAllBuffers_unsafe() {
285     if (mFramesInUse > 0) {
286         LOG(WARNING) << __func__ << ": Closing while " << mFramesInUse
287                      << " frame(s) are still in use.";
288     }
289     for (auto& buffer : mBuffers) {
290         freeOneFrame(buffer.handle);
291         buffer.handle = nullptr;
292     }
293     mBuffers.clear();
294     mBufferPosToId.clear();
295     mBufferIdToPos.clear();
296 }
297 
useBuffer_unsafe()298 std::pair<std::size_t, buffer_handle_t> EvsCamera::useBuffer_unsafe() {
299     if (mFramesInUse >= mAvailableFrames) {
300         DCHECK_EQ(mFramesInUse, mAvailableFrames);
301         return {kInvalidBufferID, nullptr};
302     }
303     const std::size_t pos = mFramesInUse++;
304     auto& buffer = mBuffers[pos];
305     DCHECK(!buffer.inUse);
306     DCHECK(buffer.handle);
307     buffer.inUse = true;
308     return {mBufferPosToId[pos], buffer.handle};
309 }
310 
returnBuffer_unsafe(const std::size_t id)311 void EvsCamera::returnBuffer_unsafe(const std::size_t id) {
312     if (id >= mBuffers.size()) {
313         LOG(ERROR) << __func__ << ": ID out-of-bound. id: " << id
314                    << " max: " << mBuffers.size() - 1;
315         return;
316     }
317     const std::size_t pos = mBufferIdToPos[id];
318 
319     if (!mBuffers[pos].inUse) {
320         LOG(ERROR) << __func__ << ": Ignoring returning frame " << id << " which is already free.";
321         return;
322     }
323     DCHECK_LT(pos, mFramesInUse);
324     const std::size_t last_in_use_pos = --mFramesInUse;
325     swapBufferFrames_unsafe(pos, last_in_use_pos);
326     mBuffers[last_in_use_pos].inUse = false;
327 }
328 
increaseAvailableFrames_unsafe(const buffer_handle_t handle)329 bool EvsCamera::increaseAvailableFrames_unsafe(const buffer_handle_t handle) {
330     if (mAvailableFrames >= kMaxBuffersInFlight) {
331         LOG(WARNING) << __func__ << ": The number of buffers has hit the upper limit ("
332                      << kMaxBuffersInFlight << "). Stop increasing.";
333         return false;
334     }
335     const std::size_t pos = mAvailableFrames++;
336     if (mAvailableFrames > mBuffers.size()) {
337         const std::size_t oldBufferSize = mBuffers.size();
338         mBuffers.resize(mAvailableFrames);
339         mBufferPosToId.resize(mAvailableFrames);
340         mBufferIdToPos.resize(mAvailableFrames);
341         // Build position/ID mapping.
342         for (std::size_t idx = oldBufferSize; idx < mBuffers.size(); ++idx) {
343             mBufferPosToId[idx] = idx;
344             mBufferIdToPos[idx] = idx;
345         }
346     }
347     auto& buffer = mBuffers[pos];
348     DCHECK(!buffer.inUse);
349     DCHECK(!buffer.handle);
350     buffer.handle = handle;
351     return true;
352 }
353 
decreaseAvailableFrames_unsafe()354 bool EvsCamera::decreaseAvailableFrames_unsafe() {
355     if (mFramesInUse >= mAvailableFrames) {
356         DCHECK_EQ(mFramesInUse, mAvailableFrames);
357         return false;
358     }
359     const std::size_t pos = --mAvailableFrames;
360     auto& buffer = mBuffers[pos];
361     DCHECK(!buffer.inUse);
362     DCHECK(buffer.handle);
363     freeOneFrame(buffer.handle);
364     buffer.handle = nullptr;
365     return true;
366 }
367 
swapBufferFrames_unsafe(const std::size_t pos1,const std::size_t pos2)368 void EvsCamera::swapBufferFrames_unsafe(const std::size_t pos1, const std::size_t pos2) {
369     if (pos1 == pos2) {
370         return;
371     }
372     if (pos1 >= mBuffers.size() || pos2 >= mBuffers.size()) {
373         LOG(ERROR) << __func__ << ": Index out-of-bound. pos1: " << pos1 << ", pos2: " << pos2
374                    << ", buffer size: " << mBuffers.size();
375         return;
376     }
377     const std::size_t id1 = mBufferPosToId[pos1];
378     const std::size_t id2 = mBufferPosToId[pos2];
379     std::swap(mBufferPosToId[pos1], mBufferPosToId[pos2]);
380     std::swap(mBufferIdToPos[id1], mBufferIdToPos[id2]);
381     std::swap(mBuffers[pos1], mBuffers[pos2]);
382 }
383 
384 }  // namespace aidl::android::hardware::automotive::evs::implementation
385