Commit 1f7bc476 authored by Sean Topping's avatar Sean Topping Committed by Commit Bot

[Chromecast] Mojo helpers for Service Manager deprecation

This change introduces some Mojo helper classes that make it easier to
create and pass services between processes. The following classes have
been added:

BinderFactory:
    Encapsulates Mojo binding boilerplate, adding simple accessors to
    bind mojo::PendingReceivers or provide a Binder callback for an
    implementation.

InterfaceBundle:
    An easy-to-use substitute for BinderRegistry and InterfaceProvider.
    This class allows interfaces to be bundled together and shipped
    across processes.

RemoteInterfaces:
    Remote client interface for accessing interfaces provided by
    InterfaceBundle. Allows for late-binding and request queueing so
    services can immediately start using interfaces.

Merge-With: eureka-internal/455677

Bug: internal b/169098077
Test: cast_mojo_unittests,
      Build/run cast_shell on display device + Chromecast

Change-Id: I2dd748db1969b65042587fd29d1a83b86987126a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2441952
Commit-Queue: Sean Topping <seantopping@chromium.org>
Reviewed-by: default avatarMichael Spang <spang@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@google.com>
Reviewed-by: default avatarKenneth MacKay <kmackay@chromium.org>
Cr-Commit-Position: refs/heads/master@{#814823}
parent fb65000a
include_rules = [ include_rules = [
"+chromecast/base", "+chromecast/base",
"+chromecast/mojo",
"+mojo/public", "+mojo/public",
"+services/service_manager/public", "+services/service_manager/public",
] ]
...@@ -15,6 +15,7 @@ source_set("external_service") { ...@@ -15,6 +15,7 @@ source_set("external_service") {
deps = [ deps = [
"//base", "//base",
"//chromecast/external_mojo/public/mojom", "//chromecast/external_mojo/public/mojom",
"//chromecast/mojo",
"//mojo/core/embedder", "//mojo/core/embedder",
"//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings",
"//mojo/public/cpp/platform", "//mojo/public/cpp/platform",
......
...@@ -22,28 +22,10 @@ ExternalService::GetReceiver() { ...@@ -22,28 +22,10 @@ ExternalService::GetReceiver() {
return service_receiver_.BindNewPipeAndPassRemote(); return service_receiver_.BindNewPipeAndPassRemote();
} }
void ExternalService::AddInterface(const std::string& interface_name,
std::unique_ptr<Binder> binder) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
RemoveInterface(interface_name);
binders_.emplace(interface_name, std::move(binder));
}
void ExternalService::RemoveInterface(const std::string& interface_name) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
binders_.erase(interface_name);
}
void ExternalService::OnBindInterface( void ExternalService::OnBindInterface(
const std::string& interface_name, const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) { mojo::ScopedMessagePipeHandle interface_pipe) {
auto it = binders_.find(interface_name); bundle_.BindInterface(interface_name, std::move(interface_pipe));
if (it == binders_.end()) {
LOG(ERROR) << interface_name << " cannot be bound (not found)";
return;
}
it->second->BindInterface(interface_name, std::move(interface_pipe));
} }
} // namespace external_service_support } // namespace external_service_support
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "chromecast/external_mojo/public/mojom/connector.mojom.h" #include "chromecast/external_mojo/public/mojom/connector.mojom.h"
#include "chromecast/mojo/interface_bundle.h"
#include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/system/message_pipe.h" #include "mojo/public/cpp/system/message_pipe.h"
...@@ -40,60 +41,31 @@ class ExternalService : public external_mojo::mojom::ExternalService { ...@@ -40,60 +41,31 @@ class ExternalService : public external_mojo::mojom::ExternalService {
base::RepeatingCallback<void(mojo::PendingReceiver<Interface>)> base::RepeatingCallback<void(mojo::PendingReceiver<Interface>)>
bind_callback) { bind_callback) {
RemoveInterface<Interface>(); RemoveInterface<Interface>();
AddInterface(Interface::Name_, std::make_unique<CallbackBinder<Interface>>( bundle_.AddBinder(std::move(bind_callback));
std::move(bind_callback))); }
// Convenience method for exposing an interface. The implementation must
// outlive the service or be explicitly removed before the implementation is
// destroyed.
template <typename Interface>
void AddInterface(Interface* interface) {
RemoveInterface<Interface>();
bundle_.AddInterface<Interface>(interface);
} }
// Removes an interface, preventing new bindings from being created. Does not // Removes an interface, preventing new bindings from being created. Does not
// affect existing bindings. // affect existing bindings.
template <typename Interface> template <typename Interface>
void RemoveInterface() { void RemoveInterface() {
RemoveInterface(Interface::Name_); bundle_.RemoveInterface<Interface>();
} }
private: private:
class Binder {
public:
virtual ~Binder() = default;
// Provides an abstract interface to the templated callback-based binder
// below.
virtual void BindInterface(
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) = 0;
};
template <typename Interface>
class CallbackBinder : public Binder {
public:
CallbackBinder(
base::RepeatingCallback<void(mojo::PendingReceiver<Interface>)>
bind_callback)
: bind_callback_(bind_callback) {}
private:
// Binder implementation:
void BindInterface(const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override {
mojo::PendingReceiver<Interface> receiver(std::move(interface_pipe));
bind_callback_.Run(std::move(receiver));
}
base::RepeatingCallback<void(mojo::PendingReceiver<Interface>)>
bind_callback_;
DISALLOW_COPY_AND_ASSIGN(CallbackBinder);
};
void AddInterface(const std::string& interface_name,
std::unique_ptr<Binder> binder);
void RemoveInterface(const std::string& interface_name);
// external_mojo::mojom::ExternalService implementation: // external_mojo::mojom::ExternalService implementation:
void OnBindInterface(const std::string& interface_name, void OnBindInterface(const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override; mojo::ScopedMessagePipeHandle interface_pipe) override;
std::map<std::string, std::unique_ptr<Binder>> binders_; InterfaceBundle bundle_;
mojo::Receiver<external_mojo::mojom::ExternalService> service_receiver_{this}; mojo::Receiver<external_mojo::mojom::ExternalService> service_receiver_{this};
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
......
# Copyright 2020 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.
import("//chromecast/chromecast.gni")
cast_source_set("mojo") {
sources = [
"binder_factory.cc",
"binder_factory.h",
"interface_bundle.cc",
"interface_bundle.h",
"remote_interfaces.cc",
"remote_interfaces.h",
]
deps = [
"//base",
"//chromecast/mojo/mojom",
"//mojo/public/cpp/bindings",
]
}
test("cast_mojo_unittests") {
sources = [
"binder_factory_test.cc",
"interface_bundle_test.cc",
]
deps = [
":mojo",
"//base",
"//base/test:test_support",
"//chromecast/mojo/test:run_all_unittests",
"//chromecast/mojo/test:test_mojom",
"//mojo/public/cpp/bindings",
"//testing/gmock",
"//testing/gtest",
]
}
include_rules = [
"+mojo/public/cpp/bindings",
]
seantopping@chromium.org
lijiawei@chromium.org
// Copyright 2020 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 "chromecast/mojo/binder_factory.h"
namespace chromecast {
MultiBinderFactory::MultiBinderFactory() = default;
MultiBinderFactory::~MultiBinderFactory() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
} // namespace chromecast
This diff is collapsed.
// Copyright 2020 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 "chromecast/mojo/binder_factory.h"
#include <string>
#include "base/bind.h"
#include "base/test/task_environment.h"
#include "chromecast/mojo/test/test_interfaces.test-mojom.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
namespace chromecast {
class MockInterface : public test::mojom::StringInterface,
public test::mojom::IntInterface,
public test::mojom::BoolInterface {
public:
MOCK_METHOD(void, StringMethod, (const std::string& s), (override));
MOCK_METHOD(void, IntMethod, (int32_t i), (override));
MOCK_METHOD(void, BoolMethod, (bool b), (override));
};
class MockErrorHandler {
public:
MOCK_METHOD(void, OnError, ());
};
class BinderFactoryTest : public testing::Test {
protected:
BinderFactoryTest() {}
base::test::TaskEnvironment task_environment_;
MockInterface mock_interface_;
};
TEST_F(BinderFactoryTest, BinderFactoryBind) {
BinderFactory<test::mojom::StringInterface> s_factory(&mock_interface_);
mojo::Remote<test::mojom::StringInterface> s_remote;
s_factory.Bind(s_remote.BindNewPipeAndPassReceiver());
// Verify implementation is connected.
std::string s = "Hello, world!";
EXPECT_CALL(mock_interface_, StringMethod(s));
s_remote->StringMethod(s);
task_environment_.RunUntilIdle();
}
TEST_F(BinderFactoryTest, BinderFactoryGetBinder) {
BinderFactory<test::mojom::StringInterface> s_factory(&mock_interface_);
auto binder = s_factory.GetBinder();
ASSERT_TRUE(!binder.is_null());
mojo::Remote<test::mojom::StringInterface> s_remote;
s_factory.Bind(s_remote.BindNewPipeAndPassReceiver());
// Verify implementation is connected.
std::string s = "Hello, world!";
EXPECT_CALL(mock_interface_, StringMethod(s));
s_remote->StringMethod(s);
task_environment_.RunUntilIdle();
}
TEST_F(BinderFactoryTest, BinderFactoryLifetime) {
MockErrorHandler s_handler;
auto s_factory =
std::make_unique<BinderFactory<test::mojom::StringInterface>>(
&mock_interface_);
auto binder = s_factory->GetBinder();
ASSERT_TRUE(!binder.is_null());
mojo::Remote<test::mojom::StringInterface> s_remote;
s_factory->Bind(s_remote.BindNewPipeAndPassReceiver());
s_remote.set_disconnect_handler(
base::BindOnce(&MockErrorHandler::OnError, base::Unretained(&s_handler)));
// Remotes which were bound via the BinderFactory will invalidated when the
// factory is destroyed.
EXPECT_CALL(s_handler, OnError()).Times(1);
s_factory.reset();
task_environment_.RunUntilIdle();
}
TEST_F(BinderFactoryTest, MultiBinderFactory) {
MockErrorHandler s_handler;
MockErrorHandler i_handler;
auto factory = std::make_unique<MultiBinderFactory>();
factory->AddInterface<test::mojom::StringInterface>(&mock_interface_);
factory->AddInterface<test::mojom::IntInterface>(&mock_interface_);
auto s_binder = factory->GetBinder<test::mojom::StringInterface>();
ASSERT_FALSE(s_binder.is_null());
auto i_binder = factory->GetBinder<test::mojom::IntInterface>();
ASSERT_FALSE(i_binder.is_null());
// There is no binder for BoolInterface.
ASSERT_TRUE(factory->GetBinder<test::mojom::BoolInterface>().is_null());
mojo::Remote<test::mojom::StringInterface> s_remote;
s_binder.Run(s_remote.BindNewPipeAndPassReceiver());
mojo::Remote<test::mojom::IntInterface> i_remote;
i_binder.Run(i_remote.BindNewPipeAndPassReceiver());
std::string s = "Hello, world!";
EXPECT_CALL(mock_interface_, StringMethod(s));
s_remote->StringMethod(s);
task_environment_.RunUntilIdle();
int i = 1234;
EXPECT_CALL(mock_interface_, IntMethod(i));
i_remote->IntMethod(i);
task_environment_.RunUntilIdle();
s_remote.set_disconnect_handler(
base::BindOnce(&MockErrorHandler::OnError, base::Unretained(&s_handler)));
i_remote.set_disconnect_handler(
base::BindOnce(&MockErrorHandler::OnError, base::Unretained(&i_handler)));
// Pointers which were bound via the MultiBinderFactory will invalidated when
// the factory is destroyed.
EXPECT_CALL(s_handler, OnError()).Times(1);
EXPECT_CALL(i_handler, OnError()).Times(1);
factory.reset();
task_environment_.RunUntilIdle();
}
} // namespace chromecast
// Copyright 2020 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 "chromecast/mojo/interface_bundle.h"
#include <utility>
namespace chromecast {
InterfaceBundle::InterfaceBundle() = default;
InterfaceBundle::~InterfaceBundle() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void InterfaceBundle::Close() {
client_receivers_.Clear();
}
mojo::PendingRemote<mojom::RemoteInterfaces> InterfaceBundle::CreateRemote() {
mojo::PendingRemote<mojom::RemoteInterfaces> pending_remote;
AddClient(pending_remote.InitWithNewPipeAndPassReceiver());
return pending_remote;
}
void InterfaceBundle::BindInterface(const std::string& interface_name,
mojo::ScopedMessagePipeHandle handle) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (local_interfaces_.HasInterface(interface_name)) {
local_interfaces_.Bind(interface_name, std::move(handle));
return;
}
LOG(WARNING) << "Interface '" << interface_name << "' is not exposed by this "
<< "bundle, but a consumer tried to bind it.";
}
void InterfaceBundle::AddClient(
mojo::PendingReceiver<mojom::RemoteInterfaces> receiver) {
client_receivers_.Add(this, std::move(receiver));
}
} // namespace chromecast
// Copyright 2020 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 CHROMECAST_MOJO_INTERFACE_BUNDLE_H_
#define CHROMECAST_MOJO_INTERFACE_BUNDLE_H_
#include <string>
#include "base/callback.h"
#include "base/logging.h"
#include "base/sequence_checker.h"
#include "chromecast/mojo/binder_factory.h"
#include "chromecast/mojo/mojom/remote_interfaces.mojom.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 : private mojom::RemoteInterfaces {
public:
// 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();
InterfaceBundle(const InterfaceBundle&) = delete;
~InterfaceBundle() final;
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) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
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) {
local_interfaces_.AddInterface<Interface>(interface);
} else {
local_interfaces_.AddSingleBinderInterface<Interface>(interface);
}
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>)>&
callback,
scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
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() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
local_interfaces_.RemoveInterface<Interface>();
}
// 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;
private:
// 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_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace chromecast
#endif // CHROMECAST_MOJO_INTERFACE_BUNDLE_H_
// Copyright 2020 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 "chromecast/mojo/interface_bundle.h"
#include <string>
#include "base/bind.h"
#include "base/test/task_environment.h"
#include "chromecast/mojo/remote_interfaces.h"
#include "chromecast/mojo/test/test_interfaces.test-mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
namespace chromecast {
class MockInterface : public test::mojom::StringInterface,
public test::mojom::IntInterface,
public test::mojom::BoolInterface {
public:
MOCK_METHOD(void, StringMethod, (const std::string& s), (override));
MOCK_METHOD(void, IntMethod, (int32_t i), (override));
MOCK_METHOD(void, BoolMethod, (bool b), (override));
};
class MockErrorHandler {
public:
MOCK_METHOD(void, OnError, ());
};
class InterfaceBundleTest : public testing::Test {
protected:
InterfaceBundleTest() {}
base::test::TaskEnvironment task_environment_;
MockInterface mock_interface_;
};
TEST_F(InterfaceBundleTest, ExampleUsage) {
InterfaceBundle bundle;
RemoteInterfaces interfaces(bundle.CreateRemote());
// ===========================================================================
// Success Scenarios
// ===========================================================================
// Add an implementation of StringInterface to this bundle.
ASSERT_TRUE(
bundle.AddInterface<test::mojom::StringInterface>(&mock_interface_));
// RemoteInterfaces dispenses Remotes on behalf of InterfaceBundle.
mojo::Remote<test::mojom::StringInterface> remote;
interfaces.BindNewPipe(&remote);
ASSERT_TRUE(remote.is_bound());
// The newly created Remote will call into the implementation.
std::string s = "Hello, world!";
EXPECT_CALL(mock_interface_, StringMethod(s));
remote->StringMethod(s);
task_environment_.RunUntilIdle();
// ===========================================================================
// Error Scenarios
// ===========================================================================
// IntInterface wasn't provided, but we allow a new Remote to be created.
// However, once the request is rejected by InterfaceBundle, the Remote will
// be disconnected.
auto bad_remote = interfaces.CreateRemote<test::mojom::IntInterface>();
ASSERT_TRUE(bad_remote.is_bound());
ASSERT_TRUE(bad_remote.is_connected());
task_environment_.RunUntilIdle();
ASSERT_FALSE(bad_remote.is_connected());
}
TEST_F(InterfaceBundleTest, Lifetime) {
MockErrorHandler error_handler_;
mojo::Remote<test::mojom::StringInterface> remote;
{
InterfaceBundle bundle;
RemoteInterfaces interfaces(bundle.CreateRemote());
ASSERT_TRUE(
bundle.AddInterface<test::mojom::StringInterface>(&mock_interface_));
interfaces.BindNewPipe(&remote);
// Verify the Remote is usable.
std::string s = "Hello, world!";
EXPECT_CALL(mock_interface_, StringMethod(s));
remote->StringMethod(s);
task_environment_.RunUntilIdle();
remote.set_disconnect_handler(base::BindOnce(
&MockErrorHandler::OnError, base::Unretained(&error_handler_)));
// When the InterfaceBundle is destroyed, all dispensed Remotes will be
// invalidated.
EXPECT_CALL(error_handler_, OnError()).Times(1);
}
task_environment_.RunUntilIdle();
ASSERT_FALSE(remote.is_connected());
}
TEST_F(InterfaceBundleTest, LateBinding) {
InterfaceBundle bundle;
// Don't link to the InterfaceBundle just yet.
RemoteInterfaces interfaces;
ASSERT_TRUE(
bundle.AddInterface<test::mojom::StringInterface>(&mock_interface_));
mojo::Remote<test::mojom::StringInterface> remote;
interfaces.BindNewPipe(&remote);
std::string s = "Hello, world!";
// We do not have a link to an implementation yet, but the client can still
// try to use the interface anyway.
EXPECT_CALL(mock_interface_, StringMethod(s)).Times(0);
remote->StringMethod(s);
remote->StringMethod(s);
remote->StringMethod(s);
task_environment_.RunUntilIdle();
// When we bind the provider late, all requests should be fulfilled.
EXPECT_CALL(mock_interface_, StringMethod(s)).Times(3);
interfaces.SetProvider(bundle.CreateRemote());
task_environment_.RunUntilIdle();
}
} // namespace chromecast
# Copyright 2020 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.
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [ "remote_interfaces.mojom" ]
}
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
// Copyright 2020 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.
module chromecast.mojom;
// Note for reviewers: Never allow an instance of RemoteInterfaces to be exposed
// to a sandboxed process (e.g. render process).
//
// Generic interface for binding a message pipe |pipe| to an interface with type
// |interface_name|. As a generic binding surface, this interface will never be
// provided to an unprivileged process. When implementing or using this
// interface, keep in mind the following:
//
// 1. This interface is accessible only to unsandboxed, privileged processes
// running trusted code.
// 2. Trusted clients are free to pass and wrap this interface around as they
// see fit. Therefore, make no assumptions about which trusted system
// components have access to it, since there is no programmatic enforcement.
//
// This interface is only used on Cast devices. This offers an easy way to move
// off of the Service Manager. For example, an instance of RemoteInterfaces can
// be injected into a process that uses the service_manager::Connector to bind
// Remotes to other services.
interface RemoteInterfaces {
// Requests to bind |pipe| to a named interface type.
BindInterface(string interface_name, handle<message_pipe> pipe);
// Binds a remote client to the implementation.
AddClient(pending_receiver<RemoteInterfaces> receiver);
};
// Copyright 2020 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 "chromecast/mojo/remote_interfaces.h"
#include "base/check.h"
namespace chromecast {
RemoteInterfaces::RemoteInterfaces() {
Init();
DCHECK(remote_provider_.is_bound());
}
RemoteInterfaces::RemoteInterfaces(
mojo::PendingRemote<mojom::RemoteInterfaces> provider) {
Init();
DCHECK(remote_provider_.is_bound());
SetProvider(std::move(provider));
}
RemoteInterfaces::~RemoteInterfaces() = default;
void RemoteInterfaces::SetProvider(
mojo::PendingRemote<mojom::RemoteInterfaces> provider) {
DCHECK(waiting_receiver_);
mojo::FusePipes(std::move(waiting_receiver_), std::move(provider));
}
mojo::PendingRemote<mojom::RemoteInterfaces> RemoteInterfaces::Forward() {
mojo::PendingRemote<mojom::RemoteInterfaces> pending_remote;
remote_provider_->AddClient(pending_remote.InitWithNewPipeAndPassReceiver());
return pending_remote;
}
mojo::PendingReceiver<mojom::RemoteInterfaces> RemoteInterfaces::GetReceiver() {
DCHECK(waiting_receiver_);
return std::move(waiting_receiver_);
}
void RemoteInterfaces::Init() {
waiting_receiver_ = remote_provider_.BindNewPipeAndPassReceiver();
remote_provider_.set_disconnect_handler(
base::BindOnce(&RemoteInterfaces::Init, base::Unretained(this)));
}
} // namespace chromecast
// Copyright 2020 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 CHROMECAST_MOJO_REMOTE_INTERFACES_H_
#define CHROMECAST_MOJO_REMOTE_INTERFACES_H_
#include "base/sequence_checker.h"
#include "chromecast/mojo/mojom/remote_interfaces.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace chromecast {
// Client helper object which wraps a mojo::Remote<mojom::RemoteInterfaces> and
// provides convenience methods for binding local Remote stubs.
//
// =============================================================================
// Example Usage
// =============================================================================
//
// From a service, create a pending RemoteInterfaces, such as from
// InterfaceBundle:
//
// InterfaceBundle bundle;
// bundle.AddInterface<mojom::Foo>(GetFooImpl());
// bundle.AddInterface<mojom::Bar>(GetBarImpl());
// mojo::PendingRemote<mojom::RemoteInterfaces> pending_provider =
// bundle.CreateRemote();
//
// In a different service, we can use RemoteInterfaces to wrap
// |pending_provider| and access the interfaces that were added:
//
// RemoteService::InjectInterfaces(
// mojo::PendingRemote<mojom::RemoteInterfaces> pending_provider) {
// RemoteInterfaces provider(std::move(pending_provider));
// mojo::Remote<mojom::Bar> bar = bundle.GetRemote<mojom::Bar>();
// bar->DoBarStuff();
// }
class RemoteInterfaces {
public:
RemoteInterfaces();
explicit RemoteInterfaces(
mojo::PendingRemote<mojom::RemoteInterfaces> provider);
~RemoteInterfaces();
RemoteInterfaces(const RemoteInterfaces&) = delete;
RemoteInterfaces& operator=(const RemoteInterfaces&) = delete;
// Late-binds a provider if one was not injected on creation.
void SetProvider(mojo::PendingRemote<mojom::RemoteInterfaces> provider);
// Exposes interfaces to a remote provider.
mojo::PendingRemote<mojom::RemoteInterfaces> Forward();
// Gets the currently unbound receiver to pass to a remote provider. There
// must not already be a receiver bound to this class.
mojo::PendingReceiver<mojom::RemoteInterfaces> GetReceiver();
// ===========================================================================
// Interface binding methods: After binding, the Remotes/PendingRemotes which
// are bound are guaranteed to be bound and connected (i.e. remote.is_bound()
// and remote.is_connected() will return true immediately). Clients can safely
// start issuing method calls at this point. However, if the remote provider
// does not fulfill the request, then the Remote/PendingRemote will become
// disconnected (remote.is_connected() == false), but still be bound.
// ===========================================================================
// Binds an <Interface> implementation in the remote provider.
template <typename Interface>
void Bind(mojo::PendingReceiver<Interface> receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
remote_provider_->BindInterface(Interface::Name_, receiver.PassPipe());
}
// Binds a new message pipe to |remote|, and passes the request to the remote
// provider.
template <typename Interface>
void BindNewPipe(mojo::Remote<Interface>* remote) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto pending_receiver = remote->BindNewPipeAndPassReceiver();
Bind(std::move(pending_receiver));
}
// Dispenses a mojo::Remote<Interface> which is bound by the remote provider.
template <typename Interface>
mojo::Remote<Interface> CreateRemote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
mojo::Remote<Interface> remote;
BindNewPipe(&remote);
return remote;
}
private:
void Init();
mojo::Remote<mojom::RemoteInterfaces> remote_provider_;
// Temporary pending receiver which allows the client to immediately
// start acquiring remote interfaces while we wait for an implementation
// to be provided.
mojo::PendingReceiver<mojom::RemoteInterfaces> waiting_receiver_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace chromecast
#endif // CHROMECAST_MOJO_REMOTE_INTERFACES_H_
# Copyright 2020 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.
import("//mojo/public/tools/bindings/mojom.gni")
mojom("test_mojom") {
testonly = true
sources = [ "test_interfaces.test-mojom" ]
}
static_library("run_all_unittests") {
testonly = true
sources = [ "run_all_unittests.cc" ]
deps = [
"//base",
"//base/test:test_support",
"//mojo/core/embedder",
]
}
include_rules = [
"+mojo/core/embedder",
]
// Copyright 2020 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/bind.h"
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h"
#include "mojo/core/embedder/embedder.h"
int main(int argc, char** argv) {
base::TestSuite test_suite(argc, argv);
mojo::core::Init();
return base::LaunchUnitTests(
argc, argv,
base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
}
// Copyright 2020 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.
module chromecast.test.mojom;
// Simple interfaces for testing.
interface StringInterface {
StringMethod(string s);
};
interface IntInterface {
IntMethod(int32 i);
};
interface BoolInterface {
BoolMethod(bool b);
};
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