0

[Chromecast] Add support for dynamic browser lifecycle

This change enables the standalone Cast Browser to start/stop
independently of the Cast Service. The following changes were made:

* Moved BrokerService from the Cast Browser to Cast Service.
* Introduced ReconnectingRemote, which silently handles Mojo reconnects.
  Optionally, users can opt-in to receive reconnect events so that they
  can properly re-initialize certain system states.

Merge-With: eureka-internal/707435

Bug: b/213495969
Test: Build/run cast_service + cast_browser, start/stop cast_browser
      and verify backdrop restarts.

Change-Id: I32a84972e7fa282cd6276115e6fe6ac323b5f4c2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3385361
Reviewed-by: Junbo Ke <juke@chromium.org>
Commit-Queue: Sean Topping <seantopping@chromium.org>
Cr-Commit-Position: refs/heads/main@{#961100}
This commit is contained in:
Sean Topping
2022-01-19 20:12:39 +00:00
committed by Chromium LUCI CQ
parent 5ac7a871bf
commit 83574b3e03
13 changed files with 204 additions and 10 deletions

@ -228,6 +228,10 @@ const char kDeferFeatureList[] = "defer-feature-list";
// running in a different process from `cast_service`.
const char kUseCastBrowserPrefConfig[] = "use-cast-browser-pref-config";
// Creates the service broker inside of this process. Only one process should
// host the service broker.
const char kInProcessBroker[] = "in-process-broker";
} // namespace switches
namespace chromecast {

@ -108,6 +108,7 @@ extern const char kExtensionsDir[];
// Switches for Cast browser decoupling.
extern const char kDeferFeatureList[];
extern const char kUseCastBrowserPrefConfig[];
extern const char kInProcessBroker[];
} // namespace switches

@ -234,6 +234,8 @@ cast_source_set("browser_base") {
"//chromecast/common/media",
"//chromecast/external_mojo/broker_service",
"//chromecast/external_mojo/external_service_support:external_service",
"//chromecast/external_mojo/external_service_support:util",
"//chromecast/external_mojo/public/cpp:common",
"//chromecast/graphics",
"//chromecast/media",
"//chromecast/media:libcast_media",

@ -60,6 +60,7 @@
#include "chromecast/external_mojo/broker_service/broker_service.h"
#include "chromecast/external_mojo/external_service_support/external_connector.h"
#include "chromecast/external_mojo/external_service_support/external_service.h"
#include "chromecast/external_mojo/public/cpp/common.h"
#include "chromecast/graphics/cast_window_manager.h"
#include "chromecast/media/base/key_systems_common.h"
#include "chromecast/media/base/video_plane_controller.h"
@ -559,12 +560,17 @@ int CastBrowserMainParts::PreCreateThreads() {
}
void CastBrowserMainParts::PostCreateThreads() {
auto* service_manager_connector =
ServiceManagerConnection::GetForProcess()->GetConnector();
broker_service_ =
std::make_unique<external_mojo::BrokerService>(service_manager_connector);
connector_ = external_service_support::ExternalConnector::Create(
broker_service_->CreateConnector());
if (GetSwitchValueBoolean(switches::kInProcessBroker, true)) {
auto* service_manager_connector =
ServiceManagerConnection::GetForProcess()->GetConnector();
broker_service_ = std::make_unique<external_mojo::BrokerService>(
service_manager_connector);
connector_ = external_service_support::ExternalConnector::Create(
broker_service_->CreateConnector());
} else {
connector_ = external_service_support::ExternalConnector::Create(
external_mojo::GetBrokerPath());
}
media_connector_ = connector_->Clone();
browser_service_ =
std::make_unique<external_service_support::ExternalService>();

@ -20,6 +20,7 @@
#include "chromecast/browser/mojom/cast_web_service.mojom.h"
#include "chromecast/common/identification_settings_manager.h"
#include "chromecast/common/mojom/identification_settings.mojom.h"
#include "chromecast/external_mojo/external_service_support/reconnecting_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "url/origin.h"
@ -69,6 +70,7 @@ class CastWebService : public mojom::CastWebService,
LRURendererCache* overlay_renderer_cache() {
return overlay_renderer_cache_.get();
}
ReconnectingRemote<mojom::CastWebService>* proxy() { return &proxy_; }
bool IsCastWebUIOrigin(const url::Origin& origin);
@ -118,6 +120,12 @@ class CastWebService : public mojom::CastWebService,
content::BrowserContext* const browser_context_;
// This is used on Aura platforms.
CastWindowManager* const window_manager_;
// This is injected into clients which live in the browser or in a remote
// process. This is temporary until the clients exclusively live in an
// external process.
ReconnectingRemote<mojom::CastWebService> proxy_{this};
CastWebViewFactory default_web_view_factory_;
CastWebViewFactory* override_web_view_factory_ = nullptr;

@ -123,7 +123,9 @@ source_set("standalone_service_main") {
":process_setup",
":tracing_client",
"//base",
"//chromecast/base:chromecast_switches",
"//chromecast/external_mojo/public/cpp:common",
"//chromecast/external_mojo/public/cpp:external_mojo_broker",
"//mojo/core/embedder",
]
public_deps = [ ":service_process" ]
@ -144,6 +146,15 @@ source_set("chromium_service") {
]
}
source_set("util") {
public = [ "reconnecting_remote.h" ]
public_deps = [
":external_service",
"//base",
"//mojo/public/cpp/bindings",
]
}
executable("standalone_mojo_broker") {
sources = [ "standalone_mojo_broker.cc" ]
deps = [

@ -103,6 +103,11 @@ class ExternalConnector {
// sequence.
virtual std::unique_ptr<ExternalConnector> Clone() = 0;
// Requests a PendingRemote for an ExternalConnector which can be passed to a
// different process.
virtual mojo::PendingRemote<external_mojo::mojom::ExternalConnector>
RequestConnector() = 0;
// Sends a request for a Chromium ServiceManager connector.
virtual void SendChromiumConnectorRequest(
mojo::ScopedMessagePipeHandle request) = 0;

@ -265,11 +265,18 @@ std::unique_ptr<ExternalConnector> ExternalConnectorImpl::Clone() {
if (broker_connection_) {
return std::make_unique<ExternalConnectorImpl>(broker_connection_);
}
// Bind to the current sequence since this is a public method.
BindConnectorIfNecessary();
return std::make_unique<ExternalConnectorImpl>(RequestConnector());
}
mojo::PendingRemote<external_mojo::mojom::ExternalConnector>
ExternalConnectorImpl::RequestConnector() {
// Bind to the current sequence since this is a public method.
BindConnectorIfNecessary();
mojo::PendingRemote<external_mojo::mojom::ExternalConnector> remote;
connector_->Clone(remote.InitWithNewPipeAndPassReceiver());
return std::make_unique<ExternalConnectorImpl>(std::move(remote));
return remote;
}
void ExternalConnectorImpl::SendChromiumConnectorRequest(

@ -56,6 +56,8 @@ class ExternalConnectorImpl : public ExternalConnector {
mojo::ScopedMessagePipeHandle interface_pipe,
bool async = true) override;
std::unique_ptr<ExternalConnector> Clone() override;
mojo::PendingRemote<external_mojo::mojom::ExternalConnector>
RequestConnector() override;
void SendChromiumConnectorRequest(
mojo::ScopedMessagePipeHandle request) override;
void QueryServiceList(

@ -87,6 +87,11 @@ FakeExternalConnector::Clone() {
return std::make_unique<FakeExternalConnector>(std::move(remote));
}
mojo::PendingRemote<external_mojo::mojom::ExternalConnector>
FakeExternalConnector::RequestConnector() {
return mojo::PendingRemote<external_mojo::mojom::ExternalConnector>();
}
void FakeExternalConnector::SendChromiumConnectorRequest(
mojo::ScopedMessagePipeHandle request) {}

@ -54,6 +54,8 @@ class FakeExternalConnector
std::vector<chromecast::external_mojo::mojom::ServiceInstanceInfoPtr>
service_instances_info) override;
std::unique_ptr<external_service_support::ExternalConnector> Clone() override;
mojo::PendingRemote<external_mojo::mojom::ExternalConnector>
RequestConnector() override;
void SendChromiumConnectorRequest(
mojo::ScopedMessagePipeHandle request) override;
void QueryServiceList(

@ -0,0 +1,112 @@
// Copyright 2022 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_EXTERNAL_MOJO_EXTERNAL_SERVICE_SUPPORT_RECONNECTING_REMOTE_H_
#define CHROMECAST_EXTERNAL_MOJO_EXTERNAL_SERVICE_SUPPORT_RECONNECTING_REMOTE_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/check.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "chromecast/external_mojo/external_service_support/external_connector.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace chromecast {
// A class which wraps a mojo::Remote with automatic reconnection logic.
//
// Two reconnection methods are supported: (1) Provide a service name and an
// ExternalConnector to reconnect, or (2) provide a callback to rebind the
// remote.
//
// Clients can register observer callbacks to be notified of reconnect events so
// that they can re-initialize some state in the remote process. Observers are
// notified in the same order they were registered. Observers should use WeakPtr
// if they expect to outlive the ReconnectingRemote.
//
// This class can also be used to wrap a local implementation. This can be used
// for (1) Client code which can exist in both in and out-of-process, and (2)
// Injecting a mock implementation. Since the impl is called directly, this
// allows for synchronous method call validation, as opposed to asynchronously
// posting mojo calls which require a base::RunLoop to verify in unit tests.
//
template <typename Interface>
class ReconnectingRemote {
public:
// Reconnect option 1: Provide an ExternalConnector to request the interface
// from a named service.
ReconnectingRemote(const std::string& service_name,
external_service_support::ExternalConnector* connector)
: service_name_(service_name), connector_(connector) {
DCHECK(connector_);
Connect();
}
// Reconnect option 2: Provide a callback to re-bind |remote_|. |remote_| is
// always in an unbound state before |connect_callback_| is run.
explicit ReconnectingRemote(
base::RepeatingCallback<void(mojo::Remote<Interface>* remote)>
connect_callback)
: connect_callback_(std::move(connect_callback)) {
Connect();
}
// Option 3: Inject an implementation directly to wrap a local implementation.
// Reconnection is not necessary since a local instance will always exist.
explicit ReconnectingRemote(Interface* impl) : remote_proxy_(impl) {}
ReconnectingRemote(const ReconnectingRemote&) = delete;
ReconnectingRemote& operator=(const ReconnectingRemote&) = delete;
~ReconnectingRemote() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
Interface* get() const { return remote_proxy_; }
Interface* operator->() const { return get(); }
Interface& operator*() const { return *get(); }
void OnReconnect(base::RepeatingClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_callbacks_.push_back(std::move(callback));
}
private:
void Connect() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
remote_.reset();
if (connector_) {
connector_->BindInterface(service_name_,
remote_.BindNewPipeAndPassReceiver());
} else {
connect_callback_.Run(&remote_);
}
DCHECK(remote_.is_bound());
remote_proxy_ = remote_.get();
remote_.set_disconnect_handler(
base::BindOnce(&ReconnectingRemote::Connect, base::Unretained(this)));
for (auto& callback : observer_callbacks_) {
callback.Run();
}
}
const std::string service_name_;
external_service_support::ExternalConnector* const connector_ = nullptr;
base::RepeatingCallback<void(mojo::Remote<Interface>* remote)>
connect_callback_;
mojo::Remote<Interface> remote_;
Interface* remote_proxy_ = nullptr;
std::vector<base::RepeatingClosure> observer_callbacks_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<ReconnectingRemote> weak_factory_{this};
};
} // namespace chromecast
#endif // CHROMECAST_EXTERNAL_MOJO_EXTERNAL_SERVICE_SUPPORT_RECONNECTING_REMOTE_H_

@ -7,18 +7,24 @@
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/message_loop/message_pump_for_io.h"
#include "base/message_loop/message_pump_type.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_executor.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/threading/sequence_bound.h"
#include "base/threading/thread.h"
#include "chromecast/base/chromecast_switches.h"
#include "chromecast/external_mojo/external_service_support/external_connector.h"
#include "chromecast/external_mojo/external_service_support/process_setup.h"
#include "chromecast/external_mojo/external_service_support/service_process.h"
#include "chromecast/external_mojo/external_service_support/tracing_client.h"
#include "chromecast/external_mojo/public/cpp/common.h"
#include "chromecast/external_mojo/public/cpp/external_mojo_broker.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
// Simple process entrypoint for standalone Mojo services.
@ -61,9 +67,32 @@ int main(int argc, char** argv) {
"StandaloneService");
GlobalState state;
chromecast::external_service_support::ExternalConnector::Connect(
chromecast::external_mojo::GetBrokerPath(),
base::BindOnce(&OnConnected, &state));
// State for in-process Mojo broker.
auto broker_thread = std::make_unique<base::Thread>("external_mojo");
base::SequenceBound<chromecast::external_mojo::ExternalMojoBroker> broker;
if (chromecast::GetSwitchValueBoolean(switches::kInProcessBroker, false)) {
// Set up the external Mojo Broker.
broker_thread->StartWithOptions(
base::Thread::Options(base::MessagePumpType::IO, 0));
broker = base::SequenceBound<chromecast::external_mojo::ExternalMojoBroker>(
broker_thread->task_runner(),
chromecast::external_mojo::GetBrokerPath());
mojo::PendingRemote<chromecast::external_mojo::mojom::ExternalConnector>
connector_remote;
broker
.AsyncCall(
&chromecast::external_mojo::ExternalMojoBroker::BindConnector)
.WithArgs(connector_remote.InitWithNewPipeAndPassReceiver());
OnConnected(&state,
chromecast::external_service_support::ExternalConnector::Create(
std::move(connector_remote)));
} else {
// Connect to existing Mojo broker.
chromecast::external_service_support::ExternalConnector::Connect(
chromecast::external_mojo::GetBrokerPath(),
base::BindOnce(&OnConnected, &state));
}
run_loop.Run();
base::ThreadPoolInstance::Get()->Shutdown();