| // 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.h" |
| |
| #include <map> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/barrier_closure.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/notreached.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_util.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 "chrome/updater/auto_run_on_os_upgrade_task.h" |
| #include "chrome/updater/check_for_updates_task.h" |
| #include "chrome/updater/configurator.h" |
| #include "chrome/updater/constants.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/update_usage_stats_task.h" |
| #include "chrome/updater/updater_scope.h" |
| #include "chrome/updater/updater_version.h" |
| #include "chrome/updater/util/util.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/update_client/crx_update_item.h" |
| #include "components/update_client/update_client.h" |
| #include "components/update_client/update_client_errors.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace updater { |
| |
| namespace { |
| |
| // The functions below are various adaptors between |update_client| and |
| // |UpdateService| types. |
| update_client::Callback MakeUpdateClientCallback( |
| UpdateService::Callback callback) { |
| return base::BindOnce( |
| [](UpdateService::Callback callback, update_client::Error error) { |
| std::move(callback).Run(static_cast<UpdateService::Result>(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: |
| case update_client::ComponentState::kDownloaded: |
| 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::kUninstalled: |
| case update_client::ComponentState::kRegistration: |
| case update_client::ComponentState::kRun: |
| case update_client::ComponentState::kLastStatus: |
| NOTREACHED(); |
| return UpdateService::UpdateState::State::kUnknown; |
| } |
| } |
| |
| 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; |
| } |
| } |
| |
| update_client::UpdateClient::CrxStateChangeCallback |
| MakeUpdateClientCrxStateChangeCallback( |
| scoped_refptr<update_client::Configurator> config, |
| UpdateService::StateChangeCallback callback) { |
| return base::BindRepeating( |
| [](scoped_refptr<update_client::Configurator> config, |
| UpdateService::StateChangeCallback callback, |
| 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; |
| |
| // TODO(crbug.com/1352307): Investigate if it is desirable to read the |
| // result from the installer result API here when update completes. |
| |
| // Commit the prefs values written by |update_client| when the |
| // update has completed, such as `pv` and `fingerprint`. |
| if (update_state.state == UpdateService::UpdateState::State::kUpdated) { |
| config->GetPrefService()->CommitPendingWrite(); |
| } |
| |
| callback.Run(update_state); |
| }, |
| config, callback); |
| } |
| |
| std::vector<absl::optional<update_client::CrxComponent>> GetComponents( |
| scoped_refptr<Configurator> config, |
| scoped_refptr<PersistedData> persisted_data, |
| const AppClientInstallData& app_client_install_data, |
| const AppInstallDataIndex& app_install_data_index, |
| UpdateService::Priority priority, |
| bool update_blocked, |
| UpdateService::PolicySameVersionUpdate policy_same_version_update, |
| const std::vector<std::string>& ids) { |
| VLOG(1) << __func__ |
| << ". Same version update: " << policy_same_version_update; |
| const bool is_foreground = priority == UpdateService::Priority::kForeground; |
| std::vector<absl::optional<update_client::CrxComponent>> components; |
| for (const auto& id : ids) { |
| components.push_back( |
| 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 : ""; |
| }(), |
| [&config, &id]() { |
| return config->GetPolicyService()->GetTargetChannel(id).policy_or( |
| std::string()); |
| }(), |
| [&config, &id]() { |
| return config->GetPolicyService() |
| ->GetTargetVersionPrefix(id) |
| .policy_or(std::string()); |
| }(), |
| [&config, &id]() { |
| return config->GetPolicyService() |
| ->IsRollbackToTargetVersionAllowed(id) |
| .policy_or(false); |
| }(), |
| [&config, &id, &is_foreground, update_blocked]() { |
| if (update_blocked) |
| return true; |
| PolicyStatus<int> app_updates = |
| config->GetPolicyService()->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, |
| config->GetCrxVerifierFormat()) |
| ->MakeCrxComponent()); |
| } |
| return components; |
| } |
| |
| } // namespace |
| |
| UpdateServiceImpl::UpdateServiceImpl(scoped_refptr<Configurator> config) |
| : config_(config), |
| persisted_data_( |
| base::MakeRefCounted<PersistedData>(GetUpdaterScope(), |
| config_->GetPrefService())), |
| main_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()), |
| update_client_(update_client::UpdateClientFactory(config)) {} |
| |
| void UpdateServiceImpl::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 UpdateServiceImpl::FetchPolicies(base::OnceCallback<void(int)> callback) { |
| VLOG(1) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| config_->GetPolicyService()->FetchPolicies(std::move(callback)); |
| } |
| |
| void UpdateServiceImpl::RegisterApp(const RegistrationRequest& request, |
| base::OnceCallback<void(int)> callback) { |
| VLOG(1) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (request.app_id != kUpdaterAppId) { |
| persisted_data_->SetHadApps(); |
| } |
| persisted_data_->RegisterApp(request); |
| std::move(callback).Run(kRegistrationSuccess); |
| } |
| |
| void UpdateServiceImpl::GetAppStates( |
| base::OnceCallback<void(const std::vector<AppState>&)> callback) { |
| VLOG(1) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| 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.ap = persisted_data_->GetAP(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); |
| apps.push_back(app_state); |
| } |
| main_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), std::move(apps))); |
| } |
| |
| void UpdateServiceImpl::RunPeriodicTasks(base::OnceClosure callback) { |
| VLOG(1) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| persisted_data_->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. |
| if (!base::Contains(persisted_data_->GetAppIds(), kUpdaterAppId)) { |
| 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(&RemoveUninstalledAppsTask::Run, |
| base::MakeRefCounted<RemoveUninstalledAppsTask>( |
| config_, GetUpdaterScope()))); |
| new_tasks.push_back(base::BindOnce(&UpdateUsageStatsTask::Run, |
| base::MakeRefCounted<UpdateUsageStatsTask>( |
| GetUpdaterScope(), persisted_data_))); |
| |
| new_tasks.push_back(base::BindOnce( |
| [](scoped_refptr<UpdateServiceImpl> update_service_impl, |
| base::OnceClosure callback) { |
| update_service_impl->FetchPolicies(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(), |
| base::BindOnce(&UpdateServiceImpl::ForceInstall, this, |
| base::DoNothing())))); |
| new_tasks.push_back( |
| base::BindOnce(&CheckForUpdatesTask::Run, |
| base::MakeRefCounted<CheckForUpdatesTask>( |
| config_, GetUpdaterScope(), |
| base::BindOnce(&UpdateServiceImpl::UpdateAll, this, |
| base::DoNothing())))); |
| new_tasks.push_back( |
| base::BindOnce(&AutoRunOnOsUpgradeTask::Run, |
| base::MakeRefCounted<AutoRunOnOsUpgradeTask>( |
| GetUpdaterScope(), persisted_data_))); |
| |
| 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( |
| &UpdateServiceImpl::TaskDone, this)))); |
| } |
| |
| if (tasks_.size() == new_tasks.size()) { |
| TaskStart(); |
| } |
| } |
| |
| void UpdateServiceImpl::TaskStart() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!tasks_.empty()) { |
| std::move(tasks_.front()).Run(); |
| } |
| } |
| |
| void UpdateServiceImpl::TaskDone() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| tasks_.pop(); |
| TaskStart(); |
| } |
| |
| void UpdateServiceImpl::ForceInstall(StateChangeCallback state_update, |
| Callback 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(); |
| DCHECK(!force_install_apps.empty()); |
| |
| std::vector<std::string> installed_app_ids = persisted_data_->GetAppIds(); |
| base::ranges::sort(force_install_apps); |
| base::ranges::sort(installed_app_ids); |
| |
| std::vector<std::string> app_ids_to_install; |
| base::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, " "); |
| |
| const Priority priority = Priority::kBackground; |
| ShouldBlockUpdateForMeteredNetwork( |
| priority, |
| base::BindOnce(&UpdateServiceImpl::OnShouldBlockUpdateForMeteredNetwork, |
| this, app_ids_to_install, AppClientInstallData(), |
| AppInstallDataIndex(), priority, |
| UpdateService::PolicySameVersionUpdate::kNotAllowed, |
| state_update, std::move(callback))); |
| } |
| |
| void UpdateServiceImpl::CheckForUpdate( |
| const std::string& app_id, |
| Priority priority, |
| PolicySameVersionUpdate policy_same_version_update, |
| StateChangeCallback state_update, |
| Callback callback) { |
| VLOG(1) << __func__ << ": " << app_id; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| int policy = kPolicyEnabled; |
| if (IsUpdateDisabledByPolicy(app_id, priority, false, policy)) { |
| HandleUpdateDisabledByPolicy(app_id, policy, false, state_update, |
| std::move(callback)); |
| return; |
| } |
| ShouldBlockUpdateForMeteredNetwork( |
| priority, |
| base::BindOnce( |
| &UpdateServiceImpl::OnShouldBlockCheckForUpdateForMeteredNetwork, |
| this, app_id, priority, policy_same_version_update, state_update, |
| std::move(callback))); |
| } |
| |
| void UpdateServiceImpl::Update( |
| const std::string& app_id, |
| const std::string& install_data_index, |
| Priority priority, |
| PolicySameVersionUpdate policy_same_version_update, |
| StateChangeCallback state_update, |
| Callback callback) { |
| VLOG(1) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| int policy = kPolicyEnabled; |
| if (IsUpdateDisabledByPolicy(app_id, priority, false, policy)) { |
| HandleUpdateDisabledByPolicy(app_id, policy, false, state_update, |
| std::move(callback)); |
| return; |
| } |
| ShouldBlockUpdateForMeteredNetwork( |
| priority, |
| base::BindOnce( |
| &UpdateServiceImpl::OnShouldBlockUpdateForMeteredNetwork, this, |
| std::vector<std::string>{app_id}, AppClientInstallData(), |
| AppInstallDataIndex({std::make_pair(app_id, install_data_index)}), |
| priority, policy_same_version_update, state_update, |
| std::move(callback))); |
| } |
| |
| void UpdateServiceImpl::UpdateAll(StateChangeCallback state_update, |
| Callback callback) { |
| VLOG(1) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| const auto app_ids = persisted_data_->GetAppIds(); |
| DCHECK(base::Contains(app_ids, kUpdaterAppId)); |
| |
| const Priority priority = Priority::kBackground; |
| ShouldBlockUpdateForMeteredNetwork( |
| priority, |
| base::BindOnce( |
| &UpdateServiceImpl::OnShouldBlockUpdateForMeteredNetwork, this, |
| app_ids, AppClientInstallData(), AppInstallDataIndex(), priority, |
| UpdateService::PolicySameVersionUpdate::kNotAllowed, state_update, |
| base::BindOnce( |
| [](Callback 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), persisted_data_))); |
| } |
| |
| void UpdateServiceImpl::Install(const RegistrationRequest& registration, |
| const std::string& client_install_data, |
| const std::string& install_data_index, |
| Priority priority, |
| StateChangeCallback state_update, |
| Callback 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, |
| state_update, std::move(callback)); |
| return; |
| } |
| if (registration.app_id != kUpdaterAppId) { |
| persisted_data_->SetHadApps(); |
| } |
| if (!persisted_data_->GetProductVersion(registration.app_id).IsValid()) { |
| // Only overwrite the registration if there's no current registration. |
| persisted_data_->RegisterApp(registration); |
| } |
| |
| 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( |
| &GetComponents, config_, persisted_data_, |
| AppClientInstallData( |
| {std::make_pair(registration.app_id, client_install_data)}), |
| AppInstallDataIndex( |
| {std::make_pair(registration.app_id, install_data_index)}), |
| priority, |
| /*update_blocked=*/false, PolicySameVersionUpdate::kAllowed), |
| MakeUpdateClientCrxStateChangeCallback(config_, state_update), |
| MakeUpdateClientCallback(std::move(callback)) |
| .Then(base::BindOnce( |
| [](scoped_refptr<UpdateServiceImpl> self, |
| const std::multimap<std::string, |
| base::RepeatingClosure>::iterator& pos) { |
| self->cancellation_callbacks_.erase(pos); |
| }, |
| base::WrapRefCounted(this), pos))); |
| } |
| |
| void UpdateServiceImpl::CancelInstalls(const std::string& app_id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| VLOG(1) << __func__; |
| auto range = cancellation_callbacks_.equal_range(app_id); |
| base::ranges::for_each(range.first, range.second, |
| [](const auto& i) { i.second.Run(); }); |
| } |
| |
| void UpdateServiceImpl::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, |
| StateChangeCallback state_update, |
| Callback 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, state_update, |
| std::move(callback)); |
| return; |
| } |
| |
| const base::Version pv = persisted_data_->GetProductVersion(app_id); |
| AppInfo app_info(GetUpdaterScope(), app_id, |
| pv.IsValid() ? persisted_data_->GetAP(app_id) : "", pv, |
| pv.IsValid() |
| ? persisted_data_->GetExistenceCheckerPath(app_id) |
| : base::FilePath()); |
| |
| 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, |
| StateChangeCallback state_update, bool usage_stats_enabled) { |
| base::ScopedTempDir temp_dir; |
| if (!temp_dir.CreateUniqueTempDir()) { |
| return InstallerResult(kErrorApplicationInstallerFailed, |
| kErrorCreatingTempDir); |
| } |
| |
| return RunApplicationInstaller( |
| app_info, installer_path, install_args, |
| WriteInstallerDataToTempFile(temp_dir.GetPath(), install_data), |
| usage_stats_enabled, kWaitForAppInstaller, |
| base::BindRepeating( |
| [](StateChangeCallback 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, |
| persisted_data_->GetUsageStatsEnabled()), |
| base::BindOnce( |
| [](scoped_refptr<Configurator> config, |
| scoped_refptr<PersistedData> persisted_data, |
| const base::Version& installer_version, |
| StateChangeCallback state_update, const std::string& app_id, |
| Callback callback, const InstallerResult& result) { |
| // Final state update after installation completes. |
| UpdateState state; |
| state.app_id = app_id; |
| state.state = result.error == 0 ? UpdateState::State::kUpdated |
| : UpdateState::State::kUpdateError; |
| |
| if (result.error == 0 && installer_version.IsValid()) { |
| persisted_data->SetProductVersion(app_id, installer_version); |
| config->GetPrefService()->CommitPendingWrite(); |
| } |
| |
| state.error_code = result.error; |
| state.extra_code1 = result.extended_error; |
| state.installer_text = result.installer_text; |
| state.installer_cmd_line = result.installer_cmd_line; |
| state_update.Run(state); |
| VLOG(1) << app_id << " installation completed: " << result.error; |
| |
| // TODO(crbug.com/1286574, crbug.com/1286581): Perform post-install |
| // actions, such as send pings (if `enterprise` is not set in |
| // install_settings) with the given `sessionid`. |
| |
| std::move(callback).Run(result.error == 0 ? Result::kSuccess |
| : Result::kInstallFailed); |
| }, |
| config_, persisted_data_, installer_version, state_update, |
| app_info.app_id, std::move(callback))); |
| } |
| |
| bool UpdateServiceImpl::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 UpdateServiceImpl::HandleUpdateDisabledByPolicy( |
| const std::string& app_id, |
| int policy, |
| bool is_install, |
| StateChangeCallback state_update, |
| Callback 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::kUpdateCheck; |
| 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; |
| |
| base::BindPostTask(main_task_runner_, state_update).Run(update_state); |
| base::BindPostTask(main_task_runner_, std::move(callback)) |
| .Run(UpdateService::Result::kUpdateCheckFailed); |
| } |
| |
| void UpdateServiceImpl::OnShouldBlockCheckForUpdateForMeteredNetwork( |
| const std::string& app_id, |
| Priority priority, |
| PolicySameVersionUpdate policy_same_version_update, |
| StateChangeCallback state_update, |
| Callback 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(&GetComponents, config_, persisted_data_, |
| AppClientInstallData(), AppInstallDataIndex(), |
| priority, update_blocked, policy_same_version_update), |
| MakeUpdateClientCrxStateChangeCallback(config_, state_update), |
| priority == Priority::kForeground, |
| MakeUpdateClientCallback(std::move(callback)))); |
| } |
| |
| void UpdateServiceImpl::OnShouldBlockUpdateForMeteredNetwork( |
| const std::vector<std::string>& app_ids, |
| const AppClientInstallData& app_client_install_data, |
| const AppInstallDataIndex& app_install_data_index, |
| Priority priority, |
| PolicySameVersionUpdate policy_same_version_update, |
| StateChangeCallback state_update, |
| Callback 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(&GetComponents, config_, persisted_data_, |
| app_client_install_data, app_install_data_index, |
| priority, update_blocked, policy_same_version_update), |
| MakeUpdateClientCrxStateChangeCallback(config_, state_update), |
| priority == Priority::kForeground, |
| MakeUpdateClientCallback(std::move(callback)))); |
| } |
| |
| UpdateServiceImpl::~UpdateServiceImpl() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| config_->GetPrefService()->SchedulePendingLossyWrites(); |
| } |
| |
| } // namespace updater |