1 /*
2 * Copyright (C) 2019 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 "Enumerator.h"
18
19 #include "HalDisplay.h"
20 #include "IPermissionsChecker.h"
21 #include "emul/EvsEmulatedCamera.h"
22 #include "stats/StatsCollector.h"
23
24 #include <android-base/chrono_utils.h>
25 #include <android-base/file.h>
26 #include <android-base/logging.h>
27 #include <android-base/parseint.h>
28 #include <android-base/stringprintf.h>
29 #include <android-base/strings.h>
30 #include <cutils/android_filesystem_config.h>
31 #include <hwbinder/IPCThreadState.h>
32
33 #include <regex> // NOLINT
34 #include <vector>
35
36 namespace {
37
38 using ::android::automotive::evs::V1_1::implementation::IPermissionsChecker;
39 using ::android::base::EqualsIgnoreCase;
40 using ::android::base::Error;
41 using ::android::base::StringAppendF;
42 using ::android::base::StringPrintf;
43 using ::android::base::WriteStringToFd;
44 using ::android::hardware::hidl_handle;
45 using ::android::hardware::IPCThreadState;
46 using ::android::hardware::Void;
47 using ::android::hardware::automotive::evs::V1_0::DisplayState;
48 using IEvsCamera_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCamera;
49 using CameraDesc_1_0 = ::android::hardware::automotive::evs::V1_0::CameraDesc;
50 using CameraDesc_1_1 = ::android::hardware::automotive::evs::V1_1::CameraDesc;
51 using ::android::hardware::camera::device::V3_2::Stream;
52
53 const char* kSingleIndent = "\t";
54 const char* kDumpOptionAll = "all";
55 const char* kDumpDeviceCamera = "camera";
56 const char* kDumpDeviceDisplay = "display";
57
58 const char* kDumpCameraCommandCurrent = "--current";
59 const char* kDumpCameraCommandCollected = "--collected";
60 const char* kDumpCameraCommandCustom = "--custom";
61 const char* kDumpCameraCommandCustomStart = "start";
62 const char* kDumpCameraCommandCustomStop = "stop";
63
64 const int kDumpCameraMinNumArgs = 4;
65 const int kOptionDumpDeviceTypeIndex = 1;
66 const int kOptionDumpCameraTypeIndex = 2;
67 const int kOptionDumpCameraCommandIndex = 3;
68 const int kOptionDumpCameraArgsStartIndex = 4;
69
70 const std::regex kEmulatedCameraNamePattern("emulated/[0-9]+", std::regex_constants::icase);
71
72 // Display ID 255 is reserved for the special purpose.
73 constexpr int kExclusiveMainDisplayId = 255;
74
75 // This surprisingly is not included in STL until C++20.
76 template <template <class> class Container, typename T>
contains(const Container<T> & container,const T & value)77 constexpr bool contains(const Container<T>& container, const T& value) {
78 return (std::find(container.begin(), container.end(), value) != container.end());
79 }
80
81 // Removes the target value if present, and optionally executes a lambda.
82 template <typename Container, typename T, typename RemovalLambda>
removeIfPresent(Container * container,const T & value,RemovalLambda removalLambda=[](){})83 constexpr void removeIfPresent(
84 Container* container, const T& value, RemovalLambda removalLambda = []() {}) {
85 auto it = std::find(container->begin(), container->end(), value);
86 if (it != container->end()) {
87 container->erase(it);
88 removalLambda();
89 }
90 }
91
92 class ProdPermissionChecker : public IPermissionsChecker {
93 public:
processHasPermissionsForEvs()94 bool processHasPermissionsForEvs() override {
95 IPCThreadState* ipc = IPCThreadState::self();
96 const auto userId = ipc->getCallingUid() / AID_USER_OFFSET;
97 const auto appId = ipc->getCallingUid() % AID_USER_OFFSET;
98 if (AID_AUTOMOTIVE_EVS != appId && AID_ROOT != appId && AID_SYSTEM != appId) {
99 LOG(ERROR) << "EVS access denied? "
100 << "pid = " << ipc->getCallingPid() << ", userId = " << userId
101 << ", appId = " << appId;
102 return false;
103 }
104
105 return true;
106 }
107 };
108
109 } // namespace
110
111 namespace android::automotive::evs::V1_1::implementation {
112
Enumerator(std::unique_ptr<ServiceFactory> serviceFactory,std::unique_ptr<IStatsCollector> statsCollector,std::unique_ptr<IPermissionsChecker> permissionChecker)113 Enumerator::Enumerator(std::unique_ptr<ServiceFactory> serviceFactory,
114 std::unique_ptr<IStatsCollector> statsCollector,
115 std::unique_ptr<IPermissionsChecker> permissionChecker) :
116 mServiceFactory(std::move(serviceFactory)),
117 mStatsCollector(std::move(statsCollector)),
118 mPermissionChecker(std::move(permissionChecker)) {
119 // Get an internal display identifier.
120 mServiceFactory->getService()->getDisplayIdList(
121 [this](const android::hardware::hidl_vec<unsigned char>& displayPorts) {
122 for (unsigned char port : displayPorts) {
123 mDisplayPorts.push_back(port);
124 }
125
126 if (mDisplayPorts.empty()) {
127 LOG(WARNING) << "No display is available to EVS service.";
128 } else {
129 // The first element must be the internal display
130 mInternalDisplayPort = mDisplayPorts.front();
131 }
132 });
133
134 removeIfPresent(&mDisplayPorts, kExclusiveMainDisplayId, []() {
135 LOG(WARNING) << kExclusiveMainDisplayId
136 << " is reserved so will not be available for EVS service.";
137 });
138
139 mMonitorEnabled = mStatsCollector->startCollection().ok();
140 }
141
build(std::unique_ptr<ServiceFactory> serviceFactory,std::unique_ptr<IStatsCollector> statsCollector,std::unique_ptr<IPermissionsChecker> permissionChecker)142 std::unique_ptr<Enumerator> Enumerator::build(
143 std::unique_ptr<ServiceFactory> serviceFactory,
144 std::unique_ptr<IStatsCollector> statsCollector,
145 std::unique_ptr<IPermissionsChecker> permissionChecker) {
146 // Connect with the underlying hardware enumerator.
147 if (!serviceFactory->getService()) {
148 return nullptr;
149 }
150
151 return std::unique_ptr<Enumerator>{new Enumerator(std::move(serviceFactory),
152 std::move(statsCollector),
153 std::move(permissionChecker))};
154 }
155
build(const char * hardwareServiceName)156 std::unique_ptr<Enumerator> Enumerator::build(const char* hardwareServiceName) {
157 if (!hardwareServiceName) {
158 return nullptr;
159 }
160
161 return build(std::make_unique<ProdServiceFactory>(hardwareServiceName),
162 std::make_unique<StatsCollector>(), std::make_unique<ProdPermissionChecker>());
163 }
164
isLogicalCamera(const camera_metadata_t * metadata)165 bool Enumerator::isLogicalCamera(const camera_metadata_t* metadata) {
166 bool found = false;
167
168 if (metadata == nullptr) {
169 LOG(ERROR) << "Metadata is null";
170 return found;
171 }
172
173 camera_metadata_ro_entry_t entry;
174 int rc =
175 find_camera_metadata_ro_entry(metadata, ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
176 if (0 != rc) {
177 // No capabilities are found in metadata.
178 LOG(DEBUG) << __FUNCTION__ << " does not find a target entry";
179 return found;
180 }
181
182 for (size_t i = 0; i < entry.count; ++i) {
183 uint8_t capability = entry.data.u8[i];
184 if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
185 found = true;
186 break;
187 }
188 }
189
190 if (!found) {
191 LOG(DEBUG) << __FUNCTION__ << " does not find a logical multi camera cap";
192 }
193 return found;
194 }
195
getPhysicalCameraIds(const std::string & id)196 std::unordered_set<std::string> Enumerator::getPhysicalCameraIds(const std::string& id) {
197 std::unordered_set<std::string> physicalCameras;
198 if (mCameraDevices.find(id) == mCameraDevices.end()) {
199 LOG(ERROR) << "Queried device " << id << " does not exist!";
200 return physicalCameras;
201 }
202
203 const camera_metadata_t* metadata =
204 reinterpret_cast<camera_metadata_t*>(&mCameraDevices[id].metadata[0]);
205 if (!isLogicalCamera(metadata)) {
206 // EVS assumes that the device w/o a valid metadata is a physical
207 // device.
208 LOG(INFO) << id << " is not a logical camera device.";
209 physicalCameras.emplace(id);
210 return physicalCameras;
211 }
212
213 camera_metadata_ro_entry entry;
214 int rc = find_camera_metadata_ro_entry(metadata, ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS,
215 &entry);
216 if (0 != rc) {
217 LOG(ERROR) << "No physical camera ID is found for a logical camera device " << id;
218 return physicalCameras;
219 }
220
221 const uint8_t* ids = entry.data.u8;
222 size_t start = 0;
223 for (size_t i = 0; i < entry.count; ++i) {
224 if (ids[i] == '\0') {
225 if (start != i) {
226 std::string id(reinterpret_cast<const char*>(ids + start));
227 physicalCameras.emplace(id);
228 }
229 start = i + 1;
230 }
231 }
232
233 LOG(INFO) << id << " consists of " << physicalCameras.size() << " physical camera devices.";
234 return physicalCameras;
235 }
236
237 // Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
getCameraList(getCameraList_cb list_cb)238 Return<void> Enumerator::getCameraList(getCameraList_cb list_cb) {
239 hardware::hidl_vec<CameraDesc_1_0> cameraList;
240 mServiceFactory->getService()->getCameraList_1_1([&cameraList](auto cameraList_1_1) {
241 cameraList.resize(cameraList_1_1.size());
242 unsigned i = 0;
243 for (auto&& cam : cameraList_1_1) {
244 cameraList[i++] = cam.v1;
245 }
246 });
247
248 list_cb(cameraList);
249
250 return Void();
251 }
252
openCamera(const hidl_string & cameraId)253 Return<sp<IEvsCamera_1_0>> Enumerator::openCamera(const hidl_string& cameraId) {
254 LOG(DEBUG) << __FUNCTION__;
255 if (!mPermissionChecker->processHasPermissionsForEvs()) {
256 return nullptr;
257 }
258
259 // Is the underlying hardware camera already open?
260 sp<HalCamera> hwCamera;
261 if (mActiveCameras.find(cameraId) != mActiveCameras.end()) {
262 hwCamera = mActiveCameras[cameraId];
263 } else {
264 // Is the hardware camera available?
265 sp<IEvsCamera_1_1> device;
266 if (std::regex_match(cameraId.c_str(), kEmulatedCameraNamePattern)) {
267 if (mEmulatedCameraDevices.find(cameraId) == mEmulatedCameraDevices.end()) {
268 LOG(ERROR) << cameraId << " is not available";
269 } else {
270 device = EvsEmulatedCamera::Create(cameraId.c_str(),
271 mEmulatedCameraDevices[cameraId]);
272 }
273 } else {
274 device = IEvsCamera_1_1::castFrom(mServiceFactory->getService()->openCamera(cameraId))
275 .withDefault(nullptr);
276 }
277 if (device == nullptr) {
278 LOG(ERROR) << "Failed to open hardware camera " << cameraId;
279 } else {
280 // Calculates the usage statistics record identifier
281 auto fn = mCameraDevices.hash_function();
282 auto recordId = fn(cameraId) & 0xFF;
283 hwCamera = new HalCamera(device, cameraId, recordId);
284 if (hwCamera == nullptr) {
285 LOG(ERROR) << "Failed to allocate camera wrapper object";
286 mServiceFactory->getService()->closeCamera(device);
287 }
288 }
289 }
290
291 // Construct a virtual camera wrapper for this hardware camera
292 sp<VirtualCamera> clientCamera;
293 if (hwCamera != nullptr) {
294 clientCamera = hwCamera->makeVirtualCamera();
295 }
296
297 // Add the hardware camera to our list, which will keep it alive via ref count
298 if (clientCamera != nullptr) {
299 mActiveCameras.try_emplace(cameraId, hwCamera);
300 } else {
301 LOG(ERROR) << "Requested camera " << cameraId << " not found or not available";
302 }
303
304 // Send the virtual camera object back to the client by strong pointer which will keep it alive
305 return clientCamera;
306 }
307
closeCamera(const::android::sp<IEvsCamera_1_0> & clientCamera)308 Return<void> Enumerator::closeCamera(const ::android::sp<IEvsCamera_1_0>& clientCamera) {
309 LOG(DEBUG) << __FUNCTION__;
310
311 if (clientCamera == nullptr) {
312 LOG(ERROR) << "Ignoring call with null camera pointer.";
313 return Void();
314 }
315
316 // All our client cameras are actually VirtualCamera objects
317 sp<VirtualCamera> virtualCamera = reinterpret_cast<VirtualCamera*>(clientCamera.get());
318
319 // Find the parent camera that backs this virtual camera
320 for (auto&& halCamera : virtualCamera->getHalCameras()) {
321 // Tell the virtual camera's parent to clean it up and drop it
322 // NOTE: The camera objects will only actually destruct when the sp<> ref counts get to
323 // zero, so it is important to break all cyclic references.
324 halCamera->disownVirtualCamera(virtualCamera);
325
326 // Did we just remove the last client of this camera?
327 if (halCamera->getClientCount() == 0) {
328 // Take this now unused camera out of our list
329 // NOTE: This should drop our last reference to the camera, resulting in its
330 // destruction.
331 mActiveCameras.erase(halCamera->getId());
332 mServiceFactory->getService()->closeCamera(halCamera->getHwCamera());
333 if (mMonitorEnabled) {
334 mStatsCollector->unregisterClientToMonitor(halCamera->getId());
335 }
336 }
337 }
338
339 // Make sure the virtual camera's stream is stopped
340 virtualCamera->stopVideoStream();
341
342 return Void();
343 }
344
345 // Methods from ::android::hardware::automotive::evs::V1_1::IEvsEnumerator follow.
openCamera_1_1(const hidl_string & cameraId,const Stream & streamCfg)346 Return<sp<IEvsCamera_1_1>> Enumerator::openCamera_1_1(const hidl_string& cameraId,
347 const Stream& streamCfg) {
348 LOG(DEBUG) << __FUNCTION__;
349 if (!mPermissionChecker->processHasPermissionsForEvs()) {
350 return nullptr;
351 }
352
353 // If hwCamera is null, a requested camera device is either a logical camera
354 // device or a hardware camera, which is not being used now.
355 std::unordered_set<std::string> physicalCameras = getPhysicalCameraIds(cameraId);
356 std::vector<sp<HalCamera>> sourceCameras;
357 sp<HalCamera> hwCamera;
358 bool success = true;
359
360 // 1. Try to open inactive camera devices.
361 for (auto&& id : physicalCameras) {
362 auto it = mActiveCameras.find(id);
363 if (it == mActiveCameras.end()) {
364 sp<IEvsCamera_1_1> device;
365 if (std::regex_match(cameraId.c_str(), kEmulatedCameraNamePattern)) {
366 if (mEmulatedCameraDevices.find(id) == mEmulatedCameraDevices.end()) {
367 LOG(ERROR) << cameraId << " is not available";
368 } else {
369 device = EvsEmulatedCamera::Create(id.c_str(), mEmulatedCameraDevices[id]);
370 }
371 } else {
372 device = mServiceFactory->getService()->openCamera_1_1(id, streamCfg);
373 }
374
375 if (device == nullptr) {
376 LOG(ERROR) << "Failed to open hardware camera " << cameraId;
377 success = false;
378 break;
379 } else {
380 // Calculates the usage statistics record identifier
381 auto fn = mCameraDevices.hash_function();
382 auto recordId = fn(id) & 0xFF;
383 hwCamera = new HalCamera(device, id, recordId, streamCfg);
384 if (hwCamera == nullptr) {
385 LOG(ERROR) << "Failed to allocate camera wrapper object";
386 mServiceFactory->getService()->closeCamera(device);
387 success = false;
388 break;
389 }
390 }
391
392 // Add the hardware camera to our list, which will keep it alive via ref count
393 mActiveCameras.try_emplace(id, hwCamera);
394 if (mMonitorEnabled) {
395 mStatsCollector->registerClientToMonitor(hwCamera);
396 }
397
398 sourceCameras.push_back(hwCamera);
399 } else {
400 if (it->second->getStreamConfig().id != streamCfg.id) {
401 LOG(WARNING) << "Requested camera is already active in different configuration.";
402 } else {
403 sourceCameras.push_back(it->second);
404 }
405 }
406 }
407
408 if (!success || sourceCameras.size() < 1) {
409 LOG(ERROR) << "Failed to open any physical camera device";
410 return nullptr;
411 }
412
413 // TODO(b/147170360): Implement a logic to handle a failure.
414 // 3. Create a proxy camera object
415 sp<VirtualCamera> clientCamera = new VirtualCamera(sourceCameras);
416 if (clientCamera == nullptr) {
417 // TODO(b/206829268): Any resource needs to be cleaned up explicitly?
418 LOG(ERROR) << "Failed to create a client camera object";
419 } else {
420 if (physicalCameras.size() > 1) {
421 // VirtualCamera, which represents a logical device, caches its
422 // descriptor.
423 clientCamera->setDescriptor(&mCameraDevices[cameraId]);
424 }
425
426 // 4. Owns created proxy camera object
427 for (auto&& hwCamera : sourceCameras) {
428 if (!hwCamera->ownVirtualCamera(clientCamera)) {
429 // TODO(b/206829268): Remove a reference to this camera from a virtual camera.
430 // object.
431 LOG(ERROR) << hwCamera->getId() << " failed to own a created proxy camera object.";
432 }
433 }
434 }
435
436 // Send the virtual camera object back to the client by strong pointer which will keep it alive
437 return clientCamera;
438 }
439
getCameraList_1_1(getCameraList_1_1_cb list_cb)440 Return<void> Enumerator::getCameraList_1_1(getCameraList_1_1_cb list_cb) {
441 LOG(DEBUG) << __FUNCTION__;
442 if (!mPermissionChecker->processHasPermissionsForEvs()) {
443 return Void();
444 }
445
446 hardware::hidl_vec<CameraDesc_1_1> hidlCameras;
447 mServiceFactory->getService()->getCameraList_1_1(
448 [&hidlCameras](hardware::hidl_vec<CameraDesc_1_1> enumeratedCameras) {
449 hidlCameras.resize(enumeratedCameras.size());
450 unsigned count = 0;
451 for (auto&& camdesc : enumeratedCameras) {
452 hidlCameras[count++] = camdesc;
453 }
454 });
455
456 // Update the cached device list
457 mCameraDevices.clear();
458 for (auto&& desc : hidlCameras) {
459 mCameraDevices.insert_or_assign(desc.v1.cameraId, desc);
460 }
461
462 // Add emulated devices if there is any
463 if (mEmulatedCameraDevices.size() > 0) {
464 int index = hidlCameras.size();
465 hidlCameras.resize(hidlCameras.size() + mEmulatedCameraDevices.size());
466 for (auto&& [id, desc] : mEmulatedCameraDevices) {
467 hidlCameras[index++].v1.cameraId = id;
468 }
469 }
470
471 list_cb(hidlCameras);
472 return Void();
473 }
474
openDisplay()475 Return<sp<IEvsDisplay_1_0>> Enumerator::openDisplay() {
476 LOG(DEBUG) << __FUNCTION__;
477
478 if (!mPermissionChecker->processHasPermissionsForEvs()) {
479 return nullptr;
480 }
481
482 if (mDisplayOwnedExclusively) {
483 LOG(ERROR) << "Display is owned exclusively by another client.";
484 return nullptr;
485 }
486
487 // We simply keep track of the most recently opened display instance.
488 // In the underlying layers we expect that a new open will cause the previous
489 // object to be destroyed. This avoids any race conditions associated with
490 // create/destroy order and provides a cleaner restart sequence if the previous owner
491 // is non-responsive for some reason.
492 // Request exclusive access to the EVS display
493 sp<IEvsDisplay_1_0> pActiveDisplay = mServiceFactory->getService()->openDisplay();
494 if (pActiveDisplay == nullptr) {
495 LOG(ERROR) << "EVS Display unavailable";
496
497 return nullptr;
498 }
499
500 // Remember (via weak pointer) who we think the most recently opened display is so that
501 // we can proxy state requests from other callers to it.
502 // TODO(b/206829268): Because of b/129284474, an additional class, HalDisplay, has been defined
503 // and wraps the IEvsDisplay object the driver returns. We may want to remove this additional
504 // class when it is fixed properly.
505 sp<IEvsDisplay_1_0> pHalDisplay = new HalDisplay(pActiveDisplay, mInternalDisplayPort);
506 mActiveDisplay = pHalDisplay;
507
508 return pHalDisplay;
509 }
510
closeDisplay(const::android::sp<IEvsDisplay_1_0> & display)511 Return<void> Enumerator::closeDisplay(const ::android::sp<IEvsDisplay_1_0>& display) {
512 LOG(DEBUG) << __FUNCTION__;
513
514 sp<IEvsDisplay_1_0> pActiveDisplay = mActiveDisplay.promote();
515
516 // Drop the active display
517 if (display.get() != pActiveDisplay.get()) {
518 LOG(WARNING) << "Ignoring call to closeDisplay with unrecognized display object.";
519 } else {
520 // Pass this request through to the hardware layer
521 sp<HalDisplay> halDisplay = reinterpret_cast<HalDisplay*>(pActiveDisplay.get());
522 mServiceFactory->getService()->closeDisplay(halDisplay->getHwDisplay());
523 mActiveDisplay = nullptr;
524 mDisplayOwnedExclusively = false;
525 }
526
527 return Void();
528 }
529
getDisplayState()530 Return<DisplayState> Enumerator::getDisplayState() {
531 LOG(DEBUG) << __FUNCTION__;
532 if (!mPermissionChecker->processHasPermissionsForEvs()) {
533 return DisplayState::DEAD;
534 }
535
536 // Do we have a display object we think should be active?
537 sp<IEvsDisplay_1_0> pActiveDisplay = mActiveDisplay.promote();
538 if (pActiveDisplay != nullptr) {
539 // Pass this request through to the hardware layer
540 return pActiveDisplay->getDisplayState();
541 } else {
542 // We don't have a live display right now
543 mActiveDisplay = nullptr;
544 return DisplayState::NOT_OPEN;
545 }
546 }
547
openDisplay_1_1(uint8_t id)548 Return<sp<IEvsDisplay_1_1>> Enumerator::openDisplay_1_1(uint8_t id) {
549 LOG(DEBUG) << __FUNCTION__;
550
551 if (!mPermissionChecker->processHasPermissionsForEvs()) {
552 return nullptr;
553 }
554
555 if (mDisplayOwnedExclusively) {
556 LOG(ERROR) << "Display is owned exclusively by another client.";
557 return nullptr;
558 }
559
560 if (id == kExclusiveMainDisplayId) {
561 // The client requests to open the primary display exclusively.
562 id = mInternalDisplayPort;
563 mDisplayOwnedExclusively = true;
564 } else if (std::find(mDisplayPorts.begin(), mDisplayPorts.end(), id) == mDisplayPorts.end()) {
565 LOG(ERROR) << "No display is available on the port " << static_cast<int32_t>(id);
566 return nullptr;
567 }
568
569 // We simply keep track of the most recently opened display instance.
570 // In the underlying layers we expect that a new open will cause the previous
571 // object to be destroyed. This avoids any race conditions associated with
572 // create/destroy order and provides a cleaner restart sequence if the previous owner
573 // is non-responsive for some reason.
574 // Request exclusive access to the EVS display
575 sp<IEvsDisplay_1_1> pActiveDisplay = mServiceFactory->getService()->openDisplay_1_1(id);
576 if (pActiveDisplay == nullptr) {
577 LOG(ERROR) << "EVS Display unavailable";
578
579 return nullptr;
580 }
581
582 // Remember (via weak pointer) who we think the most recently opened display is so that
583 // we can proxy state requests from other callers to it.
584 // TODO(b/206829268): Because of b/129284474, an additional class, HalDisplay, has been defined
585 // and wraps the IEvsDisplay object the driver returns. We may want to remove this additional
586 // class when it is fixed properly.
587 sp<IEvsDisplay_1_1> pHalDisplay = new HalDisplay(pActiveDisplay, id);
588 mActiveDisplay = pHalDisplay;
589
590 return pHalDisplay;
591 }
592
getDisplayIdList(getDisplayIdList_cb _list_cb)593 Return<void> Enumerator::getDisplayIdList(getDisplayIdList_cb _list_cb) {
594 return mServiceFactory->getService()->getDisplayIdList(_list_cb);
595 }
596
597 // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb)598 Return<void> Enumerator::getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb) {
599 hardware::hidl_vec<UltrasonicsArrayDesc> ultrasonicsArrayDesc;
600 _hidl_cb(ultrasonicsArrayDesc);
601 return Void();
602 }
603
604 // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
openUltrasonicsArray(const hidl_string & ultrasonicsArrayId)605 Return<sp<IEvsUltrasonicsArray>> Enumerator::openUltrasonicsArray(
606 const hidl_string& ultrasonicsArrayId) {
607 (void)ultrasonicsArrayId;
608 sp<IEvsUltrasonicsArray> pEvsUltrasonicsArray;
609 return pEvsUltrasonicsArray;
610 }
611
612 // TODO(b/149874793): Add implementation for EVS Manager and Sample driver
closeUltrasonicsArray(const::android::sp<IEvsUltrasonicsArray> & evsUltrasonicsArray)613 Return<void> Enumerator::closeUltrasonicsArray(
614 const ::android::sp<IEvsUltrasonicsArray>& evsUltrasonicsArray) {
615 (void)evsUltrasonicsArray;
616 return Void();
617 }
618
debug(const hidl_handle & fd,const hidl_vec<hidl_string> & options)619 Return<void> Enumerator::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
620 if (fd.getNativeHandle() != nullptr && fd->numFds > 0) {
621 cmdDump(fd->data[0], options);
622 } else {
623 LOG(ERROR) << "Given file descriptor is not valid.";
624 }
625
626 return {};
627 }
628
cmdDump(int fd,const hidl_vec<hidl_string> & options)629 void Enumerator::cmdDump(int fd, const hidl_vec<hidl_string>& options) {
630 if (options.size() == 0) {
631 WriteStringToFd("No option is given.\n", fd);
632 cmdHelp(fd);
633 return;
634 }
635
636 const std::string option = options[0];
637 if (EqualsIgnoreCase(option, "--help")) {
638 cmdHelp(fd);
639 } else if (EqualsIgnoreCase(option, "--list")) {
640 cmdList(fd, options);
641 } else if (EqualsIgnoreCase(option, "--dump")) {
642 cmdDumpDevice(fd, options);
643 } else if (EqualsIgnoreCase(option, "--configure-emulated-camera")) {
644 cmdConfigureEmulatedCamera(fd, options);
645 } else {
646 WriteStringToFd(StringPrintf("Invalid option: %s\n", option.c_str()), fd);
647 }
648 }
649
cmdHelp(int fd)650 void Enumerator::cmdHelp(int fd) {
651 WriteStringToFd("--help: shows this help.\n"
652 "--list [all|camera|display]: lists camera or display devices or both "
653 "available to EVS manager.\n"
654 "--dump camera [all|device_id] --[current|collected|custom] [args]\n"
655 "\tcurrent: shows the current status\n"
656 "\tcollected: shows 10 most recent periodically collected camera usage "
657 "statistics\n"
658 "\tcustom: starts/stops collecting the camera usage statistics\n"
659 "\t\tstart [interval] [duration]: starts collecting usage statistics "
660 "at every [interval] during [duration]. Interval and duration are in "
661 "milliseconds.\n"
662 "\t\tstop: stops collecting usage statistics and shows collected records.\n"
663 "--dump display: shows current status of the display\n"
664 "--configure-emulated-camera [id] [path] [width] [height] [interval]\n"
665 "\tid: emulated device id to use; emulated/[0-9]+\n"
666 "\tpath: a path to the directory where source files are stored\n"
667 "\twidth: image width in pixels\n"
668 "\theight: image height in pixels\n"
669 "\tinterval: interval between consecutive frames in milliseconds.\n",
670 fd);
671 }
672
cmdList(int fd,const hidl_vec<hidl_string> & options)673 void Enumerator::cmdList(int fd, const hidl_vec<hidl_string>& options) {
674 bool listCameras = true;
675 bool listDisplays = true;
676 if (options.size() > 1) {
677 const std::string option = options[1];
678 const bool listAll = EqualsIgnoreCase(option, kDumpOptionAll);
679 listCameras = listAll || EqualsIgnoreCase(option, kDumpDeviceCamera);
680 listDisplays = listAll || EqualsIgnoreCase(option, kDumpDeviceDisplay);
681 if (!listCameras && !listDisplays) {
682 WriteStringToFd(StringPrintf("Unrecognized option, %s, is ignored.\n", option.c_str()),
683 fd);
684
685 // Nothing to show, return
686 return;
687 }
688 }
689
690 std::string buffer;
691 if (listCameras) {
692 StringAppendF(&buffer, "Camera devices available to EVS service:\n");
693 if (mCameraDevices.size() < 1) {
694 // Camera devices may not be enumerated yet. This may fail if the
695 // user is not permitted to use EVS service.
696 getCameraList_1_1([](const auto cameras) {
697 if (cameras.size() < 1) {
698 LOG(WARNING) << "No camera device is available to EVS.";
699 }
700 });
701 }
702
703 for (auto& [id, desc] : mCameraDevices) {
704 StringAppendF(&buffer, "%s%s\n", kSingleIndent, id.c_str());
705 }
706
707 StringAppendF(&buffer, "%sCamera devices currently in use:\n", kSingleIndent);
708 for (auto& [id, ptr] : mActiveCameras) {
709 StringAppendF(&buffer, "%s%s\n", kSingleIndent, id.c_str());
710 }
711 StringAppendF(&buffer, "\n");
712 }
713
714 if (listDisplays) {
715 if (mServiceFactory->getService() != nullptr) {
716 StringAppendF(&buffer, "Display devices available to EVS service:\n");
717 // Get an internal display identifier.
718 mServiceFactory->getService()->getDisplayIdList([&](const auto& displayPorts) {
719 for (auto&& port : displayPorts) {
720 StringAppendF(&buffer, "%sdisplay port %u\n", kSingleIndent,
721 static_cast<unsigned>(port));
722 }
723 });
724 } else {
725 LOG(WARNING) << "EVS HAL implementation is not available.";
726 }
727 }
728
729 WriteStringToFd(buffer, fd);
730 }
731
cmdDumpDevice(int fd,const hidl_vec<hidl_string> & options)732 void Enumerator::cmdDumpDevice(int fd, const hidl_vec<hidl_string>& options) {
733 // Dumps both cameras and displays if the target device type is not given
734 bool dumpCameras = false;
735 bool dumpDisplays = false;
736 const auto numOptions = options.size();
737 if (numOptions > kOptionDumpDeviceTypeIndex) {
738 const std::string target = options[kOptionDumpDeviceTypeIndex];
739 dumpCameras = EqualsIgnoreCase(target, kDumpDeviceCamera);
740 dumpDisplays = EqualsIgnoreCase(target, kDumpDeviceDisplay);
741 if (!dumpCameras && !dumpDisplays) {
742 WriteStringToFd(StringPrintf("Unrecognized option, %s, is ignored.\n", target.c_str()),
743 fd);
744 cmdHelp(fd);
745 return;
746 }
747 } else {
748 WriteStringToFd(StringPrintf("Necessary arguments are missing. "
749 "Please check the usages:\n"),
750 fd);
751 cmdHelp(fd);
752 return;
753 }
754
755 if (dumpCameras) {
756 // --dump camera [all|device_id] --[current|collected|custom] [args]
757 if (numOptions < kDumpCameraMinNumArgs) {
758 WriteStringToFd(StringPrintf("Necessary arguments are missing. "
759 "Please check the usages:\n"),
760 fd);
761 cmdHelp(fd);
762 return;
763 }
764
765 const std::string deviceId = options[kOptionDumpCameraTypeIndex];
766 auto target = mActiveCameras.find(deviceId);
767 const bool dumpAllCameras = EqualsIgnoreCase(deviceId, kDumpOptionAll);
768 if (!dumpAllCameras && target == mActiveCameras.end()) {
769 // Unknown camera identifier
770 WriteStringToFd(StringPrintf("Given camera ID %s is unknown or not active.\n",
771 deviceId.c_str()),
772 fd);
773 return;
774 }
775
776 const std::string command = options[kOptionDumpCameraCommandIndex];
777 std::string cameraInfo;
778 if (EqualsIgnoreCase(command, kDumpCameraCommandCurrent)) {
779 // Active stream configuration from each active HalCamera objects
780 if (!dumpAllCameras) {
781 StringAppendF(&cameraInfo, "HalCamera: %s\n%s", deviceId.c_str(),
782 target->second->toString(kSingleIndent).c_str());
783 } else {
784 for (auto&& [id, handle] : mActiveCameras) {
785 // Appends the current status
786 cameraInfo += handle->toString(kSingleIndent);
787 }
788 }
789 } else if (EqualsIgnoreCase(command, kDumpCameraCommandCollected)) {
790 // Reads the usage statistics from active HalCamera objects
791 if (mMonitorEnabled) {
792 std::unordered_map<std::string, std::string> usageStrings =
793 mStatsCollector->toString(kSingleIndent);
794 if (!dumpAllCameras) {
795 cameraInfo += usageStrings[deviceId];
796 } else {
797 for (auto&& [id, stats] : usageStrings) {
798 cameraInfo += stats;
799 }
800 }
801 } else {
802 WriteStringToFd(StringPrintf("Client monitor is not available.\n"), fd);
803 return;
804 }
805 } else if (EqualsIgnoreCase(command, kDumpCameraCommandCustom)) {
806 // Additional arguments are expected for this command:
807 // --dump camera device_id --custom start [interval] [duration]
808 // or, --dump camera device_id --custom stop
809 if (numOptions < kDumpCameraMinNumArgs + 1) {
810 WriteStringToFd(StringPrintf("Necessary arguments are missing. "
811 "Please check the usages:\n"),
812 fd);
813 cmdHelp(fd);
814 return;
815 }
816
817 if (!mMonitorEnabled) {
818 WriteStringToFd(StringPrintf("Client monitor is not available."), fd);
819 return;
820 }
821
822 const std::string subcommand = options[kOptionDumpCameraArgsStartIndex];
823 if (EqualsIgnoreCase(subcommand, kDumpCameraCommandCustomStart)) {
824 using std::chrono::duration_cast;
825 using std::chrono::milliseconds;
826 using std::chrono::nanoseconds;
827 nanoseconds interval = 0ns;
828 nanoseconds duration = 0ns;
829 if (numOptions > kOptionDumpCameraArgsStartIndex + 2) {
830 duration = duration_cast<nanoseconds>(
831 milliseconds(std::stoi(options[kOptionDumpCameraArgsStartIndex + 2])));
832 }
833
834 if (numOptions > kOptionDumpCameraArgsStartIndex + 1) {
835 interval = duration_cast<nanoseconds>(
836 milliseconds(std::stoi(options[kOptionDumpCameraArgsStartIndex + 1])));
837 }
838
839 // Starts a custom collection
840 auto result = mStatsCollector->startCustomCollection(interval, duration);
841 if (!result.ok()) {
842 LOG(ERROR) << "Failed to start a custom collection. " << result.error();
843 StringAppendF(&cameraInfo, "Failed to start a custom collection. %s\n",
844 result.error().message().c_str());
845 }
846 } else if (EqualsIgnoreCase(subcommand, kDumpCameraCommandCustomStop)) {
847 if (!mMonitorEnabled) {
848 WriteStringToFd(StringPrintf("Client monitor is not available."), fd);
849 return;
850 }
851
852 auto result = mStatsCollector->stopCustomCollection(deviceId);
853 if (!result.ok()) {
854 LOG(ERROR) << "Failed to stop a custom collection. " << result.error();
855 StringAppendF(&cameraInfo, "Failed to stop a custom collection. %s\n",
856 result.error().message().c_str());
857 } else {
858 // Pull the custom collection
859 cameraInfo += *result;
860 }
861 } else {
862 WriteStringToFd(StringPrintf("Unknown argument: %s\n", subcommand.c_str()), fd);
863 cmdHelp(fd);
864 return;
865 }
866 } else {
867 WriteStringToFd(StringPrintf("Unknown command: %s\n"
868 "Please check the usages:\n",
869 command.c_str()),
870 fd);
871 cmdHelp(fd);
872 return;
873 }
874
875 // Outputs the report
876 WriteStringToFd(cameraInfo, fd);
877 }
878
879 if (dumpDisplays) {
880 HalDisplay* pDisplay = reinterpret_cast<HalDisplay*>(mActiveDisplay.promote().get());
881 if (!pDisplay) {
882 WriteStringToFd("No active display is found.\n", fd);
883 } else {
884 WriteStringToFd(pDisplay->toString(kSingleIndent), fd);
885 }
886 }
887 }
888
cmdConfigureEmulatedCamera(int fd,const hidl_vec<hidl_string> & options)889 void Enumerator::cmdConfigureEmulatedCamera(int fd, const hidl_vec<hidl_string>& options) {
890 if (options.size() < 6) {
891 WriteStringToFd(StringPrintf("Necessary arguments are missing.\n"), fd);
892 cmdHelp(fd);
893 return;
894 }
895
896 // --configure-emulated-camera [id] [path] [width] [height] [interval]
897 const std::string id = options[1];
898 if (!std::regex_match(id.c_str(), kEmulatedCameraNamePattern)) {
899 WriteStringToFd(StringPrintf("%s does not match to the pattern.\n", id.c_str()), fd);
900 return;
901 }
902
903 if (mCameraDevices.find(id) != mCameraDevices.end()) {
904 WriteStringToFd(StringPrintf("Updating %s's configuration. "
905 "This will get effective when currently active stream is "
906 "closed.\n",
907 id.c_str()),
908 fd);
909 }
910
911 std::string sourceDir = options[2];
912 int width = std::stoi(options[3]);
913 int height = std::stoi(options[4]);
914 std::chrono::nanoseconds interval = std::chrono::duration_cast<std::chrono::nanoseconds>(
915 std::chrono::milliseconds(std::stoi(options[5])));
916 WriteStringToFd(StringPrintf("Configuring %s as:\n"
917 "\tResolution: %dx%d\n"
918 "\tInterval: %f ms\n",
919 id.c_str(), width, height, interval.count() / 1000000.),
920 fd);
921
922 EmulatedCameraDesc desc = {width, height, sourceDir, interval};
923 mEmulatedCameraDevices.insert_or_assign(id, std::move(desc));
924 }
925
926 } // namespace android::automotive::evs::V1_1::implementation
927