• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "EvsEnumerator.h"
18 
19 #include "ConfigManager.h"
20 #include "EvsGlDisplay.h"
21 #include "EvsV4lCamera.h"
22 
23 #include <aidl/android/hardware/automotive/evs/DeviceStatusType.h>
24 #include <aidl/android/hardware/automotive/evs/EvsResult.h>
25 #include <aidl/android/hardware/automotive/evs/Rotation.h>
26 #include <aidl/android/hardware/graphics/common/BufferUsage.h>
27 #include <aidl/android/hardware/graphics/common/PixelFormat.h>
28 #include <android-base/file.h>
29 #include <android-base/stringprintf.h>
30 #include <android-base/strings.h>
31 #include <cutils/android_filesystem_config.h>
32 #include <cutils/properties.h>
33 
34 #include <dirent.h>
35 #include <sys/inotify.h>
36 
37 #include <string_view>
38 
39 namespace {
40 
41 using ::aidl::android::frameworks::automotive::display::ICarDisplayProxy;
42 using ::aidl::android::hardware::automotive::evs::DeviceStatusType;
43 using ::aidl::android::hardware::automotive::evs::EvsResult;
44 using ::aidl::android::hardware::automotive::evs::Rotation;
45 using ::aidl::android::hardware::graphics::common::BufferUsage;
46 using ::android::base::EqualsIgnoreCase;
47 using ::android::base::StringPrintf;
48 using ::android::base::WriteStringToFd;
49 using ::ndk::ScopedAStatus;
50 using std::chrono_literals::operator""s;
51 
52 // Constants
53 constexpr std::chrono::seconds kEnumerationTimeout = 10s;
54 constexpr std::string_view kDevicePath = "/dev/";
55 constexpr std::string_view kPrefix = "video";
56 constexpr size_t kEventBufferSize = 512;
57 constexpr uint64_t kInvalidDisplayId = std::numeric_limits<uint64_t>::max();
58 const std::set<uid_t> kAllowedUids = {AID_AUTOMOTIVE_EVS, AID_SYSTEM, AID_ROOT};
59 
60 }  // namespace
61 
62 namespace aidl::android::hardware::automotive::evs::implementation {
63 
64 // NOTE:  All members values are static so that all clients operate on the same state
65 //        That is to say, this is effectively a singleton despite the fact that HIDL
66 //        constructs a new instance for each client.
67 std::unordered_map<std::string, EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
68 std::weak_ptr<EvsGlDisplay> EvsEnumerator::sActiveDisplay;
69 std::mutex EvsEnumerator::sLock;
70 std::condition_variable EvsEnumerator::sCameraSignal;
71 std::unique_ptr<ConfigManager> EvsEnumerator::sConfigManager;
72 std::shared_ptr<ICarDisplayProxy> EvsEnumerator::sDisplayProxy;
73 std::unordered_map<uint8_t, uint64_t> EvsEnumerator::sDisplayPortList;
74 
EvsHotplugThread(std::shared_ptr<EvsEnumerator> service,std::atomic<bool> & running)75 void EvsEnumerator::EvsHotplugThread(std::shared_ptr<EvsEnumerator> service,
76                                      std::atomic<bool>& running) {
77     // Watch new video devices
78     if (!service) {
79         LOG(ERROR) << "EvsEnumerator is invalid";
80         return;
81     }
82 
83     auto notifyFd = inotify_init();
84     if (notifyFd < 0) {
85         LOG(ERROR) << "Failed to initialize inotify.  Exiting a thread loop";
86         return;
87     }
88 
89     int watchFd = inotify_add_watch(notifyFd, kDevicePath.data(), IN_CREATE | IN_DELETE);
90     if (watchFd < 0) {
91         LOG(ERROR) << "Failed to add a watch.  Exiting a thread loop";
92         return;
93     }
94 
95     LOG(INFO) << "Start monitoring new V4L2 devices";
96 
97     char eventBuf[kEventBufferSize] = {};
98     while (running) {
99         size_t len = read(notifyFd, eventBuf, sizeof(eventBuf));
100         if (len < sizeof(struct inotify_event)) {
101             // We have no valid event.
102             continue;
103         }
104 
105         size_t offset = 0;
106         while (offset < len) {
107             struct inotify_event* event =
108                     reinterpret_cast<struct inotify_event*>(&eventBuf[offset]);
109             offset += sizeof(struct inotify_event) + event->len;
110             if (event->wd != watchFd || strncmp(kPrefix.data(), event->name, kPrefix.size())) {
111                 continue;
112             }
113 
114             std::string deviceName = std::string(kDevicePath) + std::string(event->name);
115             if (event->mask & IN_CREATE) {
116                 if (addCaptureDevice(deviceName)) {
117                     service->notifyDeviceStatusChange(deviceName,
118                                                       DeviceStatusType::CAMERA_AVAILABLE);
119                 }
120             }
121 
122             if (event->mask & IN_DELETE) {
123                 if (removeCaptureDevice(deviceName)) {
124                     service->notifyDeviceStatusChange(deviceName,
125                                                       DeviceStatusType::CAMERA_NOT_AVAILABLE);
126                 }
127             }
128         }
129     }
130 }
131 
EvsEnumerator(const std::shared_ptr<ICarDisplayProxy> & proxyService)132 EvsEnumerator::EvsEnumerator(const std::shared_ptr<ICarDisplayProxy>& proxyService) {
133     LOG(DEBUG) << "EvsEnumerator is created.";
134 
135     if (!sConfigManager) {
136         /* loads and initializes ConfigManager in a separate thread */
137         sConfigManager = ConfigManager::Create();
138     }
139 
140     if (!sDisplayProxy) {
141         /* sets a car-window service handle */
142         sDisplayProxy = proxyService;
143     }
144 
145     // Enumerate existing devices
146     enumerateCameras();
147     mInternalDisplayId = enumerateDisplays();
148 }
149 
checkPermission()150 bool EvsEnumerator::checkPermission() {
151     const auto uid = AIBinder_getCallingUid();
152     if (kAllowedUids.find(uid) == kAllowedUids.end()) {
153         LOG(ERROR) << "EVS access denied: "
154                    << "pid = " << AIBinder_getCallingPid() << ", uid = " << uid;
155         return false;
156     }
157 
158     return true;
159 }
160 
addCaptureDevice(const std::string & deviceName)161 bool EvsEnumerator::addCaptureDevice(const std::string& deviceName) {
162     if (!qualifyCaptureDevice(deviceName.data())) {
163         LOG(DEBUG) << deviceName << " is not qualified for this EVS HAL implementation";
164         return false;
165     }
166 
167     CameraRecord cam(deviceName.data());
168     if (sConfigManager) {
169         std::unique_ptr<ConfigManager::CameraInfo>& camInfo =
170                 sConfigManager->getCameraInfo(deviceName);
171         if (camInfo) {
172             uint8_t* ptr = reinterpret_cast<uint8_t*>(camInfo->characteristics);
173             const size_t len = get_camera_metadata_size(camInfo->characteristics);
174             cam.desc.metadata.insert(cam.desc.metadata.end(), ptr, ptr + len);
175         }
176     }
177 
178     {
179         std::lock_guard lock(sLock);
180         // insert_or_assign() returns std::pair<std::unordered_map<>, bool>
181         auto result = sCameraList.insert_or_assign(deviceName, std::move(cam));
182         LOG(INFO) << deviceName << (std::get<1>(result) ? " is added" : " is modified");
183     }
184 
185     return true;
186 }
187 
removeCaptureDevice(const std::string & deviceName)188 bool EvsEnumerator::removeCaptureDevice(const std::string& deviceName) {
189     std::lock_guard lock(sLock);
190     if (sCameraList.erase(deviceName) != 0) {
191         LOG(INFO) << deviceName << " is removed";
192         return true;
193     }
194 
195     return false;
196 }
197 
enumerateCameras()198 void EvsEnumerator::enumerateCameras() {
199     // For every video* entry in the dev folder, see if it reports suitable capabilities
200     // WARNING:  Depending on the driver implementations this could be slow, especially if
201     //           there are timeouts or round trips to hardware required to collect the needed
202     //           information.  Platform implementers should consider hard coding this list of
203     //           known good devices to speed up the startup time of their EVS implementation.
204     //           For example, this code might be replaced with nothing more than:
205     //                   sCameraList.insert("/dev/video0");
206     //                   sCameraList.insert("/dev/video1");
207     LOG(INFO) << __FUNCTION__ << ": Starting dev/video* enumeration";
208     auto videoCount = 0;
209     auto captureCount = 0;
210     DIR* dir = opendir("/dev");
211     if (!dir) {
212         LOG_FATAL("Failed to open /dev folder\n");
213     }
214     struct dirent* entry;
215     while ((entry = readdir(dir)) != nullptr) {
216         // We're only looking for entries starting with 'video'
217         if (strncmp(entry->d_name, "video", 5) == 0) {
218             std::string deviceName("/dev/");
219             deviceName += entry->d_name;
220             ++videoCount;
221 
222             if (addCaptureDevice(deviceName)) {
223                 ++captureCount;
224             }
225         }
226     }
227 
228     LOG(INFO) << "Found " << captureCount << " qualified video capture devices "
229               << "of " << videoCount << " checked.";
230 }
231 
enumerateDisplays()232 uint64_t EvsEnumerator::enumerateDisplays() {
233     LOG(INFO) << __FUNCTION__ << ": Starting display enumeration";
234     uint64_t internalDisplayId = kInvalidDisplayId;
235     if (!sDisplayProxy) {
236         LOG(ERROR) << "ICarDisplayProxy is not available!";
237         return internalDisplayId;
238     }
239 
240     std::vector<int64_t> displayIds;
241     if (auto status = sDisplayProxy->getDisplayIdList(&displayIds); !status.isOk()) {
242         LOG(ERROR) << "Failed to retrieve a display id list"
243                    << ::android::statusToString(status.getStatus());
244         return internalDisplayId;
245     }
246 
247     if (displayIds.size() > 0) {
248         // The first entry of the list is the internal display.  See
249         // SurfaceFlinger::getPhysicalDisplayIds() implementation.
250         internalDisplayId = displayIds[0];
251         for (const auto& id : displayIds) {
252             const auto port = id & 0xFF;
253             LOG(INFO) << "Display " << std::hex << id << " is detected on the port, " << port;
254             sDisplayPortList.insert_or_assign(port, id);
255         }
256     }
257 
258     LOG(INFO) << "Found " << sDisplayPortList.size() << " displays";
259     return internalDisplayId;
260 }
261 
262 // Methods from ::android::hardware::automotive::evs::IEvsEnumerator follow.
getCameraList(std::vector<CameraDesc> * _aidl_return)263 ScopedAStatus EvsEnumerator::getCameraList(std::vector<CameraDesc>* _aidl_return) {
264     LOG(DEBUG) << __FUNCTION__;
265     if (!checkPermission()) {
266         return ScopedAStatus::fromServiceSpecificError(
267                 static_cast<int>(EvsResult::PERMISSION_DENIED));
268     }
269 
270     {
271         std::unique_lock<std::mutex> lock(sLock);
272         if (sCameraList.size() < 1) {
273             // No qualified device has been found.  Wait until new device is ready,
274             // for 10 seconds.
275             if (!sCameraSignal.wait_for(lock, kEnumerationTimeout,
276                                         [] { return sCameraList.size() > 0; })) {
277                 LOG(DEBUG) << "Timer expired.  No new device has been added.";
278             }
279         }
280     }
281 
282     // Build up a packed array of CameraDesc for return
283     _aidl_return->resize(sCameraList.size());
284     unsigned i = 0;
285     for (const auto& [key, cam] : sCameraList) {
286         (*_aidl_return)[i++] = cam.desc;
287     }
288 
289     if (sConfigManager) {
290         // Adding camera groups that represent logical camera devices
291         auto camGroups = sConfigManager->getCameraGroupIdList();
292         for (auto&& id : camGroups) {
293             if (sCameraList.find(id) != sCameraList.end()) {
294                 // Already exists in the _aidl_return
295                 continue;
296             }
297 
298             std::unique_ptr<ConfigManager::CameraGroupInfo>& tempInfo =
299                     sConfigManager->getCameraGroupInfo(id);
300             CameraRecord cam(id.data());
301             if (tempInfo) {
302                 uint8_t* ptr = reinterpret_cast<uint8_t*>(tempInfo->characteristics);
303                 const size_t len = get_camera_metadata_size(tempInfo->characteristics);
304                 cam.desc.metadata.insert(cam.desc.metadata.end(), ptr, ptr + len);
305             }
306 
307             sCameraList.insert_or_assign(id, cam);
308             _aidl_return->push_back(cam.desc);
309         }
310     }
311 
312     // Send back the results
313     LOG(DEBUG) << "Reporting " << sCameraList.size() << " cameras available";
314     return ScopedAStatus::ok();
315 }
316 
getStreamList(const CameraDesc & desc,std::vector<Stream> * _aidl_return)317 ScopedAStatus EvsEnumerator::getStreamList(const CameraDesc& desc,
318                                            std::vector<Stream>* _aidl_return) {
319     using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
320 
321     camera_metadata_t* pMetadata = const_cast<camera_metadata_t*>(
322             reinterpret_cast<const camera_metadata_t*>(desc.metadata.data()));
323     camera_metadata_entry_t streamConfig;
324     if (!find_camera_metadata_entry(pMetadata, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
325                                     &streamConfig)) {
326         const unsigned numStreamConfigs = streamConfig.count / sizeof(StreamConfiguration);
327         _aidl_return->resize(numStreamConfigs);
328         const StreamConfiguration* pCurrentConfig =
329                 reinterpret_cast<StreamConfiguration*>(streamConfig.data.i32);
330         for (unsigned i = 0; i < numStreamConfigs; ++i, ++pCurrentConfig) {
331             // Build ::aidl::android::hardware::automotive::evs::Stream from
332             // StreamConfiguration.
333             Stream current = {
334                     .id = pCurrentConfig->id,
335                     .streamType = pCurrentConfig->type ==
336                                     ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT
337                             ? StreamType::INPUT
338                             : StreamType::OUTPUT,
339                     .width = pCurrentConfig->width,
340                     .height = pCurrentConfig->height,
341                     .format = static_cast<AidlPixelFormat>(pCurrentConfig->format),
342                     .usage = BufferUsage::CAMERA_INPUT,
343                     .rotation = Rotation::ROTATION_0,
344             };
345 
346             (*_aidl_return)[i] = std::move(current);
347         }
348     }
349 
350     return ScopedAStatus::ok();
351 }
352 
openCamera(const std::string & id,const Stream & cfg,std::shared_ptr<IEvsCamera> * obj)353 ScopedAStatus EvsEnumerator::openCamera(const std::string& id, const Stream& cfg,
354                                         std::shared_ptr<IEvsCamera>* obj) {
355     LOG(DEBUG) << __FUNCTION__;
356     if (!checkPermission()) {
357         return ScopedAStatus::fromServiceSpecificError(
358                 static_cast<int>(EvsResult::PERMISSION_DENIED));
359     }
360 
361     // Is this a recognized camera id?
362     CameraRecord* pRecord = findCameraById(id);
363     if (!pRecord) {
364         LOG(ERROR) << id << " does not exist!";
365         return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
366     }
367 
368     // Has this camera already been instantiated by another caller?
369     std::shared_ptr<EvsV4lCamera> pActiveCamera = pRecord->activeInstance.lock();
370     if (pActiveCamera) {
371         LOG(WARNING) << "Killing previous camera because of new caller";
372         closeCamera(pActiveCamera);
373     }
374 
375     // Construct a camera instance for the caller
376     if (!sConfigManager) {
377         pActiveCamera = EvsV4lCamera::Create(id.data());
378     } else {
379         pActiveCamera = EvsV4lCamera::Create(id.data(), sConfigManager->getCameraInfo(id), &cfg);
380     }
381 
382     pRecord->activeInstance = pActiveCamera;
383     if (!pActiveCamera) {
384         LOG(ERROR) << "Failed to create new EvsV4lCamera object for " << id;
385         return ScopedAStatus::fromServiceSpecificError(
386                 static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
387     }
388 
389     *obj = pActiveCamera;
390     return ScopedAStatus::ok();
391 }
392 
closeCamera(const std::shared_ptr<IEvsCamera> & cameraObj)393 ScopedAStatus EvsEnumerator::closeCamera(const std::shared_ptr<IEvsCamera>& cameraObj) {
394     LOG(DEBUG) << __FUNCTION__;
395 
396     if (!cameraObj) {
397         LOG(ERROR) << "Ignoring call to closeCamera with null camera ptr";
398         return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
399     }
400 
401     // Get the camera id so we can find it in our list
402     CameraDesc desc;
403     auto status = cameraObj->getCameraInfo(&desc);
404     if (!status.isOk()) {
405         LOG(ERROR) << "Failed to read a camera descriptor";
406         return ScopedAStatus::fromServiceSpecificError(
407                 static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
408     }
409     auto cameraId = desc.id;
410     closeCamera_impl(cameraObj, cameraId);
411     return ScopedAStatus::ok();
412 }
413 
openDisplay(int32_t id,std::shared_ptr<IEvsDisplay> * displayObj)414 ScopedAStatus EvsEnumerator::openDisplay(int32_t id, std::shared_ptr<IEvsDisplay>* displayObj) {
415     LOG(DEBUG) << __FUNCTION__;
416     if (!checkPermission()) {
417         return ScopedAStatus::fromServiceSpecificError(
418                 static_cast<int>(EvsResult::PERMISSION_DENIED));
419     }
420 
421     // If we already have a display active, then we need to shut it down so we can
422     // give exclusive access to the new caller.
423     std::shared_ptr<EvsGlDisplay> pActiveDisplay = sActiveDisplay.lock();
424     if (pActiveDisplay) {
425         LOG(WARNING) << "Killing previous display because of new caller";
426         closeDisplay(pActiveDisplay);
427     }
428 
429     // Create a new display interface and return it
430     if (sDisplayPortList.find(id) == sDisplayPortList.end()) {
431         LOG(ERROR) << "No display is available on the port " << static_cast<int32_t>(id);
432         return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
433     }
434 
435     // Create a new display interface and return it.
436     pActiveDisplay = ::ndk::SharedRefBase::make<EvsGlDisplay>(sDisplayProxy, mInternalDisplayId);
437     sActiveDisplay = pActiveDisplay;
438 
439     LOG(DEBUG) << "Returning new EvsGlDisplay object " << pActiveDisplay.get();
440     *displayObj = pActiveDisplay;
441     return ScopedAStatus::ok();
442 }
443 
closeDisplay(const std::shared_ptr<IEvsDisplay> & obj)444 ScopedAStatus EvsEnumerator::closeDisplay(const std::shared_ptr<IEvsDisplay>& obj) {
445     LOG(DEBUG) << __FUNCTION__;
446 
447     // Do we still have a display object we think should be active?
448     std::shared_ptr<EvsGlDisplay> pActiveDisplay = sActiveDisplay.lock();
449     if (!pActiveDisplay) {
450         LOG(ERROR) << "Somehow a display is being destroyed "
451                    << "when the enumerator didn't know one existed";
452         return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
453     } else if (pActiveDisplay != obj) {
454         LOG(WARNING) << "Ignoring close of previously orphaned display - why did a client steal?";
455     } else {
456         // Drop the active display
457         pActiveDisplay->forceShutdown();
458     }
459 
460     return ScopedAStatus::ok();
461 }
462 
getDisplayState(DisplayState * state)463 ScopedAStatus EvsEnumerator::getDisplayState(DisplayState* state) {
464     LOG(DEBUG) << __FUNCTION__;
465     if (!checkPermission()) {
466         *state = DisplayState::DEAD;
467         return ScopedAStatus::fromServiceSpecificError(
468                 static_cast<int>(EvsResult::PERMISSION_DENIED));
469     }
470 
471     // Do we still have a display object we think should be active?
472     std::shared_ptr<IEvsDisplay> pActiveDisplay = sActiveDisplay.lock();
473     if (pActiveDisplay) {
474         return pActiveDisplay->getDisplayState(state);
475     } else {
476         *state = DisplayState::NOT_OPEN;
477         return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
478     }
479 }
480 
getDisplayIdList(std::vector<uint8_t> * list)481 ScopedAStatus EvsEnumerator::getDisplayIdList(std::vector<uint8_t>* list) {
482     std::vector<uint8_t>& output = *list;
483     if (sDisplayPortList.size() > 0) {
484         output.resize(sDisplayPortList.size());
485         unsigned i = 0;
486         output[i++] = mInternalDisplayId & 0xFF;
487         for (const auto& [port, id] : sDisplayPortList) {
488             if (mInternalDisplayId != id) {
489                 output[i++] = port;
490             }
491         }
492     }
493 
494     return ScopedAStatus::ok();
495 }
496 
isHardware(bool * flag)497 ScopedAStatus EvsEnumerator::isHardware(bool* flag) {
498     *flag = true;
499     return ScopedAStatus::ok();
500 }
501 
notifyDeviceStatusChange(const std::string_view & deviceName,DeviceStatusType type)502 void EvsEnumerator::notifyDeviceStatusChange(const std::string_view& deviceName,
503                                              DeviceStatusType type) {
504     std::lock_guard lock(sLock);
505     if (!mCallback) {
506         return;
507     }
508 
509     std::vector<DeviceStatus> status {{ .id = std::string(deviceName), .status = type }};
510     if (!mCallback->deviceStatusChanged(status).isOk()) {
511         LOG(WARNING) << "Failed to notify a device status change, name = " << deviceName
512                      << ", type = " << static_cast<int>(type);
513     }
514 }
515 
registerStatusCallback(const std::shared_ptr<IEvsEnumeratorStatusCallback> & callback)516 ScopedAStatus EvsEnumerator::registerStatusCallback(
517         const std::shared_ptr<IEvsEnumeratorStatusCallback>& callback) {
518     std::lock_guard lock(sLock);
519     if (mCallback) {
520         LOG(INFO) << "Replacing an existing device status callback";
521     }
522     mCallback = callback;
523     return ScopedAStatus::ok();
524 }
525 
closeCamera_impl(const std::shared_ptr<IEvsCamera> & pCamera,const std::string & cameraId)526 void EvsEnumerator::closeCamera_impl(const std::shared_ptr<IEvsCamera>& pCamera,
527                                      const std::string& cameraId) {
528     // Find the named camera
529     CameraRecord* pRecord = findCameraById(cameraId);
530 
531     // Is the display being destroyed actually the one we think is active?
532     if (!pRecord) {
533         LOG(ERROR) << "Asked to close a camera whose name isn't recognized";
534     } else {
535         std::shared_ptr<EvsV4lCamera> pActiveCamera = pRecord->activeInstance.lock();
536         if (!pActiveCamera) {
537             LOG(WARNING) << "Somehow a camera is being destroyed "
538                          << "when the enumerator didn't know one existed";
539         } else if (pActiveCamera != pCamera) {
540             // This can happen if the camera was aggressively reopened,
541             // orphaning this previous instance
542             LOG(WARNING) << "Ignoring close of previously orphaned camera "
543                          << "- why did a client steal?";
544         } else {
545             // Shutdown the active camera
546             pActiveCamera->shutdown();
547         }
548     }
549 
550     return;
551 }
552 
qualifyCaptureDevice(const char * deviceName)553 bool EvsEnumerator::qualifyCaptureDevice(const char* deviceName) {
554     class FileHandleWrapper {
555     public:
556         FileHandleWrapper(int fd) { mFd = fd; }
557         ~FileHandleWrapper() {
558             if (mFd > 0) close(mFd);
559         }
560         operator int() const { return mFd; }
561 
562     private:
563         int mFd = -1;
564     };
565 
566     FileHandleWrapper fd = open(deviceName, O_RDWR, 0);
567     if (fd < 0) {
568         return false;
569     }
570 
571     v4l2_capability caps;
572     int result = ioctl(fd, VIDIOC_QUERYCAP, &caps);
573     if (result < 0) {
574         return false;
575     }
576     if (((caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) ||
577         ((caps.capabilities & V4L2_CAP_STREAMING) == 0)) {
578         return false;
579     }
580 
581     // Enumerate the available capture formats (if any)
582     v4l2_fmtdesc formatDescription;
583     formatDescription.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
584     bool found = false;
585     for (int i = 0; !found; ++i) {
586         formatDescription.index = i;
587         if (ioctl(fd, VIDIOC_ENUM_FMT, &formatDescription) == 0) {
588             LOG(DEBUG) << "Format: 0x" << std::hex << formatDescription.pixelformat << " Type: 0x"
589                        << std::hex << formatDescription.type
590                        << " Desc: " << formatDescription.description << " Flags: 0x" << std::hex
591                        << formatDescription.flags;
592             switch (formatDescription.pixelformat) {
593                 case V4L2_PIX_FMT_YUYV:
594                     found = true;
595                     break;
596                 case V4L2_PIX_FMT_NV21:
597                     found = true;
598                     break;
599                 case V4L2_PIX_FMT_NV16:
600                     found = true;
601                     break;
602                 case V4L2_PIX_FMT_YVU420:
603                     found = true;
604                     break;
605                 case V4L2_PIX_FMT_RGB32:
606                     found = true;
607                     break;
608 #ifdef V4L2_PIX_FMT_ARGB32  // introduced with kernel v3.17
609                 case V4L2_PIX_FMT_ARGB32:
610                     found = true;
611                     break;
612                 case V4L2_PIX_FMT_XRGB32:
613                     found = true;
614                     break;
615 #endif  // V4L2_PIX_FMT_ARGB32
616                 default:
617                     LOG(WARNING) << "Unsupported, " << std::hex << formatDescription.pixelformat;
618                     break;
619             }
620         } else {
621             // No more formats available.
622             break;
623         }
624     }
625 
626     return found;
627 }
628 
findCameraById(const std::string & cameraId)629 EvsEnumerator::CameraRecord* EvsEnumerator::findCameraById(const std::string& cameraId) {
630     // Find the named camera
631     auto found = sCameraList.find(cameraId);
632     if (found != sCameraList.end()) {
633         // Found a match!
634         return &found->second;
635     }
636 
637     // We didn't find a match
638     return nullptr;
639 }
640 
getUltrasonicsArrayList(std::vector<UltrasonicsArrayDesc> * list)641 ScopedAStatus EvsEnumerator::getUltrasonicsArrayList(
642         [[maybe_unused]] std::vector<UltrasonicsArrayDesc>* list) {
643     // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
644     return ScopedAStatus::ok();
645 }
646 
openUltrasonicsArray(const std::string & id,std::shared_ptr<IEvsUltrasonicsArray> * obj)647 ScopedAStatus EvsEnumerator::openUltrasonicsArray(
648         [[maybe_unused]] const std::string& id,
649         [[maybe_unused]] std::shared_ptr<IEvsUltrasonicsArray>* obj) {
650     // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
651     return ScopedAStatus::ok();
652 }
653 
closeUltrasonicsArray(const std::shared_ptr<IEvsUltrasonicsArray> & obj)654 ScopedAStatus EvsEnumerator::closeUltrasonicsArray(
655         [[maybe_unused]] const std::shared_ptr<IEvsUltrasonicsArray>& obj) {
656     // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
657     return ScopedAStatus::ok();
658 }
659 
dump(int fd,const char ** args,uint32_t numArgs)660 binder_status_t EvsEnumerator::dump(int fd, const char** args, uint32_t numArgs) {
661     std::vector<std::string> options(args, args + numArgs);
662     return parseCommand(fd, options);
663 }
664 
parseCommand(int fd,const std::vector<std::string> & options)665 binder_status_t EvsEnumerator::parseCommand(int fd, const std::vector<std::string>& options) {
666     if (options.size() < 1) {
667         WriteStringToFd("No option is given.\n", fd);
668         cmdHelp(fd);
669         return STATUS_BAD_VALUE;
670     }
671 
672     const std::string command = options[0];
673     if (EqualsIgnoreCase(command, "--help")) {
674         cmdHelp(fd);
675         return STATUS_OK;
676     } else if (EqualsIgnoreCase(command, "--dump")) {
677         return cmdDump(fd, options);
678     } else {
679         WriteStringToFd(StringPrintf("Invalid option: %s\n", command.data()), fd);
680         return STATUS_INVALID_OPERATION;
681     }
682 }
683 
cmdHelp(int fd)684 void EvsEnumerator::cmdHelp(int fd) {
685     WriteStringToFd("--help: shows this help.\n"
686                     "--dump [id] [start|stop] [directory]\n"
687                     "\tDump camera frames to a target directory\n",
688                     fd);
689 }
690 
cmdDump(int fd,const std::vector<std::string> & options)691 binder_status_t EvsEnumerator::cmdDump(int fd, const std::vector<std::string>& options) {
692     if (options.size() < 3) {
693         WriteStringToFd("Necessary argument is missing\n", fd);
694         cmdHelp(fd);
695         return STATUS_BAD_VALUE;
696     }
697 
698     EvsEnumerator::CameraRecord* pRecord = findCameraById(options[1]);
699     if (pRecord == nullptr) {
700         WriteStringToFd(StringPrintf("%s is not active\n", options[1].data()), fd);
701         return STATUS_BAD_VALUE;
702     }
703 
704     auto device = pRecord->activeInstance.lock();
705     if (device == nullptr) {
706         WriteStringToFd(StringPrintf("%s seems dead\n", options[1].data()), fd);
707         return STATUS_DEAD_OBJECT;
708     }
709 
710     const std::string command = options[2];
711     if (EqualsIgnoreCase(command, "start")) {
712         // --dump [device id] start [path]
713         if (options.size() < 4) {
714             WriteStringToFd("Necessary argument is missing\n", fd);
715             cmdHelp(fd);
716             return STATUS_BAD_VALUE;
717         }
718 
719         const std::string path = options[3];
720         auto ret = device->startDumpFrames(path);
721         if (!ret.ok()) {
722             WriteStringToFd(StringPrintf("Failed to start storing frames: %s\n",
723                                          ret.error().message().data()),
724                             fd);
725             return STATUS_FAILED_TRANSACTION;
726         }
727     } else if (EqualsIgnoreCase(command, "stop")) {
728         // --dump [device id] stop
729         auto ret = device->stopDumpFrames();
730         if (!ret.ok()) {
731             WriteStringToFd(StringPrintf("Failed to stop storing frames: %s\n",
732                                          ret.error().message().data()),
733                             fd);
734             return STATUS_FAILED_TRANSACTION;
735         }
736     } else {
737         WriteStringToFd(StringPrintf("Unknown command: %s", command.data()), fd);
738         cmdHelp(fd);
739     }
740 
741     return STATUS_OK;
742 }
743 
744 }  // namespace aidl::android::hardware::automotive::evs::implementation
745