blob: 0218393e9642edd0652dd1c44db750858385d375 [file] [log] [blame]
// Copyright (c) 2010 The Chromium OS 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 "update_engine/update_check_scheduler.h"
#include "update_engine/certificate_checker.h"
#include "update_engine/utils.h"
namespace chromeos_update_engine {
const int UpdateCheckScheduler::kTimeoutOnce = 7 * 60; // at 7 minutes
const int UpdateCheckScheduler::kTimeoutPeriodic = 45 * 60; // every 45 minutes
const int UpdateCheckScheduler::kTimeoutRegularFuzz = 10 * 60; // +/- 5 minutes
const int UpdateCheckScheduler::kTimeoutMaxBackoff = 4 * 60 * 60; // 4 hours
UpdateCheckScheduler::UpdateCheckScheduler(UpdateAttempter* update_attempter)
: update_attempter_(update_attempter),
enabled_(false),
scheduled_(false),
last_interval_(0),
poll_interval_(0) {}
UpdateCheckScheduler::~UpdateCheckScheduler() {}
void UpdateCheckScheduler::Run() {
enabled_ = false;
update_attempter_->set_update_check_scheduler(NULL);
if (!IsOfficialBuild()) {
LOG(WARNING) << "Non-official build: periodic update checks disabled.";
return;
}
if (IsBootDeviceRemovable()) {
LOG(WARNING) << "Removable device boot: periodic update checks disabled.";
return;
}
enabled_ = true;
// Registers this scheduler with the update attempter so that scheduler can be
// notified of update status changes.
update_attempter_->set_update_check_scheduler(this);
// Kicks off periodic update checks. The first check is scheduled
// |kTimeoutOnce| seconds from now. Subsequent checks are scheduled by
// ScheduleNextCheck, normally at |kTimeoutPeriodic|-second intervals.
ScheduleCheck(kTimeoutOnce, kTimeoutRegularFuzz);
}
bool UpdateCheckScheduler::IsBootDeviceRemovable() {
return utils::IsRemovableDevice(utils::RootDevice(utils::BootDevice()));
}
bool UpdateCheckScheduler::IsOOBEComplete() {
return utils::IsOOBEComplete();
}
bool UpdateCheckScheduler::IsOfficialBuild() {
return utils::IsOfficialBuild();
}
guint UpdateCheckScheduler::GTimeoutAddSeconds(guint interval,
GSourceFunc function) {
return g_timeout_add_seconds(interval, function, this);
}
void UpdateCheckScheduler::ScheduleCheck(int interval, int fuzz) {
if (!CanSchedule()) {
return;
}
last_interval_ = interval;
interval = utils::FuzzInt(interval, fuzz);
if (interval < 0) {
interval = 0;
}
GTimeoutAddSeconds(interval, StaticCheck);
scheduled_ = true;
LOG(INFO) << "Next update check in " << interval << " seconds.";
}
gboolean UpdateCheckScheduler::StaticCheck(void* scheduler) {
UpdateCheckScheduler* me = reinterpret_cast<UpdateCheckScheduler*>(scheduler);
CHECK(me->scheduled_);
me->scheduled_ = false;
if (me->IsOOBEComplete()) {
// Before updating, we flush any previously generated UMA reports.
CertificateChecker::FlushReport();
me->update_attempter_->Update("", "", false, false);
} else {
// Skips all automatic update checks if the OOBE process is not complete and
// schedules a new check as if it is the first one.
LOG(WARNING) << "Skipping update check because OOBE is not complete.";
me->ScheduleCheck(kTimeoutOnce, kTimeoutRegularFuzz);
}
// This check ensures that future update checks will be or are already
// scheduled. The check should never fail. A check failure means that there's
// a bug that will most likely prevent further automatic update checks. It
// seems better to crash in such cases and restart the update_engine daemon
// into, hopefully, a known good state.
CHECK(me->update_attempter_->status() != UPDATE_STATUS_IDLE ||
!me->CanSchedule());
return FALSE; // Don't run again.
}
void UpdateCheckScheduler::ComputeNextIntervalAndFuzz(int* next_interval,
int* next_fuzz) {
int interval = 0;
if (poll_interval_ > 0) {
// Server-dictated poll interval.
interval = poll_interval_;
LOG(WARNING) << "Using server-dictated poll interval: " << interval;
} else if (update_attempter_->http_response_code() == 500 ||
update_attempter_->http_response_code() == 503) {
// Implements exponential back off on 500 (Internal Server Error) and 503
// (Service Unavailable) HTTP response codes.
interval = 2 * last_interval_;
LOG(WARNING) << "Exponential back off due to 500/503 HTTP response code.";
}
if (interval > kTimeoutMaxBackoff) {
interval = kTimeoutMaxBackoff;
}
// Back off and server-dictated poll intervals are fuzzed by +/- |interval|/2.
int fuzz = interval;
// Ensures that under normal conditions the regular update check interval and
// fuzz are used. Also covers the case where back off is required based on the
// initial update check.
if (interval < kTimeoutPeriodic) {
interval = kTimeoutPeriodic;
fuzz = kTimeoutRegularFuzz;
}
*next_interval = interval;
*next_fuzz = fuzz;
}
void UpdateCheckScheduler::ScheduleNextCheck() {
int interval, fuzz;
ComputeNextIntervalAndFuzz(&interval, &fuzz);
ScheduleCheck(interval, fuzz);
}
void UpdateCheckScheduler::SetUpdateStatus(UpdateStatus status) {
// We want to schedule the update checks for when we're idle as well as
// after we've successfully applied an update and waiting for the user
// to reboot to ensure our active count is accurate.
if (status == UPDATE_STATUS_IDLE ||
status == UPDATE_STATUS_UPDATED_NEED_REBOOT) {
ScheduleNextCheck();
}
}
} // namespace chromeos_update_engine