1 /*
2 * Copyright (C) 2016 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 "EvsEnumerator.h"
18 #include "EvsV4lCamera.h"
19 #include "EvsGlDisplay.h"
20 #include "ConfigManager.h"
21
22 #include <dirent.h>
23 #include <hardware_legacy/uevent.h>
24 #include <hwbinder/IPCThreadState.h>
25 #include <cutils/android_filesystem_config.h>
26
27
28 using namespace std::chrono_literals;
29 using CameraDesc_1_0 = ::android::hardware::automotive::evs::V1_0::CameraDesc;
30 using CameraDesc_1_1 = ::android::hardware::automotive::evs::V1_1::CameraDesc;
31
32 namespace android {
33 namespace hardware {
34 namespace automotive {
35 namespace evs {
36 namespace V1_1 {
37 namespace implementation {
38
39
40 // NOTE: All members values are static so that all clients operate on the same state
41 // That is to say, this is effectively a singleton despite the fact that HIDL
42 // constructs a new instance for each client.
43 std::unordered_map<std::string, EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
44 wp<EvsGlDisplay> EvsEnumerator::sActiveDisplay;
45 std::mutex EvsEnumerator::sLock;
46 std::condition_variable EvsEnumerator::sCameraSignal;
47 std::unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
48 sp<IAutomotiveDisplayProxyService> EvsEnumerator::sDisplayProxy;
49 std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
50 uint64_t EvsEnumerator::sInternalDisplayId;
51
52
53 // Constants
54 const auto kEnumerationTimeout = 10s;
55
56
checkPermission()57 bool EvsEnumerator::checkPermission() {
58 hardware::IPCThreadState *ipc = hardware::IPCThreadState::self();
59 if (AID_AUTOMOTIVE_EVS != ipc->getCallingUid() &&
60 AID_ROOT != ipc->getCallingUid()) {
61 LOG(ERROR) << "EVS access denied: "
62 << "pid = " << ipc->getCallingPid()
63 << ", uid = " << ipc->getCallingUid();
64 return false;
65 }
66
67 return true;
68 }
69
EvsUeventThread(std::atomic<bool> & running)70 void EvsEnumerator::EvsUeventThread(std::atomic<bool>& running) {
71 int status = uevent_init();
72 if (!status) {
73 LOG(ERROR) << "Failed to initialize uevent handler.";
74 return;
75 }
76
77 char uevent_data[PAGE_SIZE - 2] = {};
78 while (running) {
79 int length = uevent_next_event(uevent_data, static_cast<int32_t>(sizeof(uevent_data)));
80
81 // Ensure double-null termination.
82 uevent_data[length] = uevent_data[length + 1] = '\0';
83
84 const char *action = nullptr;
85 const char *devname = nullptr;
86 const char *subsys = nullptr;
87 char *cp = uevent_data;
88 while (*cp) {
89 // EVS is interested only in ACTION, SUBSYSTEM, and DEVNAME.
90 if (!std::strncmp(cp, "ACTION=", 7)) {
91 action = cp + 7;
92 } else if (!std::strncmp(cp, "SUBSYSTEM=", 10)) {
93 subsys = cp + 10;
94 } else if (!std::strncmp(cp, "DEVNAME=", 8)) {
95 devname = cp + 8;
96 }
97
98 // Advance to after next \0
99 while (*cp++);
100 }
101
102 if (!devname || !subsys || std::strcmp(subsys, "video4linux")) {
103 // EVS expects that the subsystem of enabled video devices is
104 // video4linux.
105 continue;
106 }
107
108 // Update shared list.
109 bool cmd_addition = !std::strcmp(action, "add");
110 bool cmd_removal = !std::strcmp(action, "remove");
111 {
112 std::string devpath = "/dev/";
113 devpath += devname;
114
115 std::lock_guard<std::mutex> lock(sLock);
116 if (cmd_removal) {
117 sCameraList.erase(devpath);
118 LOG(INFO) << devpath << " is removed.";
119 } else if (cmd_addition) {
120 // NOTE: we are here adding new device without a validation
121 // because it always fails to open, b/132164956.
122 CameraRecord cam(devpath.c_str());
123 if (sConfigManager != nullptr) {
124 unique_ptr<ConfigManager::CameraInfo> &camInfo =
125 sConfigManager->getCameraInfo(devpath);
126 if (camInfo != nullptr) {
127 cam.desc.metadata.setToExternal(
128 (uint8_t *)camInfo->characteristics,
129 get_camera_metadata_size(camInfo->characteristics)
130 );
131 }
132 }
133 sCameraList.emplace(devpath, cam);
134 LOG(INFO) << devpath << " is added.";
135 } else {
136 // Ignore all other actions including "change".
137 }
138
139 // Notify the change.
140 sCameraSignal.notify_all();
141 }
142 }
143
144 return;
145 }
146
EvsEnumerator(sp<IAutomotiveDisplayProxyService> proxyService)147 EvsEnumerator::EvsEnumerator(sp<IAutomotiveDisplayProxyService> proxyService) {
148 LOG(DEBUG) << "EvsEnumerator is created.";
149
150 if (sConfigManager == nullptr) {
151 /* loads and initializes ConfigManager in a separate thread */
152 sConfigManager =
153 ConfigManager::Create("/vendor/etc/automotive/evs/evs_sample_configuration.xml");
154 }
155
156 if (sDisplayProxy == nullptr) {
157 /* sets a car-window service handle */
158 sDisplayProxy = proxyService;
159 }
160
161 enumerateCameras();
162 enumerateDisplays();
163 }
164
enumerateCameras()165 void EvsEnumerator::enumerateCameras() {
166 // For every video* entry in the dev folder, see if it reports suitable capabilities
167 // WARNING: Depending on the driver implementations this could be slow, especially if
168 // there are timeouts or round trips to hardware required to collect the needed
169 // information. Platform implementers should consider hard coding this list of
170 // known good devices to speed up the startup time of their EVS implementation.
171 // For example, this code might be replaced with nothing more than:
172 // sCameraList.emplace("/dev/video0");
173 // sCameraList.emplace("/dev/video1");
174 LOG(INFO) << __FUNCTION__
175 << ": Starting dev/video* enumeration";
176 unsigned videoCount = 0;
177 unsigned captureCount = 0;
178 DIR* dir = opendir("/dev");
179 if (!dir) {
180 LOG_FATAL("Failed to open /dev folder\n");
181 }
182 struct dirent* entry;
183 {
184 std::lock_guard<std::mutex> lock(sLock);
185
186 while ((entry = readdir(dir)) != nullptr) {
187 // We're only looking for entries starting with 'video'
188 if (strncmp(entry->d_name, "video", 5) == 0) {
189 std::string deviceName("/dev/");
190 deviceName += entry->d_name;
191 videoCount++;
192 if (sCameraList.find(deviceName) != sCameraList.end()) {
193 LOG(INFO) << deviceName << " has been added already.";
194 captureCount++;
195 } else if(qualifyCaptureDevice(deviceName.c_str())) {
196 sCameraList.emplace(deviceName, deviceName.c_str());
197 captureCount++;
198 }
199 }
200 }
201 }
202
203 LOG(INFO) << "Found " << captureCount << " qualified video capture devices "
204 << "of " << videoCount << " checked.";
205 }
206
207
enumerateDisplays()208 void EvsEnumerator::enumerateDisplays() {
209 LOG(INFO) << __FUNCTION__
210 << ": Starting display enumeration";
211 if (!sDisplayProxy) {
212 LOG(ERROR) << "AutomotiveDisplayProxyService is not available!";
213 return;
214 }
215
216 sDisplayProxy->getDisplayIdList(
217 [](const auto& displayIds) {
218 // The first entry of the list is the internal display. See
219 // SurfaceFlinger::getPhysicalDisplayIds() implementation.
220 if (displayIds.size() > 0) {
221 sInternalDisplayId = displayIds[0];
222 for (const auto& id : displayIds) {
223 const auto port = id & 0xF;
224 LOG(INFO) << "Display " << std::hex << id
225 << " is detected on the port, " << port;
226 sDisplayPortList.insert_or_assign(port, id);
227 }
228 }
229 }
230 );
231
232 LOG(INFO) << "Found " << sDisplayPortList.size() << " displays";
233 }
234
235
236 // Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
getCameraList(getCameraList_cb _hidl_cb)237 Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) {
238 LOG(DEBUG) << __FUNCTION__;
239 if (!checkPermission()) {
240 return Void();
241 }
242
243 {
244 std::unique_lock<std::mutex> lock(sLock);
245 if (sCameraList.size() < 1) {
246 // No qualified device has been found. Wait until new device is ready,
247 // for 10 seconds.
248 if (!sCameraSignal.wait_for(lock,
249 kEnumerationTimeout,
250 []{ return sCameraList.size() > 0; })) {
251 LOG(DEBUG) << "Timer expired. No new device has been added.";
252 }
253 }
254 }
255
256 const unsigned numCameras = sCameraList.size();
257
258 // Build up a packed array of CameraDesc for return
259 hidl_vec<CameraDesc_1_0> hidlCameras;
260 hidlCameras.resize(numCameras);
261 unsigned i = 0;
262 for (const auto& [key, cam] : sCameraList) {
263 hidlCameras[i++] = cam.desc.v1;
264 }
265
266 // Send back the results
267 LOG(DEBUG) << "Reporting " << hidlCameras.size() << " cameras available";
268 _hidl_cb(hidlCameras);
269
270 // HIDL convention says we return Void if we sent our result back via callback
271 return Void();
272 }
273
274
openCamera(const hidl_string & cameraId)275 Return<sp<IEvsCamera_1_0>> EvsEnumerator::openCamera(const hidl_string& cameraId) {
276 LOG(DEBUG) << __FUNCTION__;
277 if (!checkPermission()) {
278 return nullptr;
279 }
280
281 // Is this a recognized camera id?
282 CameraRecord *pRecord = findCameraById(cameraId);
283 if (pRecord == nullptr) {
284 LOG(ERROR) << cameraId << " does not exist!";
285 return nullptr;
286 }
287
288 // Has this camera already been instantiated by another caller?
289 sp<EvsV4lCamera> pActiveCamera = pRecord->activeInstance.promote();
290 if (pActiveCamera != nullptr) {
291 LOG(WARNING) << "Killing previous camera because of new caller";
292 closeCamera(pActiveCamera);
293 }
294
295 // Construct a camera instance for the caller
296 if (sConfigManager == nullptr) {
297 pActiveCamera = EvsV4lCamera::Create(cameraId.c_str());
298 } else {
299 pActiveCamera = EvsV4lCamera::Create(cameraId.c_str(),
300 sConfigManager->getCameraInfo(cameraId));
301 }
302
303 pRecord->activeInstance = pActiveCamera;
304 if (pActiveCamera == nullptr) {
305 LOG(ERROR) << "Failed to create new EvsV4lCamera object for " << cameraId;
306 }
307
308 return pActiveCamera;
309 }
310
311
closeCamera(const::android::sp<IEvsCamera_1_0> & pCamera)312 Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera_1_0>& pCamera) {
313 LOG(DEBUG) << __FUNCTION__;
314
315 if (pCamera == nullptr) {
316 LOG(ERROR) << "Ignoring call to closeCamera with null camera ptr";
317 return Void();
318 }
319
320 // Get the camera id so we can find it in our list
321 std::string cameraId;
322 pCamera->getCameraInfo([&cameraId](CameraDesc_1_0 desc) {
323 cameraId = desc.cameraId;
324 }
325 );
326
327 closeCamera_impl(pCamera, cameraId);
328
329 return Void();
330 }
331
332
openDisplay()333 Return<sp<IEvsDisplay_1_0>> EvsEnumerator::openDisplay() {
334 LOG(DEBUG) << __FUNCTION__;
335 if (!checkPermission()) {
336 return nullptr;
337 }
338
339 // If we already have a display active, then we need to shut it down so we can
340 // give exclusive access to the new caller.
341 sp<EvsGlDisplay> pActiveDisplay = sActiveDisplay.promote();
342 if (pActiveDisplay != nullptr) {
343 LOG(WARNING) << "Killing previous display because of new caller";
344 closeDisplay(pActiveDisplay);
345 }
346
347 // Create a new display interface and return it.
348 pActiveDisplay = new EvsGlDisplay(sDisplayProxy, sInternalDisplayId);
349 sActiveDisplay = pActiveDisplay;
350
351 LOG(DEBUG) << "Returning new EvsGlDisplay object " << pActiveDisplay.get();
352 return pActiveDisplay;
353 }
354
355
closeDisplay(const::android::sp<IEvsDisplay_1_0> & pDisplay)356 Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay_1_0>& pDisplay) {
357 LOG(DEBUG) << __FUNCTION__;
358
359 // Do we still have a display object we think should be active?
360 sp<EvsGlDisplay> pActiveDisplay = sActiveDisplay.promote();
361 if (pActiveDisplay == nullptr) {
362 LOG(ERROR) << "Somehow a display is being destroyed "
363 << "when the enumerator didn't know one existed";
364 } else if (sActiveDisplay != pDisplay) {
365 LOG(WARNING) << "Ignoring close of previously orphaned display - why did a client steal?";
366 } else {
367 // Drop the active display
368 pActiveDisplay->forceShutdown();
369 sActiveDisplay = nullptr;
370 }
371
372 return Void();
373 }
374
375
getDisplayState()376 Return<EvsDisplayState> EvsEnumerator::getDisplayState() {
377 LOG(DEBUG) << __FUNCTION__;
378 if (!checkPermission()) {
379 return EvsDisplayState::DEAD;
380 }
381
382 // Do we still have a display object we think should be active?
383 sp<IEvsDisplay_1_0> pActiveDisplay = sActiveDisplay.promote();
384 if (pActiveDisplay != nullptr) {
385 return pActiveDisplay->getDisplayState();
386 } else {
387 return EvsDisplayState::NOT_OPEN;
388 }
389 }
390
391
392 // Methods from ::android::hardware::automotive::evs::V1_1::IEvsEnumerator follow.
getCameraList_1_1(getCameraList_1_1_cb _hidl_cb)393 Return<void> EvsEnumerator::getCameraList_1_1(getCameraList_1_1_cb _hidl_cb) {
394 LOG(DEBUG) << __FUNCTION__;
395 if (!checkPermission()) {
396 return Void();
397 }
398
399 {
400 std::unique_lock<std::mutex> lock(sLock);
401 if (sCameraList.size() < 1) {
402 // No qualified device has been found. Wait until new device is ready,
403 if (!sCameraSignal.wait_for(lock,
404 kEnumerationTimeout,
405 []{ return sCameraList.size() > 0; })) {
406 LOG(DEBUG) << "Timer expired. No new device has been added.";
407 }
408 }
409 }
410
411 std::vector<CameraDesc_1_1> hidlCameras;
412 if (sConfigManager == nullptr) {
413 auto numCameras = sCameraList.size();
414
415 // Build up a packed array of CameraDesc for return
416 hidlCameras.resize(numCameras);
417 unsigned i = 0;
418 for (auto&& [key, cam] : sCameraList) {
419 hidlCameras[i++] = cam.desc;
420 }
421 } else {
422 // Build up a packed array of CameraDesc for return
423 for (auto&& [key, cam] : sCameraList) {
424 unique_ptr<ConfigManager::CameraInfo> &tempInfo =
425 sConfigManager->getCameraInfo(key);
426 if (tempInfo != nullptr) {
427 cam.desc.metadata.setToExternal(
428 (uint8_t *)tempInfo->characteristics,
429 get_camera_metadata_size(tempInfo->characteristics)
430 );
431 }
432
433 hidlCameras.emplace_back(cam.desc);
434 }
435
436 // Adding camera groups that represent logical camera devices
437 auto camGroups = sConfigManager->getCameraGroupIdList();
438 for (auto&& id : camGroups) {
439 if (sCameraList.find(id) != sCameraList.end()) {
440 // Already exists in the list
441 continue;
442 }
443
444 unique_ptr<ConfigManager::CameraGroupInfo> &tempInfo =
445 sConfigManager->getCameraGroupInfo(id);
446 CameraRecord cam(id.c_str());
447 if (tempInfo != nullptr) {
448 cam.desc.metadata.setToExternal(
449 (uint8_t *)tempInfo->characteristics,
450 get_camera_metadata_size(tempInfo->characteristics)
451 );
452 }
453
454 sCameraList.emplace(id, cam);
455 hidlCameras.emplace_back(cam.desc);
456 }
457 }
458
459 // Send back the results
460 _hidl_cb(hidlCameras);
461
462 // HIDL convention says we return Void if we sent our result back via callback
463 return Void();
464 }
465
466
openCamera_1_1(const hidl_string & cameraId,const Stream & streamCfg)467 Return<sp<IEvsCamera_1_1>> EvsEnumerator::openCamera_1_1(const hidl_string& cameraId,
468 const Stream& streamCfg) {
469 LOG(DEBUG) << __FUNCTION__;
470 if (!checkPermission()) {
471 return nullptr;
472 }
473
474 // Is this a recognized camera id?
475 CameraRecord *pRecord = findCameraById(cameraId);
476 if (pRecord == nullptr) {
477 LOG(ERROR) << cameraId << " does not exist!";
478 return nullptr;
479 }
480
481 // Has this camera already been instantiated by another caller?
482 sp<EvsV4lCamera> pActiveCamera = pRecord->activeInstance.promote();
483 if (pActiveCamera != nullptr) {
484 LOG(WARNING) << "Killing previous camera because of new caller";
485 closeCamera(pActiveCamera);
486 }
487
488 // Construct a camera instance for the caller
489 if (sConfigManager == nullptr) {
490 LOG(WARNING) << "ConfigManager is not available. "
491 << "Given stream configuration is ignored.";
492 pActiveCamera = EvsV4lCamera::Create(cameraId.c_str());
493 } else {
494 pActiveCamera = EvsV4lCamera::Create(cameraId.c_str(),
495 sConfigManager->getCameraInfo(cameraId),
496 &streamCfg);
497 }
498 pRecord->activeInstance = pActiveCamera;
499 if (pActiveCamera == nullptr) {
500 LOG(ERROR) << "Failed to create new EvsV4lCamera object for " << cameraId;
501 }
502
503 return pActiveCamera;
504 }
505
506
getDisplayIdList(getDisplayIdList_cb _list_cb)507 Return<void> EvsEnumerator::getDisplayIdList(getDisplayIdList_cb _list_cb) {
508 hidl_vec<uint8_t> ids;
509
510 if (sDisplayPortList.size() > 0) {
511 ids.resize(sDisplayPortList.size());
512 unsigned i = 0;
513 ids[i++] = sInternalDisplayId & 0xF;
514 for (const auto& [port, id] : sDisplayPortList) {
515 if (sInternalDisplayId != id) {
516 ids[i++] = port;
517 }
518 }
519 }
520
521 _list_cb(ids);
522 return Void();
523 }
524
525
openDisplay_1_1(uint8_t port)526 Return<sp<IEvsDisplay_1_1>> EvsEnumerator::openDisplay_1_1(uint8_t port) {
527 LOG(DEBUG) << __FUNCTION__;
528 if (!checkPermission()) {
529 return nullptr;
530 }
531
532 // If we already have a display active, then we need to shut it down so we can
533 // give exclusive access to the new caller.
534 sp<EvsGlDisplay> pActiveDisplay = sActiveDisplay.promote();
535 if (pActiveDisplay != nullptr) {
536 LOG(WARNING) << "Killing previous display because of new caller";
537 closeDisplay(pActiveDisplay);
538 }
539
540 // Create a new display interface and return it
541 if (sDisplayPortList.find(port) == sDisplayPortList.end()) {
542 LOG(ERROR) << "No display is available on the port "
543 << static_cast<int32_t>(port);
544 return nullptr;
545 }
546
547 pActiveDisplay = new EvsGlDisplay(sDisplayProxy, sDisplayPortList[port]);
548 sActiveDisplay = pActiveDisplay;
549
550 LOG(DEBUG) << "Returning new EvsGlDisplay object " << pActiveDisplay.get();
551 return pActiveDisplay;
552 }
553
554
closeCamera_impl(const sp<IEvsCamera_1_0> & pCamera,const std::string & cameraId)555 void EvsEnumerator::closeCamera_impl(const sp<IEvsCamera_1_0>& pCamera,
556 const std::string& cameraId) {
557 // Find the named camera
558 CameraRecord *pRecord = findCameraById(cameraId);
559
560 // Is the display being destroyed actually the one we think is active?
561 if (!pRecord) {
562 LOG(ERROR) << "Asked to close a camera whose name isn't recognized";
563 } else {
564 sp<EvsV4lCamera> pActiveCamera = pRecord->activeInstance.promote();
565
566 if (pActiveCamera == nullptr) {
567 LOG(ERROR) << "Somehow a camera is being destroyed "
568 << "when the enumerator didn't know one existed";
569 } else if (pActiveCamera != pCamera) {
570 // This can happen if the camera was aggressively reopened,
571 // orphaning this previous instance
572 LOG(WARNING) << "Ignoring close of previously orphaned camera "
573 << "- why did a client steal?";
574 } else {
575 // Drop the active camera
576 pActiveCamera->shutdown();
577 pRecord->activeInstance = nullptr;
578 }
579 }
580
581 return;
582 }
583
584
qualifyCaptureDevice(const char * deviceName)585 bool EvsEnumerator::qualifyCaptureDevice(const char* deviceName) {
586 class FileHandleWrapper {
587 public:
588 FileHandleWrapper(int fd) { mFd = fd; }
589 ~FileHandleWrapper() { if (mFd > 0) close(mFd); }
590 operator int() const { return mFd; }
591 private:
592 int mFd = -1;
593 };
594
595
596 FileHandleWrapper fd = open(deviceName, O_RDWR, 0);
597 if (fd < 0) {
598 return false;
599 }
600
601 v4l2_capability caps;
602 int result = ioctl(fd, VIDIOC_QUERYCAP, &caps);
603 if (result < 0) {
604 return false;
605 }
606 if (((caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) ||
607 ((caps.capabilities & V4L2_CAP_STREAMING) == 0)) {
608 return false;
609 }
610
611 // Enumerate the available capture formats (if any)
612 v4l2_fmtdesc formatDescription;
613 formatDescription.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
614 bool found = false;
615 for (int i=0; !found; i++) {
616 formatDescription.index = i;
617 if (ioctl(fd, VIDIOC_ENUM_FMT, &formatDescription) == 0) {
618 LOG(INFO) << "Format: 0x" << std::hex << formatDescription.pixelformat
619 << " Type: 0x" << std::hex << formatDescription.type
620 << " Desc: " << formatDescription.description
621 << " Flags: 0x" << std::hex << formatDescription.flags;
622 switch (formatDescription.pixelformat)
623 {
624 case V4L2_PIX_FMT_YUYV: found = true; break;
625 case V4L2_PIX_FMT_NV21: found = true; break;
626 case V4L2_PIX_FMT_NV16: found = true; break;
627 case V4L2_PIX_FMT_YVU420: found = true; break;
628 case V4L2_PIX_FMT_RGB32: found = true; break;
629 #ifdef V4L2_PIX_FMT_ARGB32 // introduced with kernel v3.17
630 case V4L2_PIX_FMT_ARGB32: found = true; break;
631 case V4L2_PIX_FMT_XRGB32: found = true; break;
632 #endif // V4L2_PIX_FMT_ARGB32
633 default:
634 LOG(WARNING) << "Unsupported, "
635 << std::hex << formatDescription.pixelformat;
636 break;
637 }
638 } else {
639 // No more formats available.
640 break;
641 }
642 }
643
644 return found;
645 }
646
647
findCameraById(const std::string & cameraId)648 EvsEnumerator::CameraRecord* EvsEnumerator::findCameraById(const std::string& cameraId) {
649 // Find the named camera
650 auto found = sCameraList.find(cameraId);
651 if (sCameraList.end() != found) {
652 // Found a match!
653 return &found->second;
654 }
655
656 // We didn't find a match
657 return nullptr;
658 }
659
660
661 // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb)662 Return<void> EvsEnumerator::getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb) {
663 hidl_vec<UltrasonicsArrayDesc> ultrasonicsArrayDesc;
664 _hidl_cb(ultrasonicsArrayDesc);
665 return Void();
666 }
667
668
669 // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
openUltrasonicsArray(const hidl_string & ultrasonicsArrayId)670 Return<sp<IEvsUltrasonicsArray>> EvsEnumerator::openUltrasonicsArray(
671 const hidl_string& ultrasonicsArrayId) {
672 (void)ultrasonicsArrayId;
673 return sp<IEvsUltrasonicsArray>();
674 }
675
676
677 // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
closeUltrasonicsArray(const::android::sp<IEvsUltrasonicsArray> & evsUltrasonicsArray)678 Return<void> EvsEnumerator::closeUltrasonicsArray(
679 const ::android::sp<IEvsUltrasonicsArray>& evsUltrasonicsArray) {
680 (void)evsUltrasonicsArray;
681 return Void();
682 }
683
684 } // namespace implementation
685 } // namespace V1_1
686 } // namespace evs
687 } // namespace automotive
688 } // namespace hardware
689 } // namespace android
690