| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/update_client/action_update.h" |
| |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/callback.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "base/version.h" |
| #include "components/update_client/configurator.h" |
| #include "components/update_client/utils.h" |
| |
| using std::string; |
| using std::vector; |
| |
| namespace update_client { |
| |
| namespace { |
| |
| void AppendDownloadMetrics( |
| const std::vector<CrxDownloader::DownloadMetrics>& source, |
| std::vector<CrxDownloader::DownloadMetrics>* destination) { |
| destination->insert(destination->end(), source.begin(), source.end()); |
| } |
| |
| Action::ErrorCategory UnpackerErrorToErrorCategory( |
| ComponentUnpacker::Error error) { |
| Action::ErrorCategory error_category = Action::ErrorCategory::kErrorNone; |
| switch (error) { |
| case ComponentUnpacker::kNone: |
| break; |
| case ComponentUnpacker::kInstallerError: |
| error_category = Action::ErrorCategory::kInstallError; |
| break; |
| default: |
| error_category = Action::ErrorCategory::kUnpackError; |
| break; |
| } |
| return error_category; |
| } |
| |
| } // namespace |
| |
| ActionUpdate::ActionUpdate() { |
| } |
| |
| ActionUpdate::~ActionUpdate() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| } |
| |
| void ActionUpdate::Run(UpdateContext* update_context, Callback callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| ActionImpl::Run(update_context, callback); |
| |
| DCHECK(!update_context_->queue.empty()); |
| |
| const std::string& id = update_context_->queue.front(); |
| CrxUpdateItem* item = FindUpdateItemById(id); |
| DCHECK(item); |
| |
| const bool is_background_download(IsBackgroundDownload(item)); |
| |
| crx_downloader_.reset((*update_context_->crx_downloader_factory)( |
| is_background_download, |
| update_context_->config->RequestContext(), |
| update_context_->blocking_task_runner) |
| .release()); |
| crx_downloader_->set_progress_callback( |
| base::Bind(&ActionUpdate::DownloadProgress, base::Unretained(this), id)); |
| |
| OnDownloadStart(item); |
| |
| crx_downloader_->StartDownload( |
| GetUrls(item), GetHash(item), |
| base::Bind(&ActionUpdate::DownloadComplete, base::Unretained(this), id)); |
| } |
| |
| void ActionUpdate::DownloadProgress( |
| const std::string& id, |
| const CrxDownloader::Result& download_result) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(id == update_context_->queue.front()); |
| |
| using Events = UpdateClient::Observer::Events; |
| NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING, id); |
| } |
| |
| void ActionUpdate::DownloadComplete( |
| const std::string& id, |
| const CrxDownloader::Result& download_result) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(id == update_context_->queue.front()); |
| |
| CrxUpdateItem* item = FindUpdateItemById(id); |
| DCHECK(item); |
| |
| AppendDownloadMetrics(crx_downloader_->download_metrics(), |
| &item->download_metrics); |
| |
| crx_downloader_.reset(); |
| |
| if (download_result.error) { |
| OnDownloadError(item, download_result); |
| } else { |
| OnDownloadSuccess(item, download_result); |
| update_context_->main_task_runner->PostDelayedTask( |
| FROM_HERE, base::Bind(&ActionUpdate::Install, base::Unretained(this), |
| id, download_result.response), |
| base::TimeDelta::FromMilliseconds( |
| update_context_->config->StepDelay())); |
| } |
| } |
| |
| void ActionUpdate::Install(const std::string& id, |
| const base::FilePath& crx_path) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(id == update_context_->queue.front()); |
| |
| CrxUpdateItem* item = FindUpdateItemById(id); |
| DCHECK(item); |
| |
| OnInstallStart(item); |
| |
| update_context_->blocking_task_runner->PostTask( |
| FROM_HERE, |
| base::Bind(&ActionUpdate::DoInstallOnBlockingTaskRunner, |
| base::Unretained(this), update_context_, item, crx_path)); |
| } |
| |
| void ActionUpdate::DoInstallOnBlockingTaskRunner( |
| UpdateContext* update_context, |
| CrxUpdateItem* item, |
| const base::FilePath& crx_path) { |
| unpacker_ = new ComponentUnpacker( |
| item->component.pk_hash, crx_path, item->component.fingerprint, |
| item->component.installer, |
| update_context->config->CreateOutOfProcessPatcher(), |
| update_context->blocking_task_runner); |
| unpacker_->Unpack(base::Bind(&ActionUpdate::EndUnpackingOnBlockingTaskRunner, |
| base::Unretained(this), update_context, item, |
| crx_path)); |
| } |
| |
| void ActionUpdate::EndUnpackingOnBlockingTaskRunner( |
| UpdateContext* update_context, |
| CrxUpdateItem* item, |
| const base::FilePath& crx_path, |
| ComponentUnpacker::Error error, |
| int extended_error) { |
| unpacker_ = nullptr; |
| update_client::DeleteFileAndEmptyParentDirectory(crx_path); |
| update_context->main_task_runner->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&ActionUpdate::DoneInstalling, base::Unretained(this), |
| item->id, error, extended_error), |
| base::TimeDelta::FromMilliseconds(update_context->config->StepDelay())); |
| } |
| |
| void ActionUpdate::DoneInstalling(const std::string& id, |
| ComponentUnpacker::Error error, |
| int extended_error) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(id == update_context_->queue.front()); |
| |
| CrxUpdateItem* item = FindUpdateItemById(id); |
| DCHECK(item); |
| |
| if (error == ComponentUnpacker::kNone) |
| OnInstallSuccess(item); |
| else |
| OnInstallError(item, error, extended_error); |
| } |
| |
| ActionUpdateDiff::ActionUpdateDiff() { |
| } |
| |
| ActionUpdateDiff::~ActionUpdateDiff() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| } |
| |
| std::unique_ptr<Action> ActionUpdateDiff::Create() { |
| return std::unique_ptr<Action>(new ActionUpdateDiff); |
| } |
| |
| void ActionUpdateDiff::TryUpdateFull() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| std::unique_ptr<Action> update_action(ActionUpdateFull::Create()); |
| |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::Bind(&Action::Run, base::Unretained(update_action.get()), |
| update_context_, callback_)); |
| |
| update_context_->current_action.reset(update_action.release()); |
| } |
| |
| bool ActionUpdateDiff::IsBackgroundDownload(const CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return false; |
| } |
| |
| std::vector<GURL> ActionUpdateDiff::GetUrls(const CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return item->crx_diffurls; |
| } |
| |
| std::string ActionUpdateDiff::GetHash(const CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return item->hashdiff_sha256; |
| } |
| |
| void ActionUpdateDiff::OnDownloadStart(CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(item->state == CrxUpdateItem::State::kCanUpdate); |
| |
| ChangeItemState(item, CrxUpdateItem::State::kDownloadingDiff); |
| } |
| |
| void ActionUpdateDiff::OnDownloadSuccess( |
| CrxUpdateItem* item, |
| const CrxDownloader::Result& download_result) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(item->state == CrxUpdateItem::State::kDownloadingDiff); |
| |
| ChangeItemState(item, CrxUpdateItem::State::kDownloaded); |
| } |
| |
| void ActionUpdateDiff::OnDownloadError( |
| CrxUpdateItem* item, |
| const CrxDownloader::Result& download_result) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(item->state == CrxUpdateItem::State::kDownloadingDiff); |
| |
| item->diff_error_category = static_cast<int>(ErrorCategory::kNetworkError); |
| item->diff_error_code = download_result.error; |
| item->diff_update_failed = true; |
| |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::Bind(&ActionUpdateDiff::TryUpdateFull, base::Unretained(this))); |
| } |
| |
| void ActionUpdateDiff::OnInstallStart(CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| ChangeItemState(item, CrxUpdateItem::State::kUpdatingDiff); |
| } |
| |
| void ActionUpdateDiff::OnInstallSuccess(CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(item->state == CrxUpdateItem::State::kUpdatingDiff); |
| |
| item->component.version = item->next_version; |
| item->component.fingerprint = item->next_fp; |
| ChangeItemState(item, CrxUpdateItem::State::kUpdated); |
| |
| UpdateCrxComplete(item); |
| } |
| |
| void ActionUpdateDiff::OnInstallError(CrxUpdateItem* item, |
| ComponentUnpacker::Error error, |
| int extended_error) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| item->diff_error_category = |
| static_cast<int>(UnpackerErrorToErrorCategory(error)); |
| item->diff_error_code = error; |
| item->diff_extra_code1 = extended_error; |
| item->diff_update_failed = true; |
| |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::Bind(&ActionUpdateDiff::TryUpdateFull, base::Unretained(this))); |
| } |
| |
| ActionUpdateFull::ActionUpdateFull() { |
| } |
| |
| ActionUpdateFull::~ActionUpdateFull() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| } |
| |
| std::unique_ptr<Action> ActionUpdateFull::Create() { |
| return std::unique_ptr<Action>(new ActionUpdateFull); |
| } |
| |
| bool ActionUpdateFull::IsBackgroundDownload(const CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| // On demand component updates are always downloaded in foreground. |
| return !item->on_demand && item->component.allows_background_download && |
| update_context_->config->EnabledBackgroundDownloader(); |
| } |
| |
| std::vector<GURL> ActionUpdateFull::GetUrls(const CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return item->crx_urls; |
| } |
| |
| std::string ActionUpdateFull::GetHash(const CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return item->hash_sha256; |
| } |
| |
| void ActionUpdateFull::OnDownloadStart(CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(item->state == CrxUpdateItem::State::kCanUpdate || |
| item->diff_update_failed); |
| |
| ChangeItemState(item, CrxUpdateItem::State::kDownloading); |
| } |
| |
| void ActionUpdateFull::OnDownloadSuccess( |
| CrxUpdateItem* item, |
| const CrxDownloader::Result& download_result) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(item->state == CrxUpdateItem::State::kDownloading); |
| |
| ChangeItemState(item, CrxUpdateItem::State::kDownloaded); |
| } |
| |
| void ActionUpdateFull::OnDownloadError( |
| CrxUpdateItem* item, |
| const CrxDownloader::Result& download_result) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(item->state == CrxUpdateItem::State::kDownloading); |
| |
| item->error_category = static_cast<int>(ErrorCategory::kNetworkError); |
| item->error_code = download_result.error; |
| ChangeItemState(item, CrxUpdateItem::State::kNoUpdate); |
| |
| UpdateCrxComplete(item); |
| } |
| |
| void ActionUpdateFull::OnInstallStart(CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(item->state == CrxUpdateItem::State::kDownloaded); |
| |
| ChangeItemState(item, CrxUpdateItem::State::kUpdating); |
| } |
| |
| void ActionUpdateFull::OnInstallSuccess(CrxUpdateItem* item) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(item->state == CrxUpdateItem::State::kUpdating); |
| |
| item->component.version = item->next_version; |
| item->component.fingerprint = item->next_fp; |
| ChangeItemState(item, CrxUpdateItem::State::kUpdated); |
| |
| UpdateCrxComplete(item); |
| } |
| |
| void ActionUpdateFull::OnInstallError(CrxUpdateItem* item, |
| ComponentUnpacker::Error error, |
| int extended_error) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(item->state == CrxUpdateItem::State::kUpdating); |
| |
| item->error_category = static_cast<int>(UnpackerErrorToErrorCategory(error)); |
| item->error_code = error; |
| item->extra_code1 = extended_error; |
| ChangeItemState(item, CrxUpdateItem::State::kNoUpdate); |
| |
| UpdateCrxComplete(item); |
| } |
| |
| } // namespace update_client |