blob: 7150bfc413eaa1fd1d79454a50c67d9c5958701a [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/usb/camera_hal.h"
#include <algorithm>
#include <limits>
#include <utility>
#include <base/bind.h>
#include <base/check.h>
#include <base/check_op.h>
#include <base/files/file_util.h>
#include <base/no_destructor.h>
#include <base/strings/string_util.h>
#include <base/threading/thread_task_runner_handle.h>
#include <chromeos-config/libcros_config/cros_config.h>
#include "cros-camera/common.h"
#include "cros-camera/constants.h"
#include "cros-camera/cros_camera_hal.h"
#include "cros-camera/udev_watcher.h"
#include "cros-camera/utils/camera_config.h"
#include "hal/usb/camera_characteristics.h"
#include "hal/usb/common_types.h"
#include "hal/usb/metadata_handler.h"
#include "hal/usb/quirks.h"
#include "hal/usb/stream_format.h"
#include "hal/usb/v4l2_camera_device.h"
#include "hal/usb/vendor_tag.h"
namespace cros {
namespace {
// https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE
constexpr int64_t kMaxMinExposureTime = 100000; // 100us
bool FillMetadata(const DeviceInfo& device_info,
android::CameraMetadata* static_metadata,
android::CameraMetadata* request_metadata) {
if (MetadataHandler::FillDefaultMetadata(static_metadata, request_metadata) !=
0) {
return false;
}
if (MetadataHandler::FillMetadataFromDeviceInfo(device_info, static_metadata,
request_metadata) != 0) {
return false;
}
SupportedFormats supported_formats =
V4L2CameraDevice::GetDeviceSupportedFormats(device_info.device_path);
SupportedFormats qualified_formats =
GetQualifiedFormats(supported_formats, device_info.quirks);
if (MetadataHandler::FillMetadataFromSupportedFormats(
qualified_formats, device_info, static_metadata, request_metadata) !=
0) {
return false;
}
if (!device_info.usb_vid.empty()) {
static_metadata->update(kVendorTagVendorId, device_info.usb_vid);
}
if (!device_info.usb_pid.empty()) {
static_metadata->update(kVendorTagProductId, device_info.usb_pid);
}
static_metadata->update(kVendorTagDevicePath, device_info.device_path);
static_metadata->update(kVendorTagModelName, V4L2CameraDevice::GetModelName(
device_info.device_path));
return true;
}
void AdjustMetadataForAE(android::CameraMetadata* data) {
camera_metadata_entry_t entry =
data->find(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE);
if (entry.count == 0) {
return;
}
if (entry.data.i64[0] <= kMaxMinExposureTime)
return;
LOGF(INFO) << "Remove AE related metadata";
if (data->erase(ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE) != 0)
LOGF(WARNING) << "Fail to delete ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE";
data->update(ANDROID_CONTROL_AE_AVAILABLE_MODES,
std::vector<uint8_t>{ANDROID_CONTROL_AE_MODE_ON});
// Remove ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE in
// ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS
entry = data->find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
if (entry.count != 0) {
std::vector<int32_t> available_characteristics_keys;
for (size_t i = 0; i < entry.count; i++) {
if (entry.data.i32[i] == ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE) {
continue;
}
available_characteristics_keys.push_back(entry.data.i32[i]);
}
data->update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
available_characteristics_keys);
}
// Remove ANDROID_SENSOR_EXPOSURE_TIME in
// ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS
entry = data->find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
if (entry.count != 0) {
std::vector<int32_t> available_request_keys;
for (size_t i = 0; i < entry.count; i++) {
if (entry.data.i32[i] == ANDROID_SENSOR_EXPOSURE_TIME) {
continue;
}
available_request_keys.push_back(entry.data.i32[i]);
}
data->update(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
available_request_keys);
}
}
// TODO(mojahsu): Make it work on android side.
void AdjustMetadataForFaceDetection(android::CameraMetadata* data) {
camera_metadata_entry_t entry =
data->find(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
// Only support ANDROID_STATISTICS_FACE_DETECT_MODE_OFF or no such metadata.
if (entry.count <= 1) {
return;
}
LOGF(INFO) << "Remove Face Detection related metadata";
data->update(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES,
std::vector<uint8_t>{ANDROID_STATISTICS_FACE_DETECT_MODE_OFF});
data->update(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, std::vector<int32_t>{0});
}
ScopedCameraMetadata StaticMetadataForAndroid(
const android::CameraMetadata& static_metadata) {
android::CameraMetadata data(static_metadata);
std::vector<int32_t> stream_configurations;
std::unique_ptr<CameraConfig> camera_config =
CameraConfig::Create(constants::kCrosCameraConfigPathString);
int max_width =
camera_config->GetInteger(constants::kCrosUsbAndroidMaxStreamWidth,
std::numeric_limits<int>::max());
int max_height =
camera_config->GetInteger(constants::kCrosUsbAndroidMaxStreamHeight,
std::numeric_limits<int>::max());
camera_metadata_entry entry =
data.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
for (size_t i = 0; i < entry.count; i += 4) {
if (entry.data.i32[i] != HAL_PIXEL_FORMAT_BLOB) {
if (entry.data.i32[i + 1] > max_width ||
entry.data.i32[i + 2] > max_height) {
LOGF(INFO) << "Filter Format: 0x" << std::hex << entry.data.i32[i]
<< std::dec << "-" << entry.data.i32[i + 1] << "x"
<< entry.data.i32[i + 2] << " for Android";
continue;
}
}
stream_configurations.push_back(entry.data.i32[i]);
stream_configurations.push_back(entry.data.i32[i + 1]);
stream_configurations.push_back(entry.data.i32[i + 2]);
stream_configurations.push_back(entry.data.i32[i + 3]);
}
data.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
stream_configurations);
AdjustMetadataForAE(&data);
AdjustMetadataForFaceDetection(&data);
return ScopedCameraMetadata(data.release());
}
ScopedCameraMetadata RequestTemplateForAndroid(
const camera_metadata_t* static_metadata,
const android::CameraMetadata& request_template) {
android::CameraMetadata data(request_template);
camera_metadata_ro_entry ae_available_modes_entry;
int ret = find_camera_metadata_ro_entry(static_metadata,
ANDROID_CONTROL_AE_AVAILABLE_MODES,
&ae_available_modes_entry);
if (ret == 0) {
// Only support ANDROID_CONTROL_AE_MODE_ON
if (ae_available_modes_entry.count == 1) {
camera_metadata_entry entry = data.find(ANDROID_SENSOR_EXPOSURE_TIME);
if (entry.count == 1 && data.erase(ANDROID_SENSOR_EXPOSURE_TIME) != 0)
LOGF(WARNING) << "Failed to delete ANDROID_SENSOR_EXPOSURE_TIME";
}
}
return ScopedCameraMetadata(data.release());
}
bool IsVivid(udev_device* dev) {
const char* product = udev_device_get_property_value(dev, "ID_V4L_PRODUCT");
return product && strcmp(product, "vivid") == 0;
}
const char* GetPreferredPath(udev_device* dev) {
if (IsVivid(dev)) {
// Multiple vivid devices may have the same symlink at
// /dev/v4l/by-path/platform-vivid.0-video-index0, so we use /dev/videoX
// directly for vivid.
return udev_device_get_devnode(dev);
}
for (udev_list_entry* entry = udev_device_get_devlinks_list_entry(dev);
entry != nullptr; entry = udev_list_entry_get_next(entry)) {
const char* name = udev_list_entry_get_name(entry);
if (!name) {
LOGF(WARNING) << "udev_list_entry_get_name failed";
continue;
}
// The symlinks in /dev/v4l/by-path/ are generated by
// 60-persistent-v4l.rules, and supposed to be persistent for built-in
// cameras so we can safely reuse it across suspend/resume cycles, without
// updating |path_to_id_| for them.
// TODO(shik): Fix https://github.com/systemd/systemd/issues/10683 in the
// upstream udev.
if (base::StartsWith(name, "/dev/v4l/by-path/",
base::CompareCase::SENSITIVE)) {
return name;
}
}
return udev_device_get_devnode(dev);
}
std::string GetModelId(const DeviceInfo& info) {
if (info.is_vivid) {
return "vivid";
}
return base::JoinString({info.usb_vid, info.usb_pid}, ":");
}
} // namespace
CameraHal::CameraHal()
: task_runner_(nullptr),
udev_watcher_(std::make_unique<UdevWatcher>(this, "video4linux")),
cros_device_config_(CrosDeviceConfig::Create()),
camera_metrics_(CameraMetrics::New()) {
thread_checker_.DetachFromThread();
}
CameraHal::~CameraHal() {
udev_watcher_.reset();
}
int CameraHal::GetNumberOfCameras() const {
return num_builtin_cameras_;
}
CameraHal& CameraHal::GetInstance() {
static base::NoDestructor<CameraHal> camera_hal;
return *camera_hal;
}
CameraMojoChannelManagerToken* CameraHal::GetMojoManagerToken() {
return mojo_manager_token_;
}
int CameraHal::OpenDevice(int id,
const hw_module_t* module,
hw_device_t** hw_device,
ClientType client_type) {
VLOGFID(1, id);
DCHECK(thread_checker_.CalledOnValidThread());
if (!IsValidCameraId(id)) {
LOGF(ERROR) << "Camera id " << id << " is invalid";
return -EINVAL;
}
if (cameras_.find(id) != cameras_.end()) {
LOGF(ERROR) << "Camera " << id << " is already opened";
return -EBUSY;
}
if (!cameras_.empty() &&
(cros_device_config_ != nullptr &&
(cros_device_config_->GetModelName() == "treeya360" ||
cros_device_config_->GetModelName() == "nuwani360" ||
cros_device_config_->GetModelName() == "pompom"))) {
// It cannot open multiple cameras at the same time due to USB bandwidth
// limitation (b/147333530, b/171856355).
// TODO(shik): Use |conflicting_devices| to implement this logic after we
// hook that in the ARC++ camera HAL shim.
// TODO(b/163436311): Add a new field in the unibuild schema instead of
// checking model name here.
LOGF(WARNING) << "Can't open Camera " << id << " because Camera "
<< cameras_.begin()->first << " is already opened.";
return -EUSERS;
}
camera_metadata_t* static_metadata;
camera_metadata_t* request_template;
if (client_type == ClientType::kAndroid) {
static_metadata = static_metadata_android_[id].get();
request_template = request_template_android_[id].get();
} else {
static_metadata = static_metadata_[id].get();
request_template = request_template_[id].get();
}
cameras_[id].reset(new CameraClient(id, device_infos_[id], *static_metadata,
*request_template, module, hw_device,
&privacy_switch_monitor_, client_type));
if (cameras_[id]->OpenDevice()) {
cameras_.erase(id);
return -ENODEV;
}
if (!task_runner_) {
task_runner_ = base::ThreadTaskRunnerHandle::Get();
}
return 0;
}
bool CameraHal::IsValidCameraId(int id) {
return device_infos_.find(id) != device_infos_.end();
}
int CameraHal::GetCameraInfo(int id,
struct camera_info* info,
ClientType client_type) {
VLOGFID(1, id);
DCHECK(thread_checker_.CalledOnValidThread());
if (!IsValidCameraId(id)) {
LOGF(ERROR) << "Camera id " << id << " is invalid";
return -EINVAL;
}
switch (device_infos_[id].lens_facing) {
case LensFacing::kFront:
info->facing = CAMERA_FACING_FRONT;
break;
case LensFacing::kBack:
info->facing = CAMERA_FACING_BACK;
break;
case LensFacing::kExternal:
info->facing = CAMERA_FACING_EXTERNAL;
break;
default:
LOGF(ERROR) << "Unknown lens facing: "
<< static_cast<int>(device_infos_[id].lens_facing);
return -EINVAL;
}
info->orientation = device_infos_[id].sensor_orientation;
info->device_version = CAMERA_DEVICE_API_VERSION_3_5;
if (client_type == ClientType::kAndroid) {
info->static_camera_characteristics = static_metadata_android_[id].get();
} else {
info->static_camera_characteristics = static_metadata_[id].get();
}
info->resource_cost = 0;
info->conflicting_devices = nullptr;
info->conflicting_devices_length = 0;
return 0;
}
int CameraHal::GetCameraInfo(int id, struct camera_info* info) {
return GetCameraInfo(id, info, ClientType::kChrome);
}
int CameraHal::SetCallbacks(const camera_module_callbacks_t* callbacks) {
VLOGF(1) << "New callbacks = " << callbacks;
DCHECK(thread_checker_.CalledOnValidThread());
callbacks_ = callbacks;
// Some external cameras might be detected before SetCallbacks, we should
// enumerate existing devices again after setting the callbacks.
if (!udev_watcher_->EnumerateExistingDevices()) {
LOGF(ERROR) << "Failed to EnumerateExistingDevices()";
}
return 0;
}
int CameraHal::Init() {
DCHECK(thread_checker_.CalledOnValidThread());
if (cros_device_config_ == nullptr) {
LOGF(WARNING) << "Failed to initialize CrOS device config, camera HAL may "
"function incorrectly";
}
if (!udev_watcher_->Start(base::ThreadTaskRunnerHandle::Get())) {
LOGF(ERROR) << "Failed to Start()";
return -ENODEV;
}
if (!udev_watcher_->EnumerateExistingDevices()) {
LOGF(ERROR) << "Failed to EnumerateExistingDevices()";
return -ENODEV;
}
// TODO(shik): Some unibuild devices like vayne may have only user-facing
// camera as "camera1" in |characteristics_|. It's a workaround for them until
// we revise our config format. (b/111770440)
if (device_infos_.size() == 1 && device_infos_.cbegin()->first == 1 &&
num_builtin_cameras_ == 2) {
LOGF(INFO) << "Renumber camera1 to camera0";
device_infos_.emplace(0, std::move(device_infos_[1]));
device_infos_.erase(1);
device_infos_[0].camera_id = 0;
DCHECK_EQ(path_to_id_.size(), 1);
DCHECK_EQ(path_to_id_.begin()->second, 1);
path_to_id_.begin()->second = 0;
DCHECK_EQ(static_metadata_.size(), 1);
DCHECK_EQ(static_metadata_.begin()->first, 1);
static_metadata_.emplace(0, std::move(static_metadata_[1]));
static_metadata_.erase(1);
DCHECK_EQ(static_metadata_android_.size(), 1);
DCHECK_EQ(static_metadata_android_.begin()->first, 1);
static_metadata_android_.emplace(0, std::move(static_metadata_android_[1]));
static_metadata_android_.erase(1);
DCHECK_EQ(request_template_.size(), 1);
DCHECK_EQ(request_template_.begin()->first, 1);
request_template_.emplace(0, std::move(request_template_[1]));
request_template_.erase(1);
DCHECK_EQ(request_template_android_.size(), 1);
DCHECK_EQ(request_template_android_.begin()->first, 1);
request_template_android_.emplace(0,
std::move(request_template_android_[1]));
request_template_android_.erase(1);
num_builtin_cameras_ = 1;
}
bool enough_camera_probed = true;
if (cros_device_config_ != nullptr &&
cros_device_config_->IsUsbCameraCountAvailable()) {
if (num_builtin_cameras_ != cros_device_config_->GetUsbCameraCount()) {
LOGF(ERROR) << "Expected " << cros_device_config_->GetUsbCameraCount()
<< " cameras from Chrome OS config, found "
<< num_builtin_cameras_;
enough_camera_probed = false;
}
} else if (CameraCharacteristics::ConfigFileExists() &&
num_builtin_cameras_ == 0) {
// TODO(shik): possible race here. We may have 2 built-in cameras but just
// detect one.
LOGF(ERROR) << "Expect to find at least one camera if config file exists";
enough_camera_probed = false;
}
if (!enough_camera_probed) {
if (base::PathExists(
base::FilePath(constants::kForceStartCrosCameraPath))) {
LOGF(WARNING) << "Force starting cros-camera: Ignore missing built-in "
<< "camera error to allow external camera usage";
} else {
return -ENODEV;
}
}
for (int i = 0; i < num_builtin_cameras_; i++) {
if (!IsValidCameraId(i)) {
LOGF(ERROR)
<< "The camera devices should be numbered 0 through N-1, but id = "
<< i << " is missing";
return -ENODEV;
}
}
next_external_camera_id_ = num_builtin_cameras_;
return 0;
}
void CameraHal::SetUp(CameraMojoChannelManagerToken* token) {
mojo_manager_token_ = token;
}
void CameraHal::TearDown() {
mojo_manager_token_ = nullptr;
}
void CameraHal::SetPrivacySwitchCallback(
PrivacySwitchStateChangeCallback callback) {
privacy_switch_monitor_.RegisterCallback(std::move(callback));
}
void CameraHal::CloseDeviceOnOpsThread(int id) {
DCHECK(task_runner_);
auto future = cros::Future<void>::Create(nullptr);
task_runner_->PostTask(
FROM_HERE, base::Bind(&CameraHal::CloseDevice, base::Unretained(this), id,
base::RetainedRef(future)));
future->Wait();
}
void CameraHal::CloseDevice(int id, scoped_refptr<cros::Future<void>> future) {
VLOGFID(1, id);
DCHECK(thread_checker_.CalledOnValidThread());
if (cameras_.find(id) == cameras_.end()) {
LOGF(ERROR) << "Failed to close camera device " << id
<< ": device is not opened";
future->Set();
return;
}
cameras_.erase(id);
future->Set();
}
void CameraHal::OnDeviceAdded(ScopedUdevDevicePtr dev) {
const char* path = GetPreferredPath(dev.get());
if (!path) {
LOGF(ERROR) << "udev_device_get_devnode failed";
return;
}
const char* vid = "";
const char* pid = "";
bool is_vivid = IsVivid(dev.get());
if (!is_vivid) {
udev_device* parent_dev = udev_device_get_parent_with_subsystem_devtype(
dev.get(), "usb", "usb_device");
if (!parent_dev) {
VLOGF(2) << "Non USB device is ignored";
return;
}
vid = udev_device_get_sysattr_value(parent_dev, "idVendor");
if (!vid) {
LOGF(ERROR) << "Failed to get vid";
return;
}
pid = udev_device_get_sysattr_value(parent_dev, "idProduct");
if (!pid) {
LOGF(ERROR) << "Failed to get pid";
return;
}
}
{
// We have to check this because of:
// 1. Limitation of libudev
// 2. Reenumeration after SetCallbacks()
// 3. Suspend/Resume
auto it = path_to_id_.find(path);
if (it != path_to_id_.end()) {
int id = it->second;
const DeviceInfo& info = device_infos_[id];
if (info.usb_vid == vid && info.usb_pid == pid) {
VLOGF(1) << "Ignore " << path << " since it's already connected";
} else {
LOGF(ERROR) << "Device path conflict: " << path;
}
return;
}
}
if (!V4L2CameraDevice::IsCameraDevice(path)) {
VLOGF(1) << path << " is not a camera device";
return;
}
if (is_vivid) {
LOGF(INFO) << "New vivid camera device at " << path;
} else {
LOGF(INFO) << "New usb camera device at " << path << " vid: " << vid
<< " pid: " << pid;
}
DeviceInfo info;
const DeviceInfo* info_ptr = characteristics_.Find(vid, pid);
if (info_ptr != nullptr) {
VLOGF(1) << "Found a built-in camera";
info = *info_ptr;
num_builtin_cameras_ = std::max(num_builtin_cameras_, info.camera_id + 1);
if (info.constant_framerate_unsupported) {
LOGF(WARNING) << "Camera module " << vid << ":" << pid
<< " does not support constant frame rate";
}
} else {
VLOGF(1) << "Found an external camera";
if (callbacks_ == nullptr) {
VLOGF(1) << "No callbacks set, ignore it for now";
return;
}
}
info.device_path = path;
info.usb_vid = vid;
info.usb_pid = pid;
info.is_vivid = is_vivid;
info.power_line_frequency = V4L2CameraDevice::GetPowerLineFrequency(path);
info.constant_framerate_unsupported |=
!V4L2CameraDevice::IsControlSupported(path, kControlExposureAutoPriority);
RoiControl roi_control;
info.region_of_interest_supported =
V4L2CameraDevice::IsRegionOfInterestSupported(path, &roi_control);
if (info.region_of_interest_supported) {
if (!info.enable_face_detection) {
camera_metrics_->SendFaceAeFunction(FaceAeFunction::kNotEnabled);
} else if (base::PathExists(
base::FilePath(constants::kForceDisableFaceAePath))) {
camera_metrics_->SendFaceAeFunction(FaceAeFunction::kForceDisabled);
} else {
camera_metrics_->SendFaceAeFunction(FaceAeFunction::kEnabled);
}
} else {
camera_metrics_->SendFaceAeFunction(FaceAeFunction::kUnsupported);
}
// The force control path is managed by chrome flag, there should be only one
// file.
if (base::PathExists(base::FilePath(constants::kForceEnableFaceAePath))) {
LOGF(INFO) << "force enable face ae";
info.enable_face_detection = true;
}
if (base::PathExists(base::FilePath(constants::kForceDisableFaceAePath))) {
LOGF(INFO) << "force disable face ae";
info.enable_face_detection = false;
}
if (!is_vivid) {
info.quirks |= GetQuirks(vid, pid);
}
// Mark the camera as v1 if it is a built-in camera and the CrOS device is
// marked as a v1 device.
if (info_ptr != nullptr && cros_device_config_ != nullptr &&
cros_device_config_->IsV1Device()) {
info.quirks |= kQuirkV1Device;
}
if (info_ptr == nullptr) {
info.lens_facing = LensFacing::kExternal;
// Try to reuse the same id for the same camera.
std::string model_id = GetModelId(info);
std::set<int>& preferred_ids = previous_ids_[model_id];
if (!preferred_ids.empty()) {
info.camera_id = *preferred_ids.begin();
previous_ids_.erase(previous_ids_.begin());
VLOGF(1) << "Use the previous id " << info.camera_id << " for camera "
<< model_id;
} else {
info.camera_id = next_external_camera_id_++;
VLOGF(1) << "Use a new id " << info.camera_id << " for camera "
<< model_id;
}
// Uses software timestamp from userspace for external cameras, because the
// hardware timestamp is not reliable and sometimes even jump backwards.
info.quirks |= kQuirkUserSpaceTimestamp;
}
android::CameraMetadata static_metadata, request_template;
if (!FillMetadata(info, &static_metadata, &request_template)) {
if (info.lens_facing == LensFacing::kExternal) {
LOGF(ERROR) << "FillMetadata failed, the new external "
"camera would be ignored";
return;
} else {
LOGF(FATAL) << "FillMetadata failed for a built-in "
"camera, please check your camera config";
}
}
path_to_id_[info.device_path] = info.camera_id;
device_infos_[info.camera_id] = info;
static_metadata_android_[info.camera_id] =
StaticMetadataForAndroid(static_metadata);
request_template_android_[info.camera_id] = RequestTemplateForAndroid(
static_metadata_android_[info.camera_id].get(), request_template);
static_metadata_[info.camera_id] =
ScopedCameraMetadata(static_metadata.release());
request_template_[info.camera_id] =
ScopedCameraMetadata(request_template.release());
privacy_switch_monitor_.TrySubscribe(info.camera_id, info.device_path);
if (info.lens_facing == LensFacing::kExternal) {
callbacks_->camera_device_status_change(callbacks_, info.camera_id,
CAMERA_DEVICE_STATUS_PRESENT);
}
}
void CameraHal::OnDeviceRemoved(ScopedUdevDevicePtr dev) {
const char* path = GetPreferredPath(dev.get());
if (!path) {
LOGF(ERROR) << "udev_device_get_devnode failed";
return;
}
auto it = path_to_id_.find(path);
if (it == path_to_id_.end()) {
VLOGF(1) << "Cannot found id for " << path << ", ignore it";
return;
}
int id = it->second;
if (id < num_builtin_cameras_) {
VLOGF(1) << "Camera " << id << "is a built-in camera, ignore it";
return;
}
privacy_switch_monitor_.Unsubscribe(id);
LOGF(INFO) << "Camera " << id << " at " << path << " removed";
// TODO(shik): Handle this more gracefully, sometimes it even trigger a kernel
// panic.
if (cameras_.find(id) != cameras_.end()) {
LOGF(WARNING)
<< "Unplug an opening camera, exit the camera service to cleanup";
// Upstart will start the service again.
_exit(EIO);
}
previous_ids_[GetModelId(device_infos_[id])].insert(id);
path_to_id_.erase(it);
device_infos_.erase(id);
static_metadata_.erase(id);
static_metadata_android_.erase(id);
request_template_.erase(id);
request_template_android_.erase(id);
if (callbacks_) {
callbacks_->camera_device_status_change(callbacks_, id,
CAMERA_DEVICE_STATUS_NOT_PRESENT);
}
}
static int camera_device_open_ext(const hw_module_t* module,
const char* name,
hw_device_t** device,
ClientType client_type) {
VLOGF_ENTER();
// Make sure hal adapter loads the correct symbol.
if (module != &HAL_MODULE_INFO_SYM.common) {
LOGF(ERROR) << std::hex << "Invalid module 0x" << module << " expected 0x"
<< &HAL_MODULE_INFO_SYM.common << std::dec;
return -EINVAL;
}
char* nameEnd;
int id = strtol(name, &nameEnd, 10);
if (*nameEnd != '\0') {
LOGF(ERROR) << "Invalid camera name " << name;
return -EINVAL;
}
return CameraHal::GetInstance().OpenDevice(id, module, device, client_type);
}
static int camera_device_open(const hw_module_t* module,
const char* name,
hw_device_t** device) {
return camera_device_open_ext(module, name, device, ClientType::kChrome);
}
static int get_number_of_cameras() {
return CameraHal::GetInstance().GetNumberOfCameras();
}
static int get_camera_info(int id, struct camera_info* info) {
return CameraHal::GetInstance().GetCameraInfo(id, info);
}
static int set_callbacks(const camera_module_callbacks_t* callbacks) {
return CameraHal::GetInstance().SetCallbacks(callbacks);
}
static void get_vendor_tag_ops(vendor_tag_ops_t* ops) {
ops->get_all_tags = VendorTagOps::GetAllTags;
ops->get_tag_count = VendorTagOps::GetTagCount;
ops->get_section_name = VendorTagOps::GetSectionName;
ops->get_tag_name = VendorTagOps::GetTagName;
ops->get_tag_type = VendorTagOps::GetTagType;
}
static int open_legacy(const struct hw_module_t* /*module*/,
const char* /*id*/,
uint32_t /*halVersion*/,
struct hw_device_t** /*device*/) {
return -ENOSYS;
}
static int set_torch_mode(const char* /*camera_id*/, bool /*enabled*/) {
return -ENOSYS;
}
static int init() {
return CameraHal::GetInstance().Init();
}
static void set_up(CameraMojoChannelManagerToken* token) {
CameraHal::GetInstance().SetUp(token);
}
static void tear_down() {
CameraHal::GetInstance().TearDown();
}
static void set_privacy_switch_callback(
PrivacySwitchStateChangeCallback callback) {
CameraHal::GetInstance().SetPrivacySwitchCallback(std::move(callback));
}
static int get_camera_info_ext(int id,
struct camera_info* info,
ClientType client_type) {
return CameraHal::GetInstance().GetCameraInfo(id, info, client_type);
}
int camera_device_close(struct hw_device_t* hw_device) {
camera3_device_t* cam_dev = reinterpret_cast<camera3_device_t*>(hw_device);
CameraClient* cam = static_cast<CameraClient*>(cam_dev->priv);
if (!cam) {
LOGF(ERROR) << "Camera device is NULL";
return -EIO;
}
cam_dev->priv = NULL;
int ret = cam->CloseDevice();
CameraHal::GetInstance().CloseDeviceOnOpsThread(cam->GetId());
return ret;
}
} // namespace cros
static hw_module_methods_t gCameraModuleMethods = {
.open = cros::camera_device_open};
camera_module_t HAL_MODULE_INFO_SYM CROS_CAMERA_EXPORT = {
.common = {.tag = HARDWARE_MODULE_TAG,
.module_api_version = CAMERA_MODULE_API_VERSION_2_4,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = CAMERA_HARDWARE_MODULE_ID,
.name = "V4L2 UVC Camera HAL v3",
.author = "The Chromium OS Authors",
.methods = &gCameraModuleMethods,
.dso = NULL,
.reserved = {0}},
.get_number_of_cameras = cros::get_number_of_cameras,
.get_camera_info = cros::get_camera_info,
.set_callbacks = cros::set_callbacks,
.get_vendor_tag_ops = cros::get_vendor_tag_ops,
.open_legacy = cros::open_legacy,
.set_torch_mode = cros::set_torch_mode,
.init = cros::init,
.reserved = {0}};
cros::cros_camera_hal_t CROS_CAMERA_HAL_INFO_SYM CROS_CAMERA_EXPORT = {
.set_up = cros::set_up,
.tear_down = cros::tear_down,
.set_privacy_switch_callback = cros::set_privacy_switch_callback,
.camera_device_open_ext = cros::camera_device_open_ext,
.get_camera_info_ext = cros::get_camera_info_ext};