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