blob: 04f0fc8dd1592b1fb7b5e4a409538d6355f39393 [file] [log] [blame]
/*
* Copyright 2018 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 "common/camera_mojo_channel_manager_impl.h"
#include <grp.h>
#include <string>
#include <utility>
#include <base/bind.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/no_destructor.h>
#include <mojo/core/embedder/embedder.h>
#include "cros-camera/common.h"
#include "cros-camera/constants.h"
#include "cros-camera/ipc_util.h"
namespace cros {
namespace {
constexpr ino_t kInvalidInodeNum = 0;
// Gets the socket file by |socket_path| and checks if it is in correct group
// and has correct permission. Returns |kInvalidInodeNum| if it is invalid.
// Otherwise, returns its inode number.
ino_t GetSocketInodeNumber(const base::FilePath& socket_path) {
// Ensure that socket file is ready before trying to connect the dispatcher.
struct group arc_camera_group;
struct group* result = nullptr;
char buf[1024];
getgrnam_r(constants::kArcCameraGroup, &arc_camera_group, buf, sizeof(buf),
&result);
if (!result) {
return kInvalidInodeNum;
}
int mode;
if (!base::GetPosixFilePermissions(socket_path, &mode) || mode != 0660) {
return kInvalidInodeNum;
}
struct stat st;
if (stat(socket_path.value().c_str(), &st) ||
st.st_gid != arc_camera_group.gr_gid) {
return kInvalidInodeNum;
}
return st.st_ino;
}
base::UnguessableToken ReadServerToken() {
static constexpr char kServerTokenPath[] = "/run/camera_tokens/server/token";
base::FilePath token_path(kServerTokenPath);
std::string token_string;
if (!base::ReadFileToString(token_path, &token_string)) {
LOGF(ERROR) << "Failed to read server token";
return {};
}
return cros::TokenFromString(token_string);
}
} // namespace
// static
CameraMojoChannelManagerImpl* CameraMojoChannelManagerImpl::instance_ = nullptr;
CameraMojoChannelManagerImpl::CameraMojoChannelManagerImpl()
: ipc_thread_("MojoIpcThread"), bound_socket_inode_num_(kInvalidInodeNum) {
instance_ = this;
if (!ipc_thread_.StartWithOptions(
base::Thread::Options(base::MessagePumpType::IO, 0))) {
LOGF(ERROR) << "Failed to start IPC Thread";
return;
}
mojo::core::Init();
ipc_support_ = std::make_unique<mojo::core::ScopedIPCSupport>(
ipc_thread_.task_runner(),
mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST);
base::FilePath socket_path(constants::kCrosCameraSocketPathString);
if (!watcher_.Watch(
socket_path, false,
base::Bind(&CameraMojoChannelManagerImpl::OnSocketFileStatusChange,
base::Unretained(this)))) {
LOGF(ERROR) << "Failed to watch socket path";
return;
}
}
CameraMojoChannelManagerImpl::~CameraMojoChannelManagerImpl() {
if (ipc_thread_.IsRunning()) {
ipc_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&CameraMojoChannelManagerImpl::TearDownMojoEnvOnIpcThread,
base::Unretained(this)));
ipc_thread_.Stop();
}
}
// static
CameraMojoChannelManagerToken* CameraMojoChannelManagerToken::CreateInstance() {
return new CameraMojoChannelManagerImpl();
}
// static
CameraMojoChannelManager* CameraMojoChannelManager::GetInstance() {
DCHECK(CameraMojoChannelManagerImpl::instance_ != nullptr);
return CameraMojoChannelManagerImpl::instance_;
}
scoped_refptr<base::SingleThreadTaskRunner>
CameraMojoChannelManagerImpl::GetIpcTaskRunner() {
CHECK(ipc_thread_.task_runner());
return ipc_thread_.task_runner();
}
void CameraMojoChannelManagerImpl::RegisterServer(
mojom::CameraHalServerPtr hal_ptr,
mojom::CameraHalDispatcher::RegisterServerWithTokenCallback
on_construct_callback,
Callback on_error_callback) {
DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread());
VLOGF_ENTER();
camera_hal_server_request_ = {
.requestOrPtr = std::move(hal_ptr),
.on_construct_callback = std::move(on_construct_callback),
.on_error_callback = std::move(on_error_callback)};
ipc_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&CameraMojoChannelManagerImpl::TryConnectToDispatcher,
base::Unretained(this)));
}
void CameraMojoChannelManagerImpl::CreateMjpegDecodeAccelerator(
mojom::MjpegDecodeAcceleratorRequest request,
Callback on_construct_callback,
Callback on_error_callback) {
DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread());
VLOGF_ENTER();
JpegPendingMojoRequest<mojom::MjpegDecodeAcceleratorRequest> pending_request =
{.requestOrPtr = std::move(request),
.on_construct_callback = std::move(on_construct_callback),
.on_error_callback = std::move(on_error_callback)};
jda_requests_.push_back(std::move(pending_request));
ipc_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&CameraMojoChannelManagerImpl::TryConnectToDispatcher,
base::Unretained(this)));
}
void CameraMojoChannelManagerImpl::CreateJpegEncodeAccelerator(
mojom::JpegEncodeAcceleratorRequest request,
Callback on_construct_callback,
Callback on_error_callback) {
DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread());
VLOGF_ENTER();
JpegPendingMojoRequest<mojom::JpegEncodeAcceleratorRequest> pending_request =
{.requestOrPtr = std::move(request),
.on_construct_callback = std::move(on_construct_callback),
.on_error_callback = std::move(on_error_callback)};
jea_requests_.push_back(std::move(pending_request));
ipc_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&CameraMojoChannelManagerImpl::TryConnectToDispatcher,
base::Unretained(this)));
}
mojom::CameraAlgorithmOpsPtr
CameraMojoChannelManagerImpl::CreateCameraAlgorithmOpsPtr(
const std::string& socket_path, const std::string& pipe_name) {
VLOGF_ENTER();
mojo::ScopedMessagePipeHandle parent_pipe;
mojom::CameraAlgorithmOpsPtr algorithm_ops;
base::FilePath socket_file_path(socket_path);
MojoResult result = cros::CreateMojoChannelToChildByUnixDomainSocket(
socket_file_path, &parent_pipe, pipe_name);
if (result != MOJO_RESULT_OK) {
LOGF(WARNING) << "Failed to create Mojo Channel to "
<< socket_file_path.value();
return nullptr;
}
algorithm_ops.Bind(
mojom::CameraAlgorithmOpsPtrInfo(std::move(parent_pipe), 0u));
LOGF(INFO) << "Connected to CameraAlgorithmOps";
VLOGF_EXIT();
return algorithm_ops;
}
void CameraMojoChannelManagerImpl::OnSocketFileStatusChange(
const base::FilePath& socket_path, bool error) {
if (error) {
LOGF(ERROR) << "Error occurs in socket file watcher.";
return;
}
ipc_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(
&CameraMojoChannelManagerImpl::OnSocketFileStatusChangeOnIpcThread,
base::Unretained(this)));
}
void CameraMojoChannelManagerImpl::OnSocketFileStatusChangeOnIpcThread() {
DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread());
VLOGF_ENTER();
base::FilePath socket_path(constants::kCrosCameraSocketPathString);
if (dispatcher_.is_bound()) {
// If the dispatcher is already bound but the inode number of the socket is
// unreadable or has been changed, we assume the other side of the
// dispatcher (Chrome) might be destroyed. As a result, we fire the on error
// event here in case it is not fired correctly.
if (bound_socket_inode_num_ != GetSocketInodeNumber(socket_path)) {
ipc_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&CameraMojoChannelManagerImpl::ResetDispatcherPtr,
base::Unretained(this)));
}
return;
}
ipc_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&CameraMojoChannelManagerImpl::TryConnectToDispatcher,
base::Unretained(this)));
}
void CameraMojoChannelManagerImpl::TryConnectToDispatcher() {
DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread());
VLOGF_ENTER();
if (dispatcher_.is_bound()) {
TryConsumePendingMojoRequests();
return;
}
base::FilePath socket_path(constants::kCrosCameraSocketPathString);
ino_t socket_inode_num = GetSocketInodeNumber(socket_path);
if (socket_inode_num == kInvalidInodeNum) {
return;
}
mojo::ScopedMessagePipeHandle child_pipe;
MojoResult result = cros::CreateMojoChannelToParentByUnixDomainSocket(
socket_path, &child_pipe);
if (result != MOJO_RESULT_OK) {
LOGF(WARNING) << "Failed to create Mojo Channel to " << socket_path.value();
return;
}
dispatcher_ = mojo::MakeProxy(
mojom::CameraHalDispatcherPtrInfo(std::move(child_pipe), 0u),
ipc_thread_.task_runner());
dispatcher_.set_connection_error_handler(
base::Bind(&CameraMojoChannelManagerImpl::ResetDispatcherPtr,
base::Unretained(this)));
bound_socket_inode_num_ = socket_inode_num;
TryConsumePendingMojoRequests();
}
void CameraMojoChannelManagerImpl::TryConsumePendingMojoRequests() {
DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread());
VLOGF_ENTER();
if (camera_hal_server_request_.requestOrPtr) {
auto server_token = ReadServerToken();
if (server_token.is_empty()) {
LOGF(ERROR) << "Failed to read server token";
std::move(camera_hal_server_request_.on_construct_callback)
.Run(-EPERM, nullptr);
} else {
auto token = mojo_base::mojom::UnguessableToken::New();
token->high = server_token.GetHighForSerialization();
token->low = server_token.GetLowForSerialization();
dispatcher_->RegisterServerWithToken(
std::move(camera_hal_server_request_.requestOrPtr), std::move(token),
std::move(camera_hal_server_request_.on_construct_callback));
}
}
for (auto& request : jda_requests_) {
if (request.requestOrPtr) {
dispatcher_->GetMjpegDecodeAccelerator(std::move(request.requestOrPtr));
std::move(request.on_construct_callback).Run();
}
}
for (auto& request : jea_requests_) {
if (request.requestOrPtr) {
dispatcher_->GetJpegEncodeAccelerator(std::move(request.requestOrPtr));
std::move(request.on_construct_callback).Run();
}
}
}
void CameraMojoChannelManagerImpl::TearDownMojoEnvOnIpcThread() {
DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread());
VLOGF_ENTER();
ResetDispatcherPtr();
ipc_support_.reset();
}
void CameraMojoChannelManagerImpl::ResetDispatcherPtr() {
DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread());
VLOGF_ENTER();
if (camera_hal_server_request_.on_error_callback) {
std::move(camera_hal_server_request_.on_error_callback).Run();
camera_hal_server_request_ = {};
}
for (auto& request : jda_requests_) {
if (request.on_error_callback) {
std::move(request.on_error_callback).Run();
}
}
jda_requests_.clear();
for (auto& request : jea_requests_) {
if (request.on_error_callback) {
std::move(request.on_error_callback).Run();
}
}
jea_requests_.clear();
dispatcher_.reset();
bound_socket_inode_num_ = kInvalidInodeNum;
}
} // namespace cros