This guide explains how to export a FIDL service from a parent driver and utilize it in a child driver. This guide assumes familiarity with the following concepts:
For a complete working example in the Fuchsia source tree, see the Zircon transport example.
The following snippets utilize this FIDL definition:
library fidl.examples.echo; const MAX_STRING_LENGTH uint64 = 32; @discoverable closed protocol Echo { /// Returns the input. strict EchoString(struct { value string:<MAX_STRING_LENGTH, optional>; }) -> (struct { response string:<MAX_STRING_LENGTH, optional>; }); }; service EchoService { echo client_end:Echo; };
The parent driver implements the fidl.examples.echo/Echo protocol, serves fidl.examples.echo.EchoService in its outgoing directory, and offers that service when adding a child node.
In the parent driver's manifest (.dml), declare and expose the FIDL service capability:
capabilities: [ { service: "fidl.examples.echo.EchoService" }, ], expose: [ { service: "fidl.examples.echo.EchoService", from: "self", }, ],
#include <fidl/fidl.examples.echo/cpp/wire.h> #include <lib/driver/component/cpp/driver_base2.h> #include <lib/driver/component/cpp/driver_export2.h> #include <lib/driver/component/cpp/node_add_args.h> #include <lib/driver/logging/cpp/logger.h> // This class implements the fidl.examples.echo/Echo FIDL protocol using the // C++ Wire bindings. class ParentDriver : public fdf::DriverBase2, public fidl::WireServer<fidl_examples_echo::Echo> { public: ParentDriver() : fdf::DriverBase2("parent") {} zx::result<> Start(fdf::DriverContext context) override { // Add the FIDL service we wish to export to our child to our outgoing // directory. When a connection is attempted we bind the server end of // the channel pair to our server implementation using the driver's default // dispatcher. fidl_examples_echo::EchoService::InstanceHandler handler({ .echo = bindings_.CreateHandler(this, dispatcher(), fidl::kIgnoreBindingClosure), }); zx::result result = outgoing()->AddService<fidl_examples_echo::EchoService>(std::move(handler)); if (result.is_error()) { fdf::error("Failed to add EchoService to outgoing directory: {}", result); return result.take_error(); } // Declare our outgoing service offer and add the child node. The driver // framework uses this offer to route the service to the child and // automatically populate node properties for binding. std::vector<fuchsia_driver_framework::NodeProperty2> properties = {}; zx::result child_result = AddChild( "echo_child", properties, std::array{fdf::MakeOffer2<fidl_examples_echo::EchoService>()}); if (child_result.is_error()) { fdf::error("Failed to add child node: {}", child_result); return child_result.take_error(); } controller_.Bind(std::move(child_result.value()), dispatcher()); return zx::ok(); } private: // Implementation of the method required by our FIDL protocol. void EchoString(EchoStringRequestView request, EchoStringCompleter::Sync& completer) override { completer.Reply(request->value); } fidl::WireClient<fuchsia_driver_framework::NodeController> controller_; // Ensures that the FIDL connections do not outlive the driver instance. fidl::ServerBindingGroup<fidl_examples_echo::Echo> bindings_; }; FUCHSIA_DRIVER_EXPORT2(ParentDriver);
In the child driver's manifest (.dml), declare that the driver uses the service:
use: [ { service: "fidl.examples.echo.EchoService" }, ],
Note that the child driver's bind rules are automatically generated from its DML.
The following code snippet shows how a child driver that has bound to the parent node connects to and calls the Echo protocol:
#include <fidl/fidl.examples.echo/cpp/wire.h> #include <lib/driver/component/cpp/driver_base2.h> #include <lib/driver/component/cpp/driver_export2.h> #include <lib/driver/logging/cpp/logger.h> class ChildDriver : public fdf::DriverBase2 { public: ChildDriver() : fdf::DriverBase2("child") {} zx::result<> Start(fdf::DriverContext context) override { // Connect to the protocol offered by our parent driver through our incoming // namespace. We do not need to specify the service or protocol name string // because Connect is templated on the generated service member type. zx::result client_end = context.incoming().Connect<fidl_examples_echo::EchoService::Echo>(); if (client_end.is_error()) { fdf::error("Failed to connect to Echo protocol: {}", client_end); return client_end.take_error(); } // Turn the client side of the endpoint pair into a synchronous client. fidl::WireSyncClient client{std::move(client_end.value())}; // Utilize our client to make calls. constexpr std::string_view kInput = "Test String"; fidl::WireResult result = client->EchoString(fidl::StringView::FromExternal(kInput)); if (!result.ok()) { fdf::error("Failed to call EchoString: {}", result.error()); return zx::error(result.status()); } if (result->response.get() != kInput) { fdf::error("Unexpected response: Actual: \"{}\", Expected: \"{}\"", result->response.get(), kInput); return zx::error(ZX_ERR_INTERNAL); } return zx::ok(); } }; FUCHSIA_DRIVER_EXPORT2(ChildDriver);