| // Copyright 2015 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 "content/browser/service_manager/service_manager_context.h" |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/deferred_sequenced_task_runner.h" |
| #include "base/feature_list.h" |
| #include "base/lazy_instance.h" |
| #include "base/macros.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/message_loop/message_pump_type.h" |
| #include "base/no_destructor.h" |
| #include "base/optional.h" |
| #include "base/process/process_handle.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/post_task.h" |
| #include "build/build_config.h" |
| #include "content/browser/browser_main_loop.h" |
| #include "content/browser/builtin_service_manifests.h" |
| #include "content/browser/child_process_launcher.h" |
| #include "content/browser/gpu/gpu_process_host.h" |
| #include "content/browser/system_connector_impl.h" |
| #include "content/browser/utility_process_host.h" |
| #include "content/common/service_manager/service_manager_connection_impl.h" |
| #include "content/public/app/content_browser_manifest.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/child_process_data.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/gpu_service_registry.h" |
| #include "content/public/browser/service_process_host.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "content/public/common/service_names.mojom.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "mojo/public/cpp/platform/platform_channel.h" |
| #include "mojo/public/cpp/system/invitation.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "services/service_manager/public/cpp/constants.h" |
| #include "services/service_manager/public/cpp/manifest.h" |
| #include "services/service_manager/public/cpp/service.h" |
| #include "services/service_manager/public/mojom/service.mojom.h" |
| #include "services/service_manager/sandbox/sandbox_type.h" |
| #include "services/service_manager/service_manager.h" |
| #include "services/service_manager/service_process_host.h" |
| #include "services/service_manager/service_process_launcher.h" |
| #include "third_party/blink/public/strings/grit/blink_strings.h" |
| #include "ui/base/buildflags.h" |
| #include "ui/base/ui_base_features.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| base::LazyInstance<std::unique_ptr<service_manager::Connector>>::Leaky |
| g_io_thread_connector = LAZY_INSTANCE_INITIALIZER; |
| |
| base::LazyInstance<std::map<std::string, base::WeakPtr<UtilityProcessHost>>>:: |
| Leaky g_active_process_groups; |
| |
| service_manager::Manifest GetContentSystemManifest() { |
| // TODO(https://crbug.com/961869): This is a bit of a temporary hack so that |
| // we can make the global service instance a singleton. For now we just mirror |
| // the per-BrowserContext manifest (formerly also used for the global |
| // singleton instance), sans packaged services, since those are only meant to |
| // be tied to a BrowserContext. The per-BrowserContext service should go away |
| // soon, and then this can be removed. |
| service_manager::Manifest manifest = GetContentBrowserManifest(); |
| manifest.Amend(GetContentClient() |
| ->browser() |
| ->GetServiceManifestOverlay(mojom::kBrowserServiceName) |
| .value_or(service_manager::Manifest())); |
| manifest.service_name = mojom::kSystemServiceName; |
| manifest.packaged_services.clear(); |
| manifest.options.instance_sharing_policy = |
| service_manager::Manifest::InstanceSharingPolicy::kSingleton; |
| return manifest; |
| } |
| |
| void DestroyConnectorOnIOThread() { g_io_thread_connector.Get().reset(); } |
| |
| // A ServiceProcessHost implementation which delegates to Content-managed |
| // processes, either via a new UtilityProcessHost to launch new service |
| // processes, or the existing GpuProcessHost to run service instances in the GPU |
| // process. |
| class ContentChildServiceProcessHost |
| : public service_manager::ServiceProcessHost { |
| public: |
| ContentChildServiceProcessHost() = default; |
| ~ContentChildServiceProcessHost() override = default; |
| |
| // service_manager::ServiceProcessHost: |
| mojo::PendingRemote<service_manager::mojom::Service> Launch( |
| const service_manager::Identity& identity, |
| service_manager::SandboxType sandbox_type, |
| const base::string16& display_name, |
| LaunchCallback callback) override { |
| mojo::PendingRemote<service_manager::mojom::Service> remote; |
| auto receiver = remote.InitWithNewPipeAndPassReceiver(); |
| |
| // Start a new process for this service. |
| UtilityProcessHost* process_host = new UtilityProcessHost(); |
| process_host->SetName(display_name); |
| process_host->SetMetricsName(identity.name()); |
| process_host->SetServiceIdentity(identity); |
| process_host->SetSandboxType(sandbox_type); |
| process_host->Start(); |
| process_host->RunService( |
| identity.name(), std::move(receiver), |
| base::BindOnce( |
| [](LaunchCallback callback, |
| const base::Optional<base::ProcessId> pid) { |
| std::move(callback).Run(pid.value_or(base::kNullProcessId)); |
| }, |
| std::move(callback))); |
| return remote; |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ContentChildServiceProcessHost); |
| }; |
| |
| // A ServiceProcessHost implementation which uses the Service Manager's builtin |
| // service executable launcher. Not yet intended for use in production Chrome, |
| // hence availability is gated behind a flag. |
| class ServiceExecutableProcessHost |
| : public service_manager::ServiceProcessHost { |
| public: |
| explicit ServiceExecutableProcessHost(const base::FilePath& executable_path) |
| : launcher_(nullptr, executable_path) {} |
| ~ServiceExecutableProcessHost() override = default; |
| |
| // service_manager::ServiceProcessHost: |
| mojo::PendingRemote<service_manager::mojom::Service> Launch( |
| const service_manager::Identity& identity, |
| service_manager::SandboxType sandbox_type, |
| const base::string16& display_name, |
| LaunchCallback callback) override { |
| // TODO(https://crbug.com/781334): Support sandboxing. |
| return launcher_ |
| .Start(identity, service_manager::SandboxType::kNoSandbox, |
| std::move(callback)) |
| .PassInterface(); |
| } |
| |
| private: |
| service_manager::ServiceProcessLauncher launcher_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ServiceExecutableProcessHost); |
| }; |
| |
| using ServiceRequestHandler = base::RepeatingCallback<void( |
| const service_manager::Identity& identity, |
| mojo::PendingReceiver<service_manager::mojom::Service> receiver)>; |
| |
| // Implements in- and out-of-process service instance launching for services |
| // built into the Content embedder's binary. |
| // |
| // All methods on this object (except the constructor) are called on the Service |
| // Manager's thread, which is effectively the browser's IO thread. |
| class BrowserServiceManagerDelegate |
| : public service_manager::ServiceManager::Delegate { |
| public: |
| BrowserServiceManagerDelegate( |
| const scoped_refptr<base::SequencedTaskRunner>& main_thread_task_runner, |
| ServiceRequestHandler main_thread_request_handler) |
| : main_thread_task_runner_(main_thread_task_runner), |
| main_thread_request_handler_(std::move(main_thread_request_handler)) {} |
| ~BrowserServiceManagerDelegate() override = default; |
| |
| // service_manager::ServiceManager::Delegate: |
| bool RunBuiltinServiceInstanceInCurrentProcess( |
| const service_manager::Identity& identity, |
| mojo::PendingReceiver<service_manager::mojom::Service> receiver) |
| override { |
| main_thread_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(main_thread_request_handler_, identity, |
| std::move(receiver))); |
| return true; |
| } |
| |
| std::unique_ptr<service_manager::ServiceProcessHost> |
| CreateProcessHostForBuiltinServiceInstance( |
| const service_manager::Identity& identity) override { |
| return std::make_unique<ContentChildServiceProcessHost>(); |
| } |
| |
| std::unique_ptr<service_manager::ServiceProcessHost> |
| CreateProcessHostForServiceExecutable( |
| const base::FilePath& executable_path) override { |
| if (!base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kEnableServiceBinaryLauncher)) { |
| return nullptr; |
| } |
| |
| return std::make_unique<ServiceExecutableProcessHost>(executable_path); |
| } |
| |
| private: |
| const scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner_; |
| const ServiceRequestHandler main_thread_request_handler_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BrowserServiceManagerDelegate); |
| }; |
| |
| } // namespace |
| |
| // State which lives on the IO thread and drives the ServiceManager. |
| class ServiceManagerContext::InProcessServiceManagerContext |
| : public base::RefCountedThreadSafe<InProcessServiceManagerContext> { |
| public: |
| InProcessServiceManagerContext(scoped_refptr<base::SingleThreadTaskRunner> |
| service_manager_thread_task_runner) |
| : service_manager_thread_task_runner_( |
| service_manager_thread_task_runner) {} |
| |
| void Start(std::vector<service_manager::Manifest> manifests, |
| mojo::PendingRemote<service_manager::mojom::Service> system_remote, |
| ServiceRequestHandler request_handler) { |
| service_manager_thread_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &InProcessServiceManagerContext::StartOnServiceManagerThread, this, |
| std::move(manifests), base::ThreadTaskRunnerHandle::Get(), |
| std::move(system_remote), std::move(request_handler))); |
| } |
| |
| void ShutDown() { |
| service_manager_thread_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &InProcessServiceManagerContext::ShutDownOnServiceManagerThread, |
| this)); |
| } |
| |
| void StartServices(std::vector<std::string> service_names) { |
| service_manager_thread_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&InProcessServiceManagerContext :: |
| StartServicesOnServiceManagerThread, |
| this, std::move(service_names))); |
| } |
| |
| private: |
| friend class base::RefCountedThreadSafe<InProcessServiceManagerContext>; |
| |
| ~InProcessServiceManagerContext() = default; |
| |
| void StartOnServiceManagerThread( |
| std::vector<service_manager::Manifest> manifests, |
| scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner, |
| mojo::PendingRemote<service_manager::mojom::Service> system_remote, |
| ServiceRequestHandler request_handler) { |
| service_manager_ = std::make_unique<service_manager::ServiceManager>( |
| std::move(manifests), |
| std::make_unique<BrowserServiceManagerDelegate>( |
| ui_thread_task_runner, std::move(request_handler))); |
| |
| mojo::Remote<service_manager::mojom::ProcessMetadata> metadata; |
| service_manager_->RegisterService( |
| service_manager::Identity(mojom::kSystemServiceName, |
| service_manager::kSystemInstanceGroup, |
| base::Token{}, base::Token::CreateRandom()), |
| std::move(system_remote), metadata.BindNewPipeAndPassReceiver()); |
| metadata->SetPID(base::GetCurrentProcId()); |
| |
| service_manager_->SetInstanceQuitCallback( |
| base::BindOnce(&OnInstanceQuitOnServiceManagerThread, |
| std::move(ui_thread_task_runner))); |
| } |
| |
| static void OnInstanceQuitOnServiceManagerThread( |
| scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner, |
| const service_manager::Identity& id) { |
| ui_thread_task_runner->PostTask(FROM_HERE, |
| base::BindOnce(&OnInstanceQuit, id)); |
| } |
| |
| static void OnInstanceQuit(const service_manager::Identity& id) { |
| if (GetContentClient()->browser()->ShouldTerminateOnServiceQuit(id)) { |
| // Don't LOG(FATAL) because we don't want a browser crash report. |
| LOG(ERROR) << "Terminating because service '" << id.name() |
| << "' quit unexpectedly."; |
| // Skip shutdown to reduce the risk that other code in the browser will |
| // respond to the service pipe closing. |
| exit(1); |
| } |
| } |
| |
| void ShutDownOnServiceManagerThread() { |
| service_manager_.reset(); |
| } |
| |
| void StartServicesOnServiceManagerThread( |
| std::vector<std::string> service_names) { |
| if (!service_manager_) |
| return; |
| |
| for (const auto& service_name : service_names) |
| service_manager_->StartService(service_name); |
| } |
| |
| const scoped_refptr<base::SingleThreadTaskRunner> |
| service_manager_thread_task_runner_; |
| std::unique_ptr<service_manager::ServiceManager> service_manager_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InProcessServiceManagerContext); |
| }; |
| |
| ServiceManagerContext::ServiceManagerContext( |
| scoped_refptr<base::SingleThreadTaskRunner> |
| service_manager_thread_task_runner) |
| : service_manager_thread_task_runner_( |
| std::move(service_manager_thread_task_runner)) { |
| // The |service_manager_thread_task_runner_| must have been created before |
| // starting the ServiceManager. |
| DCHECK(service_manager_thread_task_runner_); |
| std::vector<service_manager::Manifest> manifests = |
| GetBuiltinServiceManifests(); |
| manifests.push_back(GetContentSystemManifest()); |
| for (auto& manifest : manifests) { |
| base::Optional<service_manager::Manifest> overlay = |
| GetContentClient()->browser()->GetServiceManifestOverlay( |
| manifest.service_name); |
| if (overlay) |
| manifest.Amend(*overlay); |
| } |
| for (auto& extra_manifest : |
| GetContentClient()->browser()->GetExtraServiceManifests()) { |
| manifests.emplace_back(std::move(extra_manifest)); |
| } |
| in_process_context_ = |
| new InProcessServiceManagerContext(service_manager_thread_task_runner_); |
| |
| mojo::PendingRemote<service_manager::mojom::Service> system_remote; |
| ServiceManagerConnection::SetForProcess(ServiceManagerConnection::Create( |
| system_remote.InitWithNewPipeAndPassReceiver(), |
| service_manager_thread_task_runner_)); |
| auto* system_connection = ServiceManagerConnection::GetForProcess(); |
| SetSystemConnector(system_connection->GetConnector()->Clone()); |
| |
| // This is safe to assign directly from any thread, because |
| // ServiceManagerContext must be constructed before anyone can call |
| // GetConnectorForIOThread(). |
| g_io_thread_connector.Get() = system_connection->GetConnector()->Clone(); |
| |
| GetContentClient()->browser()->WillStartServiceManager(); |
| |
| in_process_context_->Start( |
| manifests, std::move(system_remote), |
| base::BindRepeating(&ServiceManagerContext::RunServiceInstance, |
| weak_ptr_factory_.GetWeakPtr())); |
| in_process_context_->StartServices( |
| GetContentClient()->browser()->GetStartupServices()); |
| } |
| |
| ServiceManagerContext::~ServiceManagerContext() { |
| ShutDown(); |
| } |
| |
| void ServiceManagerContext::ShutDown() { |
| // NOTE: The in-process ServiceManager MUST be destroyed before the browser |
| // process-wide ServiceManagerConnection. Otherwise it's possible for the |
| // ServiceManager to receive connection requests for service:content_browser |
| // which it may attempt to service by launching a new instance of the browser. |
| if (in_process_context_) |
| in_process_context_->ShutDown(); |
| if (ServiceManagerConnection::GetForProcess()) |
| ServiceManagerConnection::DestroyForProcess(); |
| service_manager_thread_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&DestroyConnectorOnIOThread)); |
| } |
| |
| // static |
| service_manager::Connector* ServiceManagerContext::GetConnectorForIOThread() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| return g_io_thread_connector.Get().get(); |
| } |
| |
| // static |
| bool ServiceManagerContext::HasValidProcessForProcessGroup( |
| const std::string& process_group_name) { |
| auto iter = g_active_process_groups.Get().find(process_group_name); |
| if (iter == g_active_process_groups.Get().end() || !iter->second) |
| return false; |
| return iter->second->GetData().GetProcess().IsValid(); |
| } |
| |
| void ServiceManagerContext::RunServiceInstance( |
| const service_manager::Identity& identity, |
| mojo::PendingReceiver<service_manager::mojom::Service> receiver) { |
| GetContentClient()->browser()->RunServiceInstance(identity, &receiver); |
| DLOG_IF(ERROR, receiver) << "Unhandled service request for \"" |
| << identity.name() << "\""; |
| } |
| |
| } // namespace content |