blob: 4976f10ed68cb11a89d2dee6283cbb8ed06e3297 [file] [log] [blame]
// Copyright 2017 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/download/internal/background_service/scheduler/scheduler_impl.h"
#include "components/download/internal/background_service/client_set.h"
#include "components/download/internal/background_service/config.h"
#include "components/download/internal/background_service/entry_utils.h"
#include "components/download/internal/background_service/scheduler/device_status.h"
#include "components/download/public/background_service/download_params.h"
#include "components/download/public/task/task_scheduler.h"
namespace download {
namespace {
// Returns a vector of elements contained in the |set|.
template <typename T>
std::vector<T> ToList(const std::set<T>& set) {
std::vector<T> list;
for (const auto& element : set) {
list.push_back(element);
}
return list;
}
} // namespace
SchedulerImpl::SchedulerImpl(TaskScheduler* task_scheduler,
Configuration* config,
const ClientSet* clients)
: SchedulerImpl(task_scheduler,
config,
ToList<DownloadClient>(clients->GetRegisteredClients())) {}
SchedulerImpl::SchedulerImpl(TaskScheduler* task_scheduler,
Configuration* config,
const std::vector<DownloadClient>& clients)
: task_scheduler_(task_scheduler),
config_(config),
download_clients_(clients),
current_client_index_(0) {
DCHECK(task_scheduler_);
}
SchedulerImpl::~SchedulerImpl() = default;
void SchedulerImpl::Reschedule(const Model::EntryList& entries) {
if (entries.empty()) {
task_scheduler_->CancelTask(DownloadTaskType::DOWNLOAD_TASK);
return;
}
// TODO(xingliu): Support NetworkRequirements::OPTIMISTIC.
Criteria criteria = util::GetSchedulingCriteria(
entries, config_->download_battery_percentage);
task_scheduler_->ScheduleTask(
DownloadTaskType::DOWNLOAD_TASK, criteria.requires_unmetered_network,
criteria.requires_battery_charging, criteria.optimal_battery_percentage,
base::saturated_cast<long>(config_->window_start_time.InSeconds()),
base::saturated_cast<long>(config_->window_end_time.InSeconds()));
}
Entry* SchedulerImpl::Next(const Model::EntryList& entries,
const DeviceStatus& device_status) {
std::map<DownloadClient, Entry*> candidates =
FindCandidates(entries, device_status);
Entry* entry = nullptr;
size_t index = current_client_index_;
// Finds the next entry to download.
for (size_t i = 0; i < download_clients_.size(); ++i) {
DownloadClient client =
download_clients_[(index + i) % download_clients_.size()];
Entry* candidate = candidates[client];
// Some clients may have no entries, continue to check other clients.
if (!candidate)
continue;
bool ui_priority =
candidate->scheduling_params.priority == SchedulingParams::Priority::UI;
// Records the first available candidate. Keep iterating to see if there
// are UI priority entries for other clients.
if (!entry || ui_priority) {
entry = candidate;
// Load balancing between clients.
current_client_index_ = (index + i + 1) % download_clients_.size();
// UI priority entry will be processed immediately.
if (ui_priority)
break;
}
}
return entry;
}
std::map<DownloadClient, Entry*> SchedulerImpl::FindCandidates(
const Model::EntryList& entries,
const DeviceStatus& device_status) {
std::map<DownloadClient, Entry*> candidates;
if (entries.empty())
return candidates;
for (auto* const entry : entries) {
DCHECK(entry);
const SchedulingParams& current_params = entry->scheduling_params;
// Every download needs to pass the state and device status check.
if (entry->state != Entry::State::AVAILABLE ||
!device_status
.MeetsCondition(current_params,
config_->download_battery_percentage)
.MeetsRequirements()) {
continue;
}
// Find the most appropriate download based on priority and cancel time.
Entry* candidate = candidates[entry->client];
if (!candidate || util::EntryBetterThan(*entry, *candidate)) {
candidates[entry->client] = entry;
}
}
return candidates;
}
} // namespace download