blob: 7a9831a61bb76cc6f867cf1d8e11d514b13a024f [file] [log] [blame]
// 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) {}
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) {
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 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 = 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 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->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 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));
}