blob: 66373c7cf982fca8af2062dc07025b332f7d9718 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/utility/utility_thread_impl.h"
#include <memory>
#include <optional>
#include <set>
#include <utility>
#include "base/command_line.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/debug/crash_logging.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "base/process/current_process.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/trace_log.h"
#include "build/build_config.h"
#include "content/child/child_process.h"
#include "content/public/utility/content_utility_client.h"
#include "content/utility/browser_exposed_utility_interfaces.h"
#include "content/utility/services.h"
#include "content/utility/utility_blink_platform_with_sandbox_support_impl.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/service_factory.h"
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "content/child/sandboxed_process_thread_type_handler.h"
#endif
namespace content {
namespace {
class ServiceBinderImpl {
public:
explicit ServiceBinderImpl(
scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner)
: main_thread_task_runner_(std::move(main_thread_task_runner)) {}
ServiceBinderImpl(const ServiceBinderImpl&) = delete;
ServiceBinderImpl& operator=(const ServiceBinderImpl&) = delete;
~ServiceBinderImpl() = default;
void BindServiceInterface(mojo::GenericPendingReceiver* receiver) {
// Set a crash key so utility process crash reports indicate which service
// was running in the process.
static auto* const service_name_crash_key =
base::debug::AllocateCrashKeyString("service-name",
base::debug::CrashKeySize::Size32);
const std::string& service_name = receiver->interface_name().value();
base::debug::SetCrashKeyString(service_name_crash_key, service_name);
// Traces should also indicate the service name.
if (base::CurrentProcess::GetInstance().IsProcessNameEmpty()) {
base::CurrentProcess::GetInstance().SetProcessType(
GetCurrentProcessType(service_name));
}
// Ensure the ServiceFactory is (lazily) initialized.
if (!io_thread_services_) {
io_thread_services_ = std::make_unique<mojo::ServiceFactory>();
RegisterIOThreadServices(*io_thread_services_);
}
// Note that this is balanced by `termination_callback` below, which is
// always eventually run as long as the process does not begin shutting
// down beforehand.
++num_service_instances_;
auto termination_callback =
base::BindOnce(&ServiceBinderImpl::OnServiceTerminated,
weak_ptr_factory_.GetWeakPtr());
if (io_thread_services_->CanRunService(*receiver)) {
io_thread_services_->RunService(std::move(*receiver),
std::move(termination_callback));
return;
}
termination_callback =
base::BindOnce(base::IgnoreResult(&base::SequencedTaskRunner::PostTask),
base::SingleThreadTaskRunner::GetCurrentDefault(),
FROM_HERE, std::move(termination_callback));
main_thread_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ServiceBinderImpl::TryRunMainThreadService,
std::move(*receiver), std::move(termination_callback)));
}
static std::optional<ServiceBinderImpl>& GetInstanceStorage() {
static base::NoDestructor<std::optional<ServiceBinderImpl>> storage;
return *storage;
}
private:
static void TryRunMainThreadService(mojo::GenericPendingReceiver receiver,
base::OnceClosure termination_callback) {
// NOTE: UtilityThreadImpl is the only defined subclass of UtilityThread, so
// this cast is safe.
auto* thread = static_cast<UtilityThreadImpl*>(UtilityThread::Get());
thread->HandleServiceRequest(std::move(receiver),
std::move(termination_callback));
}
void OnServiceTerminated() {
if (--num_service_instances_ > 0)
return;
// There are no more services running in this process. Time to terminate.
//
// First ensure that shutdown also tears down |this|. This is necessary to
// support multiple tests in the same test suite using out-of-process
// services via the InProcessUtilityThreadHelper, and it must be done on the
// current thread to avoid data races.
auto main_thread_task_runner = main_thread_task_runner_;
GetInstanceStorage().reset();
main_thread_task_runner->PostTask(
FROM_HERE, base::BindOnce(&ServiceBinderImpl::ShutDownProcess));
}
static void ShutDownProcess() {
UtilityThread::Get()->ReleaseProcess();
}
const scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner_;
// Tracks the number of service instances currently running (or pending
// creation) in this process. When the number transitions from non-zero to
// zero, the process will self-terminate.
int num_service_instances_ = 0;
// Handles service requests for services that must run on the IO thread.
std::unique_ptr<mojo::ServiceFactory> io_thread_services_;
base::WeakPtrFactory<ServiceBinderImpl> weak_ptr_factory_{this};
};
ChildThreadImpl::Options::ServiceBinder GetServiceBinder() {
auto& storage = ServiceBinderImpl::GetInstanceStorage();
// NOTE: This may already be initialized from a previous call if we're in
// single-process mode.
if (!storage)
storage.emplace(base::SingleThreadTaskRunner::GetCurrentDefault());
return base::BindRepeating(&ServiceBinderImpl::BindServiceInterface,
base::Unretained(&storage.value()));
}
} // namespace
UtilityThreadImpl::UtilityThreadImpl(base::RepeatingClosure quit_closure)
: ChildThreadImpl(std::move(quit_closure),
ChildThreadImpl::Options::Builder()
.WithLegacyIPCChannel(false)
.ServiceBinder(GetServiceBinder())
.ExposesInterfacesToBrowser()
.Build()) {
Init();
}
UtilityThreadImpl::UtilityThreadImpl(const InProcessChildThreadParams& params)
: ChildThreadImpl(base::DoNothing(),
ChildThreadImpl::Options::Builder()
.WithLegacyIPCChannel(false)
.InBrowserProcess(params)
.ServiceBinder(GetServiceBinder())
.ExposesInterfacesToBrowser()
.Build()) {
Init();
}
UtilityThreadImpl::~UtilityThreadImpl() = default;
void UtilityThreadImpl::Shutdown() {
ChildThreadImpl::Shutdown();
}
void UtilityThreadImpl::ReleaseProcess() {
// Ensure all main-thread services are destroyed before releasing the process.
// This limits the risk of services incorrectly attempting to post
// shutdown-blocking tasks once shutdown has already begun.
main_thread_services_.reset();
if (!IsInBrowserProcess()) {
ChildProcess::current()->ReleaseProcess();
return;
}
// Disconnect from the UtilityProcessHost to cause it to be deleted. We need
// to take a different code path than the multi-process case because that case
// depends on the child process going away to close the channel, but that
// can't happen when we're in single process mode.
DisconnectChildProcessHost();
}
void UtilityThreadImpl::EnsureBlinkInitialized() {
EnsureBlinkInitializedInternal(/*sandbox_support=*/false);
}
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
void UtilityThreadImpl::EnsureBlinkInitializedWithSandboxSupport() {
EnsureBlinkInitializedInternal(/*sandbox_support=*/true);
}
#endif
void UtilityThreadImpl::HandleServiceRequest(
mojo::GenericPendingReceiver receiver,
base::OnceClosure termination_callback) {
if (!main_thread_services_) {
main_thread_services_ = std::make_unique<mojo::ServiceFactory>();
RegisterMainThreadServices(*main_thread_services_);
}
if (main_thread_services_->CanRunService(receiver)) {
main_thread_services_->RunService(std::move(receiver),
std::move(termination_callback));
return;
}
DLOG(ERROR) << "Cannot run unknown service: " << *receiver.interface_name();
std::move(termination_callback).Run();
}
void UtilityThreadImpl::EnsureBlinkInitializedInternal(bool sandbox_support) {
if (blink_platform_impl_)
return;
// We can only initialize Blink on one thread, and in single process mode
// we run the utility thread on a separate thread. This means that if any
// code needs Blink initialized in the utility process, they need to have
// another path to support single process mode.
if (IsInBrowserProcess())
return;
blink_platform_impl_ =
sandbox_support
? std::make_unique<UtilityBlinkPlatformWithSandboxSupportImpl>()
: std::make_unique<blink::Platform>();
blink::Platform::CreateMainThreadAndInitialize(blink_platform_impl_.get());
}
void UtilityThreadImpl::Init() {
ChildProcess::current()->AddRefProcess();
GetContentClient()->utility()->UtilityThreadStarted();
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
SandboxedProcessThreadTypeHandler::NotifyMainChildThreadCreated();
#endif
// NOTE: Do not add new interfaces directly within this method. Instead,
// modify the definition of |ExposeUtilityInterfacesToBrowser()| to ensure
// security review coverage.
mojo::BinderMap binders;
content::ExposeUtilityInterfacesToBrowser(&binders);
ExposeInterfacesToBrowser(std::move(binders));
}
constexpr ServiceCurrentProcessType kCurrentProcessTypes[] = {
{"network.mojom.NetworkService",
CurrentProcessType::PROCESS_SERVICE_NETWORK},
{"tracing.mojom.TracingService",
CurrentProcessType::PROCESS_SERVICE_TRACING},
{"storage.mojom.StorageService",
CurrentProcessType::PROCESS_SERVICE_STORAGE},
{"audio.mojom.AudioService", CurrentProcessType::PROCESS_SERVICE_AUDIO},
{"data_decoder.mojom.DataDecoderService",
CurrentProcessType::PROCESS_SERVICE_DATA_DECODER},
{"chrome.mojom.UtilWin", CurrentProcessType::PROCESS_SERVICE_UTIL_WIN},
{"proxy_resolver.mojom.ProxyResolverFactory",
CurrentProcessType::PROCESS_SERVICE_PROXY_RESOLVER},
{"media.mojom.CdmServiceBroker", CurrentProcessType::PROCESS_SERVICE_CDM},
{"media.mojom.MediaFoundationServiceBroker",
CurrentProcessType::PROCESS_SERVICE_MEDIA_FOUNDATION},
{"video_capture.mojom.VideoCaptureService",
CurrentProcessType::PROCESS_SERVICE_VIDEO_CAPTURE},
{"unzip.mojom.Unzipper", CurrentProcessType::PROCESS_SERVICE_UNZIPPER},
{"mirroring.mojom.MirroringService",
CurrentProcessType::PROCESS_SERVICE_MIRRORING},
{"patch.mojom.FilePatcher",
CurrentProcessType::PROCESS_SERVICE_FILEPATCHER},
{"chromeos.tts.mojom.TtsService", CurrentProcessType::PROCESS_SERVICE_TTS},
{"printing.mojom.PrintingService",
CurrentProcessType::PROCESS_SERVICE_PRINTING},
{"quarantine.mojom.Quarantine",
CurrentProcessType::PROCESS_SERVICE_QUARANTINE},
{"ash.local_search_service.mojom.LocalSearchService",
CurrentProcessType::PROCESS_SERVICE_CROS_LOCALSEARCH},
{"ash.assistant.mojom.AssistantAudioDecoderFactory",
CurrentProcessType::PROCESS_SERVICE_CROS_ASSISTANT_AUDIO_DECODER},
{"chrome.mojom.FileUtilService",
CurrentProcessType::PROCESS_SERVICE_FILEUTIL},
{"printing.mojom.PrintCompositor",
CurrentProcessType::PROCESS_SERVICE_PRINTCOMPOSITOR},
{"paint_preview.mojom.PaintPreviewCompositorCollection",
CurrentProcessType::PROCESS_SERVICE_PAINTPREVIEW},
{"media.mojom.SpeechRecognitionService",
CurrentProcessType::PROCESS_SERVICE_SPEECHRECOGNITION},
{"device.mojom.XRDeviceService",
CurrentProcessType::PROCESS_SERVICE_XRDEVICE},
{"chrome.mojom.UtilReadIcon", CurrentProcessType::PROCESS_SERVICE_READICON},
{"language_detection.mojom.LanguageDetectionService",
CurrentProcessType::PROCESS_SERVICE_LANGUAGEDETECTION},
{"sharing.mojom.Sharing", CurrentProcessType::PROCESS_SERVICE_SHARING},
{"chrome.mojom.MediaParserFactory",
CurrentProcessType::PROCESS_SERVICE_MEDIAPARSER},
{"qrcode_generator.mojom.QRCodeGeneratorService",
CurrentProcessType::PROCESS_SERVICE_QRCODEGENERATOR},
{"chrome.mojom.ProfileImport",
CurrentProcessType::PROCESS_SERVICE_PROFILEIMPORT},
{"ash.ime.mojom.ImeService", CurrentProcessType::PROCESS_SERVICE_IME},
{"recording.mojom.RecordingService",
CurrentProcessType::PROCESS_SERVICE_RECORDING},
{"shape_detection.mojom.ShapeDetectionService",
CurrentProcessType::PROCESS_SERVICE_SHAPEDETECTION},
};
CurrentProcessType GetCurrentProcessType(const std::string& name) {
for (auto kCurrentProcessType : kCurrentProcessTypes) {
if (name == kCurrentProcessType.name) {
return kCurrentProcessType.type;
}
}
return CurrentProcessType::PROCESS_UTILITY;
}
} // namespace content