#include "chrome/windows_services/service_program/crash_reporting.h"
#include <objidl.h>
#include <wrl/client.h>
#include <string>
#include <utility>
#include "base/base_paths.h"
#include "base/check.h"
#include "base/debug/leak_annotations.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
#include "base/version_info/channel.h"
#include "chrome/common/chrome_version.h"
#include "chrome/install_static/install_util.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/windows_services/service_program/is_running_unattended.h"
#include "chrome/windows_services/service_program/user_crash_state.h"
#include "components/crash/core/app/crash_reporter_client.h"
#include "components/crash/core/app/crashpad.h"
#include "components/crash/core/common/crash_key.h"
namespace {
base::FilePath GetCrashpadDir(base::FilePath::StringViewType directory_name) {
base::FilePath system_temp;
if (!base::PathService::Get(base::DIR_SYSTEM_TEMP, &system_temp)) {
return base::FilePath();
}
return system_temp.Append(directory_name)
.Append(FILE_PATH_LITERAL("Crashpad"));
}
class CrashClient : public crash_reporter::CrashReporterClient {
public:
CrashClient(base::FilePath crashpad_dir,
std::unique_ptr<UserCrashState> user_crash_state);
~CrashClient() override;
const UserCrashState& user_crash_state() const { return *user_crash_state_; }
void GetProductNameAndVersion(const std::wstring& exe_path,
std::wstring* product_name,
std::wstring* version,
std::wstring* special_build,
std::wstring* channel_name) override;
bool GetShouldDumpLargerDumps() override;
bool GetCrashDumpLocation(std::wstring* crash_dir) override;
bool IsRunningUnattended() override;
bool GetCollectStatsConsent() override;
bool GetCollectStatsInSample() override;
bool ReportingIsEnforcedByPolicy(bool* reporting_enabled) override;
private:
bool GetCurrentCollectStatsConsent();
bool GetCurrentCollectStatsInSample();
void OnClientStateMediumChanged();
void OnClientStateChanged();
void OnProductKeyChanged();
void UpdateUploadConsent();
const base::FilePath crashpad_dir_;
const std::unique_ptr<UserCrashState> user_crash_state_;
bool last_collect_stats_consent_ = false;
bool last_collect_stats_in_sample_ = false;
};
CrashClient::CrashClient(base::FilePath crashpad_dir,
std::unique_ptr<UserCrashState> user_crash_state)
: crashpad_dir_(std::move(crashpad_dir)),
user_crash_state_(std::move(user_crash_state)) {
if (user_crash_state_->client_state_medium_key().Valid()) {
user_crash_state_->client_state_medium_key().StartWatching(base::BindOnce(
&CrashClient::OnClientStateMediumChanged, base::Unretained(this)));
}
if (user_crash_state_->client_state_key().Valid()) {
user_crash_state_->client_state_key().StartWatching(base::BindOnce(
&CrashClient::OnClientStateChanged, base::Unretained(this)));
}
if (user_crash_state_->product_key().Valid()) {
user_crash_state_->product_key().StartWatching(base::BindOnce(
&CrashClient::OnProductKeyChanged, base::Unretained(this)));
}
}
CrashClient::~CrashClient() = default;
void CrashClient::GetProductNameAndVersion(const std::wstring& exe_path,
std::wstring* product_name,
std::wstring* version,
std::wstring* special_build,
std::wstring* channel_name) {
*product_name = base::ASCIIToWide(PRODUCT_SHORTNAME_STRING);
*version = base::ASCIIToWide(CHROME_VERSION_STRING);
special_build->clear();
*channel_name =
install_static::GetChromeChannelName(true);
}
bool CrashClient::GetShouldDumpLargerDumps() {
return install_static::GetChromeChannel() != version_info::Channel::STABLE;
}
bool CrashClient::GetCrashDumpLocation(std::wstring* crash_dir) {
*crash_dir = crashpad_dir_.Append(user_crash_state_->user_sid()).value();
return true;
}
bool CrashClient::IsRunningUnattended() {
return internal::IsRunningUnattended();
}
bool CrashClient::GetCollectStatsConsent() {
last_collect_stats_consent_ = GetCurrentCollectStatsConsent();
return last_collect_stats_consent_;
}
bool CrashClient::GetCollectStatsInSample() {
last_collect_stats_in_sample_ = GetCurrentCollectStatsInSample();
return last_collect_stats_in_sample_;
}
bool CrashClient::ReportingIsEnforcedByPolicy(bool* reporting_enabled) {
return install_static::ReportingIsEnforcedByPolicy(reporting_enabled);
}
bool CrashClient::GetCurrentCollectStatsConsent() {
DWORD value = 0;
if (user_crash_state_->client_state_medium_key().ReadValueDW(
google_update::kRegUsageStatsField, &value) == ERROR_SUCCESS) {
return value == google_update::TRISTATE_TRUE;
}
if (user_crash_state_->client_state_key().ReadValueDW(
google_update::kRegUsageStatsField, &value) == ERROR_SUCCESS) {
return value == google_update::TRISTATE_TRUE;
}
return false;
}
bool CrashClient::GetCurrentCollectStatsInSample() {
DWORD value = 0;
return user_crash_state_->product_key().ReadValueDW(
install_static::kRegValueChromeStatsSample, &value) !=
ERROR_SUCCESS ||
value == 1;
}
void CrashClient::OnClientStateMediumChanged() {
user_crash_state_->client_state_medium_key().StartWatching(base::BindOnce(
&CrashClient::OnClientStateMediumChanged, base::Unretained(this)));
UpdateUploadConsent();
}
void CrashClient::OnClientStateChanged() {
user_crash_state_->client_state_key().StartWatching(base::BindOnce(
&CrashClient::OnClientStateChanged, base::Unretained(this)));
UpdateUploadConsent();
}
void CrashClient::OnProductKeyChanged() {
user_crash_state_->product_key().StartWatching(base::BindOnce(
&CrashClient::OnProductKeyChanged, base::Unretained(this)));
UpdateUploadConsent();
}
void CrashClient::UpdateUploadConsent() {
bool collect_stats_consent = GetCurrentCollectStatsConsent();
bool collect_stats_in_sample = GetCurrentCollectStatsInSample();
if (collect_stats_consent != last_collect_stats_consent_ ||
collect_stats_in_sample != last_collect_stats_in_sample_) {
last_collect_stats_consent_ = collect_stats_consent;
crash_reporter::SetUploadConsent(collect_stats_consent);
}
}
void StartCrashHandlerImpl(std::unique_ptr<UserCrashState> user_crash_state,
base::FilePath::StringViewType directory_name,
std::string_view process_type) {
base::FilePath crashpad_dir = GetCrashpadDir(directory_name);
if (crashpad_dir.empty() || !base::CreateDirectory(crashpad_dir)) {
return;
}
static auto* const client =
new CrashClient(std::move(crashpad_dir), std::move(user_crash_state));
ANNOTATE_LEAKING_OBJECT_PTR(client);
if (user_crash_state) {
CHECK(client->user_crash_state().user_sid() ==
user_crash_state->user_sid());
return;
}
if (Microsoft::WRL::ComPtr<IGlobalOptions> options; SUCCEEDED(
::CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&options)))) {
options->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE);
}
crash_reporter::SetCrashReporterClient(client);
crash_reporter::InitializeCrashKeys();
crash_reporter::InitializeCrashpadWithEmbeddedHandler(
true, std::string(process_type),
std::string(),
base::FilePath());
}
}
namespace windows_services {
void StartCrashHandler(std::unique_ptr<UserCrashState> user_crash_state,
base::FilePath::StringViewType directory_name,
std::string_view process_type,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
if (task_runner) {
base::WaitableEvent event;
task_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<UserCrashState> user_crash_state,
base::FilePath::StringViewType directory_name,
std::string_view process_type, base::WaitableEvent& event) {
StartCrashHandlerImpl(std::move(user_crash_state), directory_name,
process_type);
event.Signal();
},
std::move(user_crash_state), directory_name, process_type,
std::ref(event)));
event.Wait();
return;
}
StartCrashHandlerImpl(std::move(user_crash_state), directory_name,
process_type);
}
}