| // 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"; | |
| DeviceInstance::DeviceInstance() : loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {} | |
| fit::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) { | |
| auto instance = std::make_unique<DeviceInstance>(); | |
| // Bind the camera channel. | |
| zx_status_t status = instance->camera_.Bind(std::move(camera), instance->loop_.dispatcher()); | |
| if (status != ZX_OK) { | |
| FX_PLOGS(ERROR, status); | |
| return fit::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 fit::error(status); | |
| } | |
| auto additional_services = fuchsia::sys::ServiceList::New(); | |
| 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::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE, | |
| injected_services_dir_channel.NewRequest().TakeChannel()); | |
| if (status != ZX_OK) { | |
| FX_PLOGS(ERROR, status); | |
| return fit::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->loop_.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; | |
| }); | |
| // Start the loop. | |
| ZX_ASSERT(instance->loop_.StartThread("Camera Device Instance") == ZX_OK); | |
| return fit::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(request.TakeChannel()); | |
| } |