blob: fbac4bbf1ea3727fdbd651a902ce51b901992908 [file] [log] [blame]
// Copyright 2014 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/update_checker.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "base/task_scheduler/post_task.h"
#include "base/threading/thread_checker.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/update_client/component.h"
#include "components/update_client/configurator.h"
#include "components/update_client/persisted_data.h"
#include "components/update_client/protocol_builder.h"
#include "components/update_client/protocol_parser.h"
#include "components/update_client/request_sender.h"
#include "components/update_client/task_traits.h"
#include "components/update_client/update_client.h"
#include "components/update_client/updater_state.h"
#include "components/update_client/utils.h"
#include "url/gurl.h"
namespace update_client {
namespace {
// Returns true if at least one item requires network encryption.
bool IsEncryptionRequired(const IdToComponentPtrMap& components) {
for (const auto& item : components) {
const auto& component = item.second;
if (component->crx_component().requires_network_encryption)
return true;
}
return false;
}
class UpdateCheckerImpl : public UpdateChecker {
public:
UpdateCheckerImpl(const scoped_refptr<Configurator>& config,
PersistedData* metadata);
~UpdateCheckerImpl() override;
// Overrides for UpdateChecker.
void CheckForUpdates(const std::vector<std::string>& ids_checked,
const IdToComponentPtrMap& components,
const std::string& additional_attributes,
bool enabled_component_updates,
UpdateCheckCallback update_check_callback) override;
private:
void ReadUpdaterStateAttributes();
void CheckForUpdatesHelper(const IdToComponentPtrMap& components,
const std::string& additional_attributes,
bool enabled_component_updates);
void OnRequestSenderComplete(const IdToComponentPtrMap& components,
int error,
const std::string& response,
int retry_after_sec);
void UpdateCheckSucceeded(const IdToComponentPtrMap& components,
const ProtocolParser::Results& results,
int retry_after_sec);
void UpdateCheckFailed(const IdToComponentPtrMap& components,
int error,
int retry_after_sec);
base::ThreadChecker thread_checker_;
const scoped_refptr<Configurator> config_;
PersistedData* metadata_ = nullptr;
std::vector<std::string> ids_checked_;
UpdateCheckCallback update_check_callback_;
std::unique_ptr<UpdaterState::Attributes> updater_state_attributes_;
std::unique_ptr<RequestSender> request_sender_;
DISALLOW_COPY_AND_ASSIGN(UpdateCheckerImpl);
};
UpdateCheckerImpl::UpdateCheckerImpl(const scoped_refptr<Configurator>& config,
PersistedData* metadata)
: config_(config), metadata_(metadata) {}
UpdateCheckerImpl::~UpdateCheckerImpl() {
DCHECK(thread_checker_.CalledOnValidThread());
}
void UpdateCheckerImpl::CheckForUpdates(
const std::vector<std::string>& ids_checked,
const IdToComponentPtrMap& components,
const std::string& additional_attributes,
bool enabled_component_updates,
UpdateCheckCallback update_check_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
ids_checked_ = ids_checked;
update_check_callback_ = std::move(update_check_callback);
base::PostTaskWithTraitsAndReply(
FROM_HERE, kTaskTraits,
base::BindOnce(&UpdateCheckerImpl::ReadUpdaterStateAttributes,
base::Unretained(this)),
base::BindOnce(&UpdateCheckerImpl::CheckForUpdatesHelper,
base::Unretained(this), base::ConstRef(components),
additional_attributes, enabled_component_updates));
}
// This function runs on the blocking pool task runner.
void UpdateCheckerImpl::ReadUpdaterStateAttributes() {
const bool is_machine_install = !config_->IsPerUserInstall();
updater_state_attributes_ = UpdaterState::GetState(is_machine_install);
}
void UpdateCheckerImpl::CheckForUpdatesHelper(
const IdToComponentPtrMap& components,
const std::string& additional_attributes,
bool enabled_component_updates) {
DCHECK(thread_checker_.CalledOnValidThread());
auto urls(config_->UpdateUrl());
if (IsEncryptionRequired(components))
RemoveUnsecureUrls(&urls);
request_sender_ = base::MakeUnique<RequestSender>(config_);
request_sender_->Send(
config_->EnabledCupSigning(),
BuildUpdateCheckRequest(*config_, ids_checked_, components, metadata_,
additional_attributes, enabled_component_updates,
updater_state_attributes_),
urls,
base::BindOnce(&UpdateCheckerImpl::OnRequestSenderComplete,
base::Unretained(this), base::ConstRef(components)));
}
void UpdateCheckerImpl::OnRequestSenderComplete(
const IdToComponentPtrMap& components,
int error,
const std::string& response,
int retry_after_sec) {
DCHECK(thread_checker_.CalledOnValidThread());
if (error) {
VLOG(1) << "RequestSender failed " << error;
UpdateCheckFailed(components, error, retry_after_sec);
return;
}
ProtocolParser update_response;
if (!update_response.Parse(response)) {
VLOG(1) << "Parse failed " << update_response.errors();
UpdateCheckFailed(components, -1, retry_after_sec);
return;
}
DCHECK_EQ(0, error);
UpdateCheckSucceeded(components, update_response.results(), retry_after_sec);
}
void UpdateCheckerImpl::UpdateCheckSucceeded(
const IdToComponentPtrMap& components,
const ProtocolParser::Results& results,
int retry_after_sec) {
DCHECK(thread_checker_.CalledOnValidThread());
const int daynum = results.daystart_elapsed_days;
if (daynum != ProtocolParser::kNoDaystart) {
metadata_->SetDateLastActive(ids_checked_, daynum);
metadata_->SetDateLastRollCall(ids_checked_, daynum);
}
for (const auto& result : results.list) {
auto entry = result.cohort_attrs.find(ProtocolParser::Result::kCohort);
if (entry != result.cohort_attrs.end())
metadata_->SetCohort(result.extension_id, entry->second);
entry = result.cohort_attrs.find(ProtocolParser::Result::kCohortName);
if (entry != result.cohort_attrs.end())
metadata_->SetCohortName(result.extension_id, entry->second);
entry = result.cohort_attrs.find(ProtocolParser::Result::kCohortHint);
if (entry != result.cohort_attrs.end())
metadata_->SetCohortHint(result.extension_id, entry->second);
}
for (const auto& result : results.list) {
const auto& id = result.extension_id;
const auto it = components.find(id);
if (it != components.end())
it->second->SetParseResult(result);
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(update_check_callback_), 0, retry_after_sec));
}
void UpdateCheckerImpl::UpdateCheckFailed(const IdToComponentPtrMap& components,
int error,
int retry_after_sec) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_NE(0, error);
for (const auto& item : components) {
DCHECK(item.second);
Component& component = *item.second;
component.set_update_check_error(error);
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(update_check_callback_), error,
retry_after_sec));
}
} // namespace
std::unique_ptr<UpdateChecker> UpdateChecker::Create(
const scoped_refptr<Configurator>& config,
PersistedData* persistent) {
return base::MakeUnique<UpdateCheckerImpl>(config, persistent);
}
} // namespace update_client