Commit a0f814a3 authored by Sergey Ulanov's avatar Sergey Ulanov Committed by Commit Bot

[Fuchsia] Add ServiceDirectory class.

The new ServiceDirectory can be used to publish services for other
processes to consumer.
Also refactored GN templates in fuchsia-sdk to allow building FIDL
interfaces defined outside of the SDK and added a unittest that verifies
that a service published in ServicesDirectory can be consumed in
ComponentContext.

TBR=jam@chromium.org

Bug: 831384
Change-Id: Ie82e8cd6ff7c02a791ec8b5534f55a527b08a571
Reviewed-on: https://chromium-review.googlesource.com/1027126
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarSergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#555215}
parent 6c17168f
......@@ -1305,6 +1305,9 @@ include_rules = [
'+build',
'+ipc',
# FIDL (Fuchsia IDL) generates headers under fuchsia/cpp.
"+fuchsia/cpp",
# Everybody can use headers generated by tools/generate_library_loader.
'+library_loaders',
......
......@@ -60,6 +60,10 @@ if (is_android) {
import("//build/config/android/rules.gni")
}
if (is_fuchsia) {
import("//third_party/fuchsia-sdk/fidl_library.gni")
}
config("base_flags") {
if (is_clang) {
cflags = [
......@@ -1340,6 +1344,8 @@ jumbo_component("base") {
"fuchsia/fuchsia_logging.h",
"fuchsia/scoped_zx_handle.cc",
"fuchsia/scoped_zx_handle.h",
"fuchsia/services_directory.cc",
"fuchsia/services_directory.h",
"memory/platform_shared_memory_region_fuchsia.cc",
"memory/protected_memory_posix.cc",
"memory/shared_memory_fuchsia.cc",
......@@ -1391,6 +1397,8 @@ jumbo_component("base") {
deps += [
"//third_party/fuchsia-sdk:async_default",
"//third_party/fuchsia-sdk:fdio",
"//third_party/fuchsia-sdk:fidl",
"//third_party/fuchsia-sdk:svc",
"//third_party/fuchsia-sdk:zx",
]
......@@ -2110,6 +2118,14 @@ if (is_ios || is_mac) {
}
}
if (is_fuchsia) {
fidl_library("test_fidl") {
sources = [
"fuchsia/test.fidl",
]
}
}
test("base_unittests") {
sources = [
"allocator/allocator_interception_mac_unittest.mm",
......@@ -2587,6 +2603,7 @@ test("base_unittests") {
sources += [
"files/dir_reader_posix_unittest.cc",
"files/file_descriptor_watcher_posix_unittest.cc",
"fuchsia/services_directory_unittest.cc",
"message_loop/message_loop_io_posix_unittest.cc",
"posix/file_descriptor_shuffle_unittest.cc",
"task_scheduler/task_tracker_posix_unittest.cc",
......@@ -2594,6 +2611,7 @@ test("base_unittests") {
sources += [ "fuchsia/async_dispatcher_unittest.cc" ]
deps += [
":test_fidl",
"//third_party/fuchsia-sdk:async",
"//third_party/fuchsia-sdk:async_default",
]
......
......@@ -6,6 +6,8 @@
#include <fdio/util.h>
#include "base/fuchsia/scoped_zx_handle.h"
#include "base/fuchsia/services_directory.h"
#include "base/no_destructor.h"
namespace base {
......@@ -26,12 +28,17 @@ ScopedZxHandle ConnectToServiceRoot() {
} // namespace
ComponentContext::ComponentContext() : service_root_(ConnectToServiceRoot()) {}
ComponentContext::ComponentContext(ScopedZxHandle service_root)
: service_root_(std::move(service_root)) {
DCHECK(service_root_);
}
ComponentContext::~ComponentContext() = default;
// static
ComponentContext* ComponentContext::GetDefault() {
static base::NoDestructor<ComponentContext> component_context;
static base::NoDestructor<ComponentContext> component_context(
ConnectToServiceRoot());
return component_context.get();
}
......
......@@ -24,17 +24,14 @@ class SynchronousInterfacePtr;
namespace base {
namespace fuchsia {
// Provides access to the component's environment and allows it to publish
// outgoing services.
// Provides access to the component's environment.
class BASE_EXPORT ComponentContext {
public:
ComponentContext();
explicit ComponentContext(ScopedZxHandle service_root);
~ComponentContext();
// Returns default ComponentContext instance for the current process. The
// default context uses /srv namespace to connect to environment services and
// publishes outgoing services to the directory provided by the process
// creator.
// Returns default ComponentContext instance for the current process. It uses
// /srv namespace to connect to environment services.
static ComponentContext* GetDefault();
// Satisfies the interface |request| by binding the channel to a service.
......
// Copyright 2018 The Chromium 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 BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_
#define BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_
#include <lib/fidl/cpp/binding.h>
#include "base/bind.h"
#include "base/fuchsia/services_directory.h"
namespace base {
namespace fuchsia {
class ServicesDirectory;
template <typename Interface>
class ScopedServiceBinding {
public:
// |services_directory| and |impl| must outlive the binding.
ScopedServiceBinding(ServicesDirectory* services_directory, Interface* impl)
: directory_(services_directory), binding_(impl) {
directory_->AddService(
Interface::Name_,
BindRepeating(&ScopedServiceBinding::BindClient, Unretained(this)));
}
~ScopedServiceBinding() { directory_->RemoveService(Interface::Name_); }
private:
void BindClient(ScopedZxHandle channel) {
binding_.Bind(typename fidl::InterfaceRequest<Interface>(
zx::channel(channel.release())));
}
ServicesDirectory* directory_;
fidl::Binding<Interface> binding_;
DISALLOW_COPY_AND_ASSIGN(ScopedServiceBinding);
};
} // namespace fuchsia
} // namespace base
#endif // BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_
\ No newline at end of file
// Copyright 2018 The Chromium 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 "base/fuchsia/services_directory.h"
#include <lib/async/default.h>
#include <lib/svc/dir.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include "base/fuchsia/fuchsia_logging.h"
#include "base/message_loop/message_loop_current.h"
#include "base/no_destructor.h"
namespace base {
namespace fuchsia {
ServicesDirectory::ServicesDirectory(ScopedZxHandle directory_request) {
zx_status_t status = svc_dir_create(async_get_default(),
directory_request.release(), &svc_dir_);
ZX_CHECK(status == ZX_OK, status);
}
ServicesDirectory::~ServicesDirectory() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(services_.empty());
zx_status_t status = svc_dir_destroy(svc_dir_);
ZX_DCHECK(status == ZX_OK, status);
}
// static
ServicesDirectory* ServicesDirectory::GetDefault() {
static base::NoDestructor<ServicesDirectory> directory(
ScopedZxHandle(zx_get_startup_handle(PA_DIRECTORY_REQUEST)));
return directory.get();
}
void ServicesDirectory::AddService(StringPiece name,
ConnectServiceCallback connect_callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(services_.find(name) == services_.end());
std::string name_str = name.as_string();
services_[name_str] = connect_callback;
zx_status_t status =
svc_dir_add_service(svc_dir_, "public", name_str.c_str(), this,
&ServicesDirectory::HandleConnectRequest);
ZX_DCHECK(status == ZX_OK, status);
}
void ServicesDirectory::RemoveService(StringPiece name) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
std::string name_str = name.as_string();
auto it = services_.find(name_str);
DCHECK(it != services_.end());
services_.erase(it);
zx_status_t status =
svc_dir_remove_service(svc_dir_, "public", name_str.c_str());
ZX_DCHECK(status == ZX_OK, status);
}
// static
void ServicesDirectory::HandleConnectRequest(void* context,
const char* service_name,
zx_handle_t service_request) {
auto* directory = reinterpret_cast<ServicesDirectory*>(context);
DCHECK_CALLED_ON_VALID_THREAD(directory->thread_checker_);
auto it = directory->services_.find(service_name);
// HandleConnectRequest() is expected to be called only for registered
// services.
DCHECK(it != directory->services_.end());
it->second.Run(ScopedZxHandle(service_request));
}
} // namespace fuchsia
} // namespace base
// Copyright 2018 The Chromium 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 BASE_FUCHSIA_SERVICES_DIRECTORY_H_
#define BASE_FUCHSIA_SERVICES_DIRECTORY_H_
#include "base/base_export.h"
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/fuchsia/scoped_zx_handle.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "base/threading/thread_checker.h"
typedef struct svc_dir svc_dir_t;
namespace base {
namespace fuchsia {
// Directory of FIDL services published for other processes to consume. Services
// published in this directory can be discovered from other processes by name.
// Normally this class should be used by creating a ScopedServiceBinding
// instance. This ensures that the service is unregistered when the
// implementation is destroyed. GetDefault() should be used to get the default
// ServicesDirectory for the current process. The default instance exports
// services via a channel supplied at process creation time.
//
// Not thread-safe. All methods must be called on the thread that created the
// object.
class BASE_EXPORT ServicesDirectory {
public:
// Callback called to connect incoming requests.
using ConnectServiceCallback =
base::RepeatingCallback<void(ScopedZxHandle channel)>;
// Creates services directory that will be served over the
// |directory_channel|.
explicit ServicesDirectory(ScopedZxHandle directory_channel);
~ServicesDirectory();
// Returns default ServiceDirectory instance for the current process. It
// publishes services to the directory provided by the process creator.
ServicesDirectory* GetDefault();
void AddService(StringPiece name, ConnectServiceCallback connect_callback);
void RemoveService(StringPiece name);
private:
// Called by |svc_dir_| to handle service requests.
static void HandleConnectRequest(void* context,
const char* service_name,
zx_handle_t service_request);
THREAD_CHECKER(thread_checker_);
svc_dir_t* svc_dir_ = nullptr;
base::flat_map<std::string, ConnectServiceCallback> services_;
DISALLOW_COPY_AND_ASSIGN(ServicesDirectory);
};
} // namespace fuchsia
} // namespace base
#endif // BASE_FUCHSIA_SERVICES_DIRECTORY_H_
\ No newline at end of file
// Copyright 2018 The Chromium 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 "base/fuchsia/services_directory.h"
#include <fdio/util.h>
#include "base/bind.h"
#include "base/fuchsia/component_context.h"
#include "base/fuchsia/scoped_service_binding.h"
#include "base/message_loop/message_loop.h"
#include "fuchsia/cpp/test_fidl.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace fuchsia {
class TestInterfaceImpl : public test_fidl::TestInterface {
public:
void Add(int32_t a, int32_t b, AddCallback callback) override {
callback(a + b);
}
};
// Verifies that a service connected by ServicesDirectory can be imported from
// another ServicesDirectory.
TEST(ServicesDirectoryTest, Connect) {
MessageLoopForIO message_loop_;
ScopedZxHandle dir_service_handle;
ScopedZxHandle dir_client_handle;
ASSERT_EQ(zx_channel_create(0, dir_service_handle.receive(),
dir_client_handle.receive()),
ZX_OK);
// Mount service dir and publish the service.
ServicesDirectory service_dir(std::move(dir_service_handle));
TestInterfaceImpl test_service;
ScopedServiceBinding<test_fidl::TestInterface> service_binding(&service_dir,
&test_service);
// Open public directory from the service directory.
ScopedZxHandle public_dir_service_handle;
ScopedZxHandle public_dir_client_handle;
ASSERT_EQ(zx_channel_create(0, public_dir_service_handle.receive(),
public_dir_client_handle.receive()),
ZX_OK);
ASSERT_EQ(fdio_open_at(dir_client_handle.get(), "public", 0,
public_dir_service_handle.release()),
ZX_OK);
// Create ComponentContext and connect to the test service.
ComponentContext client_context(std::move(public_dir_client_handle));
auto stub = client_context.ConnectToService<test_fidl::TestInterface>();
// Call the service and wait for response.
base::RunLoop run_loop;
bool error = false;
stub.set_error_handler([&run_loop, &error]() {
error = true;
run_loop.Quit();
});
stub->Add(2, 2, [&run_loop](int32_t result) {
EXPECT_EQ(result, 4);
run_loop.Quit();
});
run_loop.Run();
EXPECT_FALSE(error);
}
} // namespace fuchsia
} // namespace base
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library test_fidl;
[Discoverable]
interface TestInterface {
1: Add(int32 a, int32 b) -> (int32 sum);
};
......@@ -189,3 +189,9 @@ fuchsia_sdk_pkg("netstack") {
"netstack.fidl",
]
}
fuchsia_sdk_lib_pkg("svc") {
sources = [
"include/lib/svc/dir.h",
]
}
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
assert(is_fuchsia)
template("fidl_library") {
pkg_name = target_name
pkg_name = target_name
if (defined(invoker.name)) {
pkg_name = invoker.name
}
response_file = "$target_gen_dir/$target_name.rsp"
json_representation = "$target_gen_dir/$pkg_name.fidl.json"
output_gen_base = "$target_gen_dir/fidl"
output_gen_dir = "$output_gen_base/fuchsia/cpp"
tables_file = "$output_gen_base/$pkg_name.fidl-tables.cc"
action("${target_name}_response_file") {
visibility = [ ":*" ]
script = "//third_party/fuchsia-sdk/gen_fidl_response_file.py"
forward_variables_from(invoker,
[
"deps",
"public_deps",
"sources",
"testonly",
])
libraries_file = "$target_gen_dir/$pkg_name.fidl_libraries"
outputs = [
response_file,
libraries_file,
]
args = [
"--out-response-file",
rebase_path(response_file, root_build_dir),
"--out-libraries",
rebase_path(libraries_file, root_build_dir),
"--tables",
rebase_path(tables_file, root_build_dir),
"--json",
rebase_path(json_representation, root_build_dir),
"--name",
pkg_name,
"--sources",
] + rebase_path(sources, root_build_dir)
if (defined(invoker.fidl_deps)) {
dep_libraries = []
deps = []
foreach(dep, invoker.fidl_deps) {
gen_dir = get_label_info(dep, "target_gen_dir")
name = get_label_info(dep, "name")
dep_libraries += [ "$gen_dir/$name.fidl_libraries" ]
deps += [ "${dep}_response_file" ]
}
inputs = dep_libraries
args += [ "--dep-libraries" ] + rebase_path(dep_libraries, root_build_dir)
}
}
action("${target_name}_compile") {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
deps = [
":${invoker.target_name}_response_file",
]
script = "//build/gn_run_binary.py"
inputs = [
response_file,
]
outputs = [
json_representation,
tables_file,
]
rebased_response_file = rebase_path(response_file, root_build_dir)
args = [
rebase_path("//third_party/fuchsia-sdk/sdk/tools/fidlc", root_build_dir),
"@$rebased_response_file",
]
}
action("${target_name}_cpp_gen") {
visibility = [ ":${invoker.target_name}" ]
deps = [
":${invoker.target_name}_compile",
]
inputs = [
json_representation,
]
outputs = [
"$output_gen_dir/$pkg_name.h",
"$output_gen_dir/$pkg_name.cc",
]
script = "//build/gn_run_binary.py"
args = [
rebase_path("//third_party/fuchsia-sdk/sdk/tools/fidlgen",
root_build_dir),
"-generators",
"cpp",
"-json",
rebase_path("$json_representation"),
"-include-base",
rebase_path("$output_gen_base"),
"-output-base",
rebase_path("$output_gen_dir/$pkg_name"),
]
}
config("${target_name}_config") {
visibility = [ ":${invoker.target_name}" ]
include_dirs = [ output_gen_base ]
}
source_set("${target_name}") {
forward_variables_from(invoker,
[
"deps",
"public_deps",
"testonly",
"visibility",
])
sources = [
"$output_gen_dir/$pkg_name.cc",
"$output_gen_dir/$pkg_name.h",
tables_file,
]
if (!defined(deps)) {
deps = []
}
deps += [
":${invoker.target_name}_compile",
":${invoker.target_name}_cpp_gen",
]
if (!defined(public_deps)) {
public_deps = []
}
public_deps += [ "//third_party/fuchsia-sdk:fidl" ]
public_deps += [ "//third_party/fuchsia-sdk:fidl_cpp" ]
public_configs = [ ":${invoker.target_name}_config" ]
}
}
......@@ -4,6 +4,8 @@
assert(is_fuchsia)
import("fidl_library.gni")
template("fuchsia_sdk_pkg") {
pkg_name = target_name
......@@ -15,137 +17,32 @@ template("fuchsia_sdk_pkg") {
have_fidl = defined(invoker.fidl_files)
if (have_fidl) {
response_file = "$target_gen_dir/$target_name.rsp"
json_representation = "$target_gen_dir/$pkg_name.fidl.json"
output_gen_base = "$target_gen_dir/fidl"
output_gen_dir = "$output_gen_base/fuchsia/cpp"
tables_file = "$output_gen_base/$pkg_name.fidl-tables.cc"
action("${target_name}_response_file") {
visibility = [ ":*" ]
script = "gen_fidl_response_file.py"
fidl_library("${target_name}_fidl") {
name = pkg_name
forward_variables_from(invoker,
[
"deps",
"public_deps",
"testonly",
"visibility",
])
sources = []
foreach(file, invoker.fidl_files) {
sources += [ "sdk/fidl/${pkg_name}/${file}" ]
}
libraries_file = "$target_gen_dir/$pkg_name.fidl_libraries"
outputs = [
response_file,
libraries_file,
]
args = [
"--out-response-file",
rebase_path(response_file, root_build_dir),
"--out-libraries",
rebase_path(libraries_file, root_build_dir),
"--tables",
rebase_path(tables_file, root_build_dir),
"--json",
rebase_path(json_representation, root_build_dir),
"--name",
pkg_name,
"--sources",
] + rebase_path(sources, root_build_dir)
if (defined(invoker.fidl_deps)) {
dep_libraries = []
deps = []
foreach(dep, invoker.fidl_deps) {
gen_dir = get_label_info(dep, "target_gen_dir")
name = get_label_info(dep, "name")
dep_libraries += [ "$gen_dir/$name.fidl_libraries" ]
deps += [ "${dep}_response_file" ]
}
inputs = dep_libraries
args +=
[ "--dep-libraries" ] + rebase_path(dep_libraries, root_build_dir)
}
}
action("${target_name}_compile") {
forward_variables_from(invoker, [ "testonly" ])
visibility = [ ":*" ]
deps = [
":${invoker.target_name}_response_file",
]
script = "//build/gn_run_binary.py"
inputs = [
response_file,
]
outputs = [
json_representation,
tables_file,
]
rebased_response_file = rebase_path(response_file, root_build_dir)
args = [
rebase_path("sdk/tools/fidlc", root_build_dir),
"@$rebased_response_file",
]
}
action("${target_name}_cpp_gen") {
visibility = [ ":*" ]
deps = [
":${invoker.target_name}_compile",
]
inputs = [
json_representation,
]
outputs = [
"$output_gen_dir/$pkg_name.h",
"$output_gen_dir/$pkg_name.cc",
]
script = "//build/gn_run_binary.py"
args = [
rebase_path("sdk/tools/fidlgen", root_build_dir),
"-generators",
"cpp",
"-json",
rebase_path("$json_representation"),
"-include-base",
rebase_path("$output_gen_base"),
"-output-base",
rebase_path("$output_gen_dir/$pkg_name"),
]
}
}
config("${target_name}_config") {
visibility = [ ":${invoker.target_name}" ]
include_dirs = [ "sdk/pkg/${pkg_name}/include" ]
if (have_fidl) {
include_dirs += [ output_gen_base ]
}
}
static_library("${target_name}") {
forward_variables_from(invoker,
[
"data",
"deps",
"public_deps",
"testonly",
......@@ -160,25 +57,9 @@ template("fuchsia_sdk_pkg") {
}
if (have_fidl) {
if (!defined(deps)) {
deps = []
}
deps += [
":${invoker.target_name}_compile",
":${invoker.target_name}_cpp_gen",
]
sources = [
"$output_gen_dir/$pkg_name.cc",
"$output_gen_dir/$pkg_name.h",
tables_file,
public_deps = [
":${invoker.target_name}_fidl",
]
assert(pkg_name != "fidl" && pkg_name != "fidl_cpp")
if (!defined(public_deps)) {
public_deps = []
}
public_deps += [ ":fidl_cpp" ]
}
public_configs = [ ":${invoker.target_name}_config" ]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment