blob: 07ade5dbc04aa3641edcb4eba8b424766771b274 [file] [log] [blame]
// Copyright 2017 The Chromium 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 "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
#include <fcntl.h>
#include <grp.h>
#include <poll.h>
#include <sys/uio.h>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/cxx17_backports.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/notreached.h"
#include "base/posix/eintr_wrapper.h"
#include "base/rand_util.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "chromeos/components/sensors/sensor_util.h"
#include "components/device_event_log/device_event_log.h"
#include "media/base/bind_to_current_loop.h"
#include "media/capture/video/chromeos/mojom/camera_common.mojom.h"
#include "media/capture/video/chromeos/mojom/cros_camera_client.mojom.h"
#include "media/capture/video/chromeos/video_capture_features_chromeos.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/platform/named_platform_channel.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/platform/socket_utils_posix.h"
#include "mojo/public/cpp/system/invitation.h"
namespace media {
namespace {
const base::FilePath::CharType kArcCamera3SocketPath[] =
"/run/camera/camera3.sock";
const char kArcCameraGroup[] = "arc-camera";
const base::FilePath::CharType kForceEnableAePath[] =
"/run/camera/force_enable_face_ae";
const base::FilePath::CharType kForceDisableAePath[] =
"/run/camera/force_disable_face_ae";
const base::FilePath::CharType kForceEnableHdrNetPath[] =
"/run/camera/force_enable_hdrnet";
const base::FilePath::CharType kForceDisableHdrNetPath[] =
"/run/camera/force_disable_hdrnet";
std::string GenerateRandomToken() {
char random_bytes[16];
base::RandBytes(random_bytes, 16);
return base::HexEncode(random_bytes, 16);
}
// Waits until |raw_socket_fd| is readable. We signal |raw_cancel_fd| when we
// want to cancel the blocking wait and stop serving connections on
// |raw_socket_fd|. To notify such a situation, |raw_cancel_fd| is also passed
// to here, and the write side will be closed in such a case.
bool WaitForSocketReadable(int raw_socket_fd, int raw_cancel_fd) {
struct pollfd fds[2] = {
{raw_socket_fd, POLLIN, 0},
{raw_cancel_fd, POLLIN, 0},
};
if (HANDLE_EINTR(poll(fds, base::size(fds), -1)) <= 0) {
PLOG(ERROR) << "poll()";
return false;
}
if (fds[1].revents) {
VLOG(1) << "Stop() was called";
return false;
}
DCHECK(fds[0].revents);
return true;
}
bool HasCrosCameraTest() {
static constexpr char kCrosCameraTestPath[] =
"/usr/local/bin/cros_camera_test";
base::FilePath path(kCrosCameraTestPath);
return base::PathExists(path);
}
class MojoCameraClientObserver : public CameraClientObserver {
public:
explicit MojoCameraClientObserver(
mojo::PendingRemote<cros::mojom::CameraHalClient> client,
cros::mojom::CameraClientType type,
base::UnguessableToken auth_token)
: CameraClientObserver(type, std::move(auth_token)),
client_(std::move(client)) {}
void OnChannelCreated(
mojo::PendingRemote<cros::mojom::CameraModule> camera_module) override {
client_->SetUpChannel(std::move(camera_module));
}
mojo::Remote<cros::mojom::CameraHalClient>& client() { return client_; }
private:
mojo::Remote<cros::mojom::CameraHalClient> client_;
DISALLOW_IMPLICIT_CONSTRUCTORS(MojoCameraClientObserver);
};
} // namespace
CameraClientObserver::~CameraClientObserver() = default;
bool CameraClientObserver::Authenticate(TokenManager* token_manager) {
auto authenticated_type =
token_manager->AuthenticateClient(type_, auth_token_);
if (!authenticated_type) {
return false;
}
type_ = authenticated_type.value();
return true;
}
FailedCameraHalServerCallbacks::FailedCameraHalServerCallbacks()
: callbacks_(this) {}
FailedCameraHalServerCallbacks::~FailedCameraHalServerCallbacks() = default;
mojo::PendingRemote<cros::mojom::CameraHalServerCallbacks>
FailedCameraHalServerCallbacks::GetRemote() {
return callbacks_.BindNewPipeAndPassRemote();
}
void FailedCameraHalServerCallbacks::CameraDeviceActivityChange(
int32_t camera_id,
bool opened,
cros::mojom::CameraClientType type) {}
void FailedCameraHalServerCallbacks::CameraPrivacySwitchStateChange(
cros::mojom::CameraPrivacySwitchState state) {}
// static
CameraHalDispatcherImpl* CameraHalDispatcherImpl::GetInstance() {
return base::Singleton<CameraHalDispatcherImpl>::get();
}
bool CameraHalDispatcherImpl::StartThreads() {
DCHECK(!proxy_thread_.IsRunning());
DCHECK(!blocking_io_thread_.IsRunning());
if (!proxy_thread_.Start()) {
LOG(ERROR) << "Failed to start proxy thread";
return false;
}
if (!blocking_io_thread_.Start()) {
LOG(ERROR) << "Failed to start blocking IO thread";
proxy_thread_.Stop();
return false;
}
proxy_task_runner_ = proxy_thread_.task_runner();
blocking_io_task_runner_ = blocking_io_thread_.task_runner();
return true;
}
bool CameraHalDispatcherImpl::Start(
MojoMjpegDecodeAcceleratorFactoryCB jda_factory,
MojoJpegEncodeAcceleratorFactoryCB jea_factory) {
DCHECK(!IsStarted());
if (!StartThreads()) {
return false;
}
// This event is for adding camera category to categories list.
TRACE_EVENT0("camera", "CameraHalDispatcherImpl");
base::trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this);
{
base::FilePath enable_file_path(kForceEnableAePath);
base::FilePath disable_file_path(kForceDisableAePath);
if (!base::DeleteFile(enable_file_path)) {
LOG(WARNING) << "Could not delete " << kForceEnableAePath;
}
if (!base::DeleteFile(disable_file_path)) {
LOG(WARNING) << "Could not delete " << kForceDisableAePath;
}
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(media::switches::kForceControlFaceAe)) {
if (command_line->GetSwitchValueASCII(
media::switches::kForceControlFaceAe) == "enable") {
base::File file(enable_file_path, base::File::FLAG_CREATE_ALWAYS);
file.Close();
} else {
base::File file(disable_file_path, base::File::FLAG_CREATE_ALWAYS);
file.Close();
}
}
}
{
base::FilePath enable_file_path(kForceEnableHdrNetPath);
base::FilePath disable_file_path(kForceDisableHdrNetPath);
if (!base::DeleteFile(enable_file_path)) {
LOG(WARNING) << "Could not delete " << kForceEnableHdrNetPath;
}
if (!base::DeleteFile(disable_file_path)) {
LOG(WARNING) << "Could not delete " << kForceDisableHdrNetPath;
}
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(media::switches::kHdrNetOverride)) {
std::string value =
command_line->GetSwitchValueASCII(switches::kHdrNetOverride);
if (value == switches::kHdrNetForceEnabled) {
base::File file(enable_file_path, base::File::FLAG_CREATE_ALWAYS);
file.Close();
} else if (value == switches::kHdrNetForceDisabled) {
base::File file(disable_file_path, base::File::FLAG_CREATE_ALWAYS);
file.Close();
}
}
}
jda_factory_ = std::move(jda_factory);
jea_factory_ = std::move(jea_factory);
base::WaitableEvent started(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
// It's important we generate tokens before creating the socket, because once
// it is available, everyone connecting to socket would start fetching
// tokens.
if (!token_manager_.GenerateServerToken()) {
LOG(ERROR) << "Failed to generate authentication token for server";
return false;
}
if (HasCrosCameraTest() && !token_manager_.GenerateTestClientToken()) {
LOG(ERROR) << "Failed to generate token for test client";
return false;
}
if (!token_manager_.GenerateServerSensorClientToken()) {
LOG(ERROR) << "Failed to generate authentication token for server as a "
"sensor client";
}
blocking_io_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&CameraHalDispatcherImpl::CreateSocket,
base::Unretained(this), base::Unretained(&started)));
started.Wait();
return IsStarted();
}
void CameraHalDispatcherImpl::AddClientObserver(
std::unique_ptr<CameraClientObserver> observer,
base::OnceCallback<void(int32_t)> result_callback) {
// If |proxy_thread_| fails to start in Start() then CameraHalDelegate will
// not be created, and this function will not be called.
DCHECK(proxy_thread_.IsRunning());
proxy_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&CameraHalDispatcherImpl::AddClientObserverOnProxyThread,
base::Unretained(this), std::move(observer),
std::move(result_callback)));
}
bool CameraHalDispatcherImpl::IsStarted() {
return proxy_thread_.IsRunning() && blocking_io_thread_.IsRunning() &&
proxy_fd_.is_valid();
}
void CameraHalDispatcherImpl::AddActiveClientObserver(
CameraActiveClientObserver* observer) {
base::AutoLock lock(opened_camera_id_map_lock_);
for (auto& opened_camera_id_pair : opened_camera_id_map_) {
const auto& camera_client_type = opened_camera_id_pair.first;
const auto& camera_id_set = opened_camera_id_pair.second;
if (!camera_id_set.empty()) {
observer->OnActiveClientChange(camera_client_type, /*is_active=*/true);
}
}
active_client_observers_->AddObserver(observer);
}
void CameraHalDispatcherImpl::RemoveActiveClientObserver(
CameraActiveClientObserver* observer) {
active_client_observers_->RemoveObserver(observer);
}
cros::mojom::CameraPrivacySwitchState
CameraHalDispatcherImpl::AddCameraPrivacySwitchObserver(
CameraPrivacySwitchObserver* observer) {
privacy_switch_observers_->AddObserver(observer);
base::AutoLock lock(privacy_switch_state_lock_);
return current_privacy_switch_state_;
}
void CameraHalDispatcherImpl::RemoveCameraPrivacySwitchObserver(
CameraPrivacySwitchObserver* observer) {
privacy_switch_observers_->RemoveObserver(observer);
}
void CameraHalDispatcherImpl::RegisterPluginVmToken(
const base::UnguessableToken& token) {
token_manager_.RegisterPluginVmToken(token);
}
void CameraHalDispatcherImpl::UnregisterPluginVmToken(
const base::UnguessableToken& token) {
token_manager_.UnregisterPluginVmToken(token);
}
CameraHalDispatcherImpl::CameraHalDispatcherImpl()
: proxy_thread_("CameraProxyThread"),
blocking_io_thread_("CameraBlockingIOThread"),
main_task_runner_(base::SequencedTaskRunnerHandle::Get()),
camera_hal_server_callbacks_(this),
active_client_observers_(
new base::ObserverListThreadSafe<CameraActiveClientObserver>()),
current_privacy_switch_state_(
cros::mojom::CameraPrivacySwitchState::UNKNOWN),
privacy_switch_observers_(
new base::ObserverListThreadSafe<CameraPrivacySwitchObserver>()) {}
CameraHalDispatcherImpl::~CameraHalDispatcherImpl() {
VLOG(1) << "Stopping CameraHalDispatcherImpl...";
if (proxy_thread_.IsRunning()) {
proxy_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&CameraHalDispatcherImpl::StopOnProxyThread,
base::Unretained(this)));
proxy_thread_.Stop();
}
blocking_io_thread_.Stop();
CAMERA_LOG(EVENT) << "CameraHalDispatcherImpl stopped";
}
void CameraHalDispatcherImpl::RegisterServer(
mojo::PendingRemote<cros::mojom::CameraHalServer> camera_hal_server) {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
LOG(ERROR) << "CameraHalDispatcher::RegisterServer is deprecated. "
"CameraHalServer will not be registered.";
}
void CameraHalDispatcherImpl::RegisterServerWithToken(
mojo::PendingRemote<cros::mojom::CameraHalServer> camera_hal_server,
const base::UnguessableToken& token,
RegisterServerWithTokenCallback callback) {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
if (camera_hal_server_) {
LOG(ERROR) << "Camera HAL server is already registered";
std::move(callback).Run(-EALREADY,
failed_camera_hal_server_callbacks_.GetRemote());
return;
}
if (!token_manager_.AuthenticateServer(token)) {
LOG(ERROR) << "Failed to authenticate server";
std::move(callback).Run(-EPERM,
failed_camera_hal_server_callbacks_.GetRemote());
return;
}
camera_hal_server_.Bind(std::move(camera_hal_server));
camera_hal_server_.set_disconnect_handler(
base::BindOnce(&CameraHalDispatcherImpl::OnCameraHalServerConnectionError,
base::Unretained(this)));
CAMERA_LOG(EVENT) << "Camera HAL server registered";
std::move(callback).Run(
0, camera_hal_server_callbacks_.BindNewPipeAndPassRemote());
// Set up the Mojo channels for clients which registered before the server
// registers.
for (auto& client_observer : client_observers_) {
EstablishMojoChannel(client_observer.get());
}
}
void CameraHalDispatcherImpl::RegisterClient(
mojo::PendingRemote<cros::mojom::CameraHalClient> client) {
NOTREACHED() << "RegisterClient() is disabled";
}
void CameraHalDispatcherImpl::RegisterClientWithToken(
mojo::PendingRemote<cros::mojom::CameraHalClient> client,
cros::mojom::CameraClientType type,
const base::UnguessableToken& auth_token,
RegisterClientWithTokenCallback callback) {
base::UnguessableToken client_auth_token = auth_token;
// Unretained reference is safe here because CameraHalDispatcherImpl owns
// |proxy_thread_|.
proxy_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&CameraHalDispatcherImpl::RegisterClientWithTokenOnProxyThread,
base::Unretained(this), std::move(client), type,
std::move(client_auth_token),
media::BindToCurrentLoop(std::move(callback))));
}
void CameraHalDispatcherImpl::GetMjpegDecodeAccelerator(
mojo::PendingReceiver<chromeos_camera::mojom::MjpegDecodeAccelerator>
jda_receiver) {
jda_factory_.Run(std::move(jda_receiver));
}
void CameraHalDispatcherImpl::GetJpegEncodeAccelerator(
mojo::PendingReceiver<chromeos_camera::mojom::JpegEncodeAccelerator>
jea_receiver) {
jea_factory_.Run(std::move(jea_receiver));
}
void CameraHalDispatcherImpl::RegisterSensorClientWithToken(
mojo::PendingRemote<chromeos::sensors::mojom::SensorHalClient> client,
const base::UnguessableToken& auth_token,
RegisterSensorClientWithTokenCallback callback) {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&CameraHalDispatcherImpl::RegisterSensorClientWithTokenOnUIThread,
weak_factory_.GetWeakPtr(), std::move(client), auth_token,
BindToCurrentLoop(std::move(callback))));
}
void CameraHalDispatcherImpl::CameraDeviceActivityChange(
int32_t camera_id,
bool opened,
cros::mojom::CameraClientType type) {
VLOG(1) << type << (opened ? " opened " : " closed ") << "camera "
<< camera_id;
base::AutoLock lock(opened_camera_id_map_lock_);
auto& camera_id_set = opened_camera_id_map_[type];
if (opened) {
auto result = camera_id_set.insert(camera_id);
if (!result.second) { // No element inserted.
LOG(WARNING) << "Received duplicated open notification for camera "
<< camera_id;
return;
}
if (camera_id_set.size() == 1) {
VLOG(1) << type << " is active";
active_client_observers_->Notify(
FROM_HERE, &CameraActiveClientObserver::OnActiveClientChange, type,
/*is_active=*/true);
}
} else {
auto it = camera_id_set.find(camera_id);
if (it == camera_id_set.end()) {
// This can happen if something happened to the client process and it
// simultaneous lost connections to both CameraHalDispatcher and
// CameraHalServer.
LOG(WARNING) << "Received close notification for camera " << camera_id
<< " which is not opened";
return;
}
camera_id_set.erase(it);
if (camera_id_set.empty()) {
VLOG(1) << type << " is inactive";
active_client_observers_->Notify(
FROM_HERE, &CameraActiveClientObserver::OnActiveClientChange, type,
/*is_active=*/false);
}
}
}
void CameraHalDispatcherImpl::CameraPrivacySwitchStateChange(
cros::mojom::CameraPrivacySwitchState state) {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
base::AutoLock lock(privacy_switch_state_lock_);
current_privacy_switch_state_ = state;
privacy_switch_observers_->Notify(
FROM_HERE,
&CameraPrivacySwitchObserver::OnCameraPrivacySwitchStatusChanged,
current_privacy_switch_state_);
CAMERA_LOG(EVENT) << "Camera privacy switch state changed: "
<< current_privacy_switch_state_;
}
base::UnguessableToken CameraHalDispatcherImpl::GetTokenForTrustedClient(
cros::mojom::CameraClientType type) {
return token_manager_.GetTokenForTrustedClient(type);
}
void CameraHalDispatcherImpl::OnTraceLogEnabled() {
proxy_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&CameraHalDispatcherImpl::OnTraceLogEnabledOnProxyThread,
base::Unretained(this)));
}
void CameraHalDispatcherImpl::OnTraceLogDisabled() {
proxy_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&CameraHalDispatcherImpl::OnTraceLogDisabledOnProxyThread,
base::Unretained(this)));
}
void CameraHalDispatcherImpl::CreateSocket(base::WaitableEvent* started) {
DCHECK(blocking_io_task_runner_->BelongsToCurrentThread());
base::FilePath socket_path(kArcCamera3SocketPath);
mojo::NamedPlatformChannel::Options options;
options.server_name = socket_path.value();
mojo::NamedPlatformChannel channel(options);
if (!channel.server_endpoint().is_valid()) {
LOG(ERROR) << "Failed to create the socket file: " << kArcCamera3SocketPath;
started->Signal();
return;
}
// TODO(crbug.com/1053569): Remove these lines once the issue is solved.
base::File::Info info;
if (!base::GetFileInfo(socket_path, &info)) {
LOG(WARNING) << "Failed to get the socket info after building Mojo channel";
} else {
LOG(WARNING) << "Building Mojo channel. Socket info:"
<< " creation_time: " << info.creation_time
<< " last_accessed: " << info.last_accessed
<< " last_modified: " << info.last_modified;
}
// Change permissions on the socket.
struct group arc_camera_group;
struct group* result = nullptr;
char buf[1024];
if (HANDLE_EINTR(getgrnam_r(kArcCameraGroup, &arc_camera_group, buf,
sizeof(buf), &result)) < 0) {
PLOG(ERROR) << "getgrnam_r()";
started->Signal();
return;
}
if (!result) {
LOG(ERROR) << "Group '" << kArcCameraGroup << "' not found";
started->Signal();
return;
}
if (HANDLE_EINTR(chown(kArcCamera3SocketPath, -1, arc_camera_group.gr_gid)) <
0) {
PLOG(ERROR) << "chown()";
started->Signal();
return;
}
if (!base::SetPosixFilePermissions(socket_path, 0660)) {
PLOG(ERROR) << "Could not set permissions: " << socket_path.value();
started->Signal();
return;
}
blocking_io_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&CameraHalDispatcherImpl::StartServiceLoop,
base::Unretained(this),
channel.TakeServerEndpoint().TakePlatformHandle().TakeFD(),
base::Unretained(started)));
}
void CameraHalDispatcherImpl::StartServiceLoop(base::ScopedFD socket_fd,
base::WaitableEvent* started) {
DCHECK(blocking_io_task_runner_->BelongsToCurrentThread());
DCHECK(!proxy_fd_.is_valid());
DCHECK(!cancel_pipe_.is_valid());
DCHECK(socket_fd.is_valid());
base::ScopedFD cancel_fd;
if (!base::CreatePipe(&cancel_fd, &cancel_pipe_, true)) {
PLOG(ERROR) << "Failed to create cancel pipe";
started->Signal();
return;
}
proxy_fd_ = std::move(socket_fd);
started->Signal();
VLOG(1) << "CameraHalDispatcherImpl started; waiting for incoming connection";
while (true) {
if (!WaitForSocketReadable(proxy_fd_.get(), cancel_fd.get())) {
VLOG(1) << "Quit CameraHalDispatcherImpl IO thread";
return;
}
base::ScopedFD accepted_fd;
if (mojo::AcceptSocketConnection(proxy_fd_.get(), &accepted_fd, false) &&
accepted_fd.is_valid()) {
VLOG(1) << "Accepted a connection";
// Hardcode pid 0 since it is unused in mojo.
const base::ProcessHandle kUnusedChildProcessHandle = 0;
mojo::PlatformChannel channel;
mojo::OutgoingInvitation invitation;
// Generate an arbitrary 32-byte string, as we use this length as a
// protocol version identifier.
std::string token = GenerateRandomToken();
mojo::ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(token);
mojo::OutgoingInvitation::Send(std::move(invitation),
kUnusedChildProcessHandle,
channel.TakeLocalEndpoint());
auto remote_endpoint = channel.TakeRemoteEndpoint();
std::vector<base::ScopedFD> fds;
fds.emplace_back(remote_endpoint.TakePlatformHandle().TakeFD());
struct iovec iov = {const_cast<char*>(token.c_str()), token.length()};
ssize_t result =
mojo::SendmsgWithHandles(accepted_fd.get(), &iov, 1, fds);
if (result == -1) {
PLOG(ERROR) << "sendmsg()";
} else {
proxy_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&CameraHalDispatcherImpl::OnPeerConnected,
base::Unretained(this), std::move(pipe)));
}
}
}
}
void CameraHalDispatcherImpl::RegisterClientWithTokenOnProxyThread(
mojo::PendingRemote<cros::mojom::CameraHalClient> client,
cros::mojom::CameraClientType type,
base::UnguessableToken auth_token,
RegisterClientWithTokenCallback callback) {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
auto client_observer = std::make_unique<MojoCameraClientObserver>(
std::move(client), type, std::move(auth_token));
client_observer->client().set_disconnect_handler(base::BindOnce(
&CameraHalDispatcherImpl::OnCameraHalClientConnectionError,
base::Unretained(this), base::Unretained(client_observer.get())));
AddClientObserverOnProxyThread(std::move(client_observer),
std::move(callback));
}
void CameraHalDispatcherImpl::AddClientObserverOnProxyThread(
std::unique_ptr<CameraClientObserver> observer,
base::OnceCallback<void(int32_t)> result_callback) {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
if (!observer->Authenticate(&token_manager_)) {
LOG(ERROR) << "Failed to authenticate camera client observer";
std::move(result_callback).Run(-EPERM);
return;
}
if (camera_hal_server_) {
EstablishMojoChannel(observer.get());
}
client_observers_.insert(std::move(observer));
std::move(result_callback).Run(0);
CAMERA_LOG(EVENT) << "Camera HAL client registered";
}
void CameraHalDispatcherImpl::EstablishMojoChannel(
CameraClientObserver* client_observer) {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
mojo::PendingRemote<cros::mojom::CameraModule> camera_module;
const auto& type = client_observer->GetType();
CAMERA_LOG(EVENT) << "Establishing server channel for " << type;
camera_hal_server_->CreateChannel(
camera_module.InitWithNewPipeAndPassReceiver(), type);
client_observer->OnChannelCreated(std::move(camera_module));
}
void CameraHalDispatcherImpl::OnPeerConnected(
mojo::ScopedMessagePipeHandle message_pipe) {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
receiver_set_.Add(this,
mojo::PendingReceiver<cros::mojom::CameraHalDispatcher>(
std::move(message_pipe)));
VLOG(1) << "New CameraHalDispatcher binding added";
}
void CameraHalDispatcherImpl::OnCameraHalServerConnectionError() {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
base::AutoLock lock(opened_camera_id_map_lock_);
CAMERA_LOG(EVENT) << "Camera HAL server connection lost";
camera_hal_server_.reset();
camera_hal_server_callbacks_.reset();
for (auto& opened_camera_id_pair : opened_camera_id_map_) {
auto camera_client_type = opened_camera_id_pair.first;
const auto& camera_id_set = opened_camera_id_pair.second;
if (!camera_id_set.empty()) {
active_client_observers_->Notify(
FROM_HERE, &CameraActiveClientObserver::OnActiveClientChange,
camera_client_type, /*is_active=*/false);
}
}
opened_camera_id_map_.clear();
base::AutoLock privacy_lock(privacy_switch_state_lock_);
current_privacy_switch_state_ =
cros::mojom::CameraPrivacySwitchState::UNKNOWN;
privacy_switch_observers_->Notify(
FROM_HERE,
&CameraPrivacySwitchObserver::OnCameraPrivacySwitchStatusChanged,
current_privacy_switch_state_);
}
void CameraHalDispatcherImpl::OnCameraHalClientConnectionError(
CameraClientObserver* client_observer) {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
base::AutoLock lock(opened_camera_id_map_lock_);
auto camera_client_type = client_observer->GetType();
auto opened_it = opened_camera_id_map_.find(camera_client_type);
if (opened_it == opened_camera_id_map_.end()) {
// This can happen if this camera client never opened a camera.
return;
}
const auto& camera_id_set = opened_it->second;
if (!camera_id_set.empty()) {
active_client_observers_->Notify(
FROM_HERE, &CameraActiveClientObserver::OnActiveClientChange,
camera_client_type, /*is_active=*/false);
}
opened_camera_id_map_.erase(opened_it);
auto it = client_observers_.find(client_observer);
if (it != client_observers_.end()) {
client_observers_.erase(it);
CAMERA_LOG(EVENT) << "Camera HAL client connection lost";
}
}
void CameraHalDispatcherImpl::RegisterSensorClientWithTokenOnUIThread(
mojo::PendingRemote<chromeos::sensors::mojom::SensorHalClient> client,
const base::UnguessableToken& auth_token,
RegisterSensorClientWithTokenCallback callback) {
DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
if (!token_manager_.AuthenticateServerSensorClient(auth_token)) {
std::move(callback).Run(-EPERM);
return;
}
if (!chromeos::sensors::BindSensorHalClient(std::move(client))) {
LOG(ERROR) << "Failed to bind SensorHalClient to SensorHalDispatcher";
std::move(callback).Run(-ENOSYS);
return;
}
std::move(callback).Run(0);
}
void CameraHalDispatcherImpl::StopOnProxyThread() {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
// TODO(crbug.com/1053569): Remove these lines once the issue is solved.
base::File::Info info;
if (!base::GetFileInfo(base::FilePath(kArcCamera3SocketPath), &info)) {
LOG(WARNING) << "Failed to get socket info before deleting";
} else {
LOG(WARNING) << "Delete socket. Socket info:"
<< " creation_time: " << info.creation_time
<< " last_accessed: " << info.last_accessed
<< " last_modified: " << info.last_modified;
}
if (!base::DeleteFile(base::FilePath(kArcCamera3SocketPath))) {
LOG(ERROR) << "Failed to delete " << kArcCamera3SocketPath;
}
// Close |cancel_pipe_| to quit the loop in WaitForIncomingConnection.
cancel_pipe_.reset();
client_observers_.clear();
camera_hal_server_callbacks_.reset();
camera_hal_server_.reset();
receiver_set_.Clear();
}
void CameraHalDispatcherImpl::OnTraceLogEnabledOnProxyThread() {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
if (!camera_hal_server_) {
return;
}
bool camera_event_enabled = false;
TRACE_EVENT_CATEGORY_GROUP_ENABLED("camera", &camera_event_enabled);
if (camera_event_enabled) {
camera_hal_server_->SetTracingEnabled(true);
}
}
void CameraHalDispatcherImpl::OnTraceLogDisabledOnProxyThread() {
DCHECK(proxy_task_runner_->BelongsToCurrentThread());
if (!camera_hal_server_) {
return;
}
camera_hal_server_->SetTracingEnabled(false);
}
TokenManager* CameraHalDispatcherImpl::GetTokenManagerForTesting() {
return &token_manager_;
}
} // namespace media