blob: bf6e58f6b1fd77f57d1754373476d8b1b4abe326 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chrome_process_singleton.h"
#include <utility>
#include "build/build_config.h"
#include "chrome/browser/headless/headless_mode_util.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/common/chrome_switches.h"
#if BUILDFLAG(IS_WIN)
#include "base/hash/hash.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "chrome/common/channel_info.h"
#include "components/version_info/channel.h"
#endif
namespace {
constexpr char kEarlySingletonForceEnabledGroup[] = "Enabled_Forced3";
constexpr char kEarlySingletonEnabledGroup[] = "Enabled3";
constexpr char kEarlySingletonDisabledMergeGroup[] = "Disabled_Merge3";
constexpr char kEarlySingletonDefaultGroup[] = "Default3";
#if BUILDFLAG(IS_WIN)
constexpr char kEarlySingletonDisabledGroup[] = "Disabled3";
#endif // BUILDFLAG(IS_WIN)
const char* g_early_singleton_feature_group_ = nullptr;
ChromeProcessSingleton* g_chrome_process_singleton_ = nullptr;
#if BUILDFLAG(IS_WIN)
std::string GetMachineGUID() {
base::win::RegKey key;
std::wstring value;
if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Cryptography",
KEY_QUERY_VALUE | KEY_WOW64_64KEY) != ERROR_SUCCESS ||
key.ReadValue(L"MachineGuid", &value) != ERROR_SUCCESS || value.empty()) {
return std::string();
}
std::string machine_guid;
if (!base::WideToUTF8(value.c_str(), value.length(), &machine_guid))
return std::string();
return machine_guid;
}
const char* EnrollMachineInEarlySingletonFeature() {
// Run experiment on early channels only.
const version_info::Channel channel = chrome::GetChannel();
if (channel != version_info::Channel::CANARY &&
channel != version_info::Channel::DEV &&
channel != version_info::Channel::UNKNOWN) {
return kEarlySingletonDefaultGroup;
}
const std::string machine_guid = GetMachineGUID();
if (machine_guid.empty()) {
return kEarlySingletonDefaultGroup;
}
switch (base::Hash(machine_guid + "EarlyProcessSingleton") % 3) {
case 0:
return kEarlySingletonEnabledGroup;
case 1:
return kEarlySingletonDisabledGroup;
case 2:
return kEarlySingletonDisabledMergeGroup;
default:
NOTREACHED();
return kEarlySingletonDefaultGroup;
}
}
#endif // BUILDFLAG(IS_WIN)
} // namespace
ChromeProcessSingleton::ChromeProcessSingleton(
const base::FilePath& user_data_dir)
: startup_lock_(
base::BindRepeating(&ChromeProcessSingleton::NotificationCallback,
base::Unretained(this))),
modal_dialog_lock_(startup_lock_.AsNotificationCallback()),
process_singleton_(user_data_dir,
modal_dialog_lock_.AsNotificationCallback()) {}
ChromeProcessSingleton::~ChromeProcessSingleton() = default;
ProcessSingleton::NotifyResult
ChromeProcessSingleton::NotifyOtherProcessOrCreate() {
// In headless mode we don't want to hand off pages to an existing processes,
// so short circuit process singleton creation and bail out if we're not
// the only process using this user data dir.
if (headless::IsHeadlessMode()) {
return process_singleton_.Create() ? ProcessSingleton::PROCESS_NONE
: ProcessSingleton::PROFILE_IN_USE;
}
return process_singleton_.NotifyOtherProcessOrCreate();
}
void ChromeProcessSingleton::StartWatching() {
process_singleton_.StartWatching();
}
void ChromeProcessSingleton::Cleanup() {
process_singleton_.Cleanup();
}
void ChromeProcessSingleton::SetModalDialogNotificationHandler(
base::RepeatingClosure notification_handler) {
modal_dialog_lock_.SetModalDialogNotificationHandler(
std::move(notification_handler));
}
void ChromeProcessSingleton::Unlock(
const ProcessSingleton::NotificationCallback& notification_callback) {
notification_callback_ = notification_callback;
startup_lock_.Unlock();
}
// static
void ChromeProcessSingleton::CreateInstance(
const base::FilePath& user_data_dir) {
DCHECK(!g_chrome_process_singleton_);
DCHECK(!user_data_dir.empty());
g_chrome_process_singleton_ = new ChromeProcessSingleton(user_data_dir);
}
// static
void ChromeProcessSingleton::DeleteInstance() {
if (g_chrome_process_singleton_) {
delete g_chrome_process_singleton_;
g_chrome_process_singleton_ = nullptr;
}
}
// static
ChromeProcessSingleton* ChromeProcessSingleton::GetInstance() {
CHECK(g_chrome_process_singleton_);
return g_chrome_process_singleton_;
}
// static
void ChromeProcessSingleton::SetupEarlySingletonFeature(
const base::CommandLine& command_line) {
DCHECK(!g_early_singleton_feature_group_);
if (command_line.HasSwitch(switches::kEnableEarlyProcessSingleton)) {
g_early_singleton_feature_group_ = kEarlySingletonForceEnabledGroup;
return;
}
#if BUILDFLAG(IS_WIN)
g_early_singleton_feature_group_ = EnrollMachineInEarlySingletonFeature();
#else
g_early_singleton_feature_group_ = kEarlySingletonDefaultGroup;
#endif
}
// static
void ChromeProcessSingleton::RegisterEarlySingletonFeature() {
DCHECK(g_early_singleton_feature_group_);
// The synthetic trial needs to use kCurrentLog to ensure that UMA report will
// be generated from the metrics log that is open at the time of registration.
ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
"EarlyProcessSingleton", g_early_singleton_feature_group_,
variations::SyntheticTrialAnnotationMode::kCurrentLog);
}
// static
bool ChromeProcessSingleton::IsEarlySingletonFeatureEnabled() {
return g_early_singleton_feature_group_ == kEarlySingletonEnabledGroup ||
g_early_singleton_feature_group_ == kEarlySingletonForceEnabledGroup;
}
// static
bool ChromeProcessSingleton::ShouldMergeMetrics() {
// This should not be called when the early singleton feature is enabled.
DCHECK(g_early_singleton_feature_group_ && !IsEarlySingletonFeatureEnabled());
return g_early_singleton_feature_group_ == kEarlySingletonDisabledMergeGroup;
}
bool ChromeProcessSingleton::NotificationCallback(
const base::CommandLine& command_line,
const base::FilePath& current_directory) {
DCHECK(notification_callback_);
return notification_callback_.Run(command_line, current_directory);
}