blob: dec74faf2baaa4b6df5d8424857b85a9244c10cc [file] [log] [blame]
// Copyright 2025 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/platform_experience/installer/installer_win.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/win/scoped_variant.h"
#include "chrome/browser/google/google_update_app_command.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/install_static/install_details.h"
#include "chrome/installer/util/install_util.h"
namespace {
platform_experience::InstallerLauncherDelegate*
g_installer_delegate_for_testing = nullptr;
// Switch used to install platform_experience_helper
constexpr char kPlatformExperienceHelperForceInstallSwitch[] = "force-install";
// Directory under which platform_experience_helper is installed
constexpr wchar_t kPlatformExperienceHelperDir[] = L"PlatformExperienceHelper";
// Name of the platform_experience_helper executable
constexpr wchar_t kPlatformExperienceHelperExe[] =
L"platform_experience_helper.exe";
// This function might block.
// Returns true if the platform_experience_helper is installed.
// Returns true if it can't determine whether it's installed or not.
bool PlatformExperienceHelperMightBeInstalled() {
base::FilePath peh_base_dir = base::PathService::CheckedGet(
install_static::IsSystemInstall()
? static_cast<int>(base::DIR_EXE)
: static_cast<int>(chrome::DIR_USER_DATA));
base::FilePath peh_exe_path =
peh_base_dir.Append(kPlatformExperienceHelperDir)
.Append(kPlatformExperienceHelperExe);
return base::PathExists(peh_exe_path);
}
// Enum for tracking the launch status of the platform experience helper
// installer for system installs.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// LINT.IfChange(SystemInstallerLaunchStatus)
enum class SystemInstallerLaunchStatus {
kSuccess = 0,
kAppCommandNotFound = 1,
kAppCommandExecutionFailed = 2,
kMaxValue = kAppCommandExecutionFailed,
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/windows/enums.xml:SystemInstallerLaunchStatus)
// Enum for tracking the launch status of the platform experience helper
// installer for user installs.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// LINT.IfChange(UserInstallerLaunchStatus)
enum class UserInstallerLaunchStatus {
kSuccess = 0,
kFileNotFound = 1,
kAccessDenied = 2,
kOtherFailure = 3,
kMaxValue = kOtherFailure,
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/windows/enums.xml:UserInstallerLaunchStatus)
void ReportSystemInstallerLaunchStatus(SystemInstallerLaunchStatus status) {
base::UmaHistogramEnumeration(
"Windows.PlatformExperienceHelper.InstallerLaunchStatus.System", status);
}
void ReportUserInstallLaunchStatusMetric(UserInstallerLaunchStatus status) {
base::UmaHistogramEnumeration(
"Windows.PlatformExperienceHelper.InstallerLaunchStatus.User", status);
}
class DefaultInstallerLauncherDelegate
: public platform_experience::InstallerLauncherDelegate {
public:
DefaultInstallerLauncherDelegate() = default;
~DefaultInstallerLauncherDelegate() override = default;
Microsoft::WRL::ComPtr<IAppCommandWeb> GetUpdaterAppCommand(
const std::wstring& command_name) override {
return ::GetUpdaterAppCommand(command_name).value_or(nullptr);
}
base::Process LaunchProcess(const base::CommandLine& cmd_line,
const base::LaunchOptions& options) override {
return base::LaunchProcess(cmd_line, options);
}
};
platform_experience::InstallerLauncherDelegate* GetDelegate() {
if (g_installer_delegate_for_testing) {
return g_installer_delegate_for_testing;
}
static DefaultInstallerLauncherDelegate default_delegate;
return &default_delegate;
}
} // namespace
namespace platform_experience {
void SetInstallerLauncherDelegateForTesting( // IN-TEST
InstallerLauncherDelegate* delegate) {
g_installer_delegate_for_testing = delegate;
}
void MaybeInstallPlatformExperienceHelper() {
if (PlatformExperienceHelperMightBeInstalled()) {
return;
}
InstallerLauncherDelegate* delegate = GetDelegate();
if (install_static::IsSystemInstall()) {
auto command = delegate->GetUpdaterAppCommand(installer::kCmdInstallPEH);
if (!command) {
ReportSystemInstallerLaunchStatus(
SystemInstallerLaunchStatus::kAppCommandNotFound);
return;
}
const VARIANT& var = base::win::ScopedVariant::kEmptyVariant;
if (FAILED(command->execute(var, var, var, var, var, var, var, var, var))) {
ReportSystemInstallerLaunchStatus(
SystemInstallerLaunchStatus::kAppCommandExecutionFailed);
} else {
ReportSystemInstallerLaunchStatus(SystemInstallerLaunchStatus::kSuccess);
}
return;
}
base::FilePath peh_installer_path =
base::PathService::CheckedGet(base::DIR_MODULE)
.Append(FILE_PATH_LITERAL("os_update_handler.exe"));
base::CommandLine install_cmd(peh_installer_path);
install_cmd.AppendSwitch(kPlatformExperienceHelperForceInstallSwitch);
InstallUtil::AppendModeAndChannelSwitches(&install_cmd);
base::LaunchOptions launch_options;
launch_options.feedback_cursor_off = true;
launch_options.force_breakaway_from_job_ = true;
::SetLastError(ERROR_SUCCESS);
base::Process process = delegate->LaunchProcess(install_cmd, launch_options);
UserInstallerLaunchStatus status = UserInstallerLaunchStatus::kSuccess;
if (!process.IsValid()) {
switch (::GetLastError()) {
case ERROR_FILE_NOT_FOUND:
status = UserInstallerLaunchStatus::kFileNotFound;
break;
case ERROR_ACCESS_DENIED:
status = UserInstallerLaunchStatus::kAccessDenied;
break;
default:
status = UserInstallerLaunchStatus::kOtherFailure;
break;
}
}
ReportUserInstallLaunchStatusMetric(status);
}
} // namespace platform_experience