blob: 0e3c25e948738e03ceb0d76091867ffc6dd95c5f [file] [log] [blame]
// Copyright 2020 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/updater/update_service_impl_impl.h"
#include <algorithm>
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/barrier_callback.h"
#include "base/barrier_closure.h"
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/containers/queue.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_string_value_serializer.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "base/version.h"
#include "build/build_config.h"
#include "chrome/enterprise_companion/global_constants.h"
#include "chrome/updater/app/app_utils.h"
#include "chrome/updater/auto_run_on_os_upgrade_task.h"
#include "chrome/updater/branded_constants.h"
#include "chrome/updater/change_owners_task.h"
#include "chrome/updater/check_for_updates_task.h"
#include "chrome/updater/cleanup_task.h"
#include "chrome/updater/configurator.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/handle_inconsistent_apps_task.h"
#include "chrome/updater/installer.h"
#include "chrome/updater/persisted_data.h"
#include "chrome/updater/policy/service.h"
#include "chrome/updater/prefs.h"
#include "chrome/updater/registration_data.h"
#include "chrome/updater/remove_uninstalled_apps_task.h"
#include "chrome/updater/update_block_check.h"
#include "chrome/updater/update_service.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/updater_version.h"
#include "chrome/updater/usage_stats_permissions.h"
#include "chrome/updater/util/util.h"
#include "components/policy/core/common/policy_types.h"
#include "components/prefs/pref_service.h"
#include "components/update_client/crx_update_item.h"
#include "components/update_client/protocol_definition.h"
#include "components/update_client/update_client.h"
#include "components/update_client/update_client_errors.h"
#if BUILDFLAG(IS_MAC)
#include <sys/mount.h>
#endif // BUILDFLAG(IS_MAC)
#if BUILDFLAG(IS_WIN)
#include <winhttp.h>
#include "base/win/registry.h"
#include "chrome/updater/util/win_util.h"
#include "chrome/updater/win/ui/l10n_util.h"
#include "chrome/updater/win/ui/resources/resources.grh"
#include "chrome/updater/win/ui/resources/updater_installer_strings.h"
#include "chrome/updater/win/win_constants.h"
#endif // BUILDFLAG(IS_WIN)
namespace updater {
// The functions below are various adaptors between |update_client| and
// |UpdateService| types.
namespace internal {
UpdateService::Result ToResult(update_client::Error error) {
switch (error) {
case update_client::Error::NONE:
return UpdateService::Result::kSuccess;
case update_client::Error::UPDATE_IN_PROGRESS:
return UpdateService::Result::kUpdateInProgress;
case update_client::Error::UPDATE_CANCELED:
return UpdateService::Result::kUpdateCanceled;
case update_client::Error::RETRY_LATER:
return UpdateService::Result::kRetryLater;
case update_client::Error::SERVICE_ERROR:
return UpdateService::Result::kServiceFailed;
case update_client::Error::UPDATE_CHECK_ERROR:
return UpdateService::Result::kUpdateCheckFailed;
case update_client::Error::CRX_NOT_FOUND:
return UpdateService::Result::kAppNotFound;
case update_client::Error::INVALID_ARGUMENT:
case update_client::Error::BAD_CRX_DATA_CALLBACK:
return UpdateService::Result::kInvalidArgument;
case update_client::Error::MAX_VALUE:
NOTREACHED();
}
}
void GetComponents(
scoped_refptr<PolicyService> policy_service,
crx_file::VerifierFormat verifier_format,
scoped_refptr<PersistedData> persisted_data,
const base::flat_map<std::string, std::string>& app_client_install_data,
const base::flat_map<std::string, std::string>& app_install_data_index,
const std::string& install_source,
UpdateService::Priority priority,
bool update_blocked,
UpdateService::PolicySameVersionUpdate policy_same_version_update,
const std::vector<std::string>& ids,
base::OnceCallback<
void(const std::vector<std::optional<update_client::CrxComponent>>&)>
callback) {
VLOG(1) << __func__
<< ". Same version update: " << policy_same_version_update;
const bool is_foreground = priority == UpdateService::Priority::kForeground;
auto barrier_callback =
base::BarrierCallback<std::optional<update_client::CrxComponent>>(
ids.size(),
base::BindOnce(
[](const std::vector<std::string>& ids,
const std::vector<std::optional<update_client::CrxComponent>>&
unordered) {
// Re-order the vector to match the order of `ids`.
std::vector<std::optional<update_client::CrxComponent>> ordered;
for (const auto& id : ids) {
auto it = std::ranges::find_if(
unordered,
[&id](std::optional<update_client::CrxComponent> v) {
return v && v->app_id == id;
});
ordered.push_back(it != unordered.end() ? *it : std::nullopt);
}
return ordered;
},
ids)
.Then(std::move(callback)));
for (const auto& id : ids) {
base::MakeRefCounted<Installer>(
id,
[&app_client_install_data, &id] {
auto it = app_client_install_data.find(id);
return it != app_client_install_data.end() ? it->second : "";
}(),
[&app_install_data_index, &id] {
auto it = app_install_data_index.find(id);
return it != app_install_data_index.end() ? it->second : "";
}(),
install_source,
policy_service->GetTargetChannel(id).policy_or(std::string()),
policy_service->GetTargetVersionPrefix(id).policy_or(std::string()),
policy_service->IsRollbackToTargetVersionAllowed(id).policy_or(false),
policy_service->GetMajorVersionRolloutPolicy(id)
.effective_policy_value(),
policy_service->GetMinorVersionRolloutPolicy(id)
.effective_policy_value(),
[&policy_service, &id, &is_foreground, update_blocked] {
if (update_blocked) {
return true;
}
PolicyStatus<int> app_updates =
policy_service->GetPolicyForAppUpdates(id);
return app_updates &&
(app_updates.policy() == kPolicyDisabled ||
(!is_foreground &&
app_updates.policy() == kPolicyManualUpdatesOnly) ||
(is_foreground &&
app_updates.policy() == kPolicyAutomaticUpdatesOnly));
}(),
policy_same_version_update, persisted_data, verifier_format)
->MakeCrxComponent(
base::BindOnce([](update_client::CrxComponent component) {
return component;
}).Then(barrier_callback));
}
}
#if BUILDFLAG(IS_WIN)
namespace {
std::wstring GetTextForUpdateClientInstallError(int error_code,
const std::wstring& language) {
#define INSTALL_SWITCH_ENTRY(error_code) \
case static_cast<int>(error_code): \
return GetLocalizedStringF(IDS_GENERIC_INSTALL_ERROR_BASE, L#error_code, \
language)
switch (error_code) {
INSTALL_SWITCH_ENTRY(update_client::InstallError::NONE);
INSTALL_SWITCH_ENTRY(update_client::InstallError::FINGERPRINT_WRITE_FAILED);
INSTALL_SWITCH_ENTRY(update_client::InstallError::BAD_MANIFEST);
INSTALL_SWITCH_ENTRY(update_client::InstallError::GENERIC_ERROR);
INSTALL_SWITCH_ENTRY(update_client::InstallError::MOVE_FILES_ERROR);
INSTALL_SWITCH_ENTRY(update_client::InstallError::SET_PERMISSIONS_FAILED);
INSTALL_SWITCH_ENTRY(update_client::InstallError::INVALID_VERSION);
INSTALL_SWITCH_ENTRY(update_client::InstallError::VERSION_NOT_UPGRADED);
INSTALL_SWITCH_ENTRY(update_client::InstallError::NO_DIR_COMPONENT_USER);
INSTALL_SWITCH_ENTRY(update_client::InstallError::CLEAN_INSTALL_DIR_FAILED);
INSTALL_SWITCH_ENTRY(
update_client::InstallError::INSTALL_VERIFICATION_FAILED);
INSTALL_SWITCH_ENTRY(update_client::InstallError::MISSING_INSTALL_PARAMS);
INSTALL_SWITCH_ENTRY(update_client::InstallError::LAUNCH_PROCESS_FAILED);
INSTALL_SWITCH_ENTRY(update_client::InstallError::CUSTOM_ERROR_BASE);
default:
return GetLocalizedStringF(IDS_GENERIC_INSTALL_ERROR_BASE,
GetTextForSystemError(error_code), language);
}
#undef INSTALL_SWITCH_ENTRY
}
std::wstring GetTextForDownloadError(int error, const std::wstring& language) {
#define DOWNLOAD_SWITCH_ENTRY(error_code) \
case static_cast<int>(error_code): \
return GetLocalizedStringF(IDS_GENERIC_DOWNLOAD_ERROR_BASE, L#error_code, \
language)
switch (error) {
DOWNLOAD_SWITCH_ENTRY(update_client::CrxDownloaderError::NO_URL);
DOWNLOAD_SWITCH_ENTRY(update_client::CrxDownloaderError::NO_HASH);
DOWNLOAD_SWITCH_ENTRY(
update_client::CrxDownloaderError::BITS_TOO_MANY_JOBS);
DOWNLOAD_SWITCH_ENTRY(update_client::CrxDownloaderError::GENERIC_ERROR);
case static_cast<int>(update_client::CrxDownloaderError::BAD_HASH):
return GetLocalizedString(IDS_DOWNLOAD_HASH_MISMATCH_BASE);
default:
return GetLocalizedStringF(IDS_GENERIC_DOWNLOAD_ERROR_BASE,
GetTextForSystemError(error), language);
}
#undef DOWNLOAD_SWITCH_ENTRY
}
std::wstring GetTextForUnpackError(int error, const std::wstring& language) {
#define UNPACK_SWITCH_ENTRY(error_code) \
case static_cast<int>(error_code): \
return GetLocalizedStringF(IDS_GENERIC_UNPACK_ERROR_BASE, L#error_code, \
language)
#define UNPACK_CACHING_SWITCH_ENTRY(error_code) \
case static_cast<int>(error_code): \
return GetLocalizedStringF(IDS_UNPACK_CACHING_ERROR_BASE, L#error_code, \
language)
switch (error) {
UNPACK_SWITCH_ENTRY(update_client::UnpackerError::kInvalidParams);
UNPACK_SWITCH_ENTRY(update_client::UnpackerError::kInvalidFile);
UNPACK_SWITCH_ENTRY(update_client::UnpackerError::kUnzipPathError);
UNPACK_SWITCH_ENTRY(update_client::UnpackerError::kUnzipFailed);
UNPACK_SWITCH_ENTRY(update_client::UnpackerError::kBadManifest);
UNPACK_SWITCH_ENTRY(update_client::UnpackerError::kBadExtension);
UNPACK_SWITCH_ENTRY(update_client::UnpackerError::kIoError);
UNPACK_SWITCH_ENTRY(
update_client::UnpackerError::kDeltaVerificationFailure);
UNPACK_SWITCH_ENTRY(update_client::UnpackerError::kDeltaBadCommands);
UNPACK_SWITCH_ENTRY(update_client::UnpackerError::kDeltaUnsupportedCommand);
UNPACK_SWITCH_ENTRY(update_client::UnpackerError::kDeltaOperationFailure);
UNPACK_SWITCH_ENTRY(
update_client::UnpackerError::kDeltaPatchProcessFailure);
UNPACK_SWITCH_ENTRY(
update_client::UnpackerError::kDeltaMissingExistingFile);
UNPACK_SWITCH_ENTRY(
update_client::UnpackerError::kPuffinMissingPreviousCrx);
UNPACK_SWITCH_ENTRY(update_client::UnpackerError::kCrxCacheNotProvided);
UNPACK_CACHING_SWITCH_ENTRY(
update_client::UnpackerError::kFailedToAddToCache);
UNPACK_CACHING_SWITCH_ENTRY(
update_client::UnpackerError::kFailedToCreateCacheDir);
default:
return GetLocalizedStringF(IDS_GENERIC_UNPACK_ERROR_BASE,
GetTextForSystemError(error), language);
}
#undef UNPACK_SWITCH_ENTRY
#undef UNPACK_CACHING_SWITCH_ENTRY
}
std::wstring GetTextForServiceError(int error, const std::wstring& language) {
#define SERVICE_SWITCH_ENTRY(error_code) \
case static_cast<int>(error_code): \
return GetLocalizedStringF(IDS_GENERIC_SERVICE_ERROR_BASE, L#error_code, \
language)
switch (error) {
SERVICE_SWITCH_ENTRY(update_client::ServiceError::SERVICE_WAIT_FAILED);
SERVICE_SWITCH_ENTRY(update_client::ServiceError::UPDATE_DISABLED);
SERVICE_SWITCH_ENTRY(update_client::ServiceError::CHECK_FOR_UPDATE_ONLY);
case static_cast<int>(update_client::ServiceError::CANCELLED):
return GetLocalizedString(IDS_SERVICE_ERROR_CANCELLED_BASE, language);
default:
return GetLocalizedStringF(IDS_GENERIC_SERVICE_ERROR_BASE,
GetTextForSystemError(error), language);
}
#undef SERVICE_SWITCH_ENTRY
}
std::wstring GetTextForUpdateCheckError(int error,
const std::wstring& language) {
#define UPDATE_CHECK_SWITCH_ENTRY(error_code) \
case static_cast<int>(error_code): \
return GetLocalizedStringF(IDS_GENERIC_UPDATE_CHECK_ERROR_BASE, \
L#error_code, language)
switch (error) {
UPDATE_CHECK_SWITCH_ENTRY(
update_client::ProtocolError::RESPONSE_NOT_TRUSTED);
UPDATE_CHECK_SWITCH_ENTRY(update_client::ProtocolError::MISSING_URLS);
UPDATE_CHECK_SWITCH_ENTRY(update_client::ProtocolError::PARSE_FAILED);
UPDATE_CHECK_SWITCH_ENTRY(
update_client::ProtocolError::UPDATE_RESPONSE_NOT_FOUND);
UPDATE_CHECK_SWITCH_ENTRY(update_client::ProtocolError::URL_FETCHER_FAILED);
UPDATE_CHECK_SWITCH_ENTRY(update_client::ProtocolError::INVALID_APPID);
case static_cast<int>(update_client::ProtocolError::UNKNOWN_APPLICATION):
return GetLocalizedString(IDS_UNKNOWN_APPLICATION_BASE, language);
case static_cast<int>(update_client::ProtocolError::RESTRICTED_APPLICATION):
return GetLocalizedString(IDS_RESTRICTED_RESPONSE_FROM_SERVER_BASE,
language);
case static_cast<int>(update_client::ProtocolError::OS_NOT_SUPPORTED):
return GetLocalizedString(IDS_OS_NOT_SUPPORTED_BASE, language);
case static_cast<int>(update_client::ProtocolError::HW_NOT_SUPPORTED):
return GetLocalizedString(IDS_HW_NOT_SUPPORTED_BASE, language);
case static_cast<int>(update_client::ProtocolError::NO_HASH):
return GetLocalizedString(IDS_NO_HASH_BASE, language);
case static_cast<int>(update_client::ProtocolError::UNSUPPORTED_PROTOCOL):
return GetLocalizedString(IDS_UNSUPPORTED_PROTOCOL_BASE, language);
case static_cast<int>(update_client::ProtocolError::INTERNAL):
return GetLocalizedString(IDS_INTERNAL_BASE, language);
// Http Status Code `401` Unauthorized.
case 401:
return GetLocalizedString(IDS_ERROR_HTTPSTATUS_UNAUTHORIZED_BASE,
language);
// Http Status Code `403` Forbidden.
case 403:
return GetLocalizedString(IDS_ERROR_HTTPSTATUS_FORBIDDEN_BASE, language);
// Http Status Code `407` Proxy Authentication Required.
case 407:
return GetLocalizedString(IDS_ERROR_HTTPSTATUS_PROXY_AUTH_REQUIRED_BASE,
language);
case HRESULT_FROM_WIN32(ERROR_WINHTTP_NAME_NOT_RESOLVED):
return GetLocalizedStringF(IDS_NO_NETWORK_PRESENT_ERROR_BASE,
GetExecutableRelativePath().value(), language);
default:
return GetLocalizedStringF(
IDS_GENERIC_UPDATE_CHECK_ERROR_BASE,
error >= 400 && error < 600
? base::UTF8ToWide(base::StringPrintf("HTTP %d", error))
: GetTextForSystemError(error),
language);
}
#undef UPDATE_CHECK_SWITCH_ENTRY
}
std::wstring GetTextForInstallerError(int error_code,
const std::wstring& language) {
#define POLICY_ERROR_SWITCH_ENTRY(error_code) \
case error_code: \
return GetLocalizedStringF(IDS_APP_INSTALL_DISABLED_BY_GROUP_POLICY_BASE, \
L#error_code, language)
switch (error_code) {
POLICY_ERROR_SWITCH_ENTRY(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY);
POLICY_ERROR_SWITCH_ENTRY(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY);
POLICY_ERROR_SWITCH_ENTRY(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY_MANUAL);
case GOOPDATEINSTALL_E_FILENAME_INVALID:
return GetLocalizedString(IDS_INVALID_INSTALLER_FILENAME_BASE, language);
case GOOPDATEINSTALL_E_INSTALLER_FAILED_START:
return GetLocalizedString(IDS_INSTALLER_FAILED_TO_START_BASE, language);
case GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT:
return GetLocalizedString(IDS_INSTALLER_TIMED_OUT_BASE, language);
case GOOPDATEINSTALL_E_INSTALL_ALREADY_RUNNING:
return GetLocalizedStringF(
IDS_GENERIC_INSTALLER_ERROR_BASE,
GetTextForSystemError(ERROR_INSTALL_ALREADY_RUNNING), language);
case ERROR_SUCCESS_REBOOT_INITIATED:
case ERROR_SUCCESS_REBOOT_REQUIRED:
case ERROR_SUCCESS_RESTART_REQUIRED:
return GetLocalizedStringF(IDS_INSTALL_REBOOT_BASE,
GetTextForSystemError(error_code), language);
default:
return GetLocalizedStringF(IDS_GENERIC_INSTALLER_ERROR_BASE,
GetTextForSystemError(error_code), language);
}
#undef POLICY_ERROR_SWITCH_ENTRY
}
} // namespace
std::string GetInstallerText(UpdateService::ErrorCategory error_category,
int error_code,
int extra_code,
const std::string& language) {
if (!error_code) {
return {};
}
const std::wstring language_w = base::UTF8ToWide(language);
return base::WideToUTF8(base::StrCat(
{[&] {
switch (error_category) {
case UpdateService::ErrorCategory::kInstall:
return GetTextForUpdateClientInstallError(error_code, language_w);
case UpdateService::ErrorCategory::kDownload:
return GetTextForDownloadError(error_code, language_w);
case UpdateService::ErrorCategory::kUnpack:
return GetTextForUnpackError(error_code, language_w);
case UpdateService::ErrorCategory::kService:
return GetTextForServiceError(error_code, language_w);
case UpdateService::ErrorCategory::kUpdateCheck:
return GetTextForUpdateCheckError(error_code, language_w);
case UpdateService::ErrorCategory::kInstaller:
return GetTextForInstallerError(error_code, language_w);
default:
LOG(ERROR) << "Unknown error category: " << error_category;
return std::wstring();
}
}(),
[&] {
if (!extra_code) {
return std::wstring();
}
return base::StrCat(
{L"\n", GetLocalizedStringF(IDS_EXTRA_CODE_BASE,
base::UTF8ToWide(base::StringPrintf(
"%#x", extra_code)),
language_w)});
}()}));
}
#endif // BUILDFLAG(IS_WIN)
base::Version GetRegisteredInstallerVersion(const std::string& app_id) {
#if BUILDFLAG(IS_WIN)
std::wstring pv;
return base::win::RegKey(UpdaterScopeToHKeyRoot(GetUpdaterScope()),
GetAppClientsKey(app_id).c_str(), Wow6432(KEY_READ))
.ReadValue(kRegValuePV, &pv) == ERROR_SUCCESS
? base::Version(base::WideToUTF8(pv))
: base::Version();
#else // BUILDFLAG(IS_WIN)
return {};
#endif // BUILDFLAG(IS_WIN)
}
} // namespace internal
namespace {
constexpr base::flat_map<std::string, std::string> kEmptyFlatMap;
update_client::Callback MakeUpdateClientCallback(
base::OnceCallback<void(UpdateService::Result)> callback) {
return base::BindOnce(
[](base::OnceCallback<void(UpdateService::Result)> callback,
update_client::Error error) {
std::move(callback).Run(internal::ToResult(error));
},
std::move(callback));
}
UpdateService::UpdateState::State ToUpdateState(
update_client::ComponentState component_state) {
switch (component_state) {
case update_client::ComponentState::kNew:
return UpdateService::UpdateState::State::kNotStarted;
case update_client::ComponentState::kChecking:
return UpdateService::UpdateState::State::kCheckingForUpdates;
case update_client::ComponentState::kDownloading:
case update_client::ComponentState::kDownloadingDiff:
return UpdateService::UpdateState::State::kDownloading;
case update_client::ComponentState::kCanUpdate:
return UpdateService::UpdateState::State::kUpdateAvailable;
case update_client::ComponentState::kUpdating:
case update_client::ComponentState::kUpdatingDiff:
return UpdateService::UpdateState::State::kInstalling;
case update_client::ComponentState::kUpdated:
return UpdateService::UpdateState::State::kUpdated;
case update_client::ComponentState::kUpToDate:
return UpdateService::UpdateState::State::kNoUpdate;
case update_client::ComponentState::kUpdateError:
return UpdateService::UpdateState::State::kUpdateError;
case update_client::ComponentState::kRun:
case update_client::ComponentState::kLastStatus:
NOTREACHED();
}
}
UpdateService::ErrorCategory ToErrorCategory(
update_client::ErrorCategory error_category) {
switch (error_category) {
case update_client::ErrorCategory::kNone:
return UpdateService::ErrorCategory::kNone;
case update_client::ErrorCategory::kDownload:
return UpdateService::ErrorCategory::kDownload;
case update_client::ErrorCategory::kUnpack:
return UpdateService::ErrorCategory::kUnpack;
case update_client::ErrorCategory::kInstall:
return UpdateService::ErrorCategory::kInstall;
case update_client::ErrorCategory::kService:
return UpdateService::ErrorCategory::kService;
case update_client::ErrorCategory::kUpdateCheck:
return UpdateService::ErrorCategory::kUpdateCheck;
case update_client::ErrorCategory::kInstaller:
return UpdateService::ErrorCategory::kInstaller;
}
}
update_client::UpdateClient::CrxStateChangeCallback
MakeUpdateClientCrxStateChangeCallback(
scoped_refptr<update_client::Configurator> config,
scoped_refptr<PersistedData> persisted_data,
const bool new_install,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)> callback) {
return base::BindRepeating(
[](scoped_refptr<update_client::Configurator> config,
scoped_refptr<PersistedData> persisted_data, const bool new_install,
const std::string& language,
base::RepeatingCallback<void(const UpdateService::UpdateState&)>
callback,
const update_client::CrxUpdateItem& crx_update_item) {
UpdateService::UpdateState update_state;
update_state.app_id = crx_update_item.id;
update_state.state = ToUpdateState(crx_update_item.state);
update_state.next_version = crx_update_item.next_version;
update_state.downloaded_bytes = crx_update_item.downloaded_bytes;
update_state.total_bytes = crx_update_item.total_bytes;
update_state.install_progress = crx_update_item.install_progress;
update_state.error_category =
ToErrorCategory(crx_update_item.error_category);
update_state.error_code = crx_update_item.error_code;
update_state.extra_code1 = crx_update_item.extra_code1;
if (crx_update_item.installer_result) {
update_state.installer_cmd_line =
crx_update_item.installer_result->installer_cmd_line;
update_state.installer_text =
crx_update_item.installer_result->installer_text;
#if BUILDFLAG(IS_WIN)
if (update_state.installer_text.empty())
update_state.installer_text = internal::GetInstallerText(
UpdateService::ErrorCategory::kInstaller,
update_state.error_code, update_state.extra_code1, language);
#endif // BUILDFLAG(IS_WIN)
}
if (update_state.state == UpdateService::UpdateState::State::kUpdated ||
update_state.state ==
UpdateService::UpdateState::State::kUpdateError ||
update_state.state ==
UpdateService::UpdateState::State::kNoUpdate) {
#if BUILDFLAG(IS_WIN)
if (update_state.installer_text.empty())
update_state.installer_text = internal::GetInstallerText(
update_state.error_category, update_state.error_code,
update_state.extra_code1, language);
#endif // BUILDFLAG(IS_WIN)
// If a new install encounters an error, the AppId registered in
// `UpdateServiceImplImpl::Install` needs to be removed here.
// Otherwise the updater may remain installed even if there are no
// other apps to manage, and try to update the app even though the app
// was not installed.
if (new_install &&
(update_state.state ==
UpdateService::UpdateState::State::kUpdateError ||
update_state.state ==
UpdateService::UpdateState::State::kNoUpdate)) {
persisted_data->RemoveApp(update_state.app_id);
}
// Commit the prefs values written by |update_client| when the
// update has completed, such as `pv` and `fingerprint`.
config->GetPrefService()->CommitPendingWrite();
}
CHECK(!update_state.app_id.empty());
CHECK_NE(update_state.state,
UpdateService::UpdateState::State::kUnknown);
callback.Run(update_state);
},
config, persisted_data, new_install, language, callback);
}
bool IsPathOnReadOnlyMount(const base::FilePath& path) {
if (path.empty()) {
return false;
}
#if BUILDFLAG(IS_MAC)
struct statfs fsinfo = {};
if (statfs(path.value().c_str(), &fsinfo) == -1) {
LOG(ERROR) << "Failed to stat: " << path;
return false;
}
return fsinfo.f_flags & MNT_RDONLY;
#else
return false;
#endif // BUILDFLAG(IS_MAC)
}
void FetchPoliciesDone(
base::OnceCallback<void(base::OnceCallback<void(UpdateService::Result)>)>
fetch_policies_done,
base::OnceCallback<void(UpdateService::Result)> callback,
int result) {
if (result != kErrorOk) {
LOG(ERROR) << "FetchPolicies failed: " << result;
// Ignore policy fetch failures and fall through.
}
std::move(fetch_policies_done).Run(std::move(callback));
}
} // namespace
UpdateServiceImplImpl::UpdateServiceImplImpl(scoped_refptr<Configurator> config)
: config_(config),
main_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
update_client_(update_client::UpdateClientFactory(config)) {}
void UpdateServiceImplImpl::GetVersion(
base::OnceCallback<void(const base::Version&)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), base::Version(kUpdaterVersion)));
}
void UpdateServiceImplImpl::MaybeInstallEnterpriseCompanionAppOTA(
base::OnceClosure callback,
bool is_cloud_managed) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!is_cloud_managed) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(callback));
return;
}
VLOG(1) << "Starting an OTA installation of the enterprise companion app.";
RegistrationRequest registration;
registration.app_id = enterprise_companion::kCompanionAppId;
registration.version = base::Version(kNullVersion);
RegisterApp(
registration,
base::BindOnce([](int registration_result) {})
.Then(base::BindPostTask(
main_task_runner_,
base::BindOnce(
base::IgnoreResult(&update_client::UpdateClient::Install),
update_client_, enterprise_companion::kCompanionAppId,
base::BindOnce(&internal::GetComponents,
config_->GetPolicyService(),
config_->GetCrxVerifierFormat(),
config_->GetUpdaterPersistedData(),
kEmptyFlatMap, kEmptyFlatMap,
kInstallSourcePolicy, Priority::kForeground,
/*update_blocked=*/false,
PolicySameVersionUpdate::kNotAllowed),
MakeUpdateClientCrxStateChangeCallback(
config_, config_->GetUpdaterPersistedData(),
/*new_install=*/false,
/*language=*/{},
/*callback=*/base::DoNothing()),
MakeUpdateClientCallback(base::BindOnce([](Result result) {
VLOG(1)
<< "OTA installation of the "
"enterprise companion app "
"completed with result: "
<< result;
}).Then(std::move(callback)))))));
}
void UpdateServiceImplImpl::FetchPolicies(
policy::PolicyFetchReason reason,
base::OnceCallback<void(int)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (GetUpdaterScope() == UpdaterScope::kUser) {
VLOG(2) << "Policy fetch skipped for user updater.";
std::move(callback).Run(0);
return;
}
if (!config_->GetUpdaterPersistedData()
->GetProductVersion(enterprise_companion::kCompanionAppId)
.IsValid()) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::WithBaseSyncPrimitives()},
base::BindOnce(&IsCloudManaged),
base::BindOnce(
&UpdateServiceImplImpl::MaybeInstallEnterpriseCompanionAppOTA,
base::WrapRefCounted(this),
base::BindOnce(&PolicyService::FetchPolicies,
config_->GetPolicyService(), reason,
std::move(callback))));
} else {
config_->GetPolicyService()->FetchPolicies(reason, std::move(callback));
}
}
void UpdateServiceImplImpl::RegisterApp(
const RegistrationRequest& request,
base::OnceCallback<void(int)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (IsPathOnReadOnlyMount(request.existence_checker_path)) {
VLOG(1) << "Existence check path " << request.existence_checker_path
<< " is on read-only file system. Registration of "
<< request.app_id << " is skipped.";
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), kRegistrationError));
return;
}
if (!IsUpdaterOrCompanionApp(request.app_id)) {
config_->GetUpdaterPersistedData()->SetHadApps();
}
bool send_event = !config_->GetUpdaterPersistedData()
->GetProductVersion(request.app_id)
.IsValid() &&
request.version.IsValid() &&
request.version > base::Version(kNullVersion) &&
!config_->GetUpdaterPersistedData()->GetEulaRequired();
config_->GetUpdaterPersistedData()->RegisterApp(request);
if (send_event) {
update_client::CrxComponent install_data;
install_data.ap = request.ap;
install_data.app_id = request.app_id;
install_data.brand = request.brand_code;
install_data.lang = request.lang;
install_data.requires_network_encryption = false;
install_data.version = request.version;
update_client_->SendPing(
install_data,
{.event_type = update_client::protocol_request::kEventInstall,
.result = update_client::protocol_request::kEventResultSuccess},
base::BindOnce([](update_client::Error error) {
// Ignore event ping errors; registration has been successful.
}).Then(base::BindOnce(std::move(callback), kRegistrationSuccess)));
return;
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), kRegistrationSuccess));
}
void UpdateServiceImplImpl::GetAppStates(
base::OnceCallback<void(const std::vector<AppState>&)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::MakeRefCounted<HandleInconsistentAppsTask>(config_, GetUpdaterScope())
->Run(base::BindOnce(&UpdateServiceImplImpl::GetAppStatesImpl, this,
std::move(callback)));
}
void UpdateServiceImplImpl::GetAppStatesImpl(
base::OnceCallback<void(const std::vector<AppState>&)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
scoped_refptr<PersistedData> persisted_data =
config_->GetUpdaterPersistedData();
std::vector<std::string> app_ids = persisted_data->GetAppIds();
std::vector<AppState> apps;
for (const std::string& app_id : app_ids) {
AppState app_state;
app_state.app_id = app_id;
app_state.version = persisted_data->GetProductVersion(app_id);
app_state.version_path = persisted_data->GetProductVersionPath(app_id);
app_state.version_key = persisted_data->GetProductVersionKey(app_id);
app_state.ap = persisted_data->GetAP(app_id);
app_state.ap_path = persisted_data->GetAPPath(app_id);
app_state.ap_key = persisted_data->GetAPKey(app_id);
app_state.brand_code = persisted_data->GetBrandCode(app_id);
app_state.brand_path = persisted_data->GetBrandPath(app_id);
app_state.ecp = persisted_data->GetExistenceCheckerPath(app_id);
app_state.cohort = persisted_data->GetCohort(app_id);
apps.push_back(app_state);
}
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(apps)));
}
void UpdateServiceImplImpl::RunPeriodicTasks(base::OnceClosure callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
config_->GetUpdaterPersistedData()->SetLastStarted(
base::Time::NowFromSystemTime());
VLOG(1) << "last_started updated.";
// The installer should make an updater registration, but in case it halts
// before it does, synthesize a registration if necessary here.
const base::Version registered_updater_version =
config_->GetUpdaterPersistedData()->GetProductVersion(kUpdaterAppId);
if (!registered_updater_version.IsValid() ||
base::Version(kUpdaterVersion) > registered_updater_version) {
RegistrationRequest updater_request;
updater_request.app_id = kUpdaterAppId;
updater_request.version = base::Version(kUpdaterVersion);
RegisterApp(updater_request, base::DoNothing());
}
std::vector<base::OnceCallback<void(base::OnceClosure)>> new_tasks;
new_tasks.push_back(
base::BindOnce(&HandleInconsistentAppsTask::Run,
base::MakeRefCounted<HandleInconsistentAppsTask>(
config_, GetUpdaterScope())));
new_tasks.push_back(
base::BindOnce(&RemoveUninstalledAppsTask::Run,
base::MakeRefCounted<RemoveUninstalledAppsTask>(
config_, GetUpdaterScope())));
new_tasks.push_back(MakeChangeOwnersTask(config_->GetUpdaterPersistedData(),
GetUpdaterScope()));
new_tasks.push_back(base::BindOnce(
[](scoped_refptr<UpdateServiceImplImpl> update_service_impl,
base::OnceClosure callback) {
update_service_impl->FetchPolicies(
policy::PolicyFetchReason::kScheduled,
base::BindOnce(
[](base::OnceClosure callback, int /* ignore_result */) {
std::move(callback).Run();
},
std::move(callback)));
},
base::WrapRefCounted(this)));
new_tasks.push_back(
base::BindOnce(&CheckForUpdatesTask::Run,
base::MakeRefCounted<CheckForUpdatesTask>(
config_, GetUpdaterScope(),
/*task_name=*/"UpdateAll",
base::BindOnce(&UpdateServiceImplImpl::UpdateAll, this,
base::DoNothing()))));
new_tasks.push_back(base::BindOnce(
[](scoped_refptr<UpdateServiceImplImpl> self,
base::OnceClosure callback) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(
&UpdateServiceImplImpl::ForceInstall, self, base::DoNothing(),
base::BindOnce(
[](base::OnceClosure closure,
UpdateService::Result result) {
VLOG(0) << "ForceInstall task complete: " << result;
std::move(closure).Run();
},
std::move(callback))));
},
base::WrapRefCounted(this)));
new_tasks.push_back(base::BindOnce(
&AutoRunOnOsUpgradeTask::Run,
base::MakeRefCounted<AutoRunOnOsUpgradeTask>(
GetUpdaterScope(), config_->GetUpdaterPersistedData())));
new_tasks.push_back(base::BindOnce(
&CleanupTask::Run,
base::MakeRefCounted<CleanupTask>(GetUpdaterScope(), config_)));
const auto barrier_closure =
base::BarrierClosure(new_tasks.size(), std::move(callback));
for (auto& task : new_tasks) {
tasks_.push(base::BindOnce(std::move(task),
barrier_closure.Then(base::BindRepeating(
&UpdateServiceImplImpl::TaskDone, this))));
}
if (tasks_.size() == new_tasks.size()) {
TaskStart();
}
}
void UpdateServiceImplImpl::TaskStart() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!tasks_.empty()) {
main_task_runner_->PostDelayedTask(FROM_HERE, std::move(tasks_.front()),
config_->InitialDelay());
}
}
void UpdateServiceImplImpl::TaskDone() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
tasks_.pop();
TaskStart();
}
void UpdateServiceImplImpl::ForceInstall(
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
PolicyStatus<std::vector<std::string>> force_install_apps_status =
config_->GetPolicyService()->GetForceInstallApps();
if (!force_install_apps_status) {
base::BindPostTask(main_task_runner_, std::move(callback))
.Run(UpdateService::Result::kSuccess);
return;
}
std::vector<std::string> force_install_apps =
force_install_apps_status.policy();
CHECK(!force_install_apps.empty());
std::vector<std::string> installed_app_ids =
config_->GetUpdaterPersistedData()->GetAppIds();
std::ranges::sort(force_install_apps);
std::ranges::sort(installed_app_ids);
std::vector<std::string> app_ids_to_install;
std::ranges::set_difference(force_install_apps, installed_app_ids,
std::back_inserter(app_ids_to_install));
if (app_ids_to_install.empty()) {
base::BindPostTask(main_task_runner_, std::move(callback))
.Run(UpdateService::Result::kSuccess);
return;
}
VLOG(1) << __func__ << ": app_ids_to_install: "
<< base::JoinString(app_ids_to_install, " ");
ShouldBlockUpdateForMeteredNetwork(
Priority::kBackground,
base::BindOnce(
&UpdateServiceImplImpl::OnShouldBlockForceInstallForMeteredNetwork,
this, app_ids_to_install, kEmptyFlatMap, kEmptyFlatMap,
UpdateService::PolicySameVersionUpdate::kNotAllowed, state_update,
std::move(callback)));
}
void UpdateServiceImplImpl::CheckForUpdate(
const std::string& app_id,
Priority priority,
PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback) {
VLOG(1) << __func__ << ": " << app_id;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::MakeRefCounted<HandleInconsistentAppsTask>(config_, GetUpdaterScope())
->Run(base::BindOnce(
&UpdateServiceImplImpl::FetchPolicies, this,
policy::PolicyFetchReason::kUserRequest,
base::BindOnce(
&FetchPoliciesDone,
base::BindOnce(&UpdateServiceImplImpl::CheckForUpdateImpl, this,
app_id, priority, policy_same_version_update,
language, state_update),
std::move(callback))));
}
void UpdateServiceImplImpl::CheckForUpdateImpl(
const std::string& app_id,
Priority priority,
PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback) {
VLOG(1) << __func__ << ": " << app_id;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!config_->GetUpdaterPersistedData()
->GetProductVersion(app_id)
.IsValid()) {
VLOG(1) << __func__ << ": App not registered: " << app_id;
std::move(callback).Run(Result::kInvalidArgument);
return;
}
int policy = kPolicyEnabled;
if (IsUpdateDisabledByPolicy(app_id, priority, false, policy)) {
HandleUpdateDisabledByPolicy(app_id, policy, false, language, state_update,
std::move(callback));
return;
}
ShouldBlockUpdateForMeteredNetwork(
priority,
base::BindOnce(
&UpdateServiceImplImpl::OnShouldBlockCheckForUpdateForMeteredNetwork,
this, app_id, priority, policy_same_version_update, language,
state_update, std::move(callback)));
}
void UpdateServiceImplImpl::Update(
const std::string& app_id,
const std::string& install_data_index,
Priority priority,
PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::MakeRefCounted<HandleInconsistentAppsTask>(config_, GetUpdaterScope())
->Run(base::BindOnce(
&UpdateServiceImplImpl::FetchPolicies, this,
policy::PolicyFetchReason::kScheduled,
base::BindOnce(&FetchPoliciesDone,
base::BindOnce(&UpdateServiceImplImpl::UpdateImpl,
this, app_id, install_data_index,
priority, policy_same_version_update,
language, state_update),
std::move(callback))));
}
void UpdateServiceImplImpl::UpdateImpl(
const std::string& app_id,
const std::string& install_data_index,
Priority priority,
PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!config_->GetUpdaterPersistedData()
->GetProductVersion(app_id)
.IsValid()) {
std::move(callback).Run(Result::kInvalidArgument);
return;
}
int policy = kPolicyEnabled;
if (IsUpdateDisabledByPolicy(app_id, priority, false, policy)) {
HandleUpdateDisabledByPolicy(app_id, policy, false, language, state_update,
std::move(callback));
return;
}
ShouldBlockUpdateForMeteredNetwork(
priority,
base::BindOnce(
&UpdateServiceImplImpl::OnShouldBlockUpdateForMeteredNetwork, this,
std::vector<std::string>{app_id}, kEmptyFlatMap,
base::flat_map<std::string, std::string>(
{std::make_pair(app_id, install_data_index)}),
priority, policy_same_version_update, language, state_update,
std::move(callback)));
}
void UpdateServiceImplImpl::UpdateAll(
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto app_ids = config_->GetUpdaterPersistedData()->GetAppIds();
CHECK(base::Contains(
app_ids, base::ToLowerASCII(kUpdaterAppId),
static_cast<std::string (*)(std::string_view)>(&base::ToLowerASCII)));
const Priority priority = Priority::kBackground;
ShouldBlockUpdateForMeteredNetwork(
priority,
base::BindOnce(
&UpdateServiceImplImpl::OnShouldBlockUpdateForMeteredNetwork, this,
app_ids, kEmptyFlatMap, kEmptyFlatMap, priority,
UpdateService::PolicySameVersionUpdate::kNotAllowed,
/*language=*/"", state_update,
base::BindOnce(
[](base::OnceCallback<void(Result)> callback,
scoped_refptr<PersistedData> persisted_data, Result result) {
if (result == Result::kSuccess) {
persisted_data->SetLastChecked(
base::Time::NowFromSystemTime());
VLOG(1) << "last_checked updated.";
}
std::move(callback).Run(result);
},
std::move(callback), config_->GetUpdaterPersistedData())));
}
void UpdateServiceImplImpl::Install(
const RegistrationRequest& registration,
const std::string& client_install_data,
const std::string& install_data_index,
Priority priority,
const std::string& language,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::MakeRefCounted<HandleInconsistentAppsTask>(config_, GetUpdaterScope())
->Run(base::BindOnce(
&UpdateServiceImplImpl::FetchPolicies, this,
policy::PolicyFetchReason::kUserRequest,
base::BindOnce(&FetchPoliciesDone,
base::BindOnce(&UpdateServiceImplImpl::InstallImpl,
this, registration, client_install_data,
install_data_index, priority, language,
state_update),
std::move(callback))));
}
void UpdateServiceImplImpl::InstallImpl(
const RegistrationRequest& registration,
const std::string& client_install_data,
const std::string& install_data_index,
Priority priority,
const std::string& language,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback) {
VLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
int policy = kPolicyEnabled;
if (IsUpdateDisabledByPolicy(registration.app_id, priority, true, policy)) {
HandleUpdateDisabledByPolicy(registration.app_id, policy, true, language,
state_update, std::move(callback));
return;
}
if (!IsUpdaterOrCompanionApp(registration.app_id)) {
config_->GetUpdaterPersistedData()->SetHadApps();
}
const bool new_install = !config_->GetUpdaterPersistedData()
->GetProductVersion(registration.app_id)
.IsValid();
if (new_install) {
// Pre-register the app if there is no registration for it. This app
// registration is removed later if the app install encounters an error.
RegistrationRequest request = registration;
request.lang = language;
config_->GetUpdaterPersistedData()->RegisterApp(request);
} else {
// Update lang/ap/iid.
RegistrationRequest request;
request.app_id = registration.app_id;
request.lang = language;
request.ap = registration.ap;
request.install_id = registration.install_id;
config_->GetUpdaterPersistedData()->RegisterApp(request);
}
std::multimap<std::string, base::RepeatingClosure>::iterator pos =
cancellation_callbacks_.emplace(registration.app_id, base::DoNothing());
pos->second = update_client_->Install(
registration.app_id,
base::BindOnce(
&internal::GetComponents, config_->GetPolicyService(),
config_->GetCrxVerifierFormat(), config_->GetUpdaterPersistedData(),
base::flat_map<std::string, std::string>(
{std::make_pair(registration.app_id, client_install_data)}),
base::flat_map<std::string, std::string>(
{std::make_pair(registration.app_id, install_data_index)}),
kInstallSourceTaggedMetainstaller, priority,
/*update_blocked=*/false, PolicySameVersionUpdate::kAllowed),
MakeUpdateClientCrxStateChangeCallback(
config_, config_->GetUpdaterPersistedData(), new_install, language,
state_update),
MakeUpdateClientCallback(std::move(callback))
.Then(base::BindOnce(
[](scoped_refptr<UpdateServiceImplImpl> self,
const std::multimap<std::string,
base::RepeatingClosure>::iterator& pos) {
self->cancellation_callbacks_.erase(pos);
},
base::WrapRefCounted(this), pos)));
}
void UpdateServiceImplImpl::CancelInstalls(const std::string& app_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
auto [first, last] = cancellation_callbacks_.equal_range(app_id);
std::ranges::for_each(first, last, [](const auto& i) { i.second.Run(); });
}
void UpdateServiceImplImpl::RunInstaller(
const std::string& app_id,
const base::FilePath& installer_path,
const std::string& install_args,
const std::string& install_data,
const std::string& install_settings,
const std::string& language,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback) {
VLOG(1) << __func__ << ": " << app_id << ": " << installer_path << ": "
<< install_args << ": " << install_data << ": " << install_settings;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::MakeRefCounted<HandleInconsistentAppsTask>(config_, GetUpdaterScope())
->Run(base::BindOnce(
&UpdateServiceImplImpl::FetchPolicies, this,
policy::PolicyFetchReason::kUserRequest,
base::BindOnce(
&FetchPoliciesDone,
base::BindOnce(&UpdateServiceImplImpl::RunInstallerImpl, this,
app_id, installer_path, install_args, install_data,
install_settings, language, state_update),
std::move(callback))));
}
void UpdateServiceImplImpl::RunInstallerImpl(
const std::string& app_id,
const base::FilePath& installer_path,
const std::string& install_args,
const std::string& install_data,
const std::string& install_settings,
const std::string& language,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback) {
VLOG(1) << __func__ << ": " << app_id << ": " << installer_path << ": "
<< install_args << ": " << install_data << ": " << install_settings;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
int policy = kPolicyEnabled;
if (IsUpdateDisabledByPolicy(app_id, Priority::kForeground, true, policy)) {
HandleUpdateDisabledByPolicy(app_id, policy, true, language, state_update,
std::move(callback));
return;
}
if (!IsUpdaterOrCompanionApp(app_id)) {
config_->GetUpdaterPersistedData()->SetHadApps();
}
const base::Version pv =
config_->GetUpdaterPersistedData()->GetProductVersion(app_id);
const bool new_install = !pv.IsValid();
AppInfo app_info(
GetUpdaterScope(), app_id,
config_->GetUpdaterPersistedData()->GetAP(app_id),
!language.empty() ? language
: config_->GetUpdaterPersistedData()->GetLang(app_id),
config_->GetUpdaterPersistedData()->GetBrandCode(app_id), pv,
config_->GetUpdaterPersistedData()->GetExistenceCheckerPath(app_id));
const base::Version installer_version([&install_settings]() -> std::string {
std::unique_ptr<base::Value> install_settings_deserialized =
JSONStringValueDeserializer(install_settings)
.Deserialize(
/*error_code=*/nullptr, /*error_message=*/nullptr);
if (install_settings_deserialized) {
const base::Value::Dict* install_settings_dict =
install_settings_deserialized->GetIfDict();
if (install_settings_dict) {
const std::string* installer_version_value =
install_settings_dict->FindString(kInstallerVersion);
if (installer_version_value) {
return *installer_version_value;
}
}
}
return {};
}());
// Create a task runner that:
// 1) has SequencedTaskRunner::CurrentDefaultHandle set, to run
// `state_update` callback.
// 2) may block, since `RunApplicationInstaller` blocks.
// 3) has `base::WithBaseSyncPrimitives()`, since `RunApplicationInstaller`
// waits on process.
auto task_runner = base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::WithBaseSyncPrimitives(),
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
task_runner->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
[](const AppInfo& app_info, const base::FilePath& installer_path,
const std::string& install_args, const std::string& install_data,
base::RepeatingCallback<void(const UpdateState&)> state_update) {
base::ScopedTempDir temp_dir;
if (!temp_dir.CreateUniqueTempDir()) {
return InstallerResult(
{.category = update_client::ErrorCategory::kInstall,
.code = kErrorCreatingTempDir,
#if BUILDFLAG(IS_WIN)
.extra = HRESULTFromLastError()
#else
.extra = logging::GetLastSystemErrorCode()
#endif // BUILDFLAG(IS_WIN)
});
}
return RunApplicationInstaller(
app_info, installer_path, install_args,
WriteInstallerDataToTempFile(temp_dir.GetPath(), install_data),
/*usage_stats_enabled=*/
IsUpdaterOrCompanionApp(app_info.app_id) &&
AnyAppEnablesUsageStats(GetUpdaterScope()),
kWaitForAppInstaller,
base::BindRepeating(
[](base::RepeatingCallback<void(const UpdateState&)>
state_update,
const std::string& app_id, int progress) {
VLOG(4) << "Install progress: " << progress;
UpdateState state;
state.app_id = app_id;
state.state = UpdateState::State::kInstalling;
state.install_progress = progress;
state_update.Run(state);
},
state_update, app_info.app_id));
},
app_info, installer_path, install_args, install_data, state_update),
base::BindOnce(
[](scoped_refptr<Configurator> config,
scoped_refptr<PersistedData> persisted_data,
scoped_refptr<update_client::UpdateClient> update_client,
base::Version installer_version,
base::RepeatingCallback<void(const UpdateState&)> state_update,
const std::string& app_id, const std::string& ap,
const std::string& brand, const std::string& language,
bool new_install, base::OnceCallback<void(Result)> callback,
const InstallerResult& result) {
// Final state update after installation completes.
UpdateState state;
state.app_id = app_id;
state.state =
result.result.category == update_client::ErrorCategory::kNone
? UpdateState::State::kUpdated
: UpdateState::State::kUpdateError;
const base::Version registered_version =
internal::GetRegisteredInstallerVersion(app_id);
if (registered_version.IsValid()) {
VLOG(1) << app_id << " registered_version " << registered_version
<< " overrides the original installer_version "
<< installer_version;
installer_version = registered_version;
}
if (result.result.category == update_client::ErrorCategory::kNone &&
installer_version.IsValid()) {
persisted_data->SetProductVersion(app_id, installer_version);
} else if (new_install) {
persisted_data->RemoveApp(app_id);
}
config->GetPrefService()->CommitPendingWrite();
state.error_category = ToErrorCategory(result.result.category);
state.error_code = result.result.code;
state.extra_code1 = result.result.extra;
state.installer_text = result.installer_text;
#if BUILDFLAG(IS_WIN)
if (state.installer_text.empty())
state.installer_text = internal::GetInstallerText(
state.error_category, state.error_code, state.extra_code1,
language);
#endif // BUILDFLAG(IS_WIN)
state.installer_cmd_line = result.installer_cmd_line;
state_update.Run(state);
VLOG(1) << app_id
<< " installation completed: " << state.error_code;
if (!persisted_data->GetEulaRequired()) {
// Send an install ping. In some environments the ping cannot be
// sent, so do not wait for it to be sent before calling back the
// client.
update_client::CrxComponent install_data;
install_data.ap = ap;
install_data.app_id = app_id;
install_data.lang = language;
install_data.brand = brand;
install_data.requires_network_encryption = false;
install_data.install_source = kInstallSourceOffline;
install_data.version = installer_version;
update_client->SendPing(
install_data,
{.event_type = update_client::protocol_request::kEventInstall,
.result =
result.result.category ==
update_client::ErrorCategory::kNone
? update_client::protocol_request::
kEventResultSuccess
: update_client::protocol_request::kEventResultError,
.error_category = result.result.category,
.error_code = result.result.code,
.extra_code1 = result.result.extra},
base::DoNothing());
}
std::move(callback).Run(result.result.category ==
update_client::ErrorCategory::kNone
? Result::kSuccess
: Result::kInstallFailed);
},
config_, config_->GetUpdaterPersistedData(), update_client_,
installer_version, state_update, app_info.app_id, app_info.ap,
app_info.brand, language, new_install, std::move(callback)));
}
bool UpdateServiceImplImpl::IsUpdateDisabledByPolicy(const std::string& app_id,
Priority priority,
bool is_install,
int& policy) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
policy = kPolicyEnabled;
if (is_install) {
PolicyStatus<int> app_install_policy_status =
config_->GetPolicyService()->GetPolicyForAppInstalls(app_id);
if (app_install_policy_status) {
policy = app_install_policy_status.policy();
}
return app_install_policy_status &&
(policy == kPolicyDisabled || (config_->IsPerUserInstall() &&
policy == kPolicyEnabledMachineOnly));
} else {
PolicyStatus<int> app_update_policy_status =
config_->GetPolicyService()->GetPolicyForAppUpdates(app_id);
if (app_update_policy_status) {
policy = app_update_policy_status.policy();
}
return app_update_policy_status &&
(policy == kPolicyDisabled ||
((policy == kPolicyManualUpdatesOnly) &&
(priority != Priority::kForeground)) ||
((policy == kPolicyAutomaticUpdatesOnly) &&
(priority == Priority::kForeground)));
}
}
void UpdateServiceImplImpl::HandleUpdateDisabledByPolicy(
const std::string& app_id,
int policy,
bool is_install,
const std::string& language,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
UpdateState update_state;
update_state.app_id = app_id;
update_state.state = UpdateService::UpdateState::State::kUpdateError;
update_state.error_category = UpdateService::ErrorCategory::kInstaller;
update_state.error_code =
is_install ? GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY
: policy != kPolicyAutomaticUpdatesOnly
? GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY
: GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY_MANUAL;
update_state.extra_code1 = 0;
#if BUILDFLAG(IS_WIN)
update_state.installer_text = internal::GetInstallerText(
update_state.error_category, update_state.error_code,
update_state.extra_code1, language);
#endif // BUILDFLAG(IS_WIN)
base::BindPostTask(main_task_runner_, state_update).Run(update_state);
base::BindPostTask(main_task_runner_, std::move(callback))
.Run(UpdateService::Result::kUpdateCheckFailed);
}
void UpdateServiceImplImpl::OnShouldBlockCheckForUpdateForMeteredNetwork(
const std::string& app_id,
Priority priority,
PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback,
bool update_blocked) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&update_client::UpdateClient::CheckForUpdate, update_client_, app_id,
base::BindOnce(&internal::GetComponents, config_->GetPolicyService(),
config_->GetCrxVerifierFormat(),
config_->GetUpdaterPersistedData(), kEmptyFlatMap,
kEmptyFlatMap,
priority == UpdateService::Priority::kForeground
? kInstallSourceOnDemand
: "",
priority, update_blocked, policy_same_version_update),
MakeUpdateClientCrxStateChangeCallback(
config_, config_->GetUpdaterPersistedData(),
/*new_install=*/false, language, state_update),
priority == Priority::kForeground,
MakeUpdateClientCallback(std::move(callback))));
}
void UpdateServiceImplImpl::OnShouldBlockUpdateForMeteredNetwork(
const std::vector<std::string>& app_ids,
const base::flat_map<std::string, std::string>& app_client_install_data,
const base::flat_map<std::string, std::string>& app_install_data_index,
Priority priority,
PolicySameVersionUpdate policy_same_version_update,
const std::string& language,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback,
bool update_blocked) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&update_client::UpdateClient::Update, update_client_, app_ids,
base::BindOnce(&internal::GetComponents, config_->GetPolicyService(),
config_->GetCrxVerifierFormat(),
config_->GetUpdaterPersistedData(),
app_client_install_data, app_install_data_index,
priority == UpdateService::Priority::kForeground
? kInstallSourceOnDemand
: "",
priority, update_blocked, policy_same_version_update),
MakeUpdateClientCrxStateChangeCallback(
config_, config_->GetUpdaterPersistedData(),
/*new_install=*/false, language, state_update),
priority == Priority::kForeground,
MakeUpdateClientCallback(std::move(callback))));
}
void UpdateServiceImplImpl::OnShouldBlockForceInstallForMeteredNetwork(
const std::vector<std::string>& app_ids,
const base::flat_map<std::string, std::string>& app_client_install_data,
const base::flat_map<std::string, std::string>& app_install_data_index,
PolicySameVersionUpdate policy_same_version_update,
base::RepeatingCallback<void(const UpdateState&)> state_update,
base::OnceCallback<void(Result)> callback,
bool update_blocked) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The result from Install is only used for logging. Thus, arbitrarily pick
// the first non-success result to propagate.
auto barrier_callback = base::BarrierCallback<Result>(
app_ids.size(),
base::BindOnce([](const std::vector<Result>& results) {
auto error_it = std::ranges::find_if(
results, [](Result result) { return result != Result::kSuccess; });
return error_it == std::end(results) ? Result::kSuccess : *error_it;
}).Then(std::move(callback)));
for (const std::string& id : app_ids) {
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
base::IgnoreResult(&update_client::UpdateClient::Install),
update_client_, id,
base::BindOnce(&internal::GetComponents,
config_->GetPolicyService(),
config_->GetCrxVerifierFormat(),
config_->GetUpdaterPersistedData(),
app_client_install_data, app_install_data_index,
kInstallSourcePolicy, Priority::kBackground,
update_blocked, policy_same_version_update),
MakeUpdateClientCrxStateChangeCallback(
config_, config_->GetUpdaterPersistedData(),
/*new_install=*/false,
/*language=*/{}, state_update),
MakeUpdateClientCallback(barrier_callback)));
}
}
UpdateServiceImplImpl::~UpdateServiceImplImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
config_->GetPrefService()->SchedulePendingLossyWrites();
}
} // namespace updater