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