blob: f81299d6e1a968d37b95f605c5ba03df85c854a7 [file] [log] [blame]
/*
* 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