blob: c72d02dfec447b4940419106af6095160dfadaea [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "chromecast/mojo/binder_factory.h"
#include "chromecast/mojo/mojom/remote_interfaces.mojom.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace chromecast {
// This class essentially combines RemoteInterfaces and BinderRegistry into
// one. On the host side, this class can be created and used like a
// BinderRegistry. InterfaceBundle can also dispense RemoteInterfaces remotes
// for clients to invoke local binders.
// This class should only be used on one sequence. Local interfaces will be
// bound to the sequence that the InterfaceBundle is created on, unless a task
// runner is provided in AddBinder().
// Implementations that are added via raw pointer (instead of a binder callback)
// will have the Receiver owned by the InterfaceBundle. When the InterfaceBundle
// is destroyed, the connection is severed. All implementations that are added
// by raw pointer must therefore outlive the InterfaceBundle.
// =============================================================================
// Example Usage
// =============================================================================
// Add implementations to the bundle, no Binding boilerplate required:
// InterfaceBundle bundle;
// bundle.AddInterface<mojom::Foo>(GetFooImpl());
// bundle.AddInterface<mojom::Bar>(GetBarImpl());
// Dispense a RemoteInterfaces, which can be used by clients:
// mojo::Remote<mojom::RemoteInterfaces> provider(bundle.CreateRemote());
// mojo::Remote<mojom::Bar> bar;
// provider->BindNewPipe(&bar);
// bar->DoBarStuff();
class InterfaceBundle final : private mojom::RemoteInterfaces {
// Specifies the number of expected clients for a given Receiver.
enum ReceiverType {
MULTIPLE_BINDERS, // Multiple clients, use a ReceiverSet.
SINGLE_BINDER, // Single client, use a Receiver.
InterfaceBundle(const InterfaceBundle&) = delete;
~InterfaceBundle() override;
InterfaceBundle& operator=(const InterfaceBundle&) = delete;
// Adds an implementation for an interface of type <Interface>. When the
// interface is requested via one of the consumer methods below, |interface|
// will receive the method calls.
// |interface| *must* outlive the InterfaceBundle, or else mojo method calls
// could be invoked on a destroyed object.
template <typename Interface>
bool AddInterface(Interface* interface,
ReceiverType receiver_type = MULTIPLE_BINDERS) {
if (local_interfaces_.HasInterface<Interface>()) {
LOG(DFATAL) << "Local interface '" << Interface::Name_ << "' has already "
<< "been added to this bundle.";
return false;
if (receiver_type == MULTIPLE_BINDERS) {
} else {
return true;
// Similar to BinderRegistry::AddInterface(), AddBinder() allows clients to
// provide their own binder callbacks and task runners. If |task_runner| is
// provided, then |callback| will be invoked on |task_runner| for every
// incoming request to <Interface>. Subsequent calls to <Interface>'s methods
// will post to |task_runner|. If |task_runner| is not provided, the current
// SequencedTaskRunner will receive incoming bind requests and method calls.
template <typename Interface>
bool AddBinder(
const base::RepeatingCallback<void(mojo::PendingReceiver<Interface>)>&
scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
if (local_interfaces_.HasInterface<Interface>()) {
LOG(DFATAL) << "Local interface '" << Interface::Name_ << "' has already "
<< "been added to this bundle.";
return false;
local_interfaces_.AddBinder<Interface>(callback, task_runner);
return true;
template <typename Interface>
void RemoveInterface() {
// Creates a remote reference which can be passed over IPC to a remote client.
mojo::PendingRemote<mojom::RemoteInterfaces> CreateRemote();
// Severs all client connections. This should only be called on teardown.
void Close();
// mojom::RemoteInterfaces implementation:
void BindInterface(const std::string& interface_name,
mojo::ScopedMessagePipeHandle handle) override;
void AddClient(
mojo::PendingReceiver<mojom::RemoteInterfaces> receiver) override;
// Attempt to bind a generic receiver. Succeeds if there is an available
// implementation or binder callback registered.
bool TryBindReceiver(mojo::GenericPendingReceiver& receiver) {
if (local_interfaces_.HasInterface(*receiver.interface_name())) {
std::string interface_name = *receiver.interface_name();
local_interfaces_.Bind(interface_name, receiver.PassPipe());
return true;
return false;
// For interfaces that are provided as a local pointer without any binding
// logic, we can use MultiBinderFactory to expose a binding surface.
MultiBinderFactory local_interfaces_;
mojo::ReceiverSet<mojom::RemoteInterfaces> client_receivers_;
} // namespace chromecast