• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)) && !status.isOk()) {
209             needShutdown = true;
210         }
211     }
212     if (needShutdown) {
213         shutdown();
214     }
215     return status;
216 }
217 
pauseVideoStream()218 ndk::ScopedAStatus EvsCamera::pauseVideoStream() {
219     return ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
220 }
221 
resumeVideoStream()222 ndk::ScopedAStatus EvsCamera::resumeVideoStream() {
223     return ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
224 }
225 
setAvailableFrames_unsafe(const std::size_t bufferCount)226 bool EvsCamera::setAvailableFrames_unsafe(const std::size_t bufferCount) {
227     if (bufferCount < 1) {
228         LOG(ERROR) << "Ignoring request to set buffer count to zero.";
229         return false;
230     }
231     if (bufferCount > kMaxBuffersInFlight) {
232         LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
233         return false;
234     }
235 
236     if (bufferCount > mAvailableFrames) {
237         bool success = true;
238         const std::size_t numBufferBeforeAlloc = mAvailableFrames;
239         for (int numBufferToAllocate = bufferCount - mAvailableFrames;
240              success && numBufferToAllocate > 0; --numBufferToAllocate) {
241             buffer_handle_t handle = nullptr;
242             const auto result = allocateOneFrame(&handle);
243             if (result != ::android::NO_ERROR || !handle) {
244                 LOG(ERROR) << __func__ << ": Failed to allocate a graphics buffer. Error " << result
245                            << ", handle: " << handle;
246                 success = false;
247                 break;
248             }
249             success &= increaseAvailableFrames_unsafe(handle);
250         }
251         if (!success) {
252             // Rollback when failure.
253             for (int numBufferToRelease = mAvailableFrames - numBufferBeforeAlloc;
254                  numBufferToRelease > 0; --numBufferToRelease) {
255                 decreaseAvailableFrames_unsafe();
256             }
257             return false;
258         }
259     } else {
260         for (int numBufferToRelease = mAvailableFrames - std::max(bufferCount, mFramesInUse);
261              numBufferToRelease > 0; --numBufferToRelease) {
262             decreaseAvailableFrames_unsafe();
263         }
264         if (mAvailableFrames > bufferCount) {
265             // This shouldn't happen with a properly behaving client because the client
266             // should only make this call after returning sufficient outstanding buffers
267             // to allow a clean resize.
268             LOG(ERROR) << "Buffer queue shrink failed, asked: " << bufferCount
269                        << ", actual: " << mAvailableFrames
270                        << " -- too many buffers currently in use?";
271         }
272     }
273     return true;
274 }
275 
shutdown()276 void EvsCamera::shutdown() {
277     stopVideoStream();
278     std::lock_guard lck(mMutex);
279     closeAllBuffers_unsafe();
280     mStreamState = StreamState::DEAD;
281 }
282 
closeAllBuffers_unsafe()283 void EvsCamera::closeAllBuffers_unsafe() {
284     if (mFramesInUse > 0) {
285         LOG(WARNING) << __func__ << ": Closing while " << mFramesInUse
286                      << " frame(s) are still in use.";
287     }
288     for (auto& buffer : mBuffers) {
289         freeOneFrame(buffer.handle);
290         buffer.handle = nullptr;
291     }
292     mBuffers.clear();
293     mBufferPosToId.clear();
294     mBufferIdToPos.clear();
295 }
296 
useBuffer_unsafe()297 std::pair<std::size_t, buffer_handle_t> EvsCamera::useBuffer_unsafe() {
298     if (mFramesInUse >= mAvailableFrames) {
299         DCHECK_EQ(mFramesInUse, mAvailableFrames);
300         return {kInvalidBufferID, nullptr};
301     }
302     const std::size_t pos = mFramesInUse++;
303     auto& buffer = mBuffers[pos];
304     DCHECK(!buffer.inUse);
305     DCHECK(buffer.handle);
306     buffer.inUse = true;
307     return {mBufferPosToId[pos], buffer.handle};
308 }
309 
returnBuffer_unsafe(const std::size_t id)310 void EvsCamera::returnBuffer_unsafe(const std::size_t id) {
311     if (id >= mBuffers.size()) {
312         LOG(ERROR) << __func__ << ": ID out-of-bound. id: " << id
313                    << " max: " << mBuffers.size() - 1;
314         return;
315     }
316     const std::size_t pos = mBufferIdToPos[id];
317 
318     if (!mBuffers[pos].inUse) {
319         LOG(ERROR) << __func__ << ": Ignoring returning frame " << id << " which is already free.";
320         return;
321     }
322     DCHECK_LT(pos, mFramesInUse);
323     const std::size_t last_in_use_pos = --mFramesInUse;
324     swapBufferFrames_unsafe(pos, last_in_use_pos);
325     mBuffers[last_in_use_pos].inUse = false;
326 }
327 
increaseAvailableFrames_unsafe(const buffer_handle_t handle)328 bool EvsCamera::increaseAvailableFrames_unsafe(const buffer_handle_t handle) {
329     if (mAvailableFrames >= kMaxBuffersInFlight) {
330         LOG(WARNING) << __func__ << ": The number of buffers has hit the upper limit ("
331                      << kMaxBuffersInFlight << "). Stop increasing.";
332         return false;
333     }
334     const std::size_t pos = mAvailableFrames++;
335     if (mAvailableFrames > mBuffers.size()) {
336         const std::size_t oldBufferSize = mBuffers.size();
337         mBuffers.resize(mAvailableFrames);
338         mBufferPosToId.resize(mAvailableFrames);
339         mBufferIdToPos.resize(mAvailableFrames);
340         // Build position/ID mapping.
341         for (std::size_t idx = oldBufferSize; idx < mBuffers.size(); ++idx) {
342             mBufferPosToId[idx] = idx;
343             mBufferIdToPos[idx] = idx;
344         }
345     }
346     auto& buffer = mBuffers[pos];
347     DCHECK(!buffer.inUse);
348     DCHECK(!buffer.handle);
349     buffer.handle = handle;
350     return true;
351 }
352 
decreaseAvailableFrames_unsafe()353 bool EvsCamera::decreaseAvailableFrames_unsafe() {
354     if (mFramesInUse >= mAvailableFrames) {
355         DCHECK_EQ(mFramesInUse, mAvailableFrames);
356         return false;
357     }
358     const std::size_t pos = --mAvailableFrames;
359     auto& buffer = mBuffers[pos];
360     DCHECK(!buffer.inUse);
361     DCHECK(buffer.handle);
362     freeOneFrame(buffer.handle);
363     buffer.handle = nullptr;
364     return true;
365 }
366 
swapBufferFrames_unsafe(const std::size_t pos1,const std::size_t pos2)367 void EvsCamera::swapBufferFrames_unsafe(const std::size_t pos1, const std::size_t pos2) {
368     if (pos1 == pos2) {
369         return;
370     }
371     if (pos1 >= mBuffers.size() || pos2 >= mBuffers.size()) {
372         LOG(ERROR) << __func__ << ": Index out-of-bound. pos1: " << pos1 << ", pos2: " << pos2
373                    << ", buffer size: " << mBuffers.size();
374         return;
375     }
376     const std::size_t id1 = mBufferPosToId[pos1];
377     const std::size_t id2 = mBufferPosToId[pos2];
378     std::swap(mBufferPosToId[pos1], mBufferPosToId[pos2]);
379     std::swap(mBufferIdToPos[id1], mBufferIdToPos[id2]);
380     std::swap(mBuffers[pos1], mBuffers[pos2]);
381 }
382 
383 }  // namespace aidl::android::hardware::automotive::evs::implementation
384