blob: 64bf31e453d98ba3864fc3a2511ea17da9052247 [file] [log] [blame]
// Copyright 2024 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.
#ifndef LIB_DRIVER_METADATA_CPP_METADATA_SERVER_H_
#define LIB_DRIVER_METADATA_CPP_METADATA_SERVER_H_
#include <fidl/fuchsia.driver.framework/cpp/fidl.h>
#include <fidl/fuchsia.driver.metadata/cpp/fidl.h>
#include <lib/async/default.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/driver/component/cpp/node_add_args.h>
#include <lib/driver/logging/cpp/logger.h>
#include <lib/driver/logging/cpp/structured_logger.h>
#include <lib/driver/metadata/cpp/metadata.h>
#include <lib/driver/outgoing/cpp/outgoing_directory.h>
#if FUCHSIA_API_LEVEL_AT_LEAST(HEAD)
namespace fdf_metadata {
// Serves metadata that can be retrieved using `fdf_metadata::GetMetadata<|FidlType|>()`.
// `fdf_metadata::ObjectDetails<|FidlType|>::Name` must be defined. Expected to be used by driver
// components.
//
// As an example, lets say there exists a FIDl type `fuchsia.hardware.test/Metadata` to be sent from
// a driver to its child driver:
//
// library fuchsia.hardware.test;
//
// type Metadata = table {
// 1: test_property string:MAX;
// };
//
// The parent driver can define a `MetadataServer<fuchsia_hardware_test::Metadata>` server
// instance as one its members:
//
// namespace fdf_metadata {
//
// template <>
// struct ObjectDetails<fuchsia_hardware_test::Metadata> {
// inline static const char* Name = "fuchsia.hardware.test.Metadata";
// };
//
// } // namespace fdf_metadata
//
// class ParentDriver : public fdf::DriverBase {
// private:
// fdf_metadata::MetadataServer<fuchsia_hardware_test::Metadata> metadata_server_;
// }
//
// When the parent driver creates a child node, it can offer the metadata server's service to the
// child node by adding the metadata server's offers to the node-add arguments:
//
// auto args = fuchsia_driver_framework::NodeAddArgs args{{.offers2 =
// std::vector{metadata_server_.MakeOffer()}}};
//
// The parent driver should also declare the metadata server's capability and offer it in the
// driver's component manifest like so:
//
// capabilities: [
// { service: "fuchsia.hardware.test.Metadata" },
// ],
// expose: [
// {
// service: "fuchsia.hardware.test.Metadata",
// from: "self",
// },
// ],
//
// See `fdf_metadata::ObjectDetails` for more details about the name of the service.
template <typename FidlType>
class MetadataServer final : public fidl::WireServer<fuchsia_driver_metadata::Metadata> {
public:
// Make sure that the component manifest specifies `MetadataServer::kServiceName` as a service
// capability and exposes it.
explicit MetadataServer(
std::string instance_name = component::OutgoingDirectory::kDefaultServiceInstance)
: instance_name_(std::move(instance_name)) {}
// Set the metadata to be served to |metadata|. |metadata| must be persistable.
zx_status_t SetMetadata(const FidlType& metadata) {
static_assert(fidl::IsFidlType<FidlType>::value, "|FidlType| must be a FIDL domain object.");
static_assert(!fidl::IsResource<FidlType>::value,
"|FidlType| cannot be a resource type. Resources cannot be persisted.");
fit::result data = fidl::Persist(metadata);
if (data.is_error()) {
FDF_SLOG(ERROR, "Failed to persist metadata.",
KV("status", data.error_value().status_string()));
return data.error_value().status();
}
encoded_metadata_.emplace(data.value());
return ZX_OK;
}
// Sets the metadata to be served to the metadata found in |incoming|.
//
// If the metadata found in |incoming| changes after this function has been called then those
// changes will not be reflected in the metadata to be served.
//
// Make sure that the component manifest specifies that is uses the
// `fdf_metadata::ObjectDetails<|FidlType|>::Name` FIDL service
zx_status_t ForwardMetadata(
const std::shared_ptr<fdf::Namespace>& incoming,
std::string_view instance_name = component::OutgoingDirectory::kDefaultServiceInstance) {
fidl::WireSyncClient<fuchsia_driver_metadata::Metadata> client{};
{
zx::result result = ConnectToMetadataProtocol<FidlType>(incoming, instance_name);
if (result.is_error()) {
FDF_SLOG(ERROR, "Failed to connect to metadata server.",
KV("status", result.status_string()));
return result.status_value();
}
client.Bind(std::move(result.value()));
}
fidl::WireResult result = client->GetMetadata();
if (!result.ok()) {
FDF_SLOG(ERROR, "Failed to send GetMetadata request.", KV("status", result.status_string()));
return result.status();
}
if (result->is_error()) {
FDF_SLOG(ERROR, "Failed to get metadata.",
KV("status", zx_status_get_string(result->error_value())));
return result->error_value();
}
cpp20::span<uint8_t> metadata = result.value()->metadata.get();
std::vector<uint8_t> copy;
copy.insert(copy.begin(), metadata.begin(), metadata.end());
encoded_metadata_.emplace(std::move(copy));
return ZX_OK;
}
zx_status_t Serve(fdf::OutgoingDirectory& outgoing, async_dispatcher_t* dispatcher) {
return Serve(outgoing.component(), dispatcher);
}
// Serves the fuchsia.driver.metadata/Service service to |outgoing| under the service name
// `MetadataServer::kServiceName` and instance name `MetadataServer::instance_name_`.
zx_status_t Serve(component::OutgoingDirectory& outgoing, async_dispatcher_t* dispatcher) {
fuchsia_driver_metadata::Service::InstanceHandler handler{
{.metadata = bindings_.CreateHandler(this, dispatcher, fidl::kIgnoreBindingClosure)}};
zx::result result = outgoing.AddService(std::move(handler), kServiceName, instance_name_);
if (result.is_error()) {
FDF_SLOG(ERROR, "Failed to add service.", KV("status", result.status_string()));
return result.status_value();
}
return ZX_OK;
}
// Creates an offer for this `MetadataServer` instance's fuchsia.driver.metadata/Service
// service.
fuchsia_driver_framework::Offer MakeOffer() {
return fuchsia_driver_framework::Offer::WithZirconTransport(
fdf::MakeOffer(kServiceName, instance_name_));
}
fuchsia_driver_framework::wire::Offer MakeOffer(fidl::AnyArena& arena) {
return fuchsia_driver_framework::wire::Offer::WithZirconTransport(
arena, fdf::MakeOffer(arena, kServiceName, instance_name_));
}
private:
// Name of the service directory that will serve the fuchsia.driver.metadata/Service service.
inline static const std::string_view kServiceName = ObjectDetails<FidlType>::Name;
// fuchsia.driver.metadata/Metadata protocol implementation.
void GetMetadata(GetMetadataCompleter::Sync& completer) override {
if (!encoded_metadata_.has_value()) {
FDF_LOG(ERROR, "Metadata not set. Set metadata with SetMetadata() or ForwardMetadata()");
completer.ReplyError(ZX_ERR_BAD_STATE);
return;
}
completer.ReplySuccess(fidl::VectorView<uint8_t>::FromExternal(encoded_metadata_.value()));
}
fidl::ServerBindingGroup<fuchsia_driver_metadata::Metadata> bindings_;
// Encoded metadata that will be served in this instance's fuchsia.driver.metadata/Metadata
// protocol.
std::optional<std::vector<uint8_t>> encoded_metadata_;
// Name of the instance directory that will serve this instance's fuchsia.driver.metadata/Service
// service.
std::string instance_name_;
};
} // namespace fdf_metadata
#endif
#endif // LIB_DRIVER_METADATA_CPP_METADATA_SERVER_H_