| // Copyright 2020 The Fuchsia 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 "src/camera/bin/device_watcher/device_instance.h" |
| |
| #include <fuchsia/camera2/hal/cpp/fidl.h> |
| #include <lib/sys/service/cpp/service.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <zircon/errors.h> |
| |
| #include "fuchsia/io/cpp/fidl.h" |
| #include "lib/fit/function.h" |
| #include "lib/sys/cpp/service_directory.h" |
| |
| // Known path for the camera_device package manifest. |
| constexpr auto kCameraDeviceUrl = "fuchsia-pkg://fuchsia.com/camera_device#meta/camera_device.cmx"; |
| |
| // Arbitrary string identifying the camera device protocol. The protocol does not have a fixed name |
| // because it is not marked as [Discoverable]. |
| constexpr auto kCameraPublishedServiceName = "PublishedCameraService"; |
| |
| fpromise::result<std::unique_ptr<DeviceInstance>, zx_status_t> DeviceInstance::Create( |
| const fuchsia::sys::LauncherPtr& launcher, |
| fidl::InterfaceHandle<fuchsia::hardware::camera::Device> camera, |
| fit::closure on_component_unavailable, async_dispatcher_t* dispatcher) { |
| auto instance = std::make_unique<DeviceInstance>(); |
| instance->dispatcher_ = dispatcher; |
| |
| // Bind the camera channel. |
| zx_status_t status = instance->camera_.Bind(std::move(camera), instance->dispatcher_); |
| if (status != ZX_OK) { |
| FX_PLOGS(ERROR, status); |
| return fpromise::error(status); |
| } |
| |
| // Add the camera controller as an injected service. |
| fidl::InterfaceRequestHandler<fuchsia::camera2::hal::Controller> handler = |
| fit::bind_member(instance.get(), &DeviceInstance::OnControllerRequested); |
| status = instance->injected_services_dir_.AddEntry( |
| fuchsia::camera2::hal::Controller::Name_, std::make_unique<vfs::Service>(std::move(handler))); |
| if (status != ZX_OK) { |
| FX_PLOGS(ERROR, status); |
| return fpromise::error(status); |
| } |
| auto additional_services = std::make_unique<fuchsia::sys::ServiceList>(); |
| additional_services->names.push_back(fuchsia::camera2::hal::Controller::Name_); |
| |
| // Bind the injected services directory to the given channel. |
| fidl::InterfaceHandle<fuchsia::io::Directory> injected_services_dir_channel; |
| status = instance->injected_services_dir_.Serve( |
| fuchsia::io::OpenFlags::RIGHT_READABLE | fuchsia::io::OpenFlags::RIGHT_WRITABLE, |
| injected_services_dir_channel.NewRequest().TakeChannel()); |
| if (status != ZX_OK) { |
| FX_PLOGS(ERROR, status); |
| return fpromise::error(status); |
| } |
| |
| // Create a service directory into which the component will publish services. |
| zx::channel directory_request; |
| instance->published_services_ = sys::ServiceDirectory::CreateWithRequest(&directory_request); |
| |
| // Launch the Device component using the injected services channel. |
| fuchsia::sys::LaunchInfo launch_info{}; |
| launch_info.url = kCameraDeviceUrl; |
| launch_info.arguments = {kCameraPublishedServiceName}; |
| launch_info.directory_request = std::move(directory_request); |
| launch_info.additional_services = std::move(additional_services); |
| launch_info.additional_services->host_directory = injected_services_dir_channel.TakeChannel(); |
| launcher->CreateComponent(std::move(launch_info), |
| instance->component_controller_.NewRequest(instance->dispatcher_)); |
| |
| // Bind component event handlers. |
| instance->component_controller_.events().OnDirectoryReady = |
| fit::bind_member(instance.get(), &DeviceInstance::OnServicesReady); |
| instance->component_controller_.events().OnTerminated = |
| [callback = on_component_unavailable.share()](int64_t return_code, |
| fuchsia::sys::TerminationReason reason) { |
| FX_LOGS(WARNING) << "Camera Device Component exited with code " << return_code |
| << " and reason " << static_cast<uint32_t>(reason); |
| callback(); |
| }; |
| |
| // Bind error handlers. |
| instance->component_controller_.set_error_handler( |
| [callback = on_component_unavailable.share()](zx_status_t status) { |
| FX_PLOGS(WARNING, status) << "Component controller disconnected."; |
| // Invoke the callback here since OnTerminated won't be called. |
| callback(); |
| }); |
| instance->camera_.set_error_handler([instance = instance.get()](zx_status_t status) { |
| FX_PLOGS(WARNING, status) << "Camera device server disconnected."; |
| instance->camera_ = nullptr; |
| }); |
| |
| return fpromise::ok(std::move(instance)); |
| } |
| |
| void DeviceInstance::OnCameraRequested(fidl::InterfaceRequest<fuchsia::camera3::Device> request) { |
| if (!services_ready_) { |
| pending_requests_.push_back(std::move(request)); |
| return; |
| } |
| published_services_->Connect(std::move(request), kCameraPublishedServiceName); |
| } |
| |
| void DeviceInstance::OnServicesReady() { |
| services_ready_ = true; |
| for (auto& request : pending_requests_) { |
| OnCameraRequested(std::move(request)); |
| } |
| } |
| |
| void DeviceInstance::OnControllerRequested( |
| fidl::InterfaceRequest<fuchsia::camera2::hal::Controller> request) { |
| if (!camera_) { |
| request.Close(ZX_ERR_UNAVAILABLE); |
| return; |
| } |
| camera_->GetChannel2(std::move(request)); |
| } |