blob: f0873d3dc5f63fbd6d96b81b2ab17a28a651e6e4 [file] [log] [blame]
// Copyright 2019 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 <memory>
#include <string>
#include <utility>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "base/task/post_task.h"
#include "content/browser/utility_process_host.h"
#include "content/common/child_process.mojom.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/service_process_host.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace content {
namespace {
// Internal helper to track running service processes. Usage of this class is
// split across the IO thread and UI thread.
class ServiceProcessTracker {
public:
ServiceProcessTracker()
: ui_task_runner_(
base::CreateSingleThreadTaskRunner({BrowserThread::UI})) {}
~ServiceProcessTracker() = default;
ServiceProcessInfo AddProcess(const base::Process& process,
const std::string& service_interface_name) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::AutoLock lock(processes_lock_);
auto id = GenerateNextId();
ServiceProcessInfo& info = processes_[id];
info.service_process_id = id;
info.pid = process.Pid();
info.service_interface_name = service_interface_name;
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ServiceProcessTracker::NotifyLaunchOnUIThread,
base::Unretained(this), info));
return info;
}
void NotifyTerminated(ServiceProcessId id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::AutoLock lock(processes_lock_);
auto iter = processes_.find(id);
DCHECK(iter != processes_.end());
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ServiceProcessTracker::NotifyTerminatedOnUIThread,
base::Unretained(this), iter->second));
}
void NotifyCrashed(ServiceProcessId id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::AutoLock lock(processes_lock_);
auto iter = processes_.find(id);
DCHECK(iter != processes_.end());
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ServiceProcessTracker::NotifyCrashedOnUIThread,
base::Unretained(this), iter->second));
}
void AddObserver(ServiceProcessHost::Observer* observer) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
observers_.AddObserver(observer);
}
void RemoveObserver(ServiceProcessHost::Observer* observer) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
observers_.RemoveObserver(observer);
}
std::vector<ServiceProcessInfo> GetProcesses() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::AutoLock lock(processes_lock_);
std::vector<ServiceProcessInfo> processes;
for (const auto& entry : processes_)
processes.push_back(entry.second);
return processes;
}
private:
void NotifyLaunchOnUIThread(const content::ServiceProcessInfo& info) {
for (auto& observer : observers_)
observer.OnServiceProcessLaunched(info);
}
void NotifyTerminatedOnUIThread(const content::ServiceProcessInfo& info) {
for (auto& observer : observers_)
observer.OnServiceProcessTerminatedNormally(info);
}
void NotifyCrashedOnUIThread(const content::ServiceProcessInfo& info) {
for (auto& observer : observers_)
observer.OnServiceProcessCrashed(info);
}
ServiceProcessId GenerateNextId() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
auto id = next_id_;
next_id_ = ServiceProcessId::FromUnsafeValue(next_id_.GetUnsafeValue() + 1);
return id;
}
const scoped_refptr<base::TaskRunner> ui_task_runner_;
ServiceProcessId next_id_{1};
base::Lock processes_lock_;
std::map<ServiceProcessId, ServiceProcessInfo> processes_;
// Observers are owned and used exclusively on the UI thread.
base::ObserverList<ServiceProcessHost::Observer> observers_;
DISALLOW_COPY_AND_ASSIGN(ServiceProcessTracker);
};
ServiceProcessTracker& GetServiceProcessTracker() {
static base::NoDestructor<ServiceProcessTracker> tracker;
return *tracker;
}
// Helper to bridge UtilityProcessHost IO thread events to the
// ServiceProcessTracker. Every UtilityProcessHost created for a service process
// has a unique instance of this class associated with it.
class UtilityProcessClient : public UtilityProcessHost::Client {
public:
explicit UtilityProcessClient(const std::string& service_interface_name)
: service_interface_name_(service_interface_name) {}
~UtilityProcessClient() override = default;
// UtilityProcessHost::Client:
void OnProcessLaunched(const base::Process& process) override {
process_info_ =
GetServiceProcessTracker().AddProcess(process, service_interface_name_);
}
void OnProcessTerminatedNormally() override {
GetServiceProcessTracker().NotifyTerminated(
process_info_.service_process_id);
}
void OnProcessCrashed() override {
GetServiceProcessTracker().NotifyCrashed(process_info_.service_process_id);
}
private:
const std::string service_interface_name_;
ServiceProcessInfo process_info_;
DISALLOW_COPY_AND_ASSIGN(UtilityProcessClient);
};
// TODO(crbug.com/977637): Once UtilityProcessHost is used only by service
// processes, its logic can be inlined here.
void LaunchServiceProcessOnIOThread(mojo::GenericPendingReceiver receiver,
ServiceProcessHost::Options options) {
UtilityProcessHost* host = new UtilityProcessHost(
std::make_unique<UtilityProcessClient>(*receiver.interface_name()));
host->SetName(!options.display_name.empty()
? options.display_name
: base::UTF8ToUTF16(*receiver.interface_name()));
host->SetMetricsName(*receiver.interface_name());
host->SetSandboxType(options.sandbox_type);
host->SetExtraCommandLineSwitches(std::move(options.extra_switches));
if (options.child_flags)
host->set_child_flags(*options.child_flags);
host->Start();
host->GetChildProcess()->BindServiceInterface(std::move(receiver));
}
} // namespace
// static
std::vector<ServiceProcessInfo> ServiceProcessHost::GetRunningProcessInfo() {
return GetServiceProcessTracker().GetProcesses();
}
// static
void ServiceProcessHost::AddObserver(Observer* observer) {
GetServiceProcessTracker().AddObserver(observer);
}
// static
void ServiceProcessHost::RemoveObserver(Observer* observer) {
GetServiceProcessTracker().RemoveObserver(observer);
}
// static
void ServiceProcessHost::Launch(mojo::GenericPendingReceiver receiver,
Options options) {
DCHECK(receiver.interface_name().has_value());
base::CreateSingleThreadTaskRunner({BrowserThread::IO})
->PostTask(FROM_HERE,
base::BindOnce(&LaunchServiceProcessOnIOThread,
std::move(receiver), std::move(options)));
}
} // namespace content