FIDL 教學課程

本指南說明如何從父項驅動程式庫匯出 FIDL 服務,並在子項驅動程式庫中使用。本指南假設您已熟悉下列概念:

如需 Fuchsia 原始碼樹狀結構中的完整工作範例,請參閱 Zircon 傳輸範例。

FIDL 通訊協定定義

下列程式碼片段會使用這個 FIDL 定義:

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;
};

父項驅動程式 (伺服器)

父項驅動程式庫會實作 fidl.examples.echo/Echo 通訊協定,在其傳出目錄中提供 fidl.examples.echo.EchoService,並在新增子節點時提供該服務。

司機資訊清單

在父項驅動程式的資訊清單 (.dml) 中,宣告並公開 FIDL 服務能力:

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);

兒童駕駛人 (客戶)

司機資訊清單

在子驅動程式庫的資訊清單 (.dml) 中,宣告驅動程式庫使用該服務:

use: [
    { service: "fidl.examples.echo.EchoService" },
],

請注意,子驅動程式庫程式的繫結規則會自動從其 DML 產生。

客戶代碼

下列程式碼片段說明已繫結至父項節點的子項驅動程式庫,如何連線及呼叫 Echo 通訊協定:

#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);