blob: 770d6802fed81e9e44494b38e4573129c1ba3a87 [file] [log] [blame]
/*
* Copyright 2017 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_server_impl.h"
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
#include <deque>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/bind_helpers.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/logging.h>
#include <base/posix/safe_strerror.h>
#include <base/threading/thread_task_runner_handle.h>
#include "common/camera_mojo_channel_manager_impl.h"
#include "common/utils/camera_hal_enumerator.h"
#include "cros-camera/camera_mojo_channel_manager.h"
#include "cros-camera/common.h"
#include "cros-camera/future.h"
#include "cros-camera/ipc_util.h"
#include "cros-camera/utils/camera_config.h"
#include "hal_adapter/camera_hal_test_adapter.h"
#include "hal_adapter/camera_trace_event.h"
namespace cros {
CameraHalServerImpl::CameraHalServerImpl()
: mojo_manager_(CameraMojoChannelManager::FromToken(
CameraMojoChannelManagerToken::CreateInstance())),
ipc_bridge_(new IPCBridge(this, mojo_manager_.get())) {
VLOGF_ENTER();
}
CameraHalServerImpl::~CameraHalServerImpl() {
VLOGF_ENTER();
ExitOnMainThread(0);
}
bool CameraHalServerImpl::Start() {
VLOGF_ENTER();
LoadCameraHal();
// We assume that |camera_hal_adapter_| will only be set once. If the
// assumption changed, we should consider another way to provide
// CameraHalAdapter.
mojo_manager_->GetIpcTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&CameraHalServerImpl::IPCBridge::Start,
ipc_bridge_->GetWeakPtr(), camera_hal_adapter_.get(),
base::BindRepeating(
[](const std::vector<cros_camera_hal_t*>& hals,
PrivacySwitchStateChangeCallback callback) {
for (const auto* hal : hals) {
if (hal->set_privacy_switch_callback != nullptr) {
hal->set_privacy_switch_callback(
std::move(callback));
}
}
},
cros_camera_hals_)));
return true;
}
CameraHalServerImpl::IPCBridge::IPCBridge(
CameraHalServerImpl* camera_hal_server,
CameraMojoChannelManager* mojo_manager)
: camera_hal_server_(camera_hal_server),
mojo_manager_(mojo_manager),
ipc_task_runner_(mojo_manager_->GetIpcTaskRunner()),
main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
binding_(this) {}
CameraHalServerImpl::IPCBridge::~IPCBridge() {
if (binding_.is_bound()) {
binding_.Unbind();
}
callbacks_.reset();
}
void CameraHalServerImpl::IPCBridge::Start(
CameraHalAdapter* camera_hal_adapter,
SetPrivacySwitchCallback set_privacy_switch_callback) {
VLOGF_ENTER();
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
if (binding_.is_bound()) {
return;
}
camera_hal_adapter_ = camera_hal_adapter;
mojom::CameraHalServerPtr server_ptr;
binding_.Bind(mojo::MakeRequest(&server_ptr));
binding_.set_connection_error_handler(
base::Bind(&CameraHalServerImpl::IPCBridge::OnServiceMojoChannelError,
GetWeakPtr()));
mojo_manager_->RegisterServer(
std::move(server_ptr),
base::BindOnce(&CameraHalServerImpl::IPCBridge::OnServerRegistered,
GetWeakPtr(), std::move(set_privacy_switch_callback)),
base::BindOnce(&CameraHalServerImpl::IPCBridge::OnServiceMojoChannelError,
GetWeakPtr()));
}
void CameraHalServerImpl::IPCBridge::CreateChannel(
mojom::CameraModuleRequest camera_module_request,
mojom::CameraClientType camera_client_type) {
VLOGF_ENTER();
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
camera_hal_adapter_->OpenCameraHal(std::move(camera_module_request),
camera_client_type);
}
void CameraHalServerImpl::IPCBridge::SetTracingEnabled(bool enabled) {
VLOGF_ENTER();
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
TRACE_CAMERA_ENABLE(enabled);
}
void CameraHalServerImpl::IPCBridge::NotifyCameraActivityChange(
int32_t camera_id, bool opened, mojom::CameraClientType type) {
VLOGF_ENTER();
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
DCHECK(callbacks_.is_bound());
callbacks_->CameraDeviceActivityChange(camera_id, opened, type);
}
base::WeakPtr<CameraHalServerImpl::IPCBridge>
CameraHalServerImpl::IPCBridge::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void CameraHalServerImpl::IPCBridge::OnServerRegistered(
SetPrivacySwitchCallback set_privacy_switch_callback,
int32_t result,
mojom::CameraHalServerCallbacksPtr callbacks) {
VLOGF_ENTER();
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
if (result != 0) {
LOGF(ERROR) << "Failed to register camera HAL: "
<< base::safe_strerror(-result);
return;
}
callbacks_.Bind(callbacks.PassInterface());
std::move(set_privacy_switch_callback)
.Run(base::BindRepeating(
&CameraHalServerImpl::IPCBridge::OnPrivacySwitchStatusChanged,
base::Unretained(this)));
LOGF(INFO) << "Registered camera HAL";
}
void CameraHalServerImpl::IPCBridge::OnServiceMojoChannelError() {
VLOGF_ENTER();
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
// The CameraHalDispatcher Mojo parent is probably dead. We need to restart
// another process in order to connect to the new Mojo parent.
LOGF(INFO) << "Mojo connection to CameraHalDispatcher is broken";
main_task_runner_->PostTask(
FROM_HERE, base::Bind(&CameraHalServerImpl::ExitOnMainThread,
base::Unretained(camera_hal_server_), ECONNRESET));
}
void CameraHalServerImpl::IPCBridge::OnPrivacySwitchStatusChanged(
PrivacySwitchState state) {
cros::mojom::CameraPrivacySwitchState state_in_mojo;
if (state == PrivacySwitchState::kUnknown) {
state_in_mojo = cros::mojom::CameraPrivacySwitchState::UNKNOWN;
} else if (state == PrivacySwitchState::kOn) {
state_in_mojo = cros::mojom::CameraPrivacySwitchState::ON;
} else { // state == PrivacySwitchState::kOff
state_in_mojo = cros::mojom::CameraPrivacySwitchState::OFF;
}
callbacks_->CameraPrivacySwitchStateChange(state_in_mojo);
}
void CameraHalServerImpl::LoadCameraHal() {
VLOGF_ENTER();
DCHECK(!camera_hal_adapter_);
DCHECK_EQ(cros_camera_hals_.size(), 0);
std::vector<camera_module_t*> camera_modules;
std::unique_ptr<CameraConfig> config =
CameraConfig::Create(constants::kCrosCameraTestConfigPathString);
bool enable_front =
config->GetBoolean(constants::kCrosEnableFrontCameraOption, true),
enable_back =
config->GetBoolean(constants::kCrosEnableBackCameraOption, true),
enable_external =
config->GetBoolean(constants::kCrosEnableExternalCameraOption, true);
for (const auto& dll : GetCameraHalPaths()) {
LOGF(INFO) << "Try to load camera hal " << dll.value();
void* handle = dlopen(dll.value().c_str(), RTLD_NOW | RTLD_LOCAL);
if (!handle) {
LOGF(INFO) << "Failed to dlopen: " << dlerror();
ExitOnMainThread(ENOENT);
}
cros_camera_hal_t* cros_camera_hal = static_cast<cros_camera_hal_t*>(
dlsym(handle, CROS_CAMERA_HAL_INFO_SYM_AS_STR));
if (!cros_camera_hal) {
// TODO(b/151270948): We should report error here once all camera HALs
// have implemented the interface.
} else {
cros_camera_hal->set_up(mojo_manager_.get());
cros_camera_hals_.push_back(cros_camera_hal);
}
auto* module = static_cast<camera_module_t*>(
dlsym(handle, HAL_MODULE_INFO_SYM_AS_STR));
if (!module) {
LOGF(ERROR) << "Failed to get camera_module_t pointer with symbol name "
<< HAL_MODULE_INFO_SYM_AS_STR << " from " << dll.value();
ExitOnMainThread(ELIBBAD);
}
camera_modules.push_back(module);
}
auto active_callback = base::Bind(
&CameraHalServerImpl::OnCameraActivityChange, base::Unretained(this));
if (enable_front && enable_back && enable_external) {
camera_hal_adapter_.reset(new CameraHalAdapter(
camera_modules, mojo_manager_.get(), active_callback));
} else {
camera_hal_adapter_.reset(new CameraHalTestAdapter(
camera_modules, mojo_manager_.get(), active_callback, enable_front,
enable_back, enable_external));
}
LOGF(INFO) << "Running camera HAL adapter on " << getpid();
if (!camera_hal_adapter_->Start()) {
LOGF(ERROR) << "Failed to start camera HAL adapter";
ExitOnMainThread(ENODEV);
}
}
void CameraHalServerImpl::ExitOnMainThread(int exit_status) {
VLOGF_ENTER();
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (auto* cros_camera_hal : cros_camera_hals_) {
cros_camera_hal->tear_down();
}
auto future = Future<void>::Create(nullptr);
auto delete_ipc_bridge = base::BindOnce(
[](std::unique_ptr<IPCBridge> ipc_bridge,
base::Callback<void(void)> callback) { std::move(callback).Run(); },
std::move(ipc_bridge_), cros::GetFutureCallback(future));
mojo_manager_->GetIpcTaskRunner()->PostTask(FROM_HERE,
std::move(delete_ipc_bridge));
future->Wait(-1);
exit(exit_status);
}
void CameraHalServerImpl::OnCameraActivityChange(int32_t camera_id,
bool opened,
mojom::CameraClientType type) {
mojo_manager_->GetIpcTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
&CameraHalServerImpl::IPCBridge::NotifyCameraActivityChange,
ipc_bridge_->GetWeakPtr(), camera_id, opened, type));
}
} // namespace cros