927 lines
37 KiB
C++
927 lines
37 KiB
C++
/*
|
|
* Copyright (C) 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "Enumerator.h"
|
|
|
|
#include "HalDisplay.h"
|
|
#include "IPermissionsChecker.h"
|
|
#include "emul/EvsEmulatedCamera.h"
|
|
#include "stats/StatsCollector.h"
|
|
|
|
#include <android-base/chrono_utils.h>
|
|
#include <android-base/file.h>
|
|
#include <android-base/logging.h>
|
|
#include <android-base/parseint.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/strings.h>
|
|
#include <cutils/android_filesystem_config.h>
|
|
#include <hwbinder/IPCThreadState.h>
|
|
|
|
#include <regex> // NOLINT
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
using ::android::automotive::evs::V1_1::implementation::IPermissionsChecker;
|
|
using ::android::base::EqualsIgnoreCase;
|
|
using ::android::base::Error;
|
|
using ::android::base::StringAppendF;
|
|
using ::android::base::StringPrintf;
|
|
using ::android::base::WriteStringToFd;
|
|
using ::android::hardware::hidl_handle;
|
|
using ::android::hardware::IPCThreadState;
|
|
using ::android::hardware::Void;
|
|
using ::android::hardware::automotive::evs::V1_0::DisplayState;
|
|
using IEvsCamera_1_0 = ::android::hardware::automotive::evs::V1_0::IEvsCamera;
|
|
using CameraDesc_1_0 = ::android::hardware::automotive::evs::V1_0::CameraDesc;
|
|
using CameraDesc_1_1 = ::android::hardware::automotive::evs::V1_1::CameraDesc;
|
|
using ::android::hardware::camera::device::V3_2::Stream;
|
|
|
|
const char* kSingleIndent = "\t";
|
|
const char* kDumpOptionAll = "all";
|
|
const char* kDumpDeviceCamera = "camera";
|
|
const char* kDumpDeviceDisplay = "display";
|
|
|
|
const char* kDumpCameraCommandCurrent = "--current";
|
|
const char* kDumpCameraCommandCollected = "--collected";
|
|
const char* kDumpCameraCommandCustom = "--custom";
|
|
const char* kDumpCameraCommandCustomStart = "start";
|
|
const char* kDumpCameraCommandCustomStop = "stop";
|
|
|
|
const int kDumpCameraMinNumArgs = 4;
|
|
const int kOptionDumpDeviceTypeIndex = 1;
|
|
const int kOptionDumpCameraTypeIndex = 2;
|
|
const int kOptionDumpCameraCommandIndex = 3;
|
|
const int kOptionDumpCameraArgsStartIndex = 4;
|
|
|
|
const std::regex kEmulatedCameraNamePattern("emulated/[0-9]+", std::regex_constants::icase);
|
|
|
|
// Display ID 255 is reserved for the special purpose.
|
|
constexpr int kExclusiveMainDisplayId = 255;
|
|
|
|
// This surprisingly is not included in STL until C++20.
|
|
template <template <class> class Container, typename T>
|
|
constexpr bool contains(const Container<T>& container, const T& value) {
|
|
return (std::find(container.begin(), container.end(), value) != container.end());
|
|
}
|
|
|
|
// Removes the target value if present, and optionally executes a lambda.
|
|
template <typename Container, typename T, typename RemovalLambda>
|
|
constexpr void removeIfPresent(
|
|
Container* container, const T& value, RemovalLambda removalLambda = []() {}) {
|
|
auto it = std::find(container->begin(), container->end(), value);
|
|
if (it != container->end()) {
|
|
container->erase(it);
|
|
removalLambda();
|
|
}
|
|
}
|
|
|
|
class ProdPermissionChecker : public IPermissionsChecker {
|
|
public:
|
|
bool processHasPermissionsForEvs() override {
|
|
IPCThreadState* ipc = IPCThreadState::self();
|
|
const auto userId = ipc->getCallingUid() / AID_USER_OFFSET;
|
|
const auto appId = ipc->getCallingUid() % AID_USER_OFFSET;
|
|
if (AID_AUTOMOTIVE_EVS != appId && AID_ROOT != appId && AID_SYSTEM != appId) {
|
|
LOG(ERROR) << "EVS access denied? "
|
|
<< "pid = " << ipc->getCallingPid() << ", userId = " << userId
|
|
<< ", appId = " << appId;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
namespace android::automotive::evs::V1_1::implementation {
|
|
|
|
Enumerator::Enumerator(std::unique_ptr<ServiceFactory> serviceFactory,
|
|
std::unique_ptr<IStatsCollector> statsCollector,
|
|
std::unique_ptr<IPermissionsChecker> permissionChecker) :
|
|
mServiceFactory(std::move(serviceFactory)),
|
|
mStatsCollector(std::move(statsCollector)),
|
|
mPermissionChecker(std::move(permissionChecker)) {
|
|
// Get an internal display identifier.
|
|
mServiceFactory->getService()->getDisplayIdList(
|
|
[this](const android::hardware::hidl_vec<unsigned char>& displayPorts) {
|
|
for (unsigned char port : displayPorts) {
|
|
mDisplayPorts.push_back(port);
|
|
}
|
|
|
|
if (mDisplayPorts.empty()) {
|
|
LOG(WARNING) << "No display is available to EVS service.";
|
|
} else {
|
|
// The first element must be the internal display
|
|
mInternalDisplayPort = mDisplayPorts.front();
|
|
}
|
|
});
|
|
|
|
removeIfPresent(&mDisplayPorts, kExclusiveMainDisplayId, []() {
|
|
LOG(WARNING) << kExclusiveMainDisplayId
|
|
<< " is reserved so will not be available for EVS service.";
|
|
});
|
|
|
|
mMonitorEnabled = mStatsCollector->startCollection().ok();
|
|
}
|
|
|
|
std::unique_ptr<Enumerator> Enumerator::build(
|
|
std::unique_ptr<ServiceFactory> serviceFactory,
|
|
std::unique_ptr<IStatsCollector> statsCollector,
|
|
std::unique_ptr<IPermissionsChecker> permissionChecker) {
|
|
// Connect with the underlying hardware enumerator.
|
|
if (!serviceFactory->getService()) {
|
|
return nullptr;
|
|
}
|
|
|
|
return std::unique_ptr<Enumerator>{new Enumerator(std::move(serviceFactory),
|
|
std::move(statsCollector),
|
|
std::move(permissionChecker))};
|
|
}
|
|
|
|
std::unique_ptr<Enumerator> Enumerator::build(const char* hardwareServiceName) {
|
|
if (!hardwareServiceName) {
|
|
return nullptr;
|
|
}
|
|
|
|
return build(std::make_unique<ProdServiceFactory>(hardwareServiceName),
|
|
std::make_unique<StatsCollector>(), std::make_unique<ProdPermissionChecker>());
|
|
}
|
|
|
|
bool Enumerator::isLogicalCamera(const camera_metadata_t* metadata) {
|
|
bool found = false;
|
|
|
|
if (metadata == nullptr) {
|
|
LOG(ERROR) << "Metadata is null";
|
|
return found;
|
|
}
|
|
|
|
camera_metadata_ro_entry_t entry;
|
|
int rc =
|
|
find_camera_metadata_ro_entry(metadata, ANDROID_REQUEST_AVAILABLE_CAPABILITIES, &entry);
|
|
if (0 != rc) {
|
|
// No capabilities are found in metadata.
|
|
LOG(DEBUG) << __FUNCTION__ << " does not find a target entry";
|
|
return found;
|
|
}
|
|
|
|
for (size_t i = 0; i < entry.count; ++i) {
|
|
uint8_t capability = entry.data.u8[i];
|
|
if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
LOG(DEBUG) << __FUNCTION__ << " does not find a logical multi camera cap";
|
|
}
|
|
return found;
|
|
}
|
|
|
|
std::unordered_set<std::string> Enumerator::getPhysicalCameraIds(const std::string& id) {
|
|
std::unordered_set<std::string> physicalCameras;
|
|
if (mCameraDevices.find(id) == mCameraDevices.end()) {
|
|
LOG(ERROR) << "Queried device " << id << " does not exist!";
|
|
return physicalCameras;
|
|
}
|
|
|
|
const camera_metadata_t* metadata =
|
|
reinterpret_cast<camera_metadata_t*>(&mCameraDevices[id].metadata[0]);
|
|
if (!isLogicalCamera(metadata)) {
|
|
// EVS assumes that the device w/o a valid metadata is a physical
|
|
// device.
|
|
LOG(INFO) << id << " is not a logical camera device.";
|
|
physicalCameras.emplace(id);
|
|
return physicalCameras;
|
|
}
|
|
|
|
camera_metadata_ro_entry entry;
|
|
int rc = find_camera_metadata_ro_entry(metadata, ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS,
|
|
&entry);
|
|
if (0 != rc) {
|
|
LOG(ERROR) << "No physical camera ID is found for a logical camera device " << id;
|
|
return physicalCameras;
|
|
}
|
|
|
|
const uint8_t* ids = entry.data.u8;
|
|
size_t start = 0;
|
|
for (size_t i = 0; i < entry.count; ++i) {
|
|
if (ids[i] == '\0') {
|
|
if (start != i) {
|
|
std::string id(reinterpret_cast<const char*>(ids + start));
|
|
physicalCameras.emplace(id);
|
|
}
|
|
start = i + 1;
|
|
}
|
|
}
|
|
|
|
LOG(INFO) << id << " consists of " << physicalCameras.size() << " physical camera devices.";
|
|
return physicalCameras;
|
|
}
|
|
|
|
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
|
|
Return<void> Enumerator::getCameraList(getCameraList_cb list_cb) {
|
|
hardware::hidl_vec<CameraDesc_1_0> cameraList;
|
|
mServiceFactory->getService()->getCameraList_1_1([&cameraList](auto cameraList_1_1) {
|
|
cameraList.resize(cameraList_1_1.size());
|
|
unsigned i = 0;
|
|
for (auto&& cam : cameraList_1_1) {
|
|
cameraList[i++] = cam.v1;
|
|
}
|
|
});
|
|
|
|
list_cb(cameraList);
|
|
|
|
return Void();
|
|
}
|
|
|
|
Return<sp<IEvsCamera_1_0>> Enumerator::openCamera(const hidl_string& cameraId) {
|
|
LOG(DEBUG) << __FUNCTION__;
|
|
if (!mPermissionChecker->processHasPermissionsForEvs()) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Is the underlying hardware camera already open?
|
|
sp<HalCamera> hwCamera;
|
|
if (mActiveCameras.find(cameraId) != mActiveCameras.end()) {
|
|
hwCamera = mActiveCameras[cameraId];
|
|
} else {
|
|
// Is the hardware camera available?
|
|
sp<IEvsCamera_1_1> device;
|
|
if (std::regex_match(cameraId.c_str(), kEmulatedCameraNamePattern)) {
|
|
if (mEmulatedCameraDevices.find(cameraId) == mEmulatedCameraDevices.end()) {
|
|
LOG(ERROR) << cameraId << " is not available";
|
|
} else {
|
|
device = EvsEmulatedCamera::Create(cameraId.c_str(),
|
|
mEmulatedCameraDevices[cameraId]);
|
|
}
|
|
} else {
|
|
device = IEvsCamera_1_1::castFrom(mServiceFactory->getService()->openCamera(cameraId))
|
|
.withDefault(nullptr);
|
|
}
|
|
if (device == nullptr) {
|
|
LOG(ERROR) << "Failed to open hardware camera " << cameraId;
|
|
} else {
|
|
// Calculates the usage statistics record identifier
|
|
auto fn = mCameraDevices.hash_function();
|
|
auto recordId = fn(cameraId) & 0xFF;
|
|
hwCamera = new HalCamera(device, cameraId, recordId);
|
|
if (hwCamera == nullptr) {
|
|
LOG(ERROR) << "Failed to allocate camera wrapper object";
|
|
mServiceFactory->getService()->closeCamera(device);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Construct a virtual camera wrapper for this hardware camera
|
|
sp<VirtualCamera> clientCamera;
|
|
if (hwCamera != nullptr) {
|
|
clientCamera = hwCamera->makeVirtualCamera();
|
|
}
|
|
|
|
// Add the hardware camera to our list, which will keep it alive via ref count
|
|
if (clientCamera != nullptr) {
|
|
mActiveCameras.try_emplace(cameraId, hwCamera);
|
|
} else {
|
|
LOG(ERROR) << "Requested camera " << cameraId << " not found or not available";
|
|
}
|
|
|
|
// Send the virtual camera object back to the client by strong pointer which will keep it alive
|
|
return clientCamera;
|
|
}
|
|
|
|
Return<void> Enumerator::closeCamera(const ::android::sp<IEvsCamera_1_0>& clientCamera) {
|
|
LOG(DEBUG) << __FUNCTION__;
|
|
|
|
if (clientCamera == nullptr) {
|
|
LOG(ERROR) << "Ignoring call with null camera pointer.";
|
|
return Void();
|
|
}
|
|
|
|
// All our client cameras are actually VirtualCamera objects
|
|
sp<VirtualCamera> virtualCamera = reinterpret_cast<VirtualCamera*>(clientCamera.get());
|
|
|
|
// Find the parent camera that backs this virtual camera
|
|
for (auto&& halCamera : virtualCamera->getHalCameras()) {
|
|
// Tell the virtual camera's parent to clean it up and drop it
|
|
// NOTE: The camera objects will only actually destruct when the sp<> ref counts get to
|
|
// zero, so it is important to break all cyclic references.
|
|
halCamera->disownVirtualCamera(virtualCamera);
|
|
|
|
// Did we just remove the last client of this camera?
|
|
if (halCamera->getClientCount() == 0) {
|
|
// Take this now unused camera out of our list
|
|
// NOTE: This should drop our last reference to the camera, resulting in its
|
|
// destruction.
|
|
mActiveCameras.erase(halCamera->getId());
|
|
mServiceFactory->getService()->closeCamera(halCamera->getHwCamera());
|
|
if (mMonitorEnabled) {
|
|
mStatsCollector->unregisterClientToMonitor(halCamera->getId());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure the virtual camera's stream is stopped
|
|
virtualCamera->stopVideoStream();
|
|
|
|
return Void();
|
|
}
|
|
|
|
// Methods from ::android::hardware::automotive::evs::V1_1::IEvsEnumerator follow.
|
|
Return<sp<IEvsCamera_1_1>> Enumerator::openCamera_1_1(const hidl_string& cameraId,
|
|
const Stream& streamCfg) {
|
|
LOG(DEBUG) << __FUNCTION__;
|
|
if (!mPermissionChecker->processHasPermissionsForEvs()) {
|
|
return nullptr;
|
|
}
|
|
|
|
// If hwCamera is null, a requested camera device is either a logical camera
|
|
// device or a hardware camera, which is not being used now.
|
|
std::unordered_set<std::string> physicalCameras = getPhysicalCameraIds(cameraId);
|
|
std::vector<sp<HalCamera>> sourceCameras;
|
|
sp<HalCamera> hwCamera;
|
|
bool success = true;
|
|
|
|
// 1. Try to open inactive camera devices.
|
|
for (auto&& id : physicalCameras) {
|
|
auto it = mActiveCameras.find(id);
|
|
if (it == mActiveCameras.end()) {
|
|
sp<IEvsCamera_1_1> device;
|
|
if (std::regex_match(cameraId.c_str(), kEmulatedCameraNamePattern)) {
|
|
if (mEmulatedCameraDevices.find(id) == mEmulatedCameraDevices.end()) {
|
|
LOG(ERROR) << cameraId << " is not available";
|
|
} else {
|
|
device = EvsEmulatedCamera::Create(id.c_str(), mEmulatedCameraDevices[id]);
|
|
}
|
|
} else {
|
|
device = mServiceFactory->getService()->openCamera_1_1(id, streamCfg);
|
|
}
|
|
|
|
if (device == nullptr) {
|
|
LOG(ERROR) << "Failed to open hardware camera " << cameraId;
|
|
success = false;
|
|
break;
|
|
} else {
|
|
// Calculates the usage statistics record identifier
|
|
auto fn = mCameraDevices.hash_function();
|
|
auto recordId = fn(id) & 0xFF;
|
|
hwCamera = new HalCamera(device, id, recordId, streamCfg);
|
|
if (hwCamera == nullptr) {
|
|
LOG(ERROR) << "Failed to allocate camera wrapper object";
|
|
mServiceFactory->getService()->closeCamera(device);
|
|
success = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Add the hardware camera to our list, which will keep it alive via ref count
|
|
mActiveCameras.try_emplace(id, hwCamera);
|
|
if (mMonitorEnabled) {
|
|
mStatsCollector->registerClientToMonitor(hwCamera);
|
|
}
|
|
|
|
sourceCameras.push_back(hwCamera);
|
|
} else {
|
|
if (it->second->getStreamConfig().id != streamCfg.id) {
|
|
LOG(WARNING) << "Requested camera is already active in different configuration.";
|
|
} else {
|
|
sourceCameras.push_back(it->second);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!success || sourceCameras.size() < 1) {
|
|
LOG(ERROR) << "Failed to open any physical camera device";
|
|
return nullptr;
|
|
}
|
|
|
|
// TODO(b/147170360): Implement a logic to handle a failure.
|
|
// 3. Create a proxy camera object
|
|
sp<VirtualCamera> clientCamera = new VirtualCamera(sourceCameras);
|
|
if (clientCamera == nullptr) {
|
|
// TODO(b/206829268): Any resource needs to be cleaned up explicitly?
|
|
LOG(ERROR) << "Failed to create a client camera object";
|
|
} else {
|
|
if (physicalCameras.size() > 1) {
|
|
// VirtualCamera, which represents a logical device, caches its
|
|
// descriptor.
|
|
clientCamera->setDescriptor(&mCameraDevices[cameraId]);
|
|
}
|
|
|
|
// 4. Owns created proxy camera object
|
|
for (auto&& hwCamera : sourceCameras) {
|
|
if (!hwCamera->ownVirtualCamera(clientCamera)) {
|
|
// TODO(b/206829268): Remove a reference to this camera from a virtual camera.
|
|
// object.
|
|
LOG(ERROR) << hwCamera->getId() << " failed to own a created proxy camera object.";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Send the virtual camera object back to the client by strong pointer which will keep it alive
|
|
return clientCamera;
|
|
}
|
|
|
|
Return<void> Enumerator::getCameraList_1_1(getCameraList_1_1_cb list_cb) {
|
|
LOG(DEBUG) << __FUNCTION__;
|
|
if (!mPermissionChecker->processHasPermissionsForEvs()) {
|
|
return Void();
|
|
}
|
|
|
|
hardware::hidl_vec<CameraDesc_1_1> hidlCameras;
|
|
mServiceFactory->getService()->getCameraList_1_1(
|
|
[&hidlCameras](hardware::hidl_vec<CameraDesc_1_1> enumeratedCameras) {
|
|
hidlCameras.resize(enumeratedCameras.size());
|
|
unsigned count = 0;
|
|
for (auto&& camdesc : enumeratedCameras) {
|
|
hidlCameras[count++] = camdesc;
|
|
}
|
|
});
|
|
|
|
// Update the cached device list
|
|
mCameraDevices.clear();
|
|
for (auto&& desc : hidlCameras) {
|
|
mCameraDevices.insert_or_assign(desc.v1.cameraId, desc);
|
|
}
|
|
|
|
// Add emulated devices if there is any
|
|
if (mEmulatedCameraDevices.size() > 0) {
|
|
int index = hidlCameras.size();
|
|
hidlCameras.resize(hidlCameras.size() + mEmulatedCameraDevices.size());
|
|
for (auto&& [id, desc] : mEmulatedCameraDevices) {
|
|
hidlCameras[index++].v1.cameraId = id;
|
|
}
|
|
}
|
|
|
|
list_cb(hidlCameras);
|
|
return Void();
|
|
}
|
|
|
|
Return<sp<IEvsDisplay_1_0>> Enumerator::openDisplay() {
|
|
LOG(DEBUG) << __FUNCTION__;
|
|
|
|
if (!mPermissionChecker->processHasPermissionsForEvs()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (mDisplayOwnedExclusively) {
|
|
LOG(ERROR) << "Display is owned exclusively by another client.";
|
|
return nullptr;
|
|
}
|
|
|
|
// We simply keep track of the most recently opened display instance.
|
|
// In the underlying layers we expect that a new open will cause the previous
|
|
// object to be destroyed. This avoids any race conditions associated with
|
|
// create/destroy order and provides a cleaner restart sequence if the previous owner
|
|
// is non-responsive for some reason.
|
|
// Request exclusive access to the EVS display
|
|
sp<IEvsDisplay_1_0> pActiveDisplay = mServiceFactory->getService()->openDisplay();
|
|
if (pActiveDisplay == nullptr) {
|
|
LOG(ERROR) << "EVS Display unavailable";
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// Remember (via weak pointer) who we think the most recently opened display is so that
|
|
// we can proxy state requests from other callers to it.
|
|
// TODO(b/206829268): Because of b/129284474, an additional class, HalDisplay, has been defined
|
|
// and wraps the IEvsDisplay object the driver returns. We may want to remove this additional
|
|
// class when it is fixed properly.
|
|
sp<IEvsDisplay_1_0> pHalDisplay = new HalDisplay(pActiveDisplay, mInternalDisplayPort);
|
|
mActiveDisplay = pHalDisplay;
|
|
|
|
return pHalDisplay;
|
|
}
|
|
|
|
Return<void> Enumerator::closeDisplay(const ::android::sp<IEvsDisplay_1_0>& display) {
|
|
LOG(DEBUG) << __FUNCTION__;
|
|
|
|
sp<IEvsDisplay_1_0> pActiveDisplay = mActiveDisplay.promote();
|
|
|
|
// Drop the active display
|
|
if (display.get() != pActiveDisplay.get()) {
|
|
LOG(WARNING) << "Ignoring call to closeDisplay with unrecognized display object.";
|
|
} else {
|
|
// Pass this request through to the hardware layer
|
|
sp<HalDisplay> halDisplay = reinterpret_cast<HalDisplay*>(pActiveDisplay.get());
|
|
mServiceFactory->getService()->closeDisplay(halDisplay->getHwDisplay());
|
|
mActiveDisplay = nullptr;
|
|
mDisplayOwnedExclusively = false;
|
|
}
|
|
|
|
return Void();
|
|
}
|
|
|
|
Return<DisplayState> Enumerator::getDisplayState() {
|
|
LOG(DEBUG) << __FUNCTION__;
|
|
if (!mPermissionChecker->processHasPermissionsForEvs()) {
|
|
return DisplayState::DEAD;
|
|
}
|
|
|
|
// Do we have a display object we think should be active?
|
|
sp<IEvsDisplay_1_0> pActiveDisplay = mActiveDisplay.promote();
|
|
if (pActiveDisplay != nullptr) {
|
|
// Pass this request through to the hardware layer
|
|
return pActiveDisplay->getDisplayState();
|
|
} else {
|
|
// We don't have a live display right now
|
|
mActiveDisplay = nullptr;
|
|
return DisplayState::NOT_OPEN;
|
|
}
|
|
}
|
|
|
|
Return<sp<IEvsDisplay_1_1>> Enumerator::openDisplay_1_1(uint8_t id) {
|
|
LOG(DEBUG) << __FUNCTION__;
|
|
|
|
if (!mPermissionChecker->processHasPermissionsForEvs()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (mDisplayOwnedExclusively) {
|
|
LOG(ERROR) << "Display is owned exclusively by another client.";
|
|
return nullptr;
|
|
}
|
|
|
|
if (id == kExclusiveMainDisplayId) {
|
|
// The client requests to open the primary display exclusively.
|
|
id = mInternalDisplayPort;
|
|
mDisplayOwnedExclusively = true;
|
|
} else if (std::find(mDisplayPorts.begin(), mDisplayPorts.end(), id) == mDisplayPorts.end()) {
|
|
LOG(ERROR) << "No display is available on the port " << static_cast<int32_t>(id);
|
|
return nullptr;
|
|
}
|
|
|
|
// We simply keep track of the most recently opened display instance.
|
|
// In the underlying layers we expect that a new open will cause the previous
|
|
// object to be destroyed. This avoids any race conditions associated with
|
|
// create/destroy order and provides a cleaner restart sequence if the previous owner
|
|
// is non-responsive for some reason.
|
|
// Request exclusive access to the EVS display
|
|
sp<IEvsDisplay_1_1> pActiveDisplay = mServiceFactory->getService()->openDisplay_1_1(id);
|
|
if (pActiveDisplay == nullptr) {
|
|
LOG(ERROR) << "EVS Display unavailable";
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// Remember (via weak pointer) who we think the most recently opened display is so that
|
|
// we can proxy state requests from other callers to it.
|
|
// TODO(b/206829268): Because of b/129284474, an additional class, HalDisplay, has been defined
|
|
// and wraps the IEvsDisplay object the driver returns. We may want to remove this additional
|
|
// class when it is fixed properly.
|
|
sp<IEvsDisplay_1_1> pHalDisplay = new HalDisplay(pActiveDisplay, id);
|
|
mActiveDisplay = pHalDisplay;
|
|
|
|
return pHalDisplay;
|
|
}
|
|
|
|
Return<void> Enumerator::getDisplayIdList(getDisplayIdList_cb _list_cb) {
|
|
return mServiceFactory->getService()->getDisplayIdList(_list_cb);
|
|
}
|
|
|
|
// TODO(b/149874793): Add implementation for EVS Manager and Sample driver
|
|
Return<void> Enumerator::getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb) {
|
|
hardware::hidl_vec<UltrasonicsArrayDesc> ultrasonicsArrayDesc;
|
|
_hidl_cb(ultrasonicsArrayDesc);
|
|
return Void();
|
|
}
|
|
|
|
// TODO(b/149874793): Add implementation for EVS Manager and Sample driver
|
|
Return<sp<IEvsUltrasonicsArray>> Enumerator::openUltrasonicsArray(
|
|
const hidl_string& ultrasonicsArrayId) {
|
|
(void)ultrasonicsArrayId;
|
|
sp<IEvsUltrasonicsArray> pEvsUltrasonicsArray;
|
|
return pEvsUltrasonicsArray;
|
|
}
|
|
|
|
// TODO(b/149874793): Add implementation for EVS Manager and Sample driver
|
|
Return<void> Enumerator::closeUltrasonicsArray(
|
|
const ::android::sp<IEvsUltrasonicsArray>& evsUltrasonicsArray) {
|
|
(void)evsUltrasonicsArray;
|
|
return Void();
|
|
}
|
|
|
|
Return<void> Enumerator::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
|
|
if (fd.getNativeHandle() != nullptr && fd->numFds > 0) {
|
|
cmdDump(fd->data[0], options);
|
|
} else {
|
|
LOG(ERROR) << "Given file descriptor is not valid.";
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
void Enumerator::cmdDump(int fd, const hidl_vec<hidl_string>& options) {
|
|
if (options.size() == 0) {
|
|
WriteStringToFd("No option is given.\n", fd);
|
|
cmdHelp(fd);
|
|
return;
|
|
}
|
|
|
|
const std::string option = options[0];
|
|
if (EqualsIgnoreCase(option, "--help")) {
|
|
cmdHelp(fd);
|
|
} else if (EqualsIgnoreCase(option, "--list")) {
|
|
cmdList(fd, options);
|
|
} else if (EqualsIgnoreCase(option, "--dump")) {
|
|
cmdDumpDevice(fd, options);
|
|
} else if (EqualsIgnoreCase(option, "--configure-emulated-camera")) {
|
|
cmdConfigureEmulatedCamera(fd, options);
|
|
} else {
|
|
WriteStringToFd(StringPrintf("Invalid option: %s\n", option.c_str()), fd);
|
|
}
|
|
}
|
|
|
|
void Enumerator::cmdHelp(int fd) {
|
|
WriteStringToFd("--help: shows this help.\n"
|
|
"--list [all|camera|display]: lists camera or display devices or both "
|
|
"available to EVS manager.\n"
|
|
"--dump camera [all|device_id] --[current|collected|custom] [args]\n"
|
|
"\tcurrent: shows the current status\n"
|
|
"\tcollected: shows 10 most recent periodically collected camera usage "
|
|
"statistics\n"
|
|
"\tcustom: starts/stops collecting the camera usage statistics\n"
|
|
"\t\tstart [interval] [duration]: starts collecting usage statistics "
|
|
"at every [interval] during [duration]. Interval and duration are in "
|
|
"milliseconds.\n"
|
|
"\t\tstop: stops collecting usage statistics and shows collected records.\n"
|
|
"--dump display: shows current status of the display\n"
|
|
"--configure-emulated-camera [id] [path] [width] [height] [interval]\n"
|
|
"\tid: emulated device id to use; emulated/[0-9]+\n"
|
|
"\tpath: a path to the directory where source files are stored\n"
|
|
"\twidth: image width in pixels\n"
|
|
"\theight: image height in pixels\n"
|
|
"\tinterval: interval between consecutive frames in milliseconds.\n",
|
|
fd);
|
|
}
|
|
|
|
void Enumerator::cmdList(int fd, const hidl_vec<hidl_string>& options) {
|
|
bool listCameras = true;
|
|
bool listDisplays = true;
|
|
if (options.size() > 1) {
|
|
const std::string option = options[1];
|
|
const bool listAll = EqualsIgnoreCase(option, kDumpOptionAll);
|
|
listCameras = listAll || EqualsIgnoreCase(option, kDumpDeviceCamera);
|
|
listDisplays = listAll || EqualsIgnoreCase(option, kDumpDeviceDisplay);
|
|
if (!listCameras && !listDisplays) {
|
|
WriteStringToFd(StringPrintf("Unrecognized option, %s, is ignored.\n", option.c_str()),
|
|
fd);
|
|
|
|
// Nothing to show, return
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::string buffer;
|
|
if (listCameras) {
|
|
StringAppendF(&buffer, "Camera devices available to EVS service:\n");
|
|
if (mCameraDevices.size() < 1) {
|
|
// Camera devices may not be enumerated yet. This may fail if the
|
|
// user is not permitted to use EVS service.
|
|
getCameraList_1_1([](const auto cameras) {
|
|
if (cameras.size() < 1) {
|
|
LOG(WARNING) << "No camera device is available to EVS.";
|
|
}
|
|
});
|
|
}
|
|
|
|
for (auto& [id, desc] : mCameraDevices) {
|
|
StringAppendF(&buffer, "%s%s\n", kSingleIndent, id.c_str());
|
|
}
|
|
|
|
StringAppendF(&buffer, "%sCamera devices currently in use:\n", kSingleIndent);
|
|
for (auto& [id, ptr] : mActiveCameras) {
|
|
StringAppendF(&buffer, "%s%s\n", kSingleIndent, id.c_str());
|
|
}
|
|
StringAppendF(&buffer, "\n");
|
|
}
|
|
|
|
if (listDisplays) {
|
|
if (mServiceFactory->getService() != nullptr) {
|
|
StringAppendF(&buffer, "Display devices available to EVS service:\n");
|
|
// Get an internal display identifier.
|
|
mServiceFactory->getService()->getDisplayIdList([&](const auto& displayPorts) {
|
|
for (auto&& port : displayPorts) {
|
|
StringAppendF(&buffer, "%sdisplay port %u\n", kSingleIndent,
|
|
static_cast<unsigned>(port));
|
|
}
|
|
});
|
|
} else {
|
|
LOG(WARNING) << "EVS HAL implementation is not available.";
|
|
}
|
|
}
|
|
|
|
WriteStringToFd(buffer, fd);
|
|
}
|
|
|
|
void Enumerator::cmdDumpDevice(int fd, const hidl_vec<hidl_string>& options) {
|
|
// Dumps both cameras and displays if the target device type is not given
|
|
bool dumpCameras = false;
|
|
bool dumpDisplays = false;
|
|
const auto numOptions = options.size();
|
|
if (numOptions > kOptionDumpDeviceTypeIndex) {
|
|
const std::string target = options[kOptionDumpDeviceTypeIndex];
|
|
dumpCameras = EqualsIgnoreCase(target, kDumpDeviceCamera);
|
|
dumpDisplays = EqualsIgnoreCase(target, kDumpDeviceDisplay);
|
|
if (!dumpCameras && !dumpDisplays) {
|
|
WriteStringToFd(StringPrintf("Unrecognized option, %s, is ignored.\n", target.c_str()),
|
|
fd);
|
|
cmdHelp(fd);
|
|
return;
|
|
}
|
|
} else {
|
|
WriteStringToFd(StringPrintf("Necessary arguments are missing. "
|
|
"Please check the usages:\n"),
|
|
fd);
|
|
cmdHelp(fd);
|
|
return;
|
|
}
|
|
|
|
if (dumpCameras) {
|
|
// --dump camera [all|device_id] --[current|collected|custom] [args]
|
|
if (numOptions < kDumpCameraMinNumArgs) {
|
|
WriteStringToFd(StringPrintf("Necessary arguments are missing. "
|
|
"Please check the usages:\n"),
|
|
fd);
|
|
cmdHelp(fd);
|
|
return;
|
|
}
|
|
|
|
const std::string deviceId = options[kOptionDumpCameraTypeIndex];
|
|
auto target = mActiveCameras.find(deviceId);
|
|
const bool dumpAllCameras = EqualsIgnoreCase(deviceId, kDumpOptionAll);
|
|
if (!dumpAllCameras && target == mActiveCameras.end()) {
|
|
// Unknown camera identifier
|
|
WriteStringToFd(StringPrintf("Given camera ID %s is unknown or not active.\n",
|
|
deviceId.c_str()),
|
|
fd);
|
|
return;
|
|
}
|
|
|
|
const std::string command = options[kOptionDumpCameraCommandIndex];
|
|
std::string cameraInfo;
|
|
if (EqualsIgnoreCase(command, kDumpCameraCommandCurrent)) {
|
|
// Active stream configuration from each active HalCamera objects
|
|
if (!dumpAllCameras) {
|
|
StringAppendF(&cameraInfo, "HalCamera: %s\n%s", deviceId.c_str(),
|
|
target->second->toString(kSingleIndent).c_str());
|
|
} else {
|
|
for (auto&& [id, handle] : mActiveCameras) {
|
|
// Appends the current status
|
|
cameraInfo += handle->toString(kSingleIndent);
|
|
}
|
|
}
|
|
} else if (EqualsIgnoreCase(command, kDumpCameraCommandCollected)) {
|
|
// Reads the usage statistics from active HalCamera objects
|
|
if (mMonitorEnabled) {
|
|
std::unordered_map<std::string, std::string> usageStrings =
|
|
mStatsCollector->toString(kSingleIndent);
|
|
if (!dumpAllCameras) {
|
|
cameraInfo += usageStrings[deviceId];
|
|
} else {
|
|
for (auto&& [id, stats] : usageStrings) {
|
|
cameraInfo += stats;
|
|
}
|
|
}
|
|
} else {
|
|
WriteStringToFd(StringPrintf("Client monitor is not available.\n"), fd);
|
|
return;
|
|
}
|
|
} else if (EqualsIgnoreCase(command, kDumpCameraCommandCustom)) {
|
|
// Additional arguments are expected for this command:
|
|
// --dump camera device_id --custom start [interval] [duration]
|
|
// or, --dump camera device_id --custom stop
|
|
if (numOptions < kDumpCameraMinNumArgs + 1) {
|
|
WriteStringToFd(StringPrintf("Necessary arguments are missing. "
|
|
"Please check the usages:\n"),
|
|
fd);
|
|
cmdHelp(fd);
|
|
return;
|
|
}
|
|
|
|
if (!mMonitorEnabled) {
|
|
WriteStringToFd(StringPrintf("Client monitor is not available."), fd);
|
|
return;
|
|
}
|
|
|
|
const std::string subcommand = options[kOptionDumpCameraArgsStartIndex];
|
|
if (EqualsIgnoreCase(subcommand, kDumpCameraCommandCustomStart)) {
|
|
using std::chrono::duration_cast;
|
|
using std::chrono::milliseconds;
|
|
using std::chrono::nanoseconds;
|
|
nanoseconds interval = 0ns;
|
|
nanoseconds duration = 0ns;
|
|
if (numOptions > kOptionDumpCameraArgsStartIndex + 2) {
|
|
duration = duration_cast<nanoseconds>(
|
|
milliseconds(std::stoi(options[kOptionDumpCameraArgsStartIndex + 2])));
|
|
}
|
|
|
|
if (numOptions > kOptionDumpCameraArgsStartIndex + 1) {
|
|
interval = duration_cast<nanoseconds>(
|
|
milliseconds(std::stoi(options[kOptionDumpCameraArgsStartIndex + 1])));
|
|
}
|
|
|
|
// Starts a custom collection
|
|
auto result = mStatsCollector->startCustomCollection(interval, duration);
|
|
if (!result.ok()) {
|
|
LOG(ERROR) << "Failed to start a custom collection. " << result.error();
|
|
StringAppendF(&cameraInfo, "Failed to start a custom collection. %s\n",
|
|
result.error().message().c_str());
|
|
}
|
|
} else if (EqualsIgnoreCase(subcommand, kDumpCameraCommandCustomStop)) {
|
|
if (!mMonitorEnabled) {
|
|
WriteStringToFd(StringPrintf("Client monitor is not available."), fd);
|
|
return;
|
|
}
|
|
|
|
auto result = mStatsCollector->stopCustomCollection(deviceId);
|
|
if (!result.ok()) {
|
|
LOG(ERROR) << "Failed to stop a custom collection. " << result.error();
|
|
StringAppendF(&cameraInfo, "Failed to stop a custom collection. %s\n",
|
|
result.error().message().c_str());
|
|
} else {
|
|
// Pull the custom collection
|
|
cameraInfo += *result;
|
|
}
|
|
} else {
|
|
WriteStringToFd(StringPrintf("Unknown argument: %s\n", subcommand.c_str()), fd);
|
|
cmdHelp(fd);
|
|
return;
|
|
}
|
|
} else {
|
|
WriteStringToFd(StringPrintf("Unknown command: %s\n"
|
|
"Please check the usages:\n",
|
|
command.c_str()),
|
|
fd);
|
|
cmdHelp(fd);
|
|
return;
|
|
}
|
|
|
|
// Outputs the report
|
|
WriteStringToFd(cameraInfo, fd);
|
|
}
|
|
|
|
if (dumpDisplays) {
|
|
HalDisplay* pDisplay = reinterpret_cast<HalDisplay*>(mActiveDisplay.promote().get());
|
|
if (!pDisplay) {
|
|
WriteStringToFd("No active display is found.\n", fd);
|
|
} else {
|
|
WriteStringToFd(pDisplay->toString(kSingleIndent), fd);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Enumerator::cmdConfigureEmulatedCamera(int fd, const hidl_vec<hidl_string>& options) {
|
|
if (options.size() < 6) {
|
|
WriteStringToFd(StringPrintf("Necessary arguments are missing.\n"), fd);
|
|
cmdHelp(fd);
|
|
return;
|
|
}
|
|
|
|
// --configure-emulated-camera [id] [path] [width] [height] [interval]
|
|
const std::string id = options[1];
|
|
if (!std::regex_match(id.c_str(), kEmulatedCameraNamePattern)) {
|
|
WriteStringToFd(StringPrintf("%s does not match to the pattern.\n", id.c_str()), fd);
|
|
return;
|
|
}
|
|
|
|
if (mCameraDevices.find(id) != mCameraDevices.end()) {
|
|
WriteStringToFd(StringPrintf("Updating %s's configuration. "
|
|
"This will get effective when currently active stream is "
|
|
"closed.\n",
|
|
id.c_str()),
|
|
fd);
|
|
}
|
|
|
|
std::string sourceDir = options[2];
|
|
int width = std::stoi(options[3]);
|
|
int height = std::stoi(options[4]);
|
|
std::chrono::nanoseconds interval = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
std::chrono::milliseconds(std::stoi(options[5])));
|
|
WriteStringToFd(StringPrintf("Configuring %s as:\n"
|
|
"\tResolution: %dx%d\n"
|
|
"\tInterval: %f ms\n",
|
|
id.c_str(), width, height, interval.count() / 1000000.),
|
|
fd);
|
|
|
|
EmulatedCameraDesc desc = {width, height, sourceDir, interval};
|
|
mEmulatedCameraDevices.insert_or_assign(id, std::move(desc));
|
|
}
|
|
|
|
} // namespace android::automotive::evs::V1_1::implementation
|