AU: Implement server-dictated poll interval.
The server will need to include a PollInterval XML attribute in its
update check response. The requested interval is in seconds.
BUG=5984
TEST=unit tests, gmerged on device and tested with a modified dev server
Change-Id: I89d13f9f85d93bc141b74ae677cca813e3364fb5
Review URL: http://codereview.chromium.org/3275006
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 1c79fb0..c931065 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -3,17 +3,19 @@
// found in the LICENSE file.
#include "update_engine/omaha_request_action.h"
+
#include <inttypes.h>
+
#include <sstream>
+#include <base/string_number_conversions.h>
+#include <base/string_util.h>
+#include <base/time.h>
+#include <base/logging.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
-#include "base/string_number_conversions.h"
-#include "base/string_util.h"
-#include "base/time.h"
-#include "base/logging.h"
#include "update_engine/action_pipe.h"
#include "update_engine/omaha_request_params.h"
#include "update_engine/prefs_interface.h"
@@ -377,8 +379,10 @@
return;
}
- const string status(XmlGetProperty(updatecheck_node, "status"));
OmahaResponse output_object;
+ base::StringToInt(XmlGetProperty(updatecheck_node, "PollInterval"),
+ &output_object.poll_interval);
+ const string status(XmlGetProperty(updatecheck_node, "status"));
if (status == "noupdate") {
LOG(INFO) << "No update.";
output_object.update_exists = false;
@@ -409,7 +413,6 @@
output_object.is_delta =
XmlGetProperty(updatecheck_node, "IsDelta") == "true";
SetOutputObject(output_object);
- return;
}
}; // namespace chromeos_update_engine
diff --git a/omaha_request_action.h b/omaha_request_action.h
index 7533d14..1f72025 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -11,9 +11,9 @@
#include <string>
+#include <base/scoped_ptr.h>
#include <curl/curl.h>
-#include "base/scoped_ptr.h"
#include "update_engine/action.h"
#include "update_engine/http_fetcher.h"
@@ -30,10 +30,17 @@
// These strings in this struct are not XML escaped.
struct OmahaResponse {
OmahaResponse()
- : update_exists(false), size(0), needs_admin(false), prompt(false) {}
+ : update_exists(false),
+ poll_interval(0),
+ size(0),
+ needs_admin(false),
+ prompt(false) {}
// True iff there is an update to be downloaded.
bool update_exists;
+ // If non-zero, server-dictated poll frequency in seconds.
+ int poll_interval;
+
// These are only valid if update_exists is true:
std::string display_version;
std::string codebase;
diff --git a/update_attempter.cc b/update_attempter.cc
index 694e774..a1568a9 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -297,6 +297,12 @@
// If the request is not an event, then it's the update-check.
if (!omaha_request_action->IsEvent()) {
http_response_code_ = omaha_request_action->GetHTTPResponseCode();
+ // Forward the server-dictated poll interval to the update check
+ // scheduler, if any.
+ if (update_check_scheduler_) {
+ update_check_scheduler_->set_poll_interval(
+ omaha_request_action->GetOutputObject().poll_interval);
+ }
}
}
if (code != kActionCodeSuccess) {
diff --git a/update_attempter.h b/update_attempter.h
index 6a81ef2..5df6af9 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -11,10 +11,10 @@
#include <string>
#include <vector>
+#include <base/time.h>
#include <glib.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
-#include "base/time.h"
#include "update_engine/action_processor.h"
#include "update_engine/download_action.h"
#include "update_engine/omaha_request_params.h"
@@ -169,6 +169,7 @@
// Pending error event, if any.
scoped_ptr<OmahaEvent> error_event_;
+ // HTTP server response code from the last HTTP request action.
int http_response_code_;
// Current process priority.
diff --git a/update_check_scheduler.cc b/update_check_scheduler.cc
index c0a52ab..3d00ff3 100644
--- a/update_check_scheduler.cc
+++ b/update_check_scheduler.cc
@@ -17,7 +17,8 @@
: update_attempter_(update_attempter),
enabled_(false),
scheduled_(false),
- last_interval_(0) {}
+ last_interval_(0),
+ poll_interval_(0) {}
UpdateCheckScheduler::~UpdateCheckScheduler() {}
@@ -90,18 +91,23 @@
void UpdateCheckScheduler::ComputeNextIntervalAndFuzz(int* next_interval,
int* next_fuzz) {
int interval = 0;
- int fuzz = 0;
- // Implements exponential back off on 500 (Internal Server Error) and 503
- // (Service Unavailable) HTTP response codes.
- if (update_attempter_->http_response_code() == 500 ||
- update_attempter_->http_response_code() == 503) {
+ 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_;
- if (interval > kTimeoutMaxBackoff) {
- interval = kTimeoutMaxBackoff;
- }
- // Exponential back off is fuzzed by +/- |interval|/2.
- fuzz = 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.
diff --git a/update_check_scheduler.h b/update_check_scheduler.h
index 7253c5a..e3158ed 100644
--- a/update_check_scheduler.h
+++ b/update_check_scheduler.h
@@ -49,10 +49,14 @@
// Sets the new update status. This is invoked by UpdateAttempter.
void SetUpdateStatus(UpdateStatus status);
+ void set_poll_interval(int interval) { poll_interval_ = interval; }
+
private:
friend class UpdateCheckSchedulerTest;
FRIEND_TEST(UpdateCheckSchedulerTest, CanScheduleTest);
FRIEND_TEST(UpdateCheckSchedulerTest, ComputeNextIntervalAndFuzzBackoffTest);
+ FRIEND_TEST(UpdateCheckSchedulerTest, ComputeNextIntervalAndFuzzPollTest);
+ FRIEND_TEST(UpdateCheckSchedulerTest, ComputeNextIntervalAndFuzzPriorityTest);
FRIEND_TEST(UpdateCheckSchedulerTest, ComputeNextIntervalAndFuzzTest);
FRIEND_TEST(UpdateCheckSchedulerTest, GTimeoutAddSecondsTest);
FRIEND_TEST(UpdateCheckSchedulerTest, IsBootDeviceRemovableTest);
@@ -110,6 +114,9 @@
// The timeout interval (before fuzzing) for the last update check.
int last_interval_;
+ // Server dictated poll interval in seconds, if positive.
+ int poll_interval_;
+
DISALLOW_COPY_AND_ASSIGN(UpdateCheckScheduler);
};
diff --git a/update_check_scheduler_unittest.cc b/update_check_scheduler_unittest.cc
index 413453a..3aea760 100644
--- a/update_check_scheduler_unittest.cc
+++ b/update_check_scheduler_unittest.cc
@@ -48,6 +48,7 @@
EXPECT_FALSE(scheduler_.enabled_);
EXPECT_FALSE(scheduler_.scheduled_);
EXPECT_EQ(0, scheduler_.last_interval_);
+ EXPECT_EQ(0, scheduler_.poll_interval_);
}
virtual void TearDown() {
@@ -90,13 +91,42 @@
EXPECT_EQ(2 * last_interval, fuzz);
attempter_.set_http_response_code(503);
- last_interval = UpdateCheckScheduler::kTimeoutMaxBackoff / 2 + 1;
- scheduler_.last_interval_ = last_interval;
+ scheduler_.last_interval_ = UpdateCheckScheduler::kTimeoutMaxBackoff / 2 + 1;
scheduler_.ComputeNextIntervalAndFuzz(&interval, &fuzz);
EXPECT_EQ(UpdateCheckScheduler::kTimeoutMaxBackoff, interval);
EXPECT_EQ(UpdateCheckScheduler::kTimeoutMaxBackoff, fuzz);
}
+TEST_F(UpdateCheckSchedulerTest, ComputeNextIntervalAndFuzzPollTest) {
+ int interval, fuzz;
+ int poll_interval = UpdateCheckScheduler::kTimeoutPeriodic + 50;
+ scheduler_.set_poll_interval(poll_interval);
+ scheduler_.ComputeNextIntervalAndFuzz(&interval, &fuzz);
+ EXPECT_EQ(poll_interval, interval);
+ EXPECT_EQ(poll_interval, fuzz);
+
+ scheduler_.set_poll_interval(UpdateCheckScheduler::kTimeoutMaxBackoff + 1);
+ scheduler_.ComputeNextIntervalAndFuzz(&interval, &fuzz);
+ EXPECT_EQ(UpdateCheckScheduler::kTimeoutMaxBackoff, interval);
+ EXPECT_EQ(UpdateCheckScheduler::kTimeoutMaxBackoff, fuzz);
+
+ scheduler_.set_poll_interval(UpdateCheckScheduler::kTimeoutPeriodic - 1);
+ scheduler_.ComputeNextIntervalAndFuzz(&interval, &fuzz);
+ EXPECT_EQ(UpdateCheckScheduler::kTimeoutPeriodic, interval);
+ EXPECT_EQ(UpdateCheckScheduler::kTimeoutRegularFuzz, fuzz);
+}
+
+TEST_F(UpdateCheckSchedulerTest, ComputeNextIntervalAndFuzzPriorityTest) {
+ int interval, fuzz;
+ attempter_.set_http_response_code(500);
+ scheduler_.last_interval_ = UpdateCheckScheduler::kTimeoutPeriodic + 50;
+ int poll_interval = UpdateCheckScheduler::kTimeoutPeriodic + 100;
+ scheduler_.set_poll_interval(poll_interval);
+ scheduler_.ComputeNextIntervalAndFuzz(&interval, &fuzz);
+ EXPECT_EQ(poll_interval, interval);
+ EXPECT_EQ(poll_interval, fuzz);
+}
+
TEST_F(UpdateCheckSchedulerTest, ComputeNextIntervalAndFuzzTest) {
int interval, fuzz;
scheduler_.ComputeNextIntervalAndFuzz(&interval, &fuzz);