GPIO test signal safe fallback + refactoring.
This addresses the problem of spurious GPIO signals indicating a test
scenario whereas in fact this isn't one, which may lead to hosts unable
to get an update (ZGB, Lumpy). This is also a partial fix to a problem
with Stumpy, where such spurious signals are inherent to the board
implementation.
* Safe fallback: a GPIO-signaled test scenario will be ignored other
than on the first time, in both places it is being checked
(UpdateAttempter::Update() and UpdateCheckScheduler::StaticCheck()).
This will ensure that we do not (a) override EULA/OOBE-complete flag
more than once; and (b) we do not attempt to update against a local
test server more than once. This generally covers against spurious
GPIO, as long as a user cannot trigger an update check on
a non-OOBE-complete system (appears to be a safe assumption).
* The retry timeout after failing an update with the test server is
shortened to 1 minute (compared to the default 45 minute). This
substantially increases the chances for a system exhibiting spurious
GPIO signals to get updates.
* Moved the GPIO functionality into a separate module/class. This makes
more sense now that it is being used by more than one class
(UpdateAttempter and UpdateCheckScheduler). The implementation of
GpioHandler has no instance data members and so behaves like
a singleton, but otherwise looks and feels like a normal class.
* Also changing the private test server URL to use an unregistered TCP
port (further reduces the chances of anything responding on the LAN).
* Some minor fixes.
BUG=chromium-os:27077, chromium-os:27109, chromium-os:25397,
chromium-os:27157
TEST=Unittests passed; GPIO reading + fallback work on x86-alex.
Change-Id: Ide1a60a690f1263efd47872360470347e56eeb45
Reviewed-on: https://gerrit.chromium.org/gerrit/17344
Commit-Ready: Gilad Arnold <garnold@chromium.org>
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
diff --git a/update_check_scheduler.cc b/update_check_scheduler.cc
index 0218393..4d088ab 100644
--- a/update_check_scheduler.cc
+++ b/update_check_scheduler.cc
@@ -1,18 +1,22 @@
-// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 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/http_common.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
+// Default update check timeout interval/fuzz values, in seconds. Note that
+// actual fuzz is within +/- half of the indicated value.
+const int UpdateCheckScheduler::kTimeoutInitialInterval = 7 * 60;
+const int UpdateCheckScheduler::kTimeoutPeriodicInterval = 45 * 60;
+const int UpdateCheckScheduler::kTimeoutQuickInterval = 1 * 60;
+const int UpdateCheckScheduler::kTimeoutMaxBackoffInterval = 4 * 60 * 60;
+const int UpdateCheckScheduler::kTimeoutRegularFuzz = 10 * 60;
UpdateCheckScheduler::UpdateCheckScheduler(UpdateAttempter* update_attempter)
: update_attempter_(update_attempter),
@@ -42,9 +46,10 @@
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);
+ // |kTimeoutInitialInterval| seconds from now. Subsequent checks are scheduled
+ // by ScheduleNextCheck, normally at |kTimeoutPeriodicInterval|-second
+ // intervals.
+ ScheduleCheck(kTimeoutInitialInterval, kTimeoutRegularFuzz);
}
bool UpdateCheckScheduler::IsBootDeviceRemovable() {
@@ -75,22 +80,32 @@
}
GTimeoutAddSeconds(interval, StaticCheck);
scheduled_ = true;
- LOG(INFO) << "Next update check in " << interval << " seconds.";
+ LOG(INFO) << "Next update check in " << utils::SecsToHourMinSecStr(interval);
}
gboolean UpdateCheckScheduler::StaticCheck(void* scheduler) {
UpdateCheckScheduler* me = reinterpret_cast<UpdateCheckScheduler*>(scheduler);
CHECK(me->scheduled_);
me->scheduled_ = false;
- if (me->IsOOBEComplete()) {
+
+ static bool is_test_used_once = false;
+ bool is_test = false;
+ if (me->IsOOBEComplete() ||
+ (is_test = !is_test_used_once && GpioHandler::IsGpioSignalingTest())) {
+ if (is_test) {
+ LOG(WARNING)
+ << "test mode signaled, allowing update check prior to OOBE complete";
+ is_test_used_once = true;
+ }
+
// Before updating, we flush any previously generated UMA reports.
CertificateChecker::FlushReport();
- me->update_attempter_->Update("", "", false, false);
+ me->update_attempter_->Update("", "", false, false, is_test);
} 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);
+ me->ScheduleCheck(kTimeoutInitialInterval, 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
@@ -102,50 +117,66 @@
return FALSE; // Don't run again.
}
-void UpdateCheckScheduler::ComputeNextIntervalAndFuzz(int* next_interval,
+void UpdateCheckScheduler::ComputeNextIntervalAndFuzz(const int forced_interval,
+ 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;
+ CHECK(next_interval && next_fuzz);
- // 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;
+ int interval = forced_interval;
+ int fuzz = 0; // Use default fuzz value (see below)
+
+ if (interval == 0) {
+ int http_response_code;
+ if (poll_interval_ > 0) {
+ // Server-dictated poll interval.
+ interval = poll_interval_;
+ LOG(WARNING) << "Using server-dictated poll interval: " << interval;
+ } else if ((http_response_code = update_attempter_->http_response_code()) ==
+ kHttpResponseInternalServerError ||
+ http_response_code == kHttpResponseServiceUnavailable) {
+ // 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 HTTP response code ("
+ << http_response_code << ")";
+ }
+
+ // Backoff cannot exceed a predetermined maximum period.
+ if (interval > kTimeoutMaxBackoffInterval)
+ interval = kTimeoutMaxBackoffInterval;
+
+ // 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 < kTimeoutPeriodicInterval) {
+ interval = kTimeoutPeriodicInterval;
+ fuzz = kTimeoutRegularFuzz;
+ }
}
+
+ // Set default fuzz to +/- |interval|/2.
+ if (fuzz == 0)
+ fuzz = interval;
+
*next_interval = interval;
*next_fuzz = fuzz;
}
-void UpdateCheckScheduler::ScheduleNextCheck() {
+void UpdateCheckScheduler::ScheduleNextCheck(bool is_force_quick) {
int interval, fuzz;
- ComputeNextIntervalAndFuzz(&interval, &fuzz);
+ ComputeNextIntervalAndFuzz(is_force_quick ? kTimeoutQuickInterval : 0,
+ &interval, &fuzz);
ScheduleCheck(interval, fuzz);
}
-void UpdateCheckScheduler::SetUpdateStatus(UpdateStatus status) {
+void UpdateCheckScheduler::SetUpdateStatus(UpdateStatus status,
+ UpdateNotice notice) {
// 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();
+ ScheduleNextCheck(notice == kUpdateNoticeTestAddrFailed);
}
}