| /* |
| * Copyright 2016 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "hal_adapter/camera_hal_adapter.h" |
| |
| #include <algorithm> |
| #include <iomanip> |
| #include <set> |
| #include <string> |
| #include <tuple> |
| #include <unordered_map> |
| #include <utility> |
| |
| #include <base/bind.h> |
| #include <base/callback_helpers.h> |
| #include <base/check.h> |
| #include <base/check_op.h> |
| #include <base/containers/contains.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/notreached.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/threading/thread_task_runner_handle.h> |
| #include <camera/camera_metadata.h> |
| #include <system/camera_metadata_hidden.h> |
| |
| #include "common/stream_manipulator.h" |
| #include "common/utils/cros_camera_mojo_utils.h" |
| #include "cros-camera/camera_metrics.h" |
| #include "cros-camera/common.h" |
| #include "cros-camera/constants.h" |
| #include "cros-camera/future.h" |
| #include "hal_adapter/camera_device_adapter.h" |
| #include "hal_adapter/camera_module_callbacks_associated_delegate.h" |
| #include "hal_adapter/camera_module_delegate.h" |
| #include "hal_adapter/camera_trace_event.h" |
| #include "hal_adapter/vendor_tag_ops_delegate.h" |
| |
| namespace cros { |
| |
| // Defined in features/zsl/zsl_helper.cc. We use forward declaration instead |
| // including the header file to avoid having direct dependency on files under |
| // features/zsl/. |
| // |
| // TODO(jcliang): See if we can have a hook in StreamManipulator for the static |
| // metadata configuration. |
| bool TryAddEnableZslKey(android::CameraMetadata* metadata); |
| |
| namespace { |
| |
| // A special id used in ResetModuleDelegateOnThread and |
| // ResetCallbacksDelegateOnThread to specify all the entries present in the |
| // |module_delegates_| and |callbacks_delegates_| maps. |
| const uint32_t kIdAll = 0xFFFFFFFF; |
| |
| constexpr char kArcvmVendorTagSectionName[] = "com.google.arcvm"; |
| constexpr char kArcvmVendorTagHostTimeTagName[] = "hostSensorTimestamp"; |
| constexpr uint32_t kArcvmVendorTagHostTime = kArcvmVendorTagStart; |
| |
| } // namespace |
| |
| CameraHalAdapter::CameraHalAdapter( |
| std::vector<std::pair<camera_module_t*, cros_camera_hal_t*>> |
| camera_interfaces, |
| CameraMojoChannelManagerToken* token, |
| CameraActivityCallback activity_callback) |
| : camera_interfaces_(camera_interfaces), |
| main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| camera_module_thread_("CameraModuleThread"), |
| camera_module_callbacks_thread_("CameraModuleCallbacksThread"), |
| module_id_(0), |
| callbacks_id_(0), |
| vendor_tag_ops_id_(0), |
| camera_metrics_(CameraMetrics::New()), |
| mojo_manager_token_(token), |
| activity_callback_(activity_callback) { |
| VLOGF_ENTER(); |
| } |
| |
| CameraHalAdapter::~CameraHalAdapter() { |
| VLOGF_ENTER(); |
| camera_module_thread_.task_runner()->PostTask( |
| FROM_HERE, base::Bind(&CameraHalAdapter::ResetModuleDelegateOnThread, |
| base::Unretained(this), kIdAll)); |
| camera_module_callbacks_thread_.task_runner()->PostTask( |
| FROM_HERE, base::Bind(&CameraHalAdapter::ResetCallbacksDelegateOnThread, |
| base::Unretained(this), kIdAll)); |
| camera_module_thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&CameraHalAdapter::ResetVendorTagOpsDelegateOnThread, |
| base::Unretained(this), kIdAll)); |
| camera_module_thread_.Stop(); |
| camera_module_callbacks_thread_.Stop(); |
| set_camera_metadata_vendor_ops(nullptr); |
| } |
| |
| bool CameraHalAdapter::Start() { |
| VLOGF_ENTER(); |
| TRACE_CAMERA_INSTANT(); |
| |
| if (!camera_module_thread_.Start()) { |
| LOGF(ERROR) << "Failed to start CameraModuleThread"; |
| return false; |
| } |
| if (!camera_module_callbacks_thread_.Start()) { |
| LOGF(ERROR) << "Failed to start CameraCallbacksThread"; |
| return false; |
| } |
| |
| auto future = cros::Future<bool>::Create(nullptr); |
| camera_module_thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&CameraHalAdapter::StartOnThread, base::Unretained(this), |
| cros::GetFutureCallback(future))); |
| return future->Get(); |
| } |
| |
| void CameraHalAdapter::OpenCameraHal( |
| mojo::PendingReceiver<mojom::CameraModule> camera_module_receiver, |
| mojom::CameraClientType camera_client_type) { |
| VLOGF_ENTER(); |
| TRACE_CAMERA_SCOPED(); |
| auto module_delegate = std::make_unique<CameraModuleDelegate>( |
| this, camera_module_thread_.task_runner(), camera_client_type); |
| uint32_t module_id = module_id_++; |
| module_delegate->Bind( |
| std::move(camera_module_receiver), |
| base::Bind(&CameraHalAdapter::ResetModuleDelegateOnThread, |
| base::Unretained(this), module_id)); |
| base::AutoLock l(module_delegates_lock_); |
| module_delegates_[module_id] = std::move(module_delegate); |
| VLOGF(1) << "CameraModule " << module_id << " connected"; |
| } |
| |
| // Callback interface for camera_module_t APIs. |
| |
| int32_t CameraHalAdapter::OpenDevice( |
| int32_t camera_id, |
| mojo::PendingReceiver<mojom::Camera3DeviceOps> device_ops_receiver, |
| mojom::CameraClientType camera_client_type) { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| TRACE_CAMERA_SCOPED("camera_id", camera_id); |
| |
| session_timer_map_.emplace(std::piecewise_construct, |
| std::forward_as_tuple(camera_id), |
| std::forward_as_tuple()); |
| |
| camera_module_t* camera_module; |
| int internal_camera_id; |
| std::tie(camera_module, internal_camera_id) = |
| GetInternalModuleAndId(camera_id); |
| |
| LOGF(INFO) << camera_client_type << ", camera_id = " << camera_id |
| << ", camera_module = " << camera_module->common.name |
| << ", internal_camera_id = " << internal_camera_id; |
| |
| if (!camera_module) { |
| return -EINVAL; |
| } |
| |
| if (device_adapters_.find(camera_id) != device_adapters_.end()) { |
| LOGF(WARNING) << "Multiple calls to OpenDevice on device " << camera_id; |
| if (device_adapters_[camera_id]->IsRequestOrResultStalling()) { |
| LOGF(WARNING) << "The camera HAL probably hung. Restart camera service " |
| "to recover from bad state (b/155830039)."; |
| main_task_runner_->PostTask(FROM_HERE, base::BindOnce([]() { |
| // Exit directly without attempting anything |
| // else to shutdown cleanly since the HAL |
| // thread may be wedged already. |
| exit(ENODEV); |
| })); |
| return -ENODEV; |
| } |
| return -EBUSY; |
| } |
| |
| int module_id = camera_id_map_[camera_id].first; |
| cros_camera_hal_t* cros_camera_hal = camera_interfaces_[module_id].second; |
| hw_module_t* common = &camera_module->common; |
| camera3_device_t* camera_device; |
| int ret; |
| if (cros_camera_hal && cros_camera_hal->camera_device_open_ext) { |
| ret = cros_camera_hal->camera_device_open_ext( |
| common, std::to_string(internal_camera_id).c_str(), |
| reinterpret_cast<hw_device_t**>(&camera_device), |
| static_cast<ClientType>(camera_client_type)); |
| } else { |
| ret = common->methods->open( |
| common, std::to_string(internal_camera_id).c_str(), |
| reinterpret_cast<hw_device_t**>(&camera_device)); |
| } |
| if (ret != 0) { |
| LOGF(ERROR) << "Failed to open camera device " << camera_id; |
| return ret; |
| } |
| activity_callback_.Run(camera_id, /*opened=*/true, camera_client_type); |
| |
| camera_info_t info; |
| if (cros_camera_hal && cros_camera_hal->get_camera_info_ext) { |
| ret = cros_camera_hal->get_camera_info_ext( |
| internal_camera_id, &info, static_cast<ClientType>(camera_client_type)); |
| } else { |
| ret = camera_module->get_camera_info(internal_camera_id, &info); |
| } |
| if (ret != 0) { |
| LOGF(ERROR) << "Failed to get camera info of camera " << camera_id; |
| return ret; |
| } |
| const camera_metadata_t* metadata = GetUpdatedCameraMetadata( |
| camera_id, camera_client_type, info.static_camera_characteristics); |
| base::RepeatingCallback<int(int)> get_internal_camera_id_callback = |
| base::Bind(&CameraHalAdapter::GetInternalId, base::Unretained(this)); |
| base::RepeatingCallback<int(int)> get_public_camera_id_callback = base::Bind( |
| &CameraHalAdapter::GetPublicId, base::Unretained(this), module_id); |
| // This method is called by |camera_module_delegate_| on its mojo IPC |
| // handler thread. |
| // The CameraHalAdapter (and hence |camera_module_delegate_|) must out-live |
| // the CameraDeviceAdapters, so it's safe to keep a reference to the task |
| // runner of the current thread in the callback functor. |
| base::Callback<void()> close_callback = base::Bind( |
| &CameraHalAdapter::CloseDeviceCallback, base::Unretained(this), |
| base::ThreadTaskRunnerHandle::Get(), camera_id, camera_client_type); |
| StreamManipulator::Options options = { |
| .camera_module_name = camera_module->common.name, |
| .enable_cros_zsl = base::Contains(can_attempt_zsl_camera_ids_, camera_id), |
| }; |
| device_adapters_[camera_id] = std::make_unique<CameraDeviceAdapter>( |
| camera_device, info.device_version, metadata, |
| std::move(get_internal_camera_id_callback), |
| std::move(get_public_camera_id_callback), close_callback, |
| StreamManipulator::GetEnabledStreamManipulators(std::move(options))); |
| |
| CameraDeviceAdapter::HasReprocessEffectVendorTagCallback |
| has_reprocess_effect_vendor_tag_callback = |
| base::Bind(&ReprocessEffectManager::HasReprocessEffectVendorTag, |
| base::Unretained(&reprocess_effect_manager_)); |
| CameraDeviceAdapter::ReprocessEffectCallback reprocess_effect_callback = |
| base::Bind(&ReprocessEffectManager::ReprocessRequest, |
| base::Unretained(&reprocess_effect_manager_)); |
| if (!device_adapters_[camera_id]->Start( |
| std::move(has_reprocess_effect_vendor_tag_callback), |
| std::move(reprocess_effect_callback))) { |
| device_adapters_.erase(camera_id); |
| return -ENODEV; |
| } |
| device_adapters_.at(camera_id)->Bind(std::move(device_ops_receiver)); |
| camera_metrics_->SendCameraFacing(info.facing); |
| camera_metrics_->SendOpenDeviceClientType( |
| static_cast<int>(camera_client_type)); |
| camera_metrics_->SendOpenDeviceLatency( |
| session_timer_map_[camera_id].Elapsed()); |
| |
| return 0; |
| } |
| |
| int32_t CameraHalAdapter::GetNumberOfCameras() { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| TRACE_CAMERA_SCOPED(); |
| return num_builtin_cameras_; |
| } |
| |
| int32_t CameraHalAdapter::GetCameraInfo( |
| int32_t camera_id, |
| mojom::CameraInfoPtr* camera_info, |
| mojom::CameraClientType camera_client_type) { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| TRACE_CAMERA_SCOPED("camera_id", camera_id); |
| |
| camera_module_t* camera_module; |
| int internal_camera_id; |
| std::tie(camera_module, internal_camera_id) = |
| GetInternalModuleAndId(camera_id); |
| |
| if (!camera_module) { |
| camera_info->reset(); |
| return -EINVAL; |
| } |
| int ret; |
| int module_id = camera_id_map_[camera_id].first; |
| camera_info_t info; |
| cros_camera_hal_t* cros_camera_hal = camera_interfaces_[module_id].second; |
| if (cros_camera_hal && cros_camera_hal->get_camera_info_ext) { |
| ret = cros_camera_hal->get_camera_info_ext( |
| internal_camera_id, &info, static_cast<ClientType>(camera_client_type)); |
| } else { |
| ret = camera_module->get_camera_info(internal_camera_id, &info); |
| } |
| |
| if (ret != 0) { |
| LOGF(ERROR) << "Failed to get info of camera " << camera_id; |
| camera_info->reset(); |
| return ret; |
| } |
| |
| LOGF(INFO) << camera_client_type << " camera_id = " << camera_id |
| << ", facing = " << info.facing; |
| |
| const camera_metadata_t* metadata = GetUpdatedCameraMetadata( |
| camera_id, camera_client_type, info.static_camera_characteristics); |
| |
| if (VLOG_IS_ON(2)) { |
| dump_camera_metadata(metadata, 2, 3); |
| } |
| |
| mojom::CameraInfoPtr info_ptr = mojom::CameraInfo::New(); |
| info_ptr->facing = static_cast<mojom::CameraFacing>(info.facing); |
| info_ptr->orientation = info.orientation; |
| info_ptr->device_version = info.device_version; |
| info_ptr->static_camera_characteristics = |
| internal::SerializeCameraMetadata(metadata); |
| info_ptr->resource_cost = mojom::CameraResourceCost::New(); |
| info_ptr->resource_cost->resource_cost = info.resource_cost; |
| |
| std::vector<std::string> conflicting_devices; |
| for (size_t i = 0; i < info.conflicting_devices_length; i++) { |
| int conflicting_id = |
| GetPublicId(module_id, atoi(info.conflicting_devices[i])); |
| conflicting_devices.push_back(std::to_string(conflicting_id)); |
| } |
| info_ptr->conflicting_devices = std::move(conflicting_devices); |
| |
| *camera_info = std::move(info_ptr); |
| return 0; |
| } |
| |
| int32_t CameraHalAdapter::SetTorchMode(int32_t camera_id, bool enabled) { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| TRACE_CAMERA_SCOPED(); |
| |
| camera_module_t* camera_module; |
| int internal_camera_id; |
| std::tie(camera_module, internal_camera_id) = |
| GetInternalModuleAndId(camera_id); |
| |
| if (!camera_module) { |
| return -EINVAL; |
| } |
| |
| if (auto fn = camera_module->set_torch_mode) { |
| return fn(std::to_string(internal_camera_id).c_str(), enabled); |
| } |
| |
| return -ENOSYS; |
| } |
| |
| int32_t CameraHalAdapter::Init() { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| TRACE_CAMERA_SCOPED(); |
| return 0; |
| } |
| |
| void CameraHalAdapter::GetVendorTagOps( |
| mojo::PendingReceiver<mojom::VendorTagOps> vendor_tag_ops_receiver) { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| |
| auto vendor_tag_ops_delegate = std::make_unique<VendorTagOpsDelegate>( |
| camera_module_thread_.task_runner(), &vendor_tag_manager_); |
| uint32_t vendor_tag_ops_id = vendor_tag_ops_id_++; |
| vendor_tag_ops_delegate->Bind( |
| std::move(vendor_tag_ops_receiver), |
| base::Bind(&CameraHalAdapter::ResetVendorTagOpsDelegateOnThread, |
| base::Unretained(this), vendor_tag_ops_id)); |
| vendor_tag_ops_delegates_[vendor_tag_ops_id] = |
| std::move(vendor_tag_ops_delegate); |
| VLOGF(1) << "VendorTagOps " << vendor_tag_ops_id << " connected"; |
| } |
| |
| void CameraHalAdapter::CloseDeviceCallback( |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| int32_t camera_id, |
| mojom::CameraClientType camera_client_type) { |
| task_runner->PostTask(FROM_HERE, base::Bind(&CameraHalAdapter::CloseDevice, |
| base::Unretained(this), camera_id, |
| camera_client_type)); |
| } |
| |
| // static |
| void CameraHalAdapter::camera_device_status_change( |
| const camera_module_callbacks_t* callbacks, |
| int internal_camera_id, |
| int new_status) { |
| VLOGF_ENTER(); |
| TRACE_CAMERA_SCOPED(); |
| |
| auto* aux = static_cast<const CameraModuleCallbacksAux*>(callbacks); |
| CameraHalAdapter* self = aux->adapter; |
| self->camera_module_thread_.task_runner()->PostTask( |
| FROM_HERE, base::Bind(&CameraHalAdapter::CameraDeviceStatusChange, |
| base::Unretained(self), aux, internal_camera_id, |
| static_cast<camera_device_status_t>(new_status))); |
| } |
| |
| // static |
| void CameraHalAdapter::torch_mode_status_change( |
| const camera_module_callbacks_t* callbacks, |
| const char* internal_camera_id, |
| int new_status) { |
| VLOGF_ENTER(); |
| TRACE_CAMERA_SCOPED(); |
| |
| auto* aux = static_cast<const CameraModuleCallbacksAux*>(callbacks); |
| CameraHalAdapter* self = aux->adapter; |
| self->camera_module_thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&CameraHalAdapter::TorchModeStatusChange, |
| base::Unretained(self), aux, atoi(internal_camera_id), |
| static_cast<torch_mode_status_t>(new_status))); |
| } |
| |
| const camera_metadata_t* CameraHalAdapter::GetUpdatedCameraMetadata( |
| int camera_id, |
| mojom::CameraClientType camera_client_type, |
| const camera_metadata_t* static_metadata) { |
| auto& metadata_scoped = |
| static_metadata_map_[std::make_pair(camera_id, camera_client_type)]; |
| if (metadata_scoped) { |
| return metadata_scoped.get()->getAndLock(); |
| } |
| |
| metadata_scoped = std::make_unique<android::CameraMetadata>(); |
| auto* metadata = metadata_scoped.get(); |
| metadata->acquire(clone_camera_metadata(static_metadata)); |
| reprocess_effect_manager_.UpdateStaticMetadata(metadata); |
| if (TryAddEnableZslKey(metadata)) { |
| LOGF(INFO) << "Will attempt to enable ZSL by private reprocessing"; |
| can_attempt_zsl_camera_ids_.insert(camera_id); |
| } |
| if (metadata->exists(ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS)) { |
| auto it = physical_camera_id_map_.find(camera_id); |
| if (it == physical_camera_id_map_.end()) { |
| LOGF(ERROR) << "Failed to find the physical camera IDs for camera " |
| << camera_id; |
| } else { |
| if (metadata->update(ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS, |
| it->second) != 0) { |
| LOGF(ERROR) |
| << "Failed to remap ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS"; |
| } |
| } |
| } |
| return metadata->getAndLock(); |
| } |
| |
| void CameraHalAdapter::CameraDeviceStatusChange( |
| const CameraModuleCallbacksAux* aux, |
| int internal_camera_id, |
| camera_device_status_t new_status) { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| TRACE_CAMERA_SCOPED(); |
| |
| int public_camera_id = GetPublicId(aux->module_id, internal_camera_id); |
| |
| LOGF(INFO) << "module_id = " << aux->module_id |
| << ", internal_camera_id = " << internal_camera_id |
| << ", new_status = " << new_status; |
| |
| switch (new_status) { |
| case CAMERA_DEVICE_STATUS_PRESENT: |
| if (public_camera_id == -1) { |
| public_camera_id = next_external_camera_id_++; |
| camera_id_map_[public_camera_id] = |
| std::make_pair(aux->module_id, internal_camera_id); |
| camera_id_inverse_map_[aux->module_id][internal_camera_id] = |
| public_camera_id; |
| device_status_map_[public_camera_id] = CAMERA_DEVICE_STATUS_PRESENT; |
| default_device_status_map_[public_camera_id] = |
| CAMERA_DEVICE_STATUS_NOT_PRESENT; |
| torch_mode_status_map_[public_camera_id] = |
| TORCH_MODE_STATUS_NOT_AVAILABLE; |
| default_torch_mode_status_map_[public_camera_id] = |
| TORCH_MODE_STATUS_NOT_AVAILABLE; |
| } else { |
| device_status_map_[public_camera_id] = CAMERA_DEVICE_STATUS_PRESENT; |
| } |
| LOGF(INFO) << "External camera plugged, public_camera_id = " |
| << public_camera_id; |
| break; |
| case CAMERA_DEVICE_STATUS_NOT_PRESENT: |
| if (public_camera_id != -1) { |
| device_status_map_[public_camera_id] = CAMERA_DEVICE_STATUS_NOT_PRESENT; |
| torch_mode_status_map_[public_camera_id] = |
| default_torch_mode_status_map_[public_camera_id]; |
| auto it = device_adapters_.find(public_camera_id); |
| if (it != device_adapters_.end()) { |
| device_adapters_.erase(it); |
| } |
| LOGF(INFO) << "External camera unplugged" |
| << ", public_camera_id = " << public_camera_id; |
| } else { |
| LOGF(WARNING) << "Ignore nonexistent camera"; |
| } |
| break; |
| default: |
| // TODO(shik): What about CAMERA_DEVICE_STATUS_ENUMERATING? |
| NOTREACHED() << "Unexpected new status " << new_status; |
| break; |
| } |
| |
| base::AutoLock l(callbacks_delegates_lock_); |
| for (auto& it : callbacks_delegates_) { |
| NotifyCameraDeviceStatusChange(it.second.get(), public_camera_id, |
| new_status); |
| } |
| } |
| |
| void CameraHalAdapter::TorchModeStatusChange( |
| const CameraModuleCallbacksAux* aux, |
| int internal_camera_id, |
| torch_mode_status_t new_status) { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| TRACE_CAMERA_SCOPED(); |
| |
| int camera_id = GetPublicId(aux->module_id, internal_camera_id); |
| if (camera_id == -1) { |
| LOGF(WARNING) << "Ignore nonexistent camera" |
| << ", module_id = " << aux->module_id |
| << ", camera_id = " << internal_camera_id; |
| return; |
| } |
| |
| torch_mode_status_map_[camera_id] = new_status; |
| |
| base::AutoLock l(callbacks_delegates_lock_); |
| for (auto& it : callbacks_delegates_) { |
| NotifyTorchModeStatusChange(it.second.get(), camera_id, new_status); |
| } |
| } |
| |
| void CameraHalAdapter::StartOnThread(base::Callback<void(bool)> callback) { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| |
| if (reprocess_effect_manager_.Initialize(mojo_manager_token_) != 0) { |
| LOGF(ERROR) << "Failed to initialize reprocess effect manager"; |
| callback.Run(false); |
| return; |
| } |
| |
| if (!vendor_tag_manager_.Add(kArcvmVendorTagHostTime, |
| kArcvmVendorTagSectionName, |
| kArcvmVendorTagHostTimeTagName, TYPE_INT64)) { |
| LOGF(ERROR) |
| << "Failed to add the vendor tag for ARCVM timestamp synchronization"; |
| callback.Run(false); |
| return; |
| } |
| |
| if (!vendor_tag_manager_.Add(&reprocess_effect_manager_)) { |
| LOGF(ERROR) << "Failed to add the vendor tags of reprocess effect manager"; |
| callback.Run(false); |
| return; |
| } |
| |
| // The setup sequence for each camera HAL: |
| // 1. get_vendor_tag_ops() |
| // 2. init() |
| // 3. get_number_of_cameras() |
| // 4. set_callbacks() |
| // 5. get_camera_info() |
| // |
| // Normally, init() is the first invoked method in the sequence. But init() |
| // might manipulate vendor tags with libcamera_metadata, which requires |
| // set_camera_metadata_vendor_ops() to be invoked already. To prepare the |
| // aggregated |vendor_tag_ops| for set_camera_metadata_vendor_ops(), we need |
| // to collect |vendor_tag_ops| from all camera modules by calling |
| // get_vendor_tag_ops() first, which should be fine as it just set some |
| // function pointers in the struct. |
| // |
| // Note that camera HALs MAY run callbacks before set_callbacks() returns. |
| |
| for (const auto& interface : camera_interfaces_) { |
| camera_module_t* m = interface.first; |
| if (m->get_vendor_tag_ops) { |
| vendor_tag_ops ops = {}; |
| m->get_vendor_tag_ops(&ops); |
| if (ops.get_tag_count == nullptr) { |
| continue; |
| } |
| if (!vendor_tag_manager_.Add(&ops)) { |
| LOGF(ERROR) << "Failed to add the vendor tags of camera module " |
| << std::quoted(m->common.name); |
| callback.Run(false); |
| return; |
| } |
| } |
| } |
| |
| if (set_camera_metadata_vendor_ops(&vendor_tag_manager_) != 0) { |
| LOGF(ERROR) << "Failed to set vendor ops to camera metadata"; |
| } |
| |
| const bool force_start = |
| base::PathExists(base::FilePath(constants::kForceStartCrosCameraPath)); |
| for (auto iter = camera_interfaces_.begin(); |
| iter != camera_interfaces_.end();) { |
| camera_module_t* m = iter->first; |
| if (m->init) { |
| int ret = m->init(); |
| if (ret != 0) { |
| if (force_start) { |
| LOGF(WARNING) << "Disabled camera module " |
| << std::quoted(m->common.name) |
| << " to force start cros-camera"; |
| iter = camera_interfaces_.erase(iter); |
| continue; |
| } |
| LOGF(ERROR) << "Failed to init camera module " |
| << std::quoted(m->common.name); |
| callback.Run(false); |
| return; |
| } |
| } |
| iter++; |
| } |
| CHECK_GT(camera_interfaces_.size(), 0); |
| |
| std::vector<std::tuple<int, int, int>> cameras; |
| std::vector<std::vector<bool>> has_flash_unit(camera_interfaces_.size()); |
| // TODO(b/188111097): The mapping mechanism below is really complicated. |
| // Extract the logic to a dedicated class and add unittest coverage. |
| std::set<std::tuple<int, int, int>> unexposed_physical_cameras; |
| std::vector<std::map<int, std::vector<int>>> internal_physical_camera_id_map( |
| camera_interfaces_.size()); |
| |
| camera_id_inverse_map_.resize(camera_interfaces_.size()); |
| for (size_t module_id = 0; module_id < camera_interfaces_.size(); |
| module_id++) { |
| camera_module_t* m = camera_interfaces_[module_id].first; |
| |
| int n = m->get_number_of_cameras(); |
| LOGF(INFO) << "Camera module " << std::quoted(m->common.name) << " has " |
| << n << " built-in camera(s)"; |
| |
| auto aux = std::make_unique<CameraModuleCallbacksAux>(); |
| aux->camera_device_status_change = camera_device_status_change; |
| aux->torch_mode_status_change = torch_mode_status_change; |
| aux->module_id = module_id; |
| aux->adapter = this; |
| if (m->set_callbacks(aux.get()) != 0) { |
| LOGF(ERROR) << "Failed to set_callbacks on camera module " << module_id; |
| callback.Run(false); |
| return; |
| } |
| callbacks_auxs_.push_back(std::move(aux)); |
| |
| for (int camera_id = 0; camera_id < n; camera_id++) { |
| camera_info_t info; |
| if (m->get_camera_info(camera_id, &info) != 0) { |
| LOGF(ERROR) << "Failed to get info of camera " << camera_id |
| << " from module " << module_id; |
| callback.Run(false); |
| return; |
| } |
| |
| camera_metadata_ro_entry_t entry; |
| if (find_camera_metadata_ro_entry(info.static_camera_characteristics, |
| ANDROID_FLASH_INFO_AVAILABLE, |
| &entry) != 0) { |
| LOGF(ERROR) << "Failed to get flash info in metadata of camera " |
| << camera_id << " from module " << module_id; |
| callback.Run(false); |
| return; |
| } |
| |
| cameras.emplace_back(info.facing, static_cast<int>(module_id), camera_id); |
| has_flash_unit[module_id].push_back(entry.data.u8[0] == |
| ANDROID_FLASH_INFO_AVAILABLE_TRUE); |
| |
| // Determine if this is a logical multi-camera and create mappings for |
| // its physical camera IDs if true. |
| if (find_camera_metadata_ro_entry(info.static_camera_characteristics, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES, |
| &entry) != 0) { |
| LOGF(ERROR) << "Failed to find ANDROID_REQUEST_AVAILABLE_CAPABILITIES " |
| "from camera " |
| << camera_id << " from module " << module_id; |
| callback.Run(false); |
| return; |
| } |
| if (std::find( |
| entry.data.u8, entry.data.u8 + entry.count, |
| ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) == |
| entry.data.u8 + entry.count) { |
| // Not a logical multi-camera. |
| continue; |
| } |
| // Find all the physical camera IDs backing this logical multi-camera. |
| if (find_camera_metadata_ro_entry( |
| info.static_camera_characteristics, |
| ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS, &entry) != 0) { |
| LOGF(ERROR) |
| << "Failed to get the list of physical camera IDs for camera " |
| << camera_id; |
| callback.Run(false); |
| return; |
| } |
| auto& physical_camera_ids = |
| internal_physical_camera_id_map[module_id][camera_id]; |
| int start = 0; |
| for (int i = 0; i < entry.count; ++i) { |
| if (entry.data.u8[i] == '\0') { |
| if (start != i) { |
| int physical_camera_id; |
| const char* physical_camera_id_str = |
| reinterpret_cast<const char*>(entry.data.u8) + start; |
| if (!base::StringToInt(physical_camera_id_str, |
| &physical_camera_id)) { |
| LOGF(ERROR) << "Invalid physical camera ID: " |
| << physical_camera_id_str; |
| callback.Run(false); |
| return; |
| } |
| physical_camera_ids.push_back(physical_camera_id); |
| if (physical_camera_id >= n) { // unexposed physical camera |
| unexposed_physical_cameras.emplace( |
| info.facing, static_cast<int>(module_id), physical_camera_id); |
| } |
| } |
| start = i + 1; |
| } |
| } |
| } |
| } |
| |
| num_builtin_cameras_ = cameras.size(); |
| sort(cameras.begin(), cameras.end()); |
| // Ordering is important here. Unexposed physical camera IDs should be >= |n| |
| // where |n| is the number of builtin cameras. |
| cameras.insert(cameras.end(), unexposed_physical_cameras.begin(), |
| unexposed_physical_cameras.end()); |
| for (size_t i = 0; i < cameras.size(); i++) { |
| int module_id = std::get<1>(cameras[i]); |
| int camera_id = std::get<2>(cameras[i]); |
| camera_id_map_[i] = std::make_pair(module_id, camera_id); |
| camera_id_inverse_map_[module_id][camera_id] = i; |
| device_status_map_[i] = CAMERA_DEVICE_STATUS_PRESENT; |
| default_device_status_map_[i] = device_status_map_[i]; |
| torch_mode_status_map_[i] = has_flash_unit[module_id][camera_id] |
| ? TORCH_MODE_STATUS_AVAILABLE_OFF |
| : TORCH_MODE_STATUS_NOT_AVAILABLE; |
| default_torch_mode_status_map_[i] = torch_mode_status_map_[i]; |
| } |
| |
| // Now we map internal physical camera IDs to public and store the mappings |
| // in |physical_camera_id_map_|. |
| for (int module_id = 0; module_id < internal_physical_camera_id_map.size(); |
| ++module_id) { |
| const auto& module_physical_camera_id_map = |
| internal_physical_camera_id_map[module_id]; |
| for (const auto& [internal_camera_id, internal_physical_camera_ids] : |
| module_physical_camera_id_map) { |
| int camera_id = camera_id_inverse_map_[module_id][internal_camera_id]; |
| auto& physical_camera_ids = physical_camera_id_map_[camera_id]; |
| for (int i = 0; i < internal_physical_camera_ids.size(); ++i) { |
| std::string physical_camera_id = base::NumberToString( |
| camera_id_inverse_map_[module_id][internal_physical_camera_ids[i]]); |
| if (i > 0) { |
| physical_camera_ids += '\0'; |
| } |
| physical_camera_ids += physical_camera_id; |
| } |
| } |
| } |
| |
| next_external_camera_id_ = cameras.size(); |
| |
| LOGF(INFO) << "SuperHAL started with " << camera_interfaces_.size() |
| << " modules, " << num_builtin_cameras_ << " built-in cameras" |
| << " and " << unexposed_physical_cameras.size() |
| << " unexposed physical cameras"; |
| |
| callback.Run(true); |
| } |
| |
| void CameraHalAdapter::NotifyCameraDeviceStatusChange( |
| CameraModuleCallbacksAssociatedDelegate* delegate, |
| int camera_id, |
| camera_device_status_t status) { |
| delegate->CameraDeviceStatusChange(camera_id, status); |
| } |
| |
| void CameraHalAdapter::NotifyTorchModeStatusChange( |
| CameraModuleCallbacksAssociatedDelegate* delegate, |
| int camera_id, |
| torch_mode_status_t status) { |
| delegate->TorchModeStatusChange(camera_id, status); |
| } |
| |
| std::pair<camera_module_t*, int> CameraHalAdapter::GetInternalModuleAndId( |
| int camera_id) { |
| if (camera_id_map_.find(camera_id) == camera_id_map_.end()) { |
| LOGF(ERROR) << "Invalid camera id: " << camera_id; |
| return {}; |
| } |
| std::pair<int, int> idx = camera_id_map_[camera_id]; |
| return {camera_interfaces_[idx.first].first, idx.second}; |
| } |
| |
| int CameraHalAdapter::GetInternalId(int camera_id) { |
| camera_module_t* camera_module; |
| int internal_camera_id; |
| std::tie(camera_module, internal_camera_id) = |
| GetInternalModuleAndId(camera_id); |
| return internal_camera_id; |
| } |
| |
| int CameraHalAdapter::GetPublicId(int module_id, int camera_id) { |
| if (module_id < 0 || |
| static_cast<size_t>(module_id) >= camera_id_inverse_map_.size()) { |
| return -1; |
| } |
| |
| std::map<int, int>& id_map = camera_id_inverse_map_[module_id]; |
| auto it = id_map.find(camera_id); |
| return it != id_map.end() ? it->second : -1; |
| } |
| |
| void CameraHalAdapter::CloseDevice(int32_t camera_id, |
| mojom::CameraClientType camera_client_type) { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| TRACE_CAMERA_SCOPED("camera_id", camera_id); |
| LOGF(INFO) << camera_client_type << ", camera_id = " << camera_id; |
| if (device_adapters_.find(camera_id) == device_adapters_.end()) { |
| LOGF(ERROR) << "Failed to close camera device " << camera_id |
| << ": device is not opened"; |
| return; |
| } |
| device_adapters_.erase(camera_id); |
| |
| activity_callback_.Run(camera_id, /*opened=*/false, camera_client_type); |
| |
| camera_metrics_->SendSessionDuration(session_timer_map_[camera_id].Elapsed()); |
| session_timer_map_.erase(camera_id); |
| } |
| |
| void CameraHalAdapter::ResetModuleDelegateOnThread(uint32_t module_id) { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| base::AutoLock l(module_delegates_lock_); |
| if (module_id == kIdAll) { |
| module_delegates_.clear(); |
| } else { |
| module_delegates_.erase(module_id); |
| } |
| } |
| |
| void CameraHalAdapter::ResetCallbacksDelegateOnThread(uint32_t callbacks_id) { |
| VLOGF_ENTER(); |
| DCHECK( |
| camera_module_callbacks_thread_.task_runner()->BelongsToCurrentThread()); |
| base::AutoLock l(callbacks_delegates_lock_); |
| if (callbacks_id == kIdAll) { |
| callbacks_delegates_.clear(); |
| } else { |
| callbacks_delegates_.erase(callbacks_id); |
| } |
| } |
| |
| void CameraHalAdapter::ResetVendorTagOpsDelegateOnThread( |
| uint32_t vendor_tag_ops_id) { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| base::AutoLock l(module_delegates_lock_); |
| if (vendor_tag_ops_id == kIdAll) { |
| vendor_tag_ops_delegates_.clear(); |
| } else { |
| vendor_tag_ops_delegates_.erase(vendor_tag_ops_id); |
| } |
| } |
| |
| int32_t CameraHalAdapter::SetCallbacks( |
| mojo::PendingAssociatedRemote<mojom::CameraModuleCallbacks> callbacks) { |
| VLOGF_ENTER(); |
| DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread()); |
| TRACE_CAMERA_SCOPED(); |
| |
| auto callbacks_delegate = |
| std::make_unique<CameraModuleCallbacksAssociatedDelegate>( |
| camera_module_callbacks_thread_.task_runner()); |
| uint32_t callbacks_id = callbacks_id_++; |
| callbacks_delegate->Bind( |
| std::move(callbacks), |
| base::Bind(&CameraHalAdapter::ResetCallbacksDelegateOnThread, |
| base::Unretained(this), callbacks_id)); |
| |
| // Send latest status to the new client, so all presented external cameras are |
| // available to the client after SetCallbacks() returns. |
| for (const auto& it : device_status_map_) { |
| int camera_id = it.first; |
| camera_device_status_t device_status = it.second; |
| if (device_status != default_device_status_map_[camera_id]) { |
| NotifyCameraDeviceStatusChange(callbacks_delegate.get(), camera_id, |
| device_status); |
| } |
| torch_mode_status_t torch_status = torch_mode_status_map_[camera_id]; |
| if (torch_status != default_torch_mode_status_map_[camera_id]) { |
| NotifyTorchModeStatusChange(callbacks_delegate.get(), camera_id, |
| torch_status); |
| } |
| } |
| |
| base::AutoLock l(callbacks_delegates_lock_); |
| callbacks_delegates_[callbacks_id] = std::move(callbacks_delegate); |
| |
| return 0; |
| } |
| } // namespace cros |