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

Reland: [Fuchsia] Use sys::OutgoingDirectory in //base/fuchsia.

Now base::fuchsia::ServiceDirectory is a wrapper around
OutgoingDirectory. Most code outside of //base/fuchsia still uses
base::fuchsia::ServiceDirectory and should continue function the same
way as it did before.

Bug: 920920
Change-Id: I915454b8fb7929b66bd0cc5ae4372d2e6499ed8a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1721558
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#686288}
parent d46d3686
......@@ -1522,6 +1522,8 @@ jumbo_component("base") {
"files/file_posix.cc",
"files/file_util_posix.cc",
"files/memory_mapped_file_posix.cc",
"fuchsia/default_context.cc",
"fuchsia/default_context.h",
"fuchsia/default_job.cc",
"fuchsia/default_job.h",
"fuchsia/file_utils.cc",
......@@ -1530,6 +1532,8 @@ jumbo_component("base") {
"fuchsia/filtered_service_directory.h",
"fuchsia/fuchsia_logging.cc",
"fuchsia/fuchsia_logging.h",
"fuchsia/scoped_service_binding.cc",
"fuchsia/scoped_service_binding.h",
"fuchsia/service_directory.cc",
"fuchsia/service_directory.h",
"fuchsia/service_directory_client.cc",
......@@ -1592,6 +1596,7 @@ jumbo_component("base") {
"//third_party/fuchsia-sdk/sdk:fdio",
"//third_party/fuchsia-sdk/sdk:fidl_cpp",
"//third_party/fuchsia-sdk/sdk:io",
"//third_party/fuchsia-sdk/sdk:sys_cpp",
"//third_party/fuchsia-sdk/sdk:zx",
]
......@@ -1600,9 +1605,9 @@ jumbo_component("base") {
"//third_party/fuchsia-sdk/sdk:async_loop_cpp",
"//third_party/fuchsia-sdk/sdk:deprecatedtimezone",
"//third_party/fuchsia-sdk/sdk:fidl",
"//third_party/fuchsia-sdk/sdk:svc",
"//third_party/fuchsia-sdk/sdk:sys",
"//third_party/fuchsia-sdk/sdk:syslog",
"//third_party/fuchsia-sdk/sdk:vfs_cpp",
]
}
......@@ -3024,7 +3029,6 @@ test("base_unittests") {
"fuchsia/scoped_service_binding_unittest.cc",
"fuchsia/service_directory_test_base.cc",
"fuchsia/service_directory_test_base.h",
"fuchsia/service_directory_unittest.cc",
"fuchsia/service_provider_impl_unittest.cc",
"message_loop/message_loop_io_posix_unittest.cc",
"posix/file_descriptor_shuffle_unittest.cc",
......@@ -3042,6 +3046,7 @@ test("base_unittests") {
"//third_party/fuchsia-sdk/sdk:fdio",
"//third_party/fuchsia-sdk/sdk:logger",
"//third_party/fuchsia-sdk/sdk:sys",
"//third_party/fuchsia-sdk/sdk:sys_cpp",
]
}
......
// Copyright 2019 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/default_context.h"
#include <lib/sys/cpp/component_context.h>
#include "base/fuchsia/file_utils.h"
#include "base/logging.h"
#include "base/no_destructor.h"
namespace base {
namespace fuchsia {
sys::ComponentContext* ComponentContextForCurrentProcess() {
static base::NoDestructor<std::unique_ptr<sys::ComponentContext>> value(
std::make_unique<sys::ComponentContext>(
sys::ServiceDirectory::CreateFromNamespace()));
return value.get()->get();
}
} // namespace fuchsia
} // namespace base
// Copyright 2019 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_DEFAULT_CONTEXT_H_
#define BASE_FUCHSIA_DEFAULT_CONTEXT_H_
#include <memory>
#include "base/base_export.h"
namespace sys {
class ComponentContext;
} // namespace sys
namespace base {
namespace fuchsia {
// Returns default sys::ComponentContext for the current process.
BASE_EXPORT sys::ComponentContext* ComponentContextForCurrentProcess();
} // namespace fuchsia
} // namespace base
#endif // BASE_FUCHSIA_DEFAULT_CONTEXT_H_
\ No newline at end of file
......@@ -4,51 +4,42 @@
#include "base/fuchsia/filtered_service_directory.h"
#include <lib/fdio/directory.h>
#include <lib/async/default.h>
#include <utility>
#include "base/bind.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/service_directory_client.h"
namespace base {
namespace fuchsia {
FilteredServiceDirectory::FilteredServiceDirectory(
const ServiceDirectoryClient* directory)
: directory_(directory) {
outgoing_directory_ = std::make_unique<ServiceDirectory>(
outgoing_directory_client_.NewRequest());
sys::ServiceDirectory* directory)
: directory_(std::move(directory)) {
outgoing_directory_.Serve(
outgoing_directory_client_.NewRequest().TakeChannel());
}
FilteredServiceDirectory::~FilteredServiceDirectory() {
outgoing_directory_->RemoveAllServices();
}
FilteredServiceDirectory::~FilteredServiceDirectory() {}
void FilteredServiceDirectory::AddService(const char* service_name) {
outgoing_directory_->AddServiceUnsafe(
service_name,
base::BindRepeating(&FilteredServiceDirectory::HandleRequest,
base::Unretained(this), service_name));
outgoing_directory_.AddPublicService(
std::make_unique<vfs::Service>(
[this, service_name](zx::channel channel,
async_dispatcher_t* dispatcher) {
DCHECK_EQ(dispatcher, async_get_default_dispatcher());
directory_->Connect(service_name, std::move(channel));
}),
service_name);
}
fidl::InterfaceHandle<::fuchsia::io::Directory>
FilteredServiceDirectory::ConnectClient() {
fidl::InterfaceHandle<::fuchsia::io::Directory> client;
// ServiceDirectory puts public services under ./svc . Connect to that
void FilteredServiceDirectory::ConnectClient(
fidl::InterfaceRequest<::fuchsia::io::Directory> dir_request) {
// sys::OutgoingDirectory puts public services under ./svc . Connect to that
// directory and return client handle for the connection,
zx_status_t status =
fdio_service_connect_at(outgoing_directory_client_.channel().get(), "svc",
client.NewRequest().TakeChannel().release());
ZX_CHECK(status == ZX_OK, status) << "fdio_service_connect_at()";
return client;
}
void FilteredServiceDirectory::HandleRequest(const char* service_name,
zx::channel channel) {
directory_->ConnectToServiceUnsafe(service_name, std::move(channel));
outgoing_directory_.GetOrCreateDirectory("svc")->Serve(
::fuchsia::io::OPEN_RIGHT_READABLE | ::fuchsia::io::OPEN_RIGHT_WRITABLE,
dir_request.TakeChannel());
}
} // namespace fuchsia
......
......@@ -5,41 +5,39 @@
#ifndef BASE_FUCHSIA_FILTERED_SERVICE_DIRECTORY_H_
#define BASE_FUCHSIA_FILTERED_SERVICE_DIRECTORY_H_
#include "base/fuchsia/service_directory.h"
#include <fuchsia/io/cpp/fidl.h>
#include <lib/fidl/cpp/interface_handle.h>
#include <lib/sys/cpp/outgoing_directory.h>
#include <lib/sys/cpp/service_directory.h>
#include <lib/zx/channel.h>
#include <memory>
#include "base/base_export.h"
#include "base/macros.h"
namespace base {
namespace fuchsia {
class ServiceDirectoryClient;
// ServiceDirectory that uses the supplied ServiceDirectoryClient to satisfy
// requests for only a restricted set of services.
class BASE_EXPORT FilteredServiceDirectory {
public:
// Creates proxy that proxies requests to the specified service |directory|,
// which must outlive the proxy.
explicit FilteredServiceDirectory(const ServiceDirectoryClient* directory);
// Creates a directory that proxies requests to the specified service
// |directory|.
explicit FilteredServiceDirectory(sys::ServiceDirectory* directory);
~FilteredServiceDirectory();
// Adds the specified service to the list of whitelisted services.
void AddService(const char* service_name);
// Returns a client channel connected to the directory. The returned channel
// can be passed to a sandboxed process to be used for /svc namespace.
fidl::InterfaceHandle<::fuchsia::io::Directory> ConnectClient();
// Connects a directory client. The directory can be passed to a sandboxed
// process to be used for /svc namespace.
void ConnectClient(
fidl::InterfaceRequest<::fuchsia::io::Directory> dir_request);
private:
void HandleRequest(const char* service_name, zx::channel channel);
const ServiceDirectoryClient* const directory_;
std::unique_ptr<ServiceDirectory> outgoing_directory_;
const sys::ServiceDirectory* const directory_;
sys::OutgoingDirectory outgoing_directory_;
// Client side of the channel used by |outgoing_directory_|.
fidl::InterfaceHandle<::fuchsia::io::Directory> outgoing_directory_client_;
......
......@@ -6,7 +6,6 @@
#include <utility>
#include "base/fuchsia/service_directory_client.h"
#include "base/fuchsia/service_directory_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -17,21 +16,23 @@ class FilteredServiceDirectoryTest : public ServiceDirectoryTestBase {
public:
FilteredServiceDirectoryTest() {
filtered_service_directory_ = std::make_unique<FilteredServiceDirectory>(
public_service_directory_client_.get());
filtered_client_ = std::make_unique<ServiceDirectoryClient>(
filtered_service_directory_->ConnectClient());
public_service_directory_.get());
fidl::InterfaceHandle<::fuchsia::io::Directory> directory;
filtered_service_directory_->ConnectClient(directory.NewRequest());
filtered_client_ =
std::make_unique<sys::ServiceDirectory>(std::move(directory));
}
protected:
std::unique_ptr<FilteredServiceDirectory> filtered_service_directory_;
std::unique_ptr<ServiceDirectoryClient> filtered_client_;
std::unique_ptr<sys::ServiceDirectory> filtered_client_;
};
// Verify that we can connect to a whitelisted service.
TEST_F(FilteredServiceDirectoryTest, Connect) {
filtered_service_directory_->AddService(testfidl::TestInterface::Name_);
auto stub = filtered_client_->ConnectToService<testfidl::TestInterface>();
auto stub = filtered_client_->Connect<testfidl::TestInterface>();
VerifyTestInterface(&stub, ZX_OK);
}
......@@ -39,15 +40,15 @@ TEST_F(FilteredServiceDirectoryTest, Connect) {
TEST_F(FilteredServiceDirectoryTest, ConnectMultiple) {
filtered_service_directory_->AddService(testfidl::TestInterface::Name_);
auto stub1 = filtered_client_->ConnectToService<testfidl::TestInterface>();
auto stub2 = filtered_client_->ConnectToService<testfidl::TestInterface>();
auto stub1 = filtered_client_->Connect<testfidl::TestInterface>();
auto stub2 = filtered_client_->Connect<testfidl::TestInterface>();
VerifyTestInterface(&stub1, ZX_OK);
VerifyTestInterface(&stub2, ZX_OK);
}
// Verify that non-whitelisted services are blocked.
TEST_F(FilteredServiceDirectoryTest, ServiceBlocked) {
auto stub = filtered_client_->ConnectToService<testfidl::TestInterface>();
auto stub = filtered_client_->Connect<testfidl::TestInterface>();
VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
}
......@@ -58,7 +59,7 @@ TEST_F(FilteredServiceDirectoryTest, NoService) {
service_binding_.reset();
auto stub = filtered_client_->ConnectToService<testfidl::TestInterface>();
auto stub = filtered_client_->Connect<testfidl::TestInterface>();
VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
}
......@@ -68,9 +69,9 @@ TEST_F(FilteredServiceDirectoryTest, NoServiceDir) {
filtered_service_directory_->AddService(testfidl::TestInterface::Name_);
service_binding_.reset();
service_directory_.reset();
outgoing_directory_.reset();
auto stub = filtered_client_->ConnectToService<testfidl::TestInterface>();
auto stub = filtered_client_->Connect<testfidl::TestInterface>();
VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
}
......
// Copyright 2019 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/scoped_service_binding.h"
#include <lib/sys/cpp/outgoing_directory.h>
#include <lib/vfs/cpp/pseudo_dir.h>
#include <lib/vfs/cpp/service.h>
namespace base {
namespace fuchsia {
namespace internal {
ScopedServiceBindingBase::ScopedServiceBindingBase(
sys::OutgoingDirectory* outgoing_directory)
: ScopedServiceBindingBase(
outgoing_directory->GetOrCreateDirectory("svc")) {}
ScopedServiceBindingBase::ScopedServiceBindingBase(vfs::PseudoDir* pseudo_dir)
: pseudo_dir_(pseudo_dir) {}
ScopedServiceBindingBase::~ScopedServiceBindingBase() = default;
void ScopedServiceBindingBase::RegisterService(const char* service_name,
Connector connector) {
pseudo_dir_->AddEntry(service_name,
std::make_unique<vfs::Service>(std::move(connector)));
}
void ScopedServiceBindingBase::UnregisterService(const char* service_name) {
pseudo_dir_->RemoveEntry(service_name);
}
} // namespace internal
} // namespace fuchsia
} // namespace base
......@@ -5,25 +5,74 @@
#ifndef BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_
#define BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_
#include <lib/fidl/cpp/binding.h>
#include <lib/fidl/cpp/binding_set.h>
#include "base/bind.h"
#include "base/base_export.h"
#include "base/callback.h"
#include "base/fuchsia/service_directory.h"
namespace sys {
class OutgoingDirectory;
} // namespace sys
namespace vfs {
class PseudoDir;
} // namespace vfs
namespace base {
namespace fuchsia {
namespace internal {
class BASE_EXPORT ScopedServiceBindingBase {
public:
explicit ScopedServiceBindingBase(sys::OutgoingDirectory* outgoing_directory);
explicit ScopedServiceBindingBase(vfs::PseudoDir* pseudo_dir);
~ScopedServiceBindingBase();
protected:
// Same type as vfs::Service::Connector, so the value can be passed directly
// to vfs::Service.
using Connector =
fit::function<void(zx::channel channel, async_dispatcher_t* dispatcher)>;
void RegisterService(const char* service_name, Connector connector);
void UnregisterService(const char* service_name);
private:
vfs::PseudoDir* const pseudo_dir_ = nullptr;
};
} // namespace internal
template <typename Interface>
class ScopedServiceBinding {
class ScopedServiceBinding : public internal::ScopedServiceBindingBase {
public:
// |service_directory| and |impl| must outlive the binding.
ScopedServiceBinding(ServiceDirectory* service_directory, Interface* impl)
: directory_(service_directory), impl_(impl) {
directory_->AddService(
BindRepeating(&ScopedServiceBinding::BindClient, Unretained(this)));
// Published a public service in the specified |outgoing_directory|.
// |outgoing_directory| and |impl| must outlive the binding.
ScopedServiceBinding(sys::OutgoingDirectory* outgoing_directory,
Interface* impl)
: ScopedServiceBindingBase(outgoing_directory), impl_(impl) {
RegisterService(Interface::Name_,
fit::bind_member(this, &ScopedServiceBinding::BindClient));
}
// Publishes a service in the specified |pseudo_dir|. |pseudo_dir| and |impl|
// must outlive the binding.
ScopedServiceBinding(vfs::PseudoDir* pseudo_dir, Interface* impl)
: ScopedServiceBindingBase(pseudo_dir), impl_(impl) {
RegisterService(Interface::Name_,
fit::bind_member(this, &ScopedServiceBinding::BindClient));
}
~ScopedServiceBinding() { directory_->RemoveService(Interface::Name_); }
// TODO(crbug.com/974072): Remove this constructor once all code has been
// migrated from base::fuchsia::ServiceDirectory to sys::OutgoingDirectory.
ScopedServiceBinding(ServiceDirectory* service_directory, Interface* impl)
: ScopedServiceBinding(service_directory->outgoing_directory(), impl) {}
~ScopedServiceBinding() { UnregisterService(Interface::Name_); }
void SetOnLastClientCallback(base::OnceClosure on_last_client_callback) {
on_last_client_callback_ = std::move(on_last_client_callback);
......@@ -34,8 +83,10 @@ class ScopedServiceBinding {
bool has_clients() const { return bindings_.size() != 0; }
private:
void BindClient(fidl::InterfaceRequest<Interface> request) {
bindings_.AddBinding(impl_, std::move(request));
void BindClient(zx::channel channel, async_dispatcher_t* dispatcher) {
bindings_.AddBinding(impl_,
fidl::InterfaceRequest<Interface>(std::move(channel)),
dispatcher);
}
void OnBindingSetEmpty() {
......@@ -43,7 +94,8 @@ class ScopedServiceBinding {
std::move(on_last_client_callback_).Run();
}
ServiceDirectory* const directory_;
sys::OutgoingDirectory* const directory_ = nullptr;
vfs::PseudoDir* const pseudo_dir_ = nullptr;
Interface* const impl_;
fidl::BindingSet<Interface> bindings_;
base::OnceClosure on_last_client_callback_;
......@@ -58,19 +110,27 @@ enum class ScopedServiceBindingPolicy { kPreferNew, kPreferExisting };
template <typename Interface,
ScopedServiceBindingPolicy Policy =
ScopedServiceBindingPolicy::kPreferNew>
class ScopedSingleClientServiceBinding {
class ScopedSingleClientServiceBinding
: public internal::ScopedServiceBindingBase {
public:
// |service_directory| and |impl| must outlive the binding.
ScopedSingleClientServiceBinding(ServiceDirectory* service_directory,
// |outgoing_directory| and |impl| must outlive the binding.
ScopedSingleClientServiceBinding(sys::OutgoingDirectory* outgoing_directory,
Interface* impl)
: directory_(service_directory), binding_(impl) {
directory_->AddService(BindRepeating(
&ScopedSingleClientServiceBinding::BindClient, Unretained(this)));
: ScopedServiceBindingBase(outgoing_directory), binding_(impl) {
RegisterService(
Interface::Name_,
fit::bind_member(this, &ScopedSingleClientServiceBinding::BindClient));
}
~ScopedSingleClientServiceBinding() {
directory_->RemoveService(Interface::Name_);
}
// TODO(crbug.com/974072): Remove this constructor once all code has been
// migrated from base::fuchsia::ServiceDirectory to sys::OutgoingDirectory.
ScopedSingleClientServiceBinding(ServiceDirectory* service_directory,
Interface* impl)
: ScopedSingleClientServiceBinding(
service_directory->outgoing_directory(),
impl) {}
~ScopedSingleClientServiceBinding() { UnregisterService(Interface::Name_); }
typename Interface::EventSender_& events() { return binding_.events(); }
......@@ -83,11 +143,13 @@ class ScopedSingleClientServiceBinding {
bool has_clients() const { return binding_.is_bound(); }
private:
void BindClient(fidl::InterfaceRequest<Interface> request) {
void BindClient(zx::channel channel, async_dispatcher_t* dispatcher) {
if (Policy == ScopedServiceBindingPolicy::kPreferExisting &&
binding_.is_bound())
binding_.is_bound()) {
return;
binding_.Bind(std::move(request));
}
binding_.Bind(fidl::InterfaceRequest<Interface>(std::move(channel)),
dispatcher);
}
void OnBindingEmpty() {
......@@ -95,7 +157,6 @@ class ScopedSingleClientServiceBinding {
std::move(on_last_client_callback_).Run();
}
ServiceDirectory* const directory_;
fidl::Binding<Interface> binding_;
base::OnceClosure on_last_client_callback_;
......
......@@ -15,10 +15,8 @@ class ScopedServiceBindingTest : public ServiceDirectoryTestBase {};
// Verifies that ScopedServiceBinding allows connection more than once.
TEST_F(ScopedServiceBindingTest, ConnectTwice) {
auto stub = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
auto stub2 = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
auto stub = public_service_directory_->Connect<testfidl::TestInterface>();
auto stub2 = public_service_directory_->Connect<testfidl::TestInterface>();
VerifyTestInterface(&stub, ZX_OK);
VerifyTestInterface(&stub2, ZX_OK);
}
......@@ -30,17 +28,17 @@ TEST_F(ScopedServiceBindingTest, SingleClientPreferNew) {
service_binding_ = nullptr;
ScopedSingleClientServiceBinding<testfidl::TestInterface,
ScopedServiceBindingPolicy::kPreferNew>
binding(service_directory_.get(), &test_service_);
binding(outgoing_directory_.get(), &test_service_);
// Connect the first client, and verify that it is functional.
auto existing_client = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
auto existing_client =
public_service_directory_->Connect<testfidl::TestInterface>();
VerifyTestInterface(&existing_client, ZX_OK);
// Connect the second client, so the existing one should be disconnected and
// the new should be functional.
auto new_client = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
auto new_client =
public_service_directory_->Connect<testfidl::TestInterface>();
RunLoop().RunUntilIdle();
EXPECT_FALSE(existing_client);
VerifyTestInterface(&new_client, ZX_OK);
......@@ -53,17 +51,17 @@ TEST_F(ScopedServiceBindingTest, SingleClientPreferExisting) {
service_binding_ = nullptr;
ScopedSingleClientServiceBinding<testfidl::TestInterface,
ScopedServiceBindingPolicy::kPreferExisting>
binding(service_directory_.get(), &test_service_);
binding(outgoing_directory_.get(), &test_service_);
// Connect the first client, and verify that it is functional.
auto existing_client = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
auto existing_client =
public_service_directory_->Connect<testfidl::TestInterface>();
VerifyTestInterface(&existing_client, ZX_OK);
// Connect the second client, then verify that the it gets closed and the
// existing one remains functional.
auto new_client = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
auto new_client =
public_service_directory_->Connect<testfidl::TestInterface>();
RunLoop().RunUntilIdle();
EXPECT_FALSE(new_client);
VerifyTestInterface(&existing_client, ZX_OK);
......@@ -74,21 +72,39 @@ TEST_F(ScopedServiceBindingTest, SingleClientDefaultIsPreferNew) {
// Teardown the default multi-client binding and create a prefer-new one.
service_binding_ = nullptr;
ScopedSingleClientServiceBinding<testfidl::TestInterface> binding(
service_directory_.get(), &test_service_);
outgoing_directory_.get(), &test_service_);
// Connect the first client, and verify that it is functional.
auto existing_client = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
auto existing_client =
public_service_directory_->Connect<testfidl::TestInterface>();
VerifyTestInterface(&existing_client, ZX_OK);
// Connect the second client, so the existing one should be disconnected and
// the new should be functional.
auto new_client = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
auto new_client =
public_service_directory_->Connect<testfidl::TestInterface>();
RunLoop().RunUntilIdle();
EXPECT_FALSE(existing_client);
VerifyTestInterface(&new_client, ZX_OK);
}
// Verify that we can publish a debug service.
TEST_F(ScopedServiceBindingTest, ConnectDebugService) {
// Remove the public service binding.
service_binding_.reset();
// Publish the test service to the "debug" directory.
ScopedServiceBinding<testfidl::TestInterface> debug_service_binding(
outgoing_directory_->debug_dir(), &test_service_);
auto debug_stub =
debug_service_directory_->Connect<testfidl::TestInterface>();
VerifyTestInterface(&debug_stub, ZX_OK);
auto release_stub =
public_service_directory_->Connect<testfidl::TestInterface>();
VerifyTestInterface(&release_stub, ZX_ERR_PEER_CLOSED);
}
} // namespace fuchsia
} // namespace base
......@@ -4,11 +4,10 @@
#include "base/fuchsia/service_directory.h"
#include <lib/async/default.h>
#include <lib/svc/dir.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/sys/cpp/outgoing_directory.h>
#include "base/fuchsia/default_context.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop_current.h"
......@@ -17,133 +16,43 @@
namespace base {
namespace fuchsia {
namespace {
std::unique_ptr<ServiceDirectory> CreateDefaultServiceDirectory() {
sys::OutgoingDirectory* outgoing =
ComponentContextForCurrentProcess()->outgoing().get();
outgoing->ServeFromStartupInfo();
return std::make_unique<ServiceDirectory>(outgoing);
}
} // namespace
ServiceDirectory::ServiceDirectory(
fidl::InterfaceRequest<::fuchsia::io::Directory> request) {
Initialize(std::move(request));
}
ServiceDirectory::ServiceDirectory() = default;
ServiceDirectory::~ServiceDirectory() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(services_.empty());
ServiceDirectory::ServiceDirectory(sys::OutgoingDirectory* directory)
: directory_(directory) {}
// Only the root ServiceDirectory "owns" svc_dir_.
if (!sub_directory_) {
zx_status_t status = svc_dir_destroy(svc_dir_);
ZX_DCHECK(status == ZX_OK, status);
}
}
ServiceDirectory::ServiceDirectory() = default;
ServiceDirectory::~ServiceDirectory() = default;
// static
ServiceDirectory* ServiceDirectory::GetDefault() {
static NoDestructor<ServiceDirectory> directory(
fidl::InterfaceRequest<::fuchsia::io::Directory>(
zx::channel(zx_take_startup_handle(PA_DIRECTORY_REQUEST))));
return directory.get();
static NoDestructor<std::unique_ptr<ServiceDirectory>> directory(
CreateDefaultServiceDirectory());
return directory.get()->get();
}
void ServiceDirectory::Initialize(
fidl::InterfaceRequest<::fuchsia::io::Directory> request) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!svc_dir_);
zx_status_t status =
svc_dir_create(async_get_default_dispatcher(),
request.TakeChannel().release(), &svc_dir_);
ZX_CHECK(status == ZX_OK, status);
debug_ = WrapUnique(new ServiceDirectory(svc_dir_, "debug"));
}
void ServiceDirectory::AddServiceUnsafe(
StringPiece name,
RepeatingCallback<void(zx::channel)> connect_callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(svc_dir_);
DCHECK(services_.find(name) == services_.end());
std::string name_str = name.as_string();
services_[name_str] = connect_callback;
if (sub_directory_) {
zx_status_t status =
svc_dir_add_service(svc_dir_, sub_directory_, name_str.c_str(), this,
&ServiceDirectory::HandleConnectRequest);
ZX_DCHECK(status == ZX_OK, status);
} else {
// Publish to "svc".
zx_status_t status =
svc_dir_add_service(svc_dir_, "svc", name_str.c_str(), this,
&ServiceDirectory::HandleConnectRequest);
ZX_DCHECK(status == ZX_OK, status);
// Publish to "public" for compatibility.
status = svc_dir_add_service(svc_dir_, "public", name_str.c_str(), this,
&ServiceDirectory::HandleConnectRequest);
ZX_DCHECK(status == ZX_OK, status);
// Publish to the legacy "flat" namespace, which is required by some
// clients.
status = svc_dir_add_service(svc_dir_, nullptr, name_str.c_str(), this,
&ServiceDirectory::HandleConnectRequest);
ZX_DCHECK(status == ZX_OK, status);
}
}
void ServiceDirectory::RemoveService(StringPiece name) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(svc_dir_);
std::string name_str = name.as_string();
auto it = services_.find(name_str);
DCHECK(it != services_.end());
services_.erase(it);
if (sub_directory_) {
zx_status_t status =
svc_dir_remove_service(svc_dir_, sub_directory_, name_str.c_str());
ZX_DCHECK(status == ZX_OK, status);
} else {
// Unregister from "svc", "public", and flat namespace.
zx_status_t status =
svc_dir_remove_service(svc_dir_, "svc", name_str.c_str());
ZX_DCHECK(status == ZX_OK, status);
status = svc_dir_remove_service(svc_dir_, "public", name_str.c_str());
ZX_DCHECK(status == ZX_OK, status);
status = svc_dir_remove_service(svc_dir_, nullptr, name_str.c_str());
ZX_DCHECK(status == ZX_OK, status);
}
}
void ServiceDirectory::RemoveAllServices() {
while (!services_.empty()) {
RemoveService(services_.begin()->first);
}
}
// static
void ServiceDirectory::HandleConnectRequest(void* context,
const char* service_name,
zx_handle_t service_request) {
auto* directory = reinterpret_cast<ServiceDirectory*>(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(zx::channel(service_request));
}
ServiceDirectory::ServiceDirectory(svc_dir_t* svc_dir, const char* name) {
DCHECK(svc_dir);
svc_dir_ = svc_dir;
sub_directory_ = name;
DCHECK(!owned_directory_);
owned_directory_ = std::make_unique<sys::OutgoingDirectory>();
directory_ = owned_directory_.get();
directory_->GetOrCreateDirectory("svc")->Serve(
::fuchsia::io::OPEN_RIGHT_READABLE | ::fuchsia::io::OPEN_RIGHT_WRITABLE,
request.TakeChannel());
}
} // namespace fuchsia
......
......@@ -17,9 +17,10 @@
#include "base/containers/flat_map.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 sys {
class OutgoingDirectory;
} // namespace sys
namespace base {
namespace fuchsia {
......@@ -34,14 +35,19 @@ namespace fuchsia {
// Debug services are published to a "debug" sub-directory only accessible by
// other services via the Hub.
//
// Not thread-safe. All methods must be called on the thread that created the
// object.
// TODO(crbug.com/974072): Currently this class is just a wrapper around
// sys::OutgoingDirectory. Migrate all code to use sys::OutgoingDirectory and
// remove this class.
class BASE_EXPORT ServiceDirectory {
public:
// Responds to service requests over the supplied |request| channel.
explicit ServiceDirectory(
fidl::InterfaceRequest<::fuchsia::io::Directory> request);
// Wraps a sys::OutgoingDirectory. The |directory| must outlive
// the ServiceDirectory object.
explicit ServiceDirectory(sys::OutgoingDirectory* directory);
// Creates an uninitialized ServiceDirectory instance. Initialize must be
// called on the instance before any services can be registered. Unless you
// need separate construction & initialization for a ServiceDirectory member,
......@@ -58,51 +64,11 @@ class BASE_EXPORT ServiceDirectory {
// supplied |directory_request| channel.
void Initialize(fidl::InterfaceRequest<::fuchsia::io::Directory> request);
template <typename Interface>
void AddService(RepeatingCallback<void(fidl::InterfaceRequest<Interface>)>
connect_callback) {
AddServiceUnsafe(
Interface::Name_,
BindRepeating(
[](decltype(connect_callback) callback, zx::channel request) {
callback.Run(
fidl::InterfaceRequest<Interface>(std::move(request)));
},
connect_callback));
}
void RemoveService(StringPiece name);
void RemoveAllServices();
// Returns the debug ServiceDirectory.
ServiceDirectory* debug() const { return debug_.get(); }
// Passes requests for |name| through to a generic |connect_callback|.
// This is used only when proxying requests for interfaces not known at
// compile-time. Use the type-safe APIs above whenever possible.
void AddServiceUnsafe(StringPiece name,
RepeatingCallback<void(zx::channel)> connect_callback);
sys::OutgoingDirectory* outgoing_directory() { return directory_; }
private:
// Sub-directory constructor.
ServiceDirectory(svc_dir_t* svc_dir, const char* name);
// 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_);
// Owned by the root directory.
svc_dir_t* svc_dir_ = nullptr;
flat_map<std::string, RepeatingCallback<void(zx::channel)>> services_;
// The debug sub-directory. Empty if this is a sub-directory.
std::unique_ptr<ServiceDirectory> debug_;
// If mon-null, this directory represents a sub-directory of the root
// ServiceDirectory.
const char* sub_directory_ = nullptr;
std::unique_ptr<sys::OutgoingDirectory> owned_directory_;
sys::OutgoingDirectory* directory_;
DISALLOW_COPY_AND_ASSIGN(ServiceDirectory);
};
......
......@@ -8,6 +8,7 @@
#include <utility>
#include "base/bind.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/test/test_timeouts.h"
namespace base {
......@@ -17,48 +18,37 @@ ServiceDirectoryTestBase::ServiceDirectoryTestBase()
: run_timeout_(TestTimeouts::action_timeout(), BindRepeating([]() {
ADD_FAILURE() << "Run() timed out.";
})) {
// TODO(https://crbug.com/920920): Remove the ServiceDirectory's implicit
// "public" sub-directory and update this setup logic.
// Mount service dir and publish the service.
outgoing_directory_ = std::make_unique<sys::OutgoingDirectory>();
fidl::InterfaceHandle<::fuchsia::io::Directory> directory;
service_directory_ =
std::make_unique<ServiceDirectory>(directory.NewRequest());
zx_status_t status =
outgoing_directory_->Serve(directory.NewRequest().TakeChannel());
ZX_CHECK(status == ZX_OK, status);
service_binding_ =
std::make_unique<ScopedServiceBinding<testfidl::TestInterface>>(
service_directory_.get(), &test_service_);
outgoing_directory_.get(), &test_service_);
// Create the ServiceDirectoryClient, connected to the "svc" sub-directory.
// Create the sys::ServiceDirectory, connected to the "svc" sub-directory.
fidl::InterfaceHandle<::fuchsia::io::Directory> svc_directory;
CHECK_EQ(fdio_service_connect_at(
directory.channel().get(), "/svc/.",
directory.channel().get(), "svc",
svc_directory.NewRequest().TakeChannel().release()),
ZX_OK);
public_service_directory_client_ =
std::make_unique<ServiceDirectoryClient>(std::move(svc_directory));
public_service_directory_ =
std::make_unique<sys::ServiceDirectory>(std::move(svc_directory));
// Create the ServiceDirectoryClient, connected to the "debug" sub-directory.
// Create the sys::ServiceDirectory, connected to the "debug" sub-directory.
fidl::InterfaceHandle<::fuchsia::io::Directory> debug_directory;
CHECK_EQ(fdio_service_connect_at(
directory.channel().get(), "/debug/.",
directory.channel().get(), "debug",
debug_directory.NewRequest().TakeChannel().release()),
ZX_OK);
debug_service_directory_client_ =
std::make_unique<ServiceDirectoryClient>(std::move(debug_directory));
// Create the ServiceDirectoryClient, connected to the "public" sub-directory
// (same contents as "svc", provided for compatibility).
fidl::InterfaceHandle<::fuchsia::io::Directory> public_directory;
CHECK_EQ(fdio_service_connect_at(
directory.channel().get(), "/public/.",
public_directory.NewRequest().TakeChannel().release()),
ZX_OK);
legacy_public_service_directory_client_ =
std::make_unique<ServiceDirectoryClient>(std::move(public_directory));
debug_service_directory_ =
std::make_unique<sys::ServiceDirectory>(std::move(debug_directory));
// Create a ServiceDirectoryClient for the "private" part of the directory.
root_service_directory_client_ =
std::make_unique<ServiceDirectoryClient>(std::move(directory));
// Create a sys::ServiceDirectory for the "private" part of the directory.
root_service_directory_ =
std::make_unique<sys::ServiceDirectory>(std::move(directory));
}
ServiceDirectoryTestBase::~ServiceDirectoryTestBase() = default;
......
......@@ -5,11 +5,12 @@
#ifndef BASE_FUCHSIA_SERVICE_DIRECTORY_TEST_BASE_H_
#define BASE_FUCHSIA_SERVICE_DIRECTORY_TEST_BASE_H_
#include <lib/sys/cpp/outgoing_directory.h>
#include <lib/sys/cpp/service_directory.h>
#include <zircon/types.h>
#include <memory>
#include "base/fuchsia/scoped_service_binding.h"
#include "base/fuchsia/service_directory_client.h"
#include "base/fuchsia/test_interface_impl.h"
#include "base/fuchsia/testfidl/cpp/fidl.h"
#include "base/run_loop.h"
......@@ -34,16 +35,14 @@ class ServiceDirectoryTestBase : public testing::Test {
base::test::ScopedTaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY,
base::test::ScopedTaskEnvironment::MainThreadType::IO};
std::unique_ptr<ServiceDirectory> service_directory_;
std::unique_ptr<sys::OutgoingDirectory> outgoing_directory_;
TestInterfaceImpl test_service_;
std::unique_ptr<ScopedServiceBinding<testfidl::TestInterface>>
service_binding_;
std::unique_ptr<ServiceDirectoryClient> public_service_directory_client_;
std::unique_ptr<ServiceDirectoryClient> debug_service_directory_client_;
std::unique_ptr<ServiceDirectoryClient>
legacy_public_service_directory_client_;
std::unique_ptr<ServiceDirectoryClient> root_service_directory_client_;
std::unique_ptr<sys::ServiceDirectory> public_service_directory_;
std::unique_ptr<sys::ServiceDirectory> debug_service_directory_;
std::unique_ptr<sys::ServiceDirectory> root_service_directory_;
DISALLOW_COPY_AND_ASSIGN(ServiceDirectoryTestBase);
};
......
// 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/service_directory.h"
#include <utility>
#include "base/fuchsia/service_directory_test_base.h"
#include "base/run_loop.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace fuchsia {
class ServiceDirectoryTest : public ServiceDirectoryTestBase {};
// Verifies that ServiceDirectoryClient can consume a public service in
// ServiceDirectory and that connection is disconnected when the client stub is
// destroyed.
TEST_F(ServiceDirectoryTest, ConnectDisconnect) {
auto stub = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
VerifyTestInterface(&stub, ZX_OK);
RunLoop run_loop;
service_binding_->SetOnLastClientCallback(run_loop.QuitClosure());
stub.Unbind();
run_loop.Run();
}
// Verify that we can connect to a service through both "public" and "svc".
TEST_F(ServiceDirectoryTest, ConnectNewAndLegacyServices) {
auto stub = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
auto stub2 = legacy_public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
VerifyTestInterface(&stub, ZX_OK);
VerifyTestInterface(&stub2, ZX_OK);
}
// Verify that we can connect to the same service more than once.
TEST_F(ServiceDirectoryTest, ConnectMulti) {
auto stub = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
auto stub2 = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
VerifyTestInterface(&stub, ZX_OK);
VerifyTestInterface(&stub2, ZX_OK);
}
// Verify that services are also exported to the legacy flat service namespace.
TEST_F(ServiceDirectoryTest, ConnectLegacy) {
auto stub = root_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
VerifyTestInterface(&stub, ZX_OK);
}
// Verify that ServiceDirectoryClient can handle the case when the service
// directory connection is disconnected.
TEST_F(ServiceDirectoryTest, DirectoryGone) {
service_binding_.reset();
service_directory_.reset();
fidl::InterfacePtr<testfidl::TestInterface> stub;
zx_status_t status =
public_service_directory_client_->ConnectToService(stub.NewRequest());
EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
}
// Verify that the case when the service doesn't exist is handled properly.
TEST_F(ServiceDirectoryTest, NoService) {
service_binding_.reset();
auto stub = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
}
// Verify that we can connect to a debug service.
TEST_F(ServiceDirectoryTest, ConnectDebugService) {
// Remove the public service binding.
service_binding_.reset();
// Publish the test service to the "debug" directory.
ScopedServiceBinding<testfidl::TestInterface> debug_service_binding(
service_directory_->debug(), &test_service_);
auto debug_stub = debug_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
VerifyTestInterface(&debug_stub, ZX_OK);
auto release_stub = public_service_directory_client_
->ConnectToService<testfidl::TestInterface>();
VerifyTestInterface(&release_stub, ZX_ERR_PEER_CLOSED);
}
} // namespace fuchsia
} // namespace base
......@@ -4,11 +4,23 @@
#include "base/fuchsia/service_provider_impl.h"
#include <lib/sys/cpp/outgoing_directory.h>
#include <utility>
namespace base {
namespace fuchsia {
// static
std::unique_ptr<ServiceProviderImpl>
ServiceProviderImpl::CreateForOutgoingDirectory(
sys::OutgoingDirectory* outgoing_directory) {
fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory;
outgoing_directory->GetOrCreateDirectory("svc")->Serve(
::fuchsia::io::OPEN_RIGHT_READABLE | ::fuchsia::io::OPEN_RIGHT_WRITABLE,
service_directory.NewRequest().TakeChannel());
return std::make_unique<ServiceProviderImpl>(std::move(service_directory));
}
ServiceProviderImpl::ServiceProviderImpl(
fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory)
: directory_(std::move(service_directory)) {}
......
......@@ -17,6 +17,10 @@
#include "base/fuchsia/service_directory_client.h"
#include "base/macros.h"
namespace sys {
class OutgoingDirectory;
} // namespace sys
namespace base {
namespace fuchsia {
......@@ -25,6 +29,11 @@ namespace fuchsia {
// TODO(https://crbug.com/920920): Remove this when ServiceProvider is gone.
class BASE_EXPORT ServiceProviderImpl : public ::fuchsia::sys::ServiceProvider {
public:
// Constructor that creates ServiceProvider for public services in the
// specified OutgoingDirectory.
static std::unique_ptr<ServiceProviderImpl> CreateForOutgoingDirectory(
sys::OutgoingDirectory* outgoing_directory);
explicit ServiceProviderImpl(
fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory);
~ServiceProviderImpl() override;
......
......@@ -4,12 +4,12 @@
#include "base/fuchsia/service_provider_impl.h"
#include <lib/sys/cpp/outgoing_directory.h>
#include <lib/zx/channel.h>
#include <utility>
#include "base/fuchsia/scoped_service_binding.h"
#include "base/fuchsia/service_directory.h"
#include "base/fuchsia/test_interface_impl.h"
#include "base/fuchsia/testfidl/cpp/fidl.h"
#include "base/run_loop.h"
......@@ -21,7 +21,12 @@ namespace fuchsia {
class ServiceProviderImplTest : public testing::Test {
public:
ServiceProviderImplTest() = default;
ServiceProviderImplTest() {
provider_impl_ =
ServiceProviderImpl::CreateForOutgoingDirectory(&service_directory_);
provider_impl_->AddBinding(provider_client_.NewRequest());
}
~ServiceProviderImplTest() override = default;
void VerifyTestInterface(fidl::InterfacePtr<testfidl::TestInterface>* stub,
......@@ -55,27 +60,25 @@ class ServiceProviderImplTest : public testing::Test {
base::test::ScopedTaskEnvironment::MainThreadType::IO};
TestInterfaceImpl test_service_;
sys::OutgoingDirectory service_directory_;
std::unique_ptr<ServiceProviderImpl> provider_impl_;
::fuchsia::sys::ServiceProviderPtr provider_client_;
DISALLOW_COPY_AND_ASSIGN(ServiceProviderImplTest);
};
// Verifies that we can connect to the service service more than once.
// Verifies that we can connect to the service more than once.
TEST_F(ServiceProviderImplTest, ConnectMulti) {
fidl::InterfaceHandle<::fuchsia::io::Directory> directory_channel;
ServiceDirectory service_directory(directory_channel.NewRequest());
ServiceProviderImpl provider_impl(std::move(directory_channel));
ScopedServiceBinding<testfidl::TestInterface> service_binding(
&service_directory, &test_service_);
::fuchsia::sys::ServiceProviderPtr provider_client;
provider_impl.AddBinding(provider_client.NewRequest());
&service_directory_, &test_service_);
testfidl::TestInterfacePtr stub;
provider_client->ConnectToService(testfidl::TestInterface::Name_,
stub.NewRequest().TakeChannel());
provider_client_->ConnectToService(testfidl::TestInterface::Name_,
stub.NewRequest().TakeChannel());
testfidl::TestInterfacePtr stub2;
provider_client->ConnectToService(testfidl::TestInterface::Name_,
stub2.NewRequest().TakeChannel());
provider_client_->ConnectToService(testfidl::TestInterface::Name_,
stub2.NewRequest().TakeChannel());
VerifyTestInterface(&stub, ZX_OK);
VerifyTestInterface(&stub2, ZX_OK);
......@@ -83,16 +86,9 @@ TEST_F(ServiceProviderImplTest, ConnectMulti) {
// Verify that the case when the service doesn't exist is handled properly.
TEST_F(ServiceProviderImplTest, NoService) {
fidl::InterfaceHandle<::fuchsia::io::Directory> directory_channel;
ServiceDirectory service_directory(directory_channel.NewRequest());
ServiceProviderImpl provider_impl(std::move(directory_channel));
::fuchsia::sys::ServiceProviderPtr provider_client;
provider_impl.AddBinding(provider_client.NewRequest());
testfidl::TestInterfacePtr stub;
provider_client->ConnectToService(testfidl::TestInterface::Name_,
stub.NewRequest().TakeChannel());
provider_client_->ConnectToService(testfidl::TestInterface::Name_,
stub.NewRequest().TakeChannel());
VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
}
......
......@@ -5,91 +5,106 @@
#include "base/fuchsia/startup_context.h"
#include <fuchsia/io/cpp/fidl.h>
#include <lib/sys/cpp/outgoing_directory.h>
#include <lib/sys/cpp/service_directory.h>
#include "base/fuchsia/file_utils.h"
namespace base {
namespace fuchsia {
StartupContext::StartupContext(::fuchsia::sys::StartupInfo startup_info)
: startup_info_(std::move(startup_info)) {
StartupContext::StartupContext(::fuchsia::sys::StartupInfo startup_info) {
std::unique_ptr<sys::ServiceDirectory> incoming_services;
// Component manager generates |flat_namespace|, so things are horribly broken
// if |flat_namespace| is malformed.
CHECK_EQ(startup_info_.flat_namespace.directories.size(),
startup_info_.flat_namespace.paths.size());
// Find the /svc directory and wrap it into a ServiceDirectoryClient.
for (size_t i = 0; i < startup_info_.flat_namespace.paths.size(); ++i) {
if (startup_info_.flat_namespace.paths[i] == kServiceDirectoryPath) {
incoming_services_ = std::make_unique<ServiceDirectoryClient>(
fidl::InterfaceHandle<::fuchsia::io::Directory>(
std::move(startup_info_.flat_namespace.directories[i])));
CHECK_EQ(startup_info.flat_namespace.directories.size(),
startup_info.flat_namespace.paths.size());
// Find the /svc directory and wrap it into a sys::ServiceDirectory.
for (size_t i = 0; i < startup_info.flat_namespace.paths.size(); ++i) {
if (startup_info.flat_namespace.paths[i] == kServiceDirectoryPath) {
incoming_services = std::make_unique<sys::ServiceDirectory>(
std::move(startup_info.flat_namespace.directories[i]));
break;
}
}
// TODO(https://crbug.com/933834): Remove these workarounds when we migrate to
// the new component manager.
if (!incoming_services_ && startup_info_.launch_info.flat_namespace) {
if (!incoming_services && startup_info.launch_info.flat_namespace) {
LOG(WARNING) << "Falling back to LaunchInfo namespace";
for (size_t i = 0;
i < startup_info_.launch_info.flat_namespace->paths.size(); ++i) {
if (startup_info_.launch_info.flat_namespace->paths[i] ==
i < startup_info.launch_info.flat_namespace->paths.size(); ++i) {
if (startup_info.launch_info.flat_namespace->paths[i] ==
kServiceDirectoryPath) {
incoming_services_ = std::make_unique<ServiceDirectoryClient>(
fidl::InterfaceHandle<::fuchsia::io::Directory>(std::move(
startup_info_.launch_info.flat_namespace->directories[i])));
incoming_services = std::make_unique<sys::ServiceDirectory>(
std::move(startup_info.launch_info.flat_namespace->directories[i]));
break;
}
}
}
if (!incoming_services_ && startup_info_.launch_info.additional_services) {
if (!incoming_services && startup_info.launch_info.additional_services) {
LOG(WARNING) << "Falling back to additional ServiceList services";
// Construct a ServiceDirectory and publish the additional services into it.
fidl::InterfaceHandle<::fuchsia::io::Directory> incoming_directory;
// Construct a OutgoingDirectory and publish the additional services into
// it.
additional_services_.Bind(
std::move(startup_info_.launch_info.additional_services->provider));
additional_services_directory_ =
std::make_unique<ServiceDirectory>(incoming_directory.NewRequest());
for (auto& name : startup_info_.launch_info.additional_services->names) {
additional_services_directory_->AddServiceUnsafe(
name, base::BindRepeating(
&::fuchsia::sys::ServiceProvider::ConnectToService,
base::Unretained(additional_services_.get()), name));
std::move(startup_info.launch_info.additional_services->provider));
additional_services_directory_ = std::make_unique<sys::OutgoingDirectory>();
for (auto& name : startup_info.launch_info.additional_services->names) {
additional_services_directory_->AddPublicService(
std::make_unique<vfs::Service>([this, name](
zx::channel channel,
async_dispatcher_t* dispatcher) {
additional_services_->ConnectToService(name, std::move(channel));
}),
name);
}
// Publish those services to the caller as |incoming_services_|.
incoming_services_ = std::make_unique<ServiceDirectoryClient>(
fidl::InterfaceHandle<::fuchsia::io::Directory>(
std::move(incoming_directory)));
// Publish those services to the caller as |incoming_services|.
fidl::InterfaceHandle<::fuchsia::io::Directory> incoming_directory;
additional_services_directory_->GetOrCreateDirectory("svc")->Serve(
::fuchsia::io::OPEN_RIGHT_READABLE | ::fuchsia::io::OPEN_RIGHT_WRITABLE,
incoming_directory.NewRequest().TakeChannel());
incoming_services =
std::make_unique<sys::ServiceDirectory>(std::move(incoming_directory));
}
if (!incoming_services_) {
if (!incoming_services) {
LOG(WARNING) << "Component started without a service directory";
// Create a dummy ServiceDirectoryClient with a channel that's not
// connected on the other end.
fidl::InterfaceHandle<::fuchsia::io::Directory> dummy_directory;
ignore_result(dummy_directory.NewRequest());
incoming_services_ =
std::make_unique<ServiceDirectoryClient>(std::move(dummy_directory));
incoming_services =
std::make_unique<sys::ServiceDirectory>(std::move(dummy_directory));
}
component_context_ =
std::make_unique<sys::ComponentContext>(std::move(incoming_services));
outgoing_directory_request_ =
std::move(startup_info.launch_info.directory_request);
service_directory_ =
std::make_unique<ServiceDirectory>(component_context_->outgoing().get());
service_directory_client_ = std::make_unique<ServiceDirectoryClient>(
component_context_->svc()->CloneChannel());
}
StartupContext::~StartupContext() {
// |additional_services_directory_| needs to be empty for clean teardown.
if (additional_services_directory_)
additional_services_directory_->RemoveAllServices();
StartupContext::~StartupContext() = default;
void StartupContext::ServeOutgoingDirectory() {
DCHECK(outgoing_directory_request_);
component_context_->outgoing()->Serve(std::move(outgoing_directory_request_));
}
ServiceDirectory* StartupContext::public_services() {
if (!public_services_ && startup_info_.launch_info.directory_request) {
public_services_ = std::make_unique<ServiceDirectory>(
fidl::InterfaceRequest<::fuchsia::io::Directory>(
std::move(startup_info_.launch_info.directory_request)));
}
return public_services_.get();
if (outgoing_directory_request_)
ServeOutgoingDirectory();
return service_directory_.get();
}
} // namespace fuchsia
......
......@@ -6,6 +6,7 @@
#define BASE_FUCHSIA_STARTUP_CONTEXT_H_
#include <fuchsia/sys/cpp/fidl.h>
#include <lib/sys/cpp/component_context.h>
#include <memory>
#include "base/base_export.h"
......@@ -26,28 +27,42 @@ class BASE_EXPORT StartupContext {
explicit StartupContext(::fuchsia::sys::StartupInfo startup_info);
virtual ~StartupContext();
// Returns the namespace of services published for use by the component.
const ServiceDirectoryClient* incoming_services() const {
DCHECK(incoming_services_);
return incoming_services_.get();
// Returns the ComponentContext for the current component.
sys::ComponentContext* component_context() const {
return component_context_.get();
}
// Returns the outgoing directory into which this component binds services.
// Note that all services should be bound immediately after the first call to
// this API, before returning control to the message loop, at which point we
// will start processing service connection requests.
ServiceDirectory* public_services();
// Starts serving outgoing directory in the |component_context()|. Can be
// called at most once. All outgoing services should be published in
// |component_context()->outgoing()| before calling this function.
void ServeOutgoingDirectory();
private:
::fuchsia::sys::StartupInfo startup_info_;
// TODO(crbug.com/974072): ServiceDirectory and ServiceDirectoryClient are
// deprecated. Remove incoming_services() and public_services() once all
// clients have been migrated to sys::OutgoingDirectory and
// sys::ServiceDirectory.
ServiceDirectoryClient* incoming_services() const {
return service_directory_client_.get();
}
std::unique_ptr<ServiceDirectoryClient> incoming_services_;
std::unique_ptr<ServiceDirectory> public_services_;
// Legacy ServiceDirectory instance. Also calls |ServeOutgoingDirectory()| if
// it hasn't been called yet, which means outgoing services should be bound
// immediately after the first call to this API.
ServiceDirectory* public_services();
private:
// TODO(https://crbug.com/933834): Remove these when we migrate to the new
// component manager APIs.
::fuchsia::sys::ServiceProviderPtr additional_services_;
std::unique_ptr<ServiceDirectory> additional_services_directory_;
std::unique_ptr<sys::OutgoingDirectory> additional_services_directory_;
std::unique_ptr<sys::ComponentContext> component_context_;
// Used to store outgoing directory until ServeOutgoingDirectory() is called.
zx::channel outgoing_directory_request_;
std::unique_ptr<ServiceDirectory> service_directory_;
std::unique_ptr<ServiceDirectoryClient> service_directory_client_;
DISALLOW_COPY_AND_ASSIGN(StartupContext);
};
......
......@@ -4,6 +4,8 @@
#include "fuchsia/engine/context_provider_main.h"
#include <lib/sys/cpp/outgoing_directory.h>
#include "base/fuchsia/scoped_service_binding.h"
#include "base/fuchsia/service_directory.h"
#include "base/logging.h"
......@@ -23,7 +25,7 @@ int ContextProviderMain() {
directory, &context_provider);
base::fuchsia::ScopedServiceBinding<fuchsia::web::Debug> debug_binding(
directory->debug(), &context_provider);
directory->outgoing_directory()->debug_dir(), &context_provider);
base::RunLoop run_loop;
cr_fuchsia::LifecycleImpl lifecycle(directory, run_loop.QuitClosure());
......
......@@ -163,6 +163,7 @@ test("cast_runner_integration_tests") {
"//fuchsia/base:test_support",
"//net:test_support",
"//testing/gtest",
"//third_party/fuchsia-sdk/sdk:modular",
]
package_deps = [ [
"//fuchsia/engine:web_engine",
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fuchsia/modular/cpp/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/zx/channel.h>
......@@ -208,6 +210,20 @@ class CastRunnerIntegrationTest : public testing::Test {
fuchsia::sys::StartupInfo startup_info;
startup_info.launch_info.url = component_url.as_string();
fidl::InterfaceHandle<fuchsia::io::Directory> outgoing_directory;
startup_info.launch_info.directory_request =
outgoing_directory.NewRequest().TakeChannel();
fidl::InterfaceHandle<::fuchsia::io::Directory> svc_directory;
CHECK_EQ(fdio_service_connect_at(
outgoing_directory.channel().get(), "svc",
svc_directory.NewRequest().TakeChannel().release()),
ZX_OK);
component_services_client_ =
std::make_unique<base::fuchsia::ServiceDirectoryClient>(
std::move(svc_directory));
// Place the ServiceDirectory in the |flat_namespace|.
startup_info.flat_namespace.paths.emplace_back(
base::fuchsia::kServiceDirectoryPath);
......@@ -259,6 +275,8 @@ class CastRunnerIntegrationTest : public testing::Test {
// Incoming service directory, ComponentContext and per-component state.
std::unique_ptr<base::fuchsia::ServiceDirectory> component_services_;
std::unique_ptr<cr_fuchsia::FakeComponentContext> component_context_;
std::unique_ptr<base::fuchsia::ServiceDirectoryClient>
component_services_client_;
FakeComponentState* component_state_ = nullptr;
// ServiceDirectory into which the CastRunner will publish itself.
......@@ -455,4 +473,33 @@ TEST_F(CastRunnerIntegrationTest, ApplicationControllerNotSupported) {
ASSERT_TRUE(component_state_);
EXPECT_FALSE(component_state_->controller_receiver()->controller());
}
// Verify that we can connect to the Lifecycle interface and terminate the
// component. Service connection is sent immediately after the component start
// request, so this test also verifies that the component doesn't start handling
// incoming service requests before the service has been published.
TEST_F(CastRunnerIntegrationTest, Lifecycle) {
const char kCastChannelAppId[] = "00000001";
const char kCastChannelAppPath[] = "/defaultresponse";
provide_controller_receiver_ = false;
app_config_manager_.AddAppMapping(kCastChannelAppId,
test_server_.GetURL(kCastChannelAppPath));
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(base::StringPrintf("cast:%s", kCastChannelAppId));
fuchsia::modular::LifecyclePtr lifecycle;
component_services_client_->ConnectToService(lifecycle.NewRequest());
lifecycle->Terminate();
base::RunLoop run_loop;
component_controller.set_error_handler([&run_loop](zx_status_t status) {
EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
run_loop.Quit();
});
run_loop.Run();
}
} // namespace castrunner
......@@ -16,6 +16,9 @@
#include <fuchsia/netstack/cpp/fidl.h>
#include <fuchsia/sysmem/cpp/fidl.h>
#include <fuchsia/ui/scenic/cpp/fidl.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/sys/cpp/service_directory.h>
#include <memory>
#include <utility>
......@@ -23,8 +26,8 @@
#include "base/command_line.h"
#include "base/containers/span.h"
#include "base/files/file_util.h"
#include "base/fuchsia/default_context.h"
#include "base/fuchsia/filtered_service_directory.h"
#include "base/fuchsia/service_directory_client.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/process/process.h"
......@@ -149,7 +152,7 @@ void SandboxPolicyFuchsia::Initialize(service_manager::SandboxType type) {
service_directory_task_runner_ = base::ThreadTaskRunnerHandle::Get();
service_directory_ =
std::make_unique<base::fuchsia::FilteredServiceDirectory>(
base::fuchsia::ServiceDirectoryClient::ForCurrentProcess());
base::fuchsia::ComponentContextForCurrentProcess()->svc().get());
for (const char* service_name : kDefaultServices) {
service_directory_->AddService(service_name);
}
......@@ -158,7 +161,7 @@ void SandboxPolicyFuchsia::Initialize(service_manager::SandboxType type) {
}
// Bind the service directory and store the client channel for
// UpdateLaunchOptionsForSandbox()'s use.
service_directory_client_ = service_directory_->ConnectClient();
service_directory_->ConnectClient(service_directory_client_.NewRequest());
CHECK(service_directory_client_);
}
}
......
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