1 /*
2 * Copyright (C) 2022 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 "HalCamera.h"
18
19 #include "Enumerator.h"
20 #include "ScopedTrace.h"
21 #include "VirtualCamera.h"
22 #include "utils/include/Utils.h"
23
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26
27 namespace aidl::android::automotive::evs::implementation {
28
29 using ::aidl::android::hardware::automotive::evs::BufferDesc;
30 using ::aidl::android::hardware::automotive::evs::CameraParam;
31 using ::aidl::android::hardware::automotive::evs::EvsEventDesc;
32 using ::aidl::android::hardware::automotive::evs::EvsEventType;
33 using ::aidl::android::hardware::automotive::evs::EvsResult;
34 using ::aidl::android::hardware::automotive::evs::Stream;
35 using ::android::base::StringAppendF;
36 using ::ndk::ScopedAStatus;
37
38 // TODO(b/213108625):
39 // We need to hook up death monitoring to detect stream death so we can attempt a reconnect
40
~HalCamera()41 HalCamera::~HalCamera() {
42 // Reports the usage statistics before the destruction
43 // EvsUsageStatsReported atom is defined in
44 // frameworks/proto_logging/stats/atoms.proto
45 mUsageStats->writeStats();
46 }
47
makeVirtualCamera()48 std::shared_ptr<VirtualCamera> HalCamera::makeVirtualCamera() {
49 // Create the client camera interface object
50 std::vector<std::shared_ptr<HalCamera>> sourceCameras;
51 sourceCameras.reserve(1);
52 sourceCameras.push_back(ref<HalCamera>());
53 std::shared_ptr<VirtualCamera> client =
54 ::ndk::SharedRefBase::make<VirtualCamera>(sourceCameras);
55 if (!client || !ownVirtualCamera(client)) {
56 LOG(ERROR) << "Failed to create client camera object";
57 return nullptr;
58 }
59
60 return client;
61 }
62
ownVirtualCamera(const std::shared_ptr<VirtualCamera> & virtualCamera)63 bool HalCamera::ownVirtualCamera(const std::shared_ptr<VirtualCamera>& virtualCamera) {
64 if (!virtualCamera) {
65 LOG(ERROR) << "A virtual camera object is invalid";
66 return false;
67 }
68
69 // Make sure we have enough buffers available for all our clients
70 if (!changeFramesInFlight(virtualCamera->getAllowedBuffers())) {
71 // Gah! We couldn't get enough buffers, so we can't support this virtualCamera
72 // Null the pointer, dropping our reference, thus destroying the virtualCamera object
73 return false;
74 }
75
76 // Add this virtualCamera to our ownership list via weak pointer
77 mClients.push_back(virtualCamera);
78
79 // Update statistics
80 mUsageStats->updateNumClients(mClients.size());
81
82 return true;
83 }
84
disownVirtualCamera(const VirtualCamera * clientToDisown)85 void HalCamera::disownVirtualCamera(const VirtualCamera* clientToDisown) {
86 // Ignore calls with null pointers
87 if (!clientToDisown) {
88 LOG(WARNING) << "Ignoring disownVirtualCamera call with null pointer";
89 return;
90 }
91
92 // Remove the virtual camera from our client list
93 const auto clientCount = mClients.size();
94 mClients.remove_if([clientToDisown](std::weak_ptr<VirtualCamera>& client) {
95 auto current = client.lock();
96 return current == nullptr || current.get() == clientToDisown;
97 });
98
99 if (clientCount == mClients.size()) {
100 LOG(WARNING) << "Couldn't find camera in our client list to remove it; "
101 << "this client may be removed already.";
102 }
103
104 // Recompute the number of buffers required with the target camera removed from the list
105 if (!changeFramesInFlight(/* delta= */ 0)) {
106 LOG(WARNING) << "Error when trying to reduce the in flight buffer count";
107 }
108
109 // Update statistics
110 mUsageStats->updateNumClients(mClients.size());
111 }
112
changeFramesInFlight(int delta)113 bool HalCamera::changeFramesInFlight(int delta) {
114 // Walk all our clients and count their currently required frames
115 unsigned bufferCount = 0;
116 for (auto&& client : mClients) {
117 std::shared_ptr<VirtualCamera> virtCam = client.lock();
118 if (virtCam) {
119 bufferCount += virtCam->getAllowedBuffers();
120 }
121 }
122
123 // Add the requested delta
124 bufferCount += delta;
125
126 // Never drop below 1 buffer -- even if all client cameras get closed
127 if (bufferCount < 1) {
128 bufferCount = 1;
129 }
130
131 // Ask the hardware for the resulting buffer count
132 if (!mHwCamera->setMaxFramesInFlight(bufferCount).isOk()) {
133 return false;
134 }
135
136 // Update the size of our array of outstanding frame records
137 std::vector<FrameRecord> newRecords;
138 newRecords.reserve(bufferCount);
139
140 // Copy and compact the old records that are still active
141 {
142 std::lock_guard lock(mFrameMutex);
143 for (const auto& rec : mFrames) {
144 if (rec.refCount > 0) {
145 newRecords.push_back(std::move(rec));
146 }
147 }
148 if (newRecords.size() > static_cast<unsigned>(bufferCount)) {
149 LOG(WARNING) << "We found more frames in use than requested.";
150 }
151
152 mFrames.swap(newRecords);
153 }
154 return true;
155 }
156
changeFramesInFlight(const std::vector<BufferDesc> & buffers,int * delta)157 bool HalCamera::changeFramesInFlight(const std::vector<BufferDesc>& buffers, int* delta) {
158 // Return immediately if a list is empty.
159 if (buffers.empty()) {
160 LOG(DEBUG) << "No external buffers to add.";
161 return true;
162 }
163
164 // Walk all our clients and count their currently required frames
165 auto bufferCount = 0;
166 for (auto&& client : mClients) {
167 std::shared_ptr<VirtualCamera> virtCam = client.lock();
168 if (virtCam) {
169 bufferCount += virtCam->getAllowedBuffers();
170 }
171 }
172
173 // Ask the hardware for the resulting buffer count
174 if (!mHwCamera->importExternalBuffers(buffers, delta).isOk()) {
175 LOG(ERROR) << "Failed to add external capture buffers.";
176 return false;
177 }
178
179 bufferCount += *delta;
180
181 // Update the size of our array of outstanding frame records
182 std::vector<FrameRecord> newRecords;
183 newRecords.reserve(bufferCount);
184
185 {
186 std::lock_guard lock(mFrameMutex);
187 // Copy and compact the old records that are still active
188 for (const auto& rec : mFrames) {
189 if (rec.refCount > 0) {
190 newRecords.push_back(std::move(rec));
191 }
192 }
193
194 if (newRecords.size() > static_cast<unsigned>(bufferCount)) {
195 LOG(WARNING) << "We found more frames in use than requested.";
196 }
197
198 mFrames.swap(newRecords);
199 }
200
201 return true;
202 }
203
requestNewFrame(std::shared_ptr<VirtualCamera> client,int64_t lastTimestamp)204 void HalCamera::requestNewFrame(std::shared_ptr<VirtualCamera> client, int64_t lastTimestamp) {
205 ScopedTrace trace("Camera " + getId(), __PRETTY_FUNCTION__);
206 FrameRequest req;
207 req.client = client;
208 req.timestamp = lastTimestamp;
209
210 std::lock_guard<std::mutex> lock(mFrameMutex);
211 mNextRequests.push_back(req);
212 }
213
clientStreamStarting()214 ScopedAStatus HalCamera::clientStreamStarting() {
215 {
216 std::lock_guard lock(mFrameMutex);
217 if (mStreamState == RUNNING) {
218 // This camera device is already active.
219 return ScopedAStatus::ok();
220 }
221
222 if (mStreamState == STOPPED) {
223 // Try to start a video stream.
224 ScopedAStatus status = mHwCamera->startVideoStream(ref<HalCamera>());
225 if (status.isOk()) {
226 mStreamState = RUNNING;
227 }
228 return status;
229 }
230
231 // We cannot start a video stream.
232 return Utils::buildScopedAStatusFromEvsResult(
233 mStreamState == STOPPING ? EvsResult::RESOURCE_BUSY
234 : EvsResult::UNDERLYING_SERVICE_ERROR);
235 }
236 }
237
clientStreamEnding(const VirtualCamera * client)238 void HalCamera::clientStreamEnding(const VirtualCamera* client) {
239 {
240 std::lock_guard<std::mutex> lock(mFrameMutex);
241 if (mStreamState != RUNNING) {
242 // We are being stopped or stopped already.
243 return;
244 }
245
246 mNextRequests.erase(std::remove_if(mNextRequests.begin(), mNextRequests.end(),
247 [client](const auto& r) {
248 return r.client.lock().get() == client;
249 }),
250 mNextRequests.end());
251 }
252
253 // Do we still have a running client?
254 bool stillRunning = false;
255 for (auto&& client : mClients) {
256 std::shared_ptr<VirtualCamera> virtCam = client.lock();
257 if (virtCam) {
258 stillRunning |= virtCam->isStreaming();
259 }
260 }
261
262 // If not, then stop the hardware stream
263 if (!stillRunning) {
264 {
265 std::lock_guard lock(mFrameMutex);
266 mStreamState = STOPPING;
267 }
268 auto status = mHwCamera->stopVideoStream();
269 if (!status.isOk()) {
270 LOG(WARNING) << "Failed to stop a video stream, error = "
271 << status.getServiceSpecificError();
272 }
273 }
274 }
275
doneWithFrame(BufferDesc buffer)276 ScopedAStatus HalCamera::doneWithFrame(BufferDesc buffer) {
277 ScopedTrace trace("Camera " + getId(), __PRETTY_FUNCTION__, buffer.bufferId);
278 std::unique_lock lock(mFrameMutex);
279 ::android::base::ScopedLockAssertion lock_assertion(mFrameMutex);
280 mFrameOpDone.wait(lock, [this]() REQUIRES(mFrameMutex) { return mFrameOpInProgress != true; });
281
282 // Find this frame in our list of outstanding frames
283 auto it = std::find_if(mFrames.begin(), mFrames.end(),
284 [id = buffer.bufferId](const FrameRecord& rec) {
285 return rec.frameId == id;
286 });
287 if (it == mFrames.end()) {
288 LOG(WARNING) << "We got a frame back with an ID we don't recognize!";
289 return ScopedAStatus::ok();
290 }
291
292 if (it->refCount < 1) {
293 LOG(WARNING) << "Buffer " << buffer.bufferId
294 << " is returned with a zero reference counter.";
295 return ScopedAStatus::ok();
296 }
297
298 // Are there still clients using this buffer?
299 it->refCount = it->refCount - 1;
300 if (it->refCount > 0) {
301 LOG(DEBUG) << "Buffer " << buffer.bufferId << " is still being used by " << it->refCount
302 << " other client(s).";
303 return ScopedAStatus::ok();
304 }
305
306 // Since all our clients are done with this buffer, return it to the device layer
307 std::vector<BufferDesc> buffersToReturn(1);
308 buffersToReturn[0] = std::move(buffer);
309 auto status = mHwCamera->doneWithFrame(buffersToReturn);
310 if (!status.isOk()) {
311 LOG(WARNING) << "Failed to return a buffer";
312 }
313
314 // Counts a returned buffer
315 mUsageStats->framesReturned(buffersToReturn);
316
317 return status;
318 }
319
320 // Methods from ::aidl::android::hardware::automotive::evs::IEvsCameraStream follow.
deliverFrame(const std::vector<BufferDesc> & buffers)321 ScopedAStatus HalCamera::deliverFrame(const std::vector<BufferDesc>& buffers) {
322 LOG(VERBOSE) << "Received a frame";
323
324 ScopedTrace trace("Camera " + getId(), __PRETTY_FUNCTION__,
325 buffers.empty() ? std::numeric_limits<int>::min() : buffers[0].bufferId);
326
327 // Reports the number of received buffers
328 mUsageStats->framesReceived(buffers);
329
330 // Frames are being forwarded to HIDL v1.1 and AIDL clients only who requested new frame.
331 const auto timestamp = buffers[0].timestamp;
332 // TODO(b/145750636): For now, we are using a approximately half of 1 seconds / 30 frames = 33ms
333 // but this must be derived from current framerate.
334 constexpr int64_t kThreshold = 16'000; // ms
335 unsigned frameDeliveries = 0;
336 std::deque<FrameRequest> currentRequests;
337 std::deque<FrameRequest> puntedRequests;
338 {
339 std::lock_guard<std::mutex> lock(mFrameMutex);
340 currentRequests.insert(currentRequests.end(),
341 std::make_move_iterator(mNextRequests.begin()),
342 std::make_move_iterator(mNextRequests.end()));
343 mNextRequests.clear();
344 mFrameOpInProgress = true;
345 }
346
347 while (!currentRequests.empty()) {
348 auto req = currentRequests.front();
349 currentRequests.pop_front();
350 std::shared_ptr<VirtualCamera> vCam = req.client.lock();
351 if (!vCam) {
352 // Ignore a client already dead.
353 continue;
354 }
355
356 if (timestamp - req.timestamp < kThreshold) {
357 // Skip current frame because it arrives too soon.
358 LOG(DEBUG) << "Skips a frame from " << getId();
359 mUsageStats->framesSkippedToSync();
360 puntedRequests.push_back(req);
361 continue;
362 }
363
364 if (!vCam->deliverFrame(buffers[0])) {
365 LOG(WARNING) << getId() << " failed to forward the buffer to " << vCam.get();
366 } else {
367 LOG(DEBUG) << getId() << " forwarded the buffer #" << buffers[0].bufferId << " to "
368 << vCam.get() << " from " << this;
369 ++frameDeliveries;
370 }
371 }
372
373 if (frameDeliveries < 1) {
374 // If none of our clients could accept the frame, then return it
375 // right away.
376 LOG(INFO) << "Trivially rejecting frame (" << buffers[0].bufferId << ") from " << getId()
377 << " with no acceptance";
378 if (!mHwCamera->doneWithFrame(buffers).isOk()) {
379 LOG(WARNING) << "Failed to return buffers";
380 }
381
382 // Reports a returned buffer
383 mUsageStats->framesReturned(buffers);
384
385 // Adding skipped capture requests back to the queue.
386 std::lock_guard<std::mutex> lock(mFrameMutex);
387 mNextRequests.insert(mNextRequests.end(), std::make_move_iterator(puntedRequests.begin()),
388 std::make_move_iterator(puntedRequests.end()));
389 mFrameOpInProgress = false;
390 mFrameOpDone.notify_all();
391 } else {
392 std::lock_guard lock(mFrameMutex);
393
394 // Add an entry for this frame in our tracking list.
395 unsigned i;
396 for (i = 0; i < mFrames.size(); ++i) {
397 if (mFrames[i].refCount == 0) {
398 break;
399 }
400 }
401
402 if (i == mFrames.size()) {
403 mFrames.emplace_back(buffers[0].bufferId, frameDeliveries);
404 } else {
405 mFrames[i].frameId = buffers[0].bufferId;
406 mFrames[i].refCount = frameDeliveries;
407 }
408
409 // Adding skipped capture requests back to the queue.
410 mNextRequests.insert(mNextRequests.end(), std::make_move_iterator(puntedRequests.begin()),
411 std::make_move_iterator(puntedRequests.end()));
412 mFrameOpInProgress = false;
413 mFrameOpDone.notify_all();
414 }
415
416 return ScopedAStatus::ok();
417 }
418
notify(const EvsEventDesc & event)419 ScopedAStatus HalCamera::notify(const EvsEventDesc& event) {
420 LOG(DEBUG) << "Received an event id: " << static_cast<int32_t>(event.aType);
421 ScopedTrace trace("Camera " + getId(), __PRETTY_FUNCTION__, static_cast<int>(event.aType));
422 if (event.aType == EvsEventType::STREAM_STOPPED) {
423 // This event happens only when there is no more active client.
424 std::lock_guard lock(mFrameMutex);
425 if (mStreamState != STOPPING) {
426 LOG(WARNING) << "Stream stopped unexpectedly";
427 }
428
429 mStreamState = STOPPED;
430 }
431
432 // Forward all other events to the clients
433 for (auto&& client : mClients) {
434 std::shared_ptr<VirtualCamera> virtCam = client.lock();
435 if (virtCam) {
436 if (!virtCam->notify(event)) {
437 LOG(WARNING) << "Failed to forward an event";
438 }
439 }
440 }
441
442 return ScopedAStatus::ok();
443 }
444
setPrimaryClient(const std::shared_ptr<VirtualCamera> & virtualCamera)445 ScopedAStatus HalCamera::setPrimaryClient(const std::shared_ptr<VirtualCamera>& virtualCamera) {
446 if (mPrimaryClient.lock() == nullptr) {
447 LOG(DEBUG) << __FUNCTION__ << ": " << virtualCamera.get() << " becomes a primary client.";
448 mPrimaryClient = virtualCamera;
449 return ScopedAStatus::ok();
450 } else {
451 LOG(INFO) << "This camera already has a primary client.";
452 return Utils::buildScopedAStatusFromEvsResult(EvsResult::PERMISSION_DENIED);
453 }
454 }
455
forcePrimaryClient(const std::shared_ptr<VirtualCamera> & virtualCamera)456 ScopedAStatus HalCamera::forcePrimaryClient(const std::shared_ptr<VirtualCamera>& virtualCamera) {
457 std::shared_ptr<VirtualCamera> prevPrimary = mPrimaryClient.lock();
458 if (prevPrimary == virtualCamera) {
459 LOG(DEBUG) << "Client " << virtualCamera.get() << " is already a primary client";
460 return ScopedAStatus::ok();
461 }
462
463 mPrimaryClient = virtualCamera;
464 if (prevPrimary) {
465 LOG(INFO) << "High priority client " << virtualCamera.get()
466 << " steals a primary role from " << prevPrimary.get();
467
468 /* Notify a previous primary client the loss of a primary role */
469 EvsEventDesc event;
470 event.aType = EvsEventType::MASTER_RELEASED;
471 auto cbResult = prevPrimary->notify(event);
472 if (!cbResult) {
473 LOG(WARNING) << "Fail to deliver a primary role lost notification";
474 }
475 }
476
477 return ScopedAStatus::ok();
478 }
479
unsetPrimaryClient(const VirtualCamera * virtualCamera)480 ScopedAStatus HalCamera::unsetPrimaryClient(const VirtualCamera* virtualCamera) {
481 if (mPrimaryClient.lock().get() != virtualCamera) {
482 return Utils::buildScopedAStatusFromEvsResult(EvsResult::INVALID_ARG);
483 }
484
485 LOG(INFO) << "Unset a primary camera client";
486 mPrimaryClient.reset();
487
488 /* Notify other clients that a primary role becomes available. */
489 EvsEventDesc event;
490 event.aType = EvsEventType::MASTER_RELEASED;
491 if (!notify(event).isOk()) {
492 LOG(WARNING) << "Fail to deliver a parameter change notification";
493 }
494
495 return ScopedAStatus::ok();
496 }
497
setParameter(const std::shared_ptr<VirtualCamera> & virtualCamera,CameraParam id,int32_t * value)498 ScopedAStatus HalCamera::setParameter(const std::shared_ptr<VirtualCamera>& virtualCamera,
499 CameraParam id, int32_t* value) {
500 if (virtualCamera != mPrimaryClient.lock()) {
501 LOG(WARNING) << "A parameter change request from the non-primary client is declined.";
502
503 /* Read a current value of a requested camera parameter */
504 getParameter(id, value);
505 return Utils::buildScopedAStatusFromEvsResult(EvsResult::PERMISSION_DENIED);
506 }
507
508 std::vector<int32_t> effectiveValues;
509 auto result = mHwCamera->setIntParameter(id, *value, &effectiveValues);
510 if (result.isOk()) {
511 /* Notify a parameter change */
512 EvsEventDesc event;
513 event.aType = EvsEventType::PARAMETER_CHANGED;
514 event.payload.push_back(static_cast<int32_t>(id));
515 event.payload.push_back(effectiveValues[0]);
516 if (!notify(event).isOk()) {
517 LOG(WARNING) << "Fail to deliver a parameter change notification";
518 }
519
520 *value = effectiveValues[0];
521 }
522
523 return result;
524 }
525
getParameter(CameraParam id,int32_t * value)526 ScopedAStatus HalCamera::getParameter(CameraParam id, int32_t* value) {
527 std::vector<int32_t> effectiveValues;
528 auto result = mHwCamera->getIntParameter(id, &effectiveValues);
529 if (result.isOk()) {
530 *value = effectiveValues[0];
531 }
532
533 return result;
534 }
535
getStats() const536 CameraUsageStatsRecord HalCamera::getStats() const {
537 return mUsageStats->snapshot();
538 }
539
getStreamConfiguration() const540 Stream HalCamera::getStreamConfiguration() const {
541 return mStreamConfig;
542 }
543
toString(const char * indent) const544 std::string HalCamera::toString(const char* indent) const {
545 std::string buffer;
546
547 const auto timeElapsedMs = ::android::uptimeMillis() - mTimeCreatedMs;
548 StringAppendF(&buffer, "%sCreated: @%" PRId64 " (elapsed %" PRId64 " ms)\n", indent,
549 mTimeCreatedMs, timeElapsedMs);
550
551 std::string double_indent(indent);
552 double_indent += indent;
553 buffer += CameraUsageStats::toString(getStats(), double_indent.data());
554 for (auto&& client : mClients) {
555 auto handle = client.lock();
556 if (!handle) {
557 continue;
558 }
559
560 StringAppendF(&buffer, "%sClient %p\n", indent, handle.get());
561 buffer += handle->toString(double_indent.data());
562 }
563
564 StringAppendF(&buffer, "%sPrimary client: %p\n", indent, mPrimaryClient.lock().get());
565
566 buffer += HalCamera::toString(mStreamConfig, indent);
567
568 return buffer;
569 }
570
toString(Stream configuration,const char * indent)571 std::string HalCamera::toString(Stream configuration, const char* indent) {
572 std::string streamInfo;
573 std::string double_indent(indent);
574 double_indent += indent;
575 StringAppendF(&streamInfo,
576 "%sActive Stream Configuration\n"
577 "%sid: %d\n"
578 "%swidth: %d\n"
579 "%sheight: %d\n"
580 "%sformat: 0x%X\n"
581 "%susage: 0x%" PRIx64 "\n"
582 "%srotation: 0x%X\n\n",
583 indent, double_indent.data(), configuration.id, double_indent.data(),
584 configuration.width, double_indent.data(), configuration.height,
585 double_indent.data(), static_cast<unsigned int>(configuration.format),
586 double_indent.data(), static_cast<int64_t>(configuration.usage),
587 double_indent.data(), static_cast<unsigned int>(configuration.rotation));
588
589 return streamInfo;
590 }
591
592 } // namespace aidl::android::automotive::evs::implementation
593