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