209 lines
7.4 KiB
C++
209 lines
7.4 KiB
C++
// Copyright 2018 The Chromium Authors
|
|
// 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 <utility>
|
|
|
|
// TODO(crbug.com/1427626): Remove this include once the explicit
|
|
// async_get_default_dispatcher() is no longer needed.
|
|
#include <lib/async/default.h>
|
|
#include <lib/fidl/cpp/binding.h>
|
|
#include <lib/fidl/cpp/binding_set.h>
|
|
#include <lib/fidl/cpp/interface_request.h>
|
|
#include <lib/fidl/cpp/wire/connect_service.h>
|
|
#include <lib/zx/channel.h>
|
|
|
|
#include "base/base_export.h"
|
|
#include "base/fuchsia/scoped_service_publisher.h"
|
|
#include "base/functional/callback.h"
|
|
#include "base/strings/string_piece_forward.h"
|
|
#include "third_party/abseil-cpp/absl/types/optional.h"
|
|
|
|
namespace sys {
|
|
class OutgoingDirectory;
|
|
} // namespace sys
|
|
|
|
namespace vfs {
|
|
class PseudoDir;
|
|
} // namespace vfs
|
|
|
|
namespace base {
|
|
|
|
template <typename Interface>
|
|
class BASE_EXPORT ScopedServiceBinding {
|
|
public:
|
|
// Publishes a public service in the specified |outgoing_directory|.
|
|
// |outgoing_directory| and |impl| must outlive the binding. The service is
|
|
// unpublished on destruction.
|
|
ScopedServiceBinding(sys::OutgoingDirectory* outgoing_directory,
|
|
Interface* impl,
|
|
base::StringPiece name = Interface::Name_)
|
|
: publisher_(outgoing_directory, bindings_.GetHandler(impl), name) {}
|
|
|
|
// Publishes a service in the specified |pseudo_dir|. |pseudo_dir| and |impl|
|
|
// must outlive the binding. The service is unpublished on destruction.
|
|
ScopedServiceBinding(vfs::PseudoDir* pseudo_dir, Interface* impl,
|
|
base::StringPiece name = Interface::Name_)
|
|
: publisher_(pseudo_dir, bindings_.GetHandler(impl), name) {}
|
|
|
|
ScopedServiceBinding(const ScopedServiceBinding&) = delete;
|
|
ScopedServiceBinding& operator=(const ScopedServiceBinding&) = delete;
|
|
|
|
~ScopedServiceBinding() = default;
|
|
|
|
// |on_last_client_callback| will be called every time the number of connected
|
|
// clients drops to 0.
|
|
void SetOnLastClientCallback(base::RepeatingClosure on_last_client_callback) {
|
|
bindings_.set_empty_set_handler(
|
|
[callback = std::move(on_last_client_callback)] { callback.Run(); });
|
|
}
|
|
|
|
bool has_clients() const { return bindings_.size() != 0; }
|
|
|
|
private:
|
|
fidl::BindingSet<Interface> bindings_;
|
|
ScopedServicePublisher<Interface> publisher_;
|
|
};
|
|
|
|
template <typename Protocol>
|
|
class BASE_EXPORT ScopedNaturalServiceBinding {
|
|
public:
|
|
// Publishes a public service in the specified |outgoing_directory|.
|
|
// |outgoing_directory| and |impl| must outlive the binding. The service is
|
|
// unpublished on destruction.
|
|
ScopedNaturalServiceBinding(
|
|
sys::OutgoingDirectory* outgoing_directory,
|
|
fidl::Server<Protocol>* impl,
|
|
base::StringPiece name = fidl::DiscoverableProtocolName<Protocol>)
|
|
: publisher_(
|
|
outgoing_directory,
|
|
bindings_.CreateHandler(
|
|
impl,
|
|
// TODO(crbug.com/1427626): Remove this param once there's an
|
|
// overload of `CreateHandler` that doesn't require it.
|
|
async_get_default_dispatcher(),
|
|
[](fidl::UnbindInfo info) {}),
|
|
name) {}
|
|
|
|
// Publishes a service in the specified |pseudo_dir|. |pseudo_dir| and |impl|
|
|
// must outlive the binding. The service is unpublished on destruction.
|
|
ScopedNaturalServiceBinding(
|
|
vfs::PseudoDir* pseudo_dir,
|
|
fidl::Server<Protocol>* impl,
|
|
base::StringPiece name = fidl::DiscoverableProtocolName<Protocol>)
|
|
: publisher_(
|
|
pseudo_dir,
|
|
bindings_.CreateHandler(
|
|
impl,
|
|
// TODO(crbug.com/1427626): Remove this param once there's an
|
|
// overload of `CreateHandler` that doesn't require it.
|
|
async_get_default_dispatcher(),
|
|
[](fidl::UnbindInfo info) {}),
|
|
name) {}
|
|
|
|
ScopedNaturalServiceBinding(const ScopedNaturalServiceBinding&) = delete;
|
|
ScopedNaturalServiceBinding& operator=(const ScopedNaturalServiceBinding&) =
|
|
delete;
|
|
|
|
~ScopedNaturalServiceBinding() = default;
|
|
|
|
// Registers `on_last_client_callback` to be called every time the number of
|
|
// connected clients drops to 0.
|
|
void SetOnLastClientCallback(base::RepeatingClosure on_last_client_callback) {
|
|
bindings_.set_empty_set_handler(
|
|
[callback = std::move(on_last_client_callback)] { callback.Run(); });
|
|
}
|
|
|
|
bool has_clients() const { return bindings_.size() != 0; }
|
|
|
|
private:
|
|
fidl::ServerBindingGroup<Protocol> bindings_;
|
|
ScopedNaturalServicePublisher<Protocol> publisher_;
|
|
};
|
|
|
|
// Scoped service binding which allows only a single client to be connected
|
|
// at any time. By default a new connection will disconnect an existing client.
|
|
enum class ScopedServiceBindingPolicy {
|
|
kPreferNew,
|
|
kPreferExisting,
|
|
kConnectOnce
|
|
};
|
|
|
|
template <typename Interface,
|
|
ScopedServiceBindingPolicy Policy =
|
|
ScopedServiceBindingPolicy::kPreferNew>
|
|
class BASE_EXPORT ScopedSingleClientServiceBinding {
|
|
public:
|
|
// |outgoing_directory| and |impl| must outlive the binding.
|
|
ScopedSingleClientServiceBinding(sys::OutgoingDirectory* outgoing_directory,
|
|
Interface* impl,
|
|
base::StringPiece name = Interface::Name_)
|
|
: binding_(impl) {
|
|
publisher_.emplace(
|
|
outgoing_directory,
|
|
fit::bind_member(this, &ScopedSingleClientServiceBinding::BindClient),
|
|
name);
|
|
binding_.set_error_handler(fit::bind_member(
|
|
this, &ScopedSingleClientServiceBinding::OnBindingEmpty));
|
|
}
|
|
|
|
ScopedSingleClientServiceBinding(vfs::PseudoDir* publish_to,
|
|
Interface* impl,
|
|
base::StringPiece name = Interface::Name_)
|
|
: binding_(impl) {
|
|
publisher_.emplace(
|
|
publish_to,
|
|
fit::bind_member(this, &ScopedSingleClientServiceBinding::BindClient),
|
|
name);
|
|
binding_.set_error_handler(fit::bind_member(
|
|
this, &ScopedSingleClientServiceBinding::OnBindingEmpty));
|
|
}
|
|
|
|
ScopedSingleClientServiceBinding(const ScopedSingleClientServiceBinding&) =
|
|
delete;
|
|
ScopedSingleClientServiceBinding& operator=(
|
|
const ScopedSingleClientServiceBinding&) = delete;
|
|
|
|
~ScopedSingleClientServiceBinding() = default;
|
|
|
|
typename Interface::EventSender_& events() { return binding_.events(); }
|
|
|
|
// |on_last_client_callback| will be called the first time a client
|
|
// disconnects. It is still possible for a client to connect after that point
|
|
// if Policy is kPreferNew of kPreferExisting.
|
|
void SetOnLastClientCallback(base::OnceClosure on_last_client_callback) {
|
|
on_last_client_callback_ = std::move(on_last_client_callback);
|
|
}
|
|
|
|
bool has_clients() const { return binding_.is_bound(); }
|
|
|
|
private:
|
|
void BindClient(fidl::InterfaceRequest<Interface> request) {
|
|
if (Policy == ScopedServiceBindingPolicy::kPreferExisting &&
|
|
binding_.is_bound()) {
|
|
return;
|
|
}
|
|
binding_.Bind(std::move(request));
|
|
if (Policy == ScopedServiceBindingPolicy::kConnectOnce) {
|
|
publisher_.reset();
|
|
}
|
|
}
|
|
|
|
void OnBindingEmpty(zx_status_t status) {
|
|
if (on_last_client_callback_) {
|
|
std::move(on_last_client_callback_).Run();
|
|
}
|
|
}
|
|
|
|
fidl::Binding<Interface> binding_;
|
|
absl::optional<ScopedServicePublisher<Interface>> publisher_;
|
|
base::OnceClosure on_last_client_callback_;
|
|
};
|
|
|
|
} // namespace base
|
|
|
|
#endif // BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_
|