| /* |
| * Copyright 2020 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/libcamera_connector/camera_service_connector_impl.h" |
| |
| #include <errno.h> |
| #include <utility> |
| |
| #include <base/no_destructor.h> |
| #include <base/sequence_checker.h> |
| #include <mojo/core/embedder/embedder.h> |
| #include <mojo/core/embedder/scoped_ipc_support.h> |
| #include <mojo/public/cpp/bindings/binding.h> |
| |
| #include "common/libcamera_connector/types.h" |
| #include "cros-camera/common.h" |
| #include "cros-camera/constants.h" |
| #include "cros-camera/future.h" |
| #include "cros-camera/ipc_util.h" |
| #include "mojo/unguessable_token.mojom.h" |
| |
| namespace cros { |
| |
| CameraServiceConnector::CameraServiceConnector() |
| : ipc_thread_("CamConn"), camera_client_(nullptr) {} |
| |
| CameraServiceConnector* CameraServiceConnector::GetInstance() { |
| static base::NoDestructor<CameraServiceConnector> instance; |
| return instance.get(); |
| } |
| |
| int CameraServiceConnector::Init(const cros_cam_init_option_t* option) { |
| VLOGF_ENTER(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (initialized_.IsSet()) { |
| LOGF(ERROR) << "Should not run init() more than once"; |
| return -EPERM; |
| } |
| |
| // TODO(b/170075468): Remove support for api_version 0 when Parallels migrates |
| // to api_version 1. |
| if (option->api_version >= 1) { |
| token_ = TokenFromString(option->token); |
| if (token_.is_empty()) { |
| LOGF(ERROR) << "Failed to parse token string"; |
| return -EPERM; |
| } |
| } |
| |
| mojo::core::Init(); |
| bool ret = ipc_thread_.StartWithOptions( |
| base::Thread::Options(base::MessagePumpType::IO, 0)); |
| if (!ret) { |
| LOGF(ERROR) << "Failed to start IPC thread"; |
| return -ENODEV; |
| } |
| ipc_support_ = std::make_unique<mojo::core::ScopedIPCSupport>( |
| ipc_thread_.task_runner(), |
| mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN); |
| |
| auto future = cros::Future<int>::Create(nullptr); |
| ipc_thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CameraServiceConnector::InitOnThread, |
| base::Unretained(this), GetFutureCallback(future))); |
| int result = future->Get(); |
| if (result == 0) { |
| initialized_.Set(); |
| } |
| return result; |
| } |
| |
| int CameraServiceConnector::Exit() { |
| VLOGF_ENTER(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!initialized_.IsSet()) { |
| LOGF(ERROR) << "Should run init() before other functions"; |
| return -EPERM; |
| } |
| |
| int ret = camera_client_->Exit(); |
| |
| ipc_support_ = nullptr; |
| ipc_thread_.Stop(); |
| |
| return ret; |
| } |
| |
| int CameraServiceConnector::GetCameraInfo(cros_cam_get_cam_info_cb_t callback, |
| void* context) { |
| if (!initialized_.IsSet()) { |
| LOGF(ERROR) << "Should run init() before other functions"; |
| return -EPERM; |
| } |
| |
| return camera_client_->SetCameraInfoCallback(callback, context); |
| } |
| |
| int CameraServiceConnector::StartCapture( |
| const cros_cam_capture_request_t* request, |
| cros_cam_capture_cb_t callback, |
| void* context) { |
| if (!initialized_.IsSet()) { |
| LOGF(ERROR) << "Should run init() before other functions"; |
| return -EPERM; |
| } |
| |
| LOGF(INFO) << "StartCapture"; |
| return camera_client_->StartCapture(request, callback, context); |
| } |
| |
| int CameraServiceConnector::StopCapture(int id) { |
| if (!initialized_.IsSet()) { |
| LOGF(ERROR) << "Should run init() before other functions"; |
| return -EPERM; |
| } |
| |
| return camera_client_->StopCapture(id); |
| } |
| |
| void CameraServiceConnector::RegisterClient( |
| mojom::CameraHalClientPtr camera_hal_client, |
| IntOnceCallback on_registered_callback) { |
| VLOGF_ENTER(); |
| // This may be called from a different thread than the main thread, |
| // (for example here it is called from CameraClient thread), |
| // but mojo operations have to run on the same thread that bound |
| // the interface, so we bounce the request over to that thread/runner. |
| ipc_thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CameraServiceConnector::RegisterClientOnThread, |
| base::Unretained(this), std::move(camera_hal_client), |
| std::move(on_registered_callback))); |
| } |
| |
| void CameraServiceConnector::RegisterClientOnThread( |
| mojom::CameraHalClientPtr camera_hal_client, |
| IntOnceCallback on_registered_callback) { |
| VLOGF_ENTER(); |
| DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread()); |
| |
| if (token_.is_empty()) { |
| // TODO(b/170075468): Remove when this method is deprecated. |
| dispatcher_->RegisterClient(std::move(camera_hal_client)); |
| std::move(on_registered_callback).Run(0); |
| } else { |
| auto mojo_token = mojo_base::mojom::UnguessableToken::New(); |
| mojo_token->high = token_.GetHighForSerialization(); |
| mojo_token->low = token_.GetLowForSerialization(); |
| dispatcher_->RegisterClientWithToken( |
| std::move(camera_hal_client), cros::mojom::CameraClientType::UNKNOWN, |
| std::move(mojo_token), |
| base::BindOnce(&CameraServiceConnector::OnRegisteredClient, |
| base::Unretained(this), |
| std::move(on_registered_callback))); |
| } |
| } |
| |
| void CameraServiceConnector::OnRegisteredClient( |
| IntOnceCallback on_registered_callback, int32_t result) { |
| VLOGF_ENTER(); |
| DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread()); |
| |
| if (result != 0) { |
| LOGF(ERROR) << "Failed to register client: " << result; |
| } |
| std::move(on_registered_callback).Run(result); |
| } |
| |
| void CameraServiceConnector::InitOnThread(IntOnceCallback init_callback) { |
| VLOGF_ENTER(); |
| DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread()); |
| |
| mojo::ScopedMessagePipeHandle child_pipe; |
| base::FilePath socket_path(constants::kCrosCameraSocketPathString); |
| MojoResult res = |
| CreateMojoChannelToParentByUnixDomainSocket(socket_path, &child_pipe); |
| if (res != MOJO_RESULT_OK) { |
| LOGF(ERROR) << "Failed to create mojo channel to dispatcher"; |
| std::move(init_callback).Run(-ENODEV); |
| return; |
| } |
| |
| dispatcher_ = mojo::MakeProxy( |
| mojom::CameraHalDispatcherPtrInfo(std::move(child_pipe), 0u), |
| ipc_thread_.task_runner()); |
| bool connected = dispatcher_.is_bound(); |
| if (!connected) { |
| LOGF(ERROR) << "Failed to make a proxy to dispatcher"; |
| std::move(init_callback).Run(-ENODEV); |
| return; |
| } |
| dispatcher_.set_connection_error_handler(base::BindOnce( |
| &CameraServiceConnector::OnDispatcherError, base::Unretained(this))); |
| LOGF(INFO) << "Dispatcher connected"; |
| |
| camera_client_ = std::make_unique<CameraClient>(); |
| camera_client_->Init(base::BindOnce(&CameraServiceConnector::RegisterClient, |
| base::Unretained(this)), |
| std::move(init_callback)); |
| } |
| |
| void CameraServiceConnector::OnDispatcherError() { |
| VLOGF_ENTER(); |
| // TODO(b/151047930): Attempt to reconnect on dispatcher error. |
| LOGF(FATAL) << "Connection to camera dispatcher lost"; |
| } |
| |
| } // namespace cros |