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