Download via HTTP only if enterprise policy allows.

In order to rollout HTTP-downloads for AU to stable channel, we want to
be a bit more conservative to preseve the defense in depth we have now
with HTTPS. So, we're introduced a new enterprise policy which should be
explicitly enabled in order for the payloads to be downloaded via HTTP.

This CL adds the support for honoring such a policy in update engine.

BUG=chromium:235562
TEST=New unit tests added, existing ones updated and they all pass.
TEST=Tested on ZGB with and without policy and it works as expected.
Change-Id: I356efbe237b10031161a57c70cb851c521915a76
Reviewed-on: https://gerrit.chromium.org/gerrit/55805
Reviewed-by: Chris Sosa <[email protected]>
Tested-by: Jay Srinivasan <[email protected]>
Commit-Queue: Jay Srinivasan <[email protected]>
diff --git a/mock_payload_state.h b/mock_payload_state.h
index 65d99da..6513a83 100644
--- a/mock_payload_state.h
+++ b/mock_payload_state.h
@@ -30,7 +30,7 @@
   // Getters.
   MOCK_METHOD0(GetResponseSignature, std::string());
   MOCK_METHOD0(GetPayloadAttemptNumber, uint32_t());
-  MOCK_METHOD0(GetUrlIndex, uint32_t());
+  MOCK_METHOD0(GetCurrentUrl, std::string());
   MOCK_METHOD0(GetUrlFailureCount, uint32_t());
   MOCK_METHOD0(GetUrlSwitchCount, uint32_t());
   MOCK_METHOD0(GetBackoffExpiryTime, base::Time());
diff --git a/mock_system_state.h b/mock_system_state.h
index eccfd9b..e03546e 100644
--- a/mock_system_state.h
+++ b/mock_system_state.h
@@ -30,6 +30,7 @@
   virtual ~MockSystemState();
 
   MOCK_METHOD0(IsOOBEComplete, bool());
+  MOCK_METHOD0(IsOfficialBuild, bool());
 
   MOCK_METHOD1(set_device_policy, void(const policy::DevicePolicy*));
   MOCK_CONST_METHOD0(device_policy, const policy::DevicePolicy*());
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index f2c5c6b..c28e0d6 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -544,7 +544,6 @@
   output_object->payload_urls.clear();
   for (int i = 0; i < nodeset->nodeNr; i++) {
     xmlNode* url_node = nodeset->nodeTab[i];
-
     const string codebase(XmlGetProperty(url_node, "codebase"));
     if (codebase.empty()) {
       LOG(ERROR) << "Omaha Response URL has empty codebase";
@@ -593,10 +592,8 @@
   // Append the package name to each URL in our list so that we don't
   // propagate the urlBase vs packageName distinctions beyond this point.
   // From now on, we only need to use payload_urls.
-  for (size_t i = 0; i < output_object->payload_urls.size(); i++) {
+  for (size_t i = 0; i < output_object->payload_urls.size(); i++)
     output_object->payload_urls[i] += package_name;
-    LOG(INFO) << "Url" << i << ": " << output_object->payload_urls[i];
-  }
 
   // Parse the payload size.
   off_t size = ParseInt(XmlGetProperty(package_node, "size"));
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index ad6a087..c85711b 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -39,11 +39,17 @@
   }
 
   // All decisions as to which URL should be used have already been done. So,
-  // make the download URL as the payload URL at the current url index.
-  uint32_t url_index = system_state_->payload_state()->GetUrlIndex();
-  LOG(INFO) << "Using Url" << url_index << " as the download url this time";
-  CHECK(url_index < response.payload_urls.size());
-  install_plan_.download_url = response.payload_urls[url_index];
+  // make the current URL as the download URL.
+  string current_url = system_state_->payload_state()->GetCurrentUrl();
+  if (current_url.empty()) {
+    // This shouldn't happen as we should always supply the HTTPS backup URL.
+    // Handling this anyway, just in case.
+    LOG(ERROR) << "There are no suitable URLs in the response to use.";
+    completer.set_code(kErrorCodeOmahaResponseInvalid);
+    return;
+  }
+
+  install_plan_.download_url = current_url;
 
   // Fill up the other properties based on the response.
   install_plan_.payload_size = response.size;
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index 15a1f06..ae8fccb 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -77,6 +77,11 @@
                 SetString(kPrefsUpdateCheckResponseHash, in.hash))
         .WillOnce(Return(true));
   }
+
+  string current_url = in.payload_urls.size() ? in.payload_urls[0] : "";
+  EXPECT_CALL(*(mock_system_state->mock_payload_state()), GetCurrentUrl())
+      .WillRepeatedly(Return(current_url));
+
   OmahaResponseHandlerAction response_handler_action(mock_system_state);
   response_handler_action.set_boot_device(boot_dev);
   BondActions(&feeder_action, &response_handler_action);
diff --git a/payload_state.cc b/payload_state.cc
index 5ab6028..3de9e04 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -67,6 +67,12 @@
   // Always store the latest response.
   response_ = omaha_response;
 
+  // Compute the candidate URLs first as they are used to calculate the
+  // response signature so that a change in enterprise policy for
+  // HTTP downloads being enabled or not could be honored as soon as the
+  // next update check happens.
+  ComputeCandidateUrls();
+
   // Check if the "signature" of this response (i.e. the fields we care about)
   // has changed.
   string new_response_signature = CalculateResponseSignature();
@@ -85,7 +91,7 @@
   // we loaded from the persisted state is a valid value. If the response
   // hasn't changed but the URL index is invalid, it's indicative of some
   // tampering of the persisted state.
-  if (url_index_ >= GetNumUrls()) {
+  if (static_cast<uint32_t>(url_index_) >= candidate_urls_.size()) {
     LOG(INFO) << "Resetting all payload state as the url index seems to have "
                  "been tampered with";
     ResetPersistedState();
@@ -152,8 +158,9 @@
   LOG(INFO) << "Updating payload state for error code: " << base_error
             << " (" << utils::CodeToString(base_error) << ")";
 
-  if (GetNumUrls() == 0) {
-    // This means we got this error even before we got a valid Omaha response.
+  if (candidate_urls_.size() == 0) {
+    // This means we got this error even before we got a valid Omaha response
+    // or don't have any valid candidates in the Omaha response.
     // So we should not advance the url_index_ in such cases.
     LOG(INFO) << "Ignoring failures until we get a valid Omaha response.";
     return;
@@ -307,18 +314,19 @@
 
 void PayloadState::IncrementUrlIndex() {
   uint32_t next_url_index = GetUrlIndex() + 1;
-  if (next_url_index < GetNumUrls()) {
+  if (next_url_index < candidate_urls_.size()) {
     LOG(INFO) << "Incrementing the URL index for next attempt";
     SetUrlIndex(next_url_index);
   } else {
     LOG(INFO) << "Resetting the current URL index (" << GetUrlIndex() << ") to "
-              << "0 as we only have " << GetNumUrls() << " URL(s)";
+              << "0 as we only have " << candidate_urls_.size()
+              << " candidate URL(s)";
     SetUrlIndex(0);
     IncrementPayloadAttemptNumber();
   }
 
   // If we have multiple URLs, record that we just switched to another one
-  if (GetNumUrls() > 1)
+  if (candidate_urls_.size() > 1)
     SetUrlSwitchCount(url_switch_count_ + 1);
 
   // Whenever we update the URL index, we should also clear the URL failure
@@ -377,8 +385,8 @@
 void PayloadState::UpdateCurrentDownloadSource() {
   current_download_source_ = kNumDownloadSources;
 
-  if (GetUrlIndex() < response_.payload_urls.size())  {
-    string current_url = response_.payload_urls[GetUrlIndex()];
+  if (GetUrlIndex() < candidate_urls_.size())  {
+    string current_url = candidate_urls_[GetUrlIndex()];
     if (StartsWithASCII(current_url, "https://", false))
       current_download_source_ = kDownloadSourceHttpsServer;
     else if (StartsWithASCII(current_url, "http://", false))
@@ -552,11 +560,11 @@
 
 string PayloadState::CalculateResponseSignature() {
   string response_sign = StringPrintf("NumURLs = %d\n",
-                                      response_.payload_urls.size());
+                                      candidate_urls_.size());
 
-  for (size_t i = 0; i < response_.payload_urls.size(); i++)
-    response_sign += StringPrintf("Url%d = %s\n",
-                                  i, response_.payload_urls[i].c_str());
+  for (size_t i = 0; i < candidate_urls_.size(); i++)
+    response_sign += StringPrintf("Candidate Url%d = %s\n",
+                                  i, candidate_urls_[i].c_str());
 
   response_sign += StringPrintf("Payload Size = %llu\n"
                                 "Payload Sha256 Hash = %s\n"
@@ -875,4 +883,31 @@
                     << GetTotalBytesDownloaded(source);
 }
 
+void PayloadState::ComputeCandidateUrls() {
+  bool http_url_ok = false;
+
+  if (system_state_->IsOfficialBuild()) {
+    const policy::DevicePolicy* policy = system_state_->device_policy();
+    if (!(policy && policy->GetHttpDownloadsEnabled(&http_url_ok) &&
+          http_url_ok))
+      LOG(INFO) << "Downloads via HTTP Url are not enabled by device policy";
+  } else {
+    LOG(INFO) << "Allowing HTTP downloads for unofficial builds";
+    http_url_ok = true;
+  }
+
+  candidate_urls_.clear();
+  for (size_t i = 0; i < response_.payload_urls.size(); i++) {
+    string candidate_url = response_.payload_urls[i];
+    if (StartsWithASCII(candidate_url, "http://", false) && !http_url_ok)
+        continue;
+    candidate_urls_.push_back(candidate_url);
+    LOG(INFO) << "Candidate Url" << (candidate_urls_.size() - 1)
+              << ": " << candidate_url;
+  }
+
+  LOG(INFO) << "Found " << candidate_urls_.size() << " candidate URLs "
+            << "out of " << response_.payload_urls.size() << " URLs supplied";
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_state.h b/payload_state.h
index 1b0899c..e28d5e8 100644
--- a/payload_state.h
+++ b/payload_state.h
@@ -49,8 +49,8 @@
     return payload_attempt_number_;
   }
 
-  virtual inline uint32_t GetUrlIndex() {
-    return url_index_;
+  virtual inline std::string GetCurrentUrl() {
+    return candidate_urls_.size() ? candidate_urls_[url_index_] : "";
   }
 
   virtual inline uint32_t GetUrlFailureCount() {
@@ -243,6 +243,14 @@
                                uint64_t total_bytes_downloaded,
                                bool log);
 
+  inline uint32_t GetUrlIndex() {
+    return url_index_;
+  }
+
+  // Computes the list of candidate URLs from the total list of payload URLs in
+  // the Omaha response.
+  void ComputeCandidateUrls();
+
   // The global state of the system.
   SystemState* system_state_;
 
@@ -341,18 +349,15 @@
   // return value from GetCurrentDownloadSource is used without validation.
   uint64_t total_bytes_downloaded_[kNumDownloadSources + 1];
 
-  // Returns the number of URLs in the current response.
-  // Note: This value will be 0 if this method is called before we receive
-  // the first valid Omaha response in this process.
-  uint32_t GetNumUrls() {
-    return response_.payload_urls.size();
-  }
-
   // A small timespan used when comparing wall-clock times for coping
   // with the fact that clocks drift and consequently are adjusted
   // (either forwards or backwards) via NTP.
   static const base::TimeDelta kDurationSlack;
 
+  // The ordered list of the subset of payload URL candidates which are
+  // allowed as per device policy.
+  std::vector<std::string> candidate_urls_;
+
   DISALLOW_COPY_AND_ASSIGN(PayloadState);
 };
 
diff --git a/payload_state_interface.h b/payload_state_interface.h
index 205cb91..cd49abf 100644
--- a/payload_state_interface.h
+++ b/payload_state_interface.h
@@ -69,8 +69,8 @@
   // Returns the payload attempt number.
   virtual uint32_t GetPayloadAttemptNumber() = 0;
 
-  // Returns the current URL index.
-  virtual uint32_t GetUrlIndex() = 0;
+  // Returns the current URL. Returns an empty string if there's no valid URL.
+  virtual std::string GetCurrentUrl() = 0;
 
   // Returns the current URL's failure count.
   virtual uint32_t GetUrlFailureCount() = 0;
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index 440d8b7..77a8d02 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -42,6 +42,7 @@
   "total-bytes-downloaded-from-HttpServer";
 
 static void SetupPayloadStateWith2Urls(string hash,
+                                       bool http_enabled,
                                        PayloadState* payload_state,
                                        OmahaResponse* response) {
   response->payload_urls.clear();
@@ -54,21 +55,29 @@
   response->max_failure_count_per_url = 3;
   payload_state->SetResponse(*response);
   string stored_response_sign = payload_state->GetResponseSignature();
-  string expected_response_sign = StringPrintf(
+
+  string expected_url_https_only =
+      "NumURLs = 1\n"
+      "Candidate Url0 = https://test\n";
+
+  string expected_urls_both =
       "NumURLs = 2\n"
-      "Url0 = http://test\n"
-      "Url1 = https://test\n"
-      "Payload Size = 523456789\n"
-      "Payload Sha256 Hash = %s\n"
-      "Metadata Size = 558123\n"
-      "Metadata Signature = metasign\n"
-      "Is Delta Payload = %d\n"
-      "Max Failure Count Per Url = %d\n"
-      "Disable Payload Backoff = %d\n",
-      hash.c_str(),
-      response->is_delta_payload,
-      response->max_failure_count_per_url,
-      response->disable_payload_backoff);
+      "Candidate Url0 = http://test\n"
+      "Candidate Url1 = https://test\n";
+
+  string expected_response_sign =
+      (http_enabled ? expected_urls_both : expected_url_https_only) +
+      StringPrintf("Payload Size = 523456789\n"
+                   "Payload Sha256 Hash = %s\n"
+                   "Metadata Size = 558123\n"
+                   "Metadata Signature = metasign\n"
+                   "Is Delta Payload = %d\n"
+                   "Max Failure Count Per Url = %d\n"
+                   "Disable Payload Backoff = %d\n",
+                   hash.c_str(),
+                   response->is_delta_payload,
+                   response->max_failure_count_per_url,
+                   response->disable_payload_backoff);
   EXPECT_EQ(expected_response_sign, stored_response_sign);
 }
 
@@ -118,14 +127,14 @@
                                   "Max Failure Count Per Url = 0\n"
                                   "Disable Payload Backoff = 0\n";
   EXPECT_EQ(expected_response_sign, stored_response_sign);
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
 }
 
 TEST(PayloadStateTest, SetResponseWorksWithSingleUrl) {
   OmahaResponse response;
-  response.payload_urls.push_back("http://single.url.test");
+  response.payload_urls.push_back("https://single.url.test");
   response.size = 123456789;
   response.hash = "hash";
   response.metadata_size = 58123;
@@ -156,7 +165,7 @@
   payload_state.SetResponse(response);
   string stored_response_sign = payload_state.GetResponseSignature();
   string expected_response_sign = "NumURLs = 1\n"
-                                  "Url0 = http://single.url.test\n"
+                                  "Candidate Url0 = https://single.url.test\n"
                                   "Payload Size = 123456789\n"
                                   "Payload Sha256 Hash = hash\n"
                                   "Metadata Size = 58123\n"
@@ -165,7 +174,7 @@
                                   "Max Failure Count Per Url = 0\n"
                                   "Disable Payload Backoff = 0\n";
   EXPECT_EQ(expected_response_sign, stored_response_sign);
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("https://single.url.test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
 }
@@ -195,13 +204,14 @@
     .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 0))
       .Times(AtLeast(1));
+
   PayloadState payload_state;
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
   payload_state.SetResponse(response);
   string stored_response_sign = payload_state.GetResponseSignature();
   string expected_response_sign = "NumURLs = 2\n"
-                                  "Url0 = http://multiple.url.test\n"
-                                  "Url1 = https://multiple.url.test\n"
+                                  "Candidate Url0 = http://multiple.url.test\n"
+                                  "Candidate Url1 = https://multiple.url.test\n"
                                   "Payload Size = 523456789\n"
                                   "Payload Sha256 Hash = rhash\n"
                                   "Metadata Size = 558123\n"
@@ -210,7 +220,7 @@
                                   "Max Failure Count Per Url = 0\n"
                                   "Disable Payload Backoff = 0\n";
   EXPECT_EQ(expected_response_sign, stored_response_sign);
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("http://multiple.url.test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
 }
@@ -245,21 +255,21 @@
 
   // This does a SetResponse which causes all the states to be set to 0 for
   // the first time.
-  SetupPayloadStateWith2Urls("Hash1235", &payload_state, &response);
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  SetupPayloadStateWith2Urls("Hash1235", true, &payload_state, &response);
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
 
   // Verify that on the first error, the URL index advances to 1.
   ErrorCode error = kErrorCodeDownloadMetadataSignatureMismatch;
   payload_state.UpdateFailed(error);
-  EXPECT_EQ(1, payload_state.GetUrlIndex());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
 
   // Verify that on the next error, the URL index wraps around to 0.
   payload_state.UpdateFailed(error);
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
 
   // Verify that on the next error, it again advances to 1.
   payload_state.UpdateFailed(error);
-  EXPECT_EQ(1, payload_state.GetUrlIndex());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
 
   // Verify that we switched URLs three times
   EXPECT_EQ(3, payload_state.GetUrlSwitchCount());
@@ -273,19 +283,19 @@
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
 
   // Set the first response.
-  SetupPayloadStateWith2Urls("Hash5823", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash5823", true, &payload_state, &response);
 
   // Advance the URL index to 1 by faking an error.
   ErrorCode error = kErrorCodeDownloadMetadataSignatureMismatch;
   payload_state.UpdateFailed(error);
-  EXPECT_EQ(1, payload_state.GetUrlIndex());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
 
   // Now, slightly change the response and set it again.
-  SetupPayloadStateWith2Urls("Hash8225", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash8225", true, &payload_state, &response);
 
   // Make sure the url index was reset to 0 because of the new response.
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
   EXPECT_EQ(0,
@@ -343,26 +353,26 @@
 
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
 
-  SetupPayloadStateWith2Urls("Hash5873", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash5873", true, &payload_state, &response);
 
   // This should advance the URL index.
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMismatch);
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(1, payload_state.GetUrlIndex());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
 
   // This should advance the failure count only.
   payload_state.UpdateFailed(kErrorCodeDownloadTransferError);
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(1, payload_state.GetUrlIndex());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(1, payload_state.GetUrlFailureCount());
   EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
 
   // This should advance the failure count only.
   payload_state.UpdateFailed(kErrorCodeDownloadTransferError);
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(1, payload_state.GetUrlIndex());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(2, payload_state.GetUrlFailureCount());
   EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
 
@@ -372,7 +382,7 @@
   // attempt number to be incremented.
   payload_state.UpdateFailed(kErrorCodeDownloadTransferError);
   EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(2, payload_state.GetUrlSwitchCount());
   EXPECT_TRUE(payload_state.ShouldBackoffDownload());
@@ -380,7 +390,7 @@
   // This should advance the URL index.
   payload_state.UpdateFailed(kErrorCodePayloadHashMismatchError);
   EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(1, payload_state.GetUrlIndex());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(3, payload_state.GetUrlSwitchCount());
   EXPECT_TRUE(payload_state.ShouldBackoffDownload());
@@ -389,7 +399,7 @@
   // wrap-around of URL index.
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMissingError);
   EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(4, payload_state.GetUrlSwitchCount());
   EXPECT_TRUE(payload_state.ShouldBackoffDownload());
@@ -398,7 +408,7 @@
   payload_state.UpdateFailed(static_cast<ErrorCode>(
       kErrorCodeOmahaRequestHTTPResponseBase + 404));
   EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(1, payload_state.GetUrlFailureCount());
   EXPECT_EQ(4, payload_state.GetUrlSwitchCount());
   EXPECT_TRUE(payload_state.ShouldBackoffDownload());
@@ -407,17 +417,17 @@
   // afterwards.
   payload_state.DownloadProgress(progress_bytes);
   EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(4, payload_state.GetUrlSwitchCount());
   EXPECT_TRUE(payload_state.ShouldBackoffDownload());
 
   // Now, slightly change the response and set it again.
-  SetupPayloadStateWith2Urls("Hash8532", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash8532", true, &payload_state, &response);
 
   // Make sure the url index was reset to 0 because of the new response.
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
   EXPECT_FALSE(payload_state.ShouldBackoffDownload());
@@ -445,13 +455,13 @@
 
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
 
-  SetupPayloadStateWith2Urls("Hash8593", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
 
   // This should just advance the payload attempt number;
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
   payload_state.DownloadComplete();
   EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
 }
@@ -462,7 +472,7 @@
   MockSystemState mock_system_state;
 
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
-  SetupPayloadStateWith2Urls("Hash4427", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash4427", true, &payload_state, &response);
 
   // Generate enough events to advance URL index, failure count and
   // payload attempt number all to 1.
@@ -470,7 +480,7 @@
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMismatch);
   payload_state.UpdateFailed(kErrorCodeDownloadTransferError);
   EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(1, payload_state.GetUrlIndex());
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(1, payload_state.GetUrlFailureCount());
   EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
 
@@ -495,12 +505,12 @@
   // response was different. We want to specifically test that even if the
   // response is same, we should reset the state if we find it corrupted.
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state2));
-  SetupPayloadStateWith2Urls("Hash4427", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash4427", true, &payload_state, &response);
 
   // Make sure all counters get reset to 0 because of the corrupted URL index
   // we supplied above.
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
 }
@@ -512,7 +522,7 @@
   MockSystemState mock_system_state;
 
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
-  SetupPayloadStateWith2Urls("Hash6437", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
 
   // Simulate a successful download and see that we're ready to download
   // again without any backoff as this is a delta payload.
@@ -525,7 +535,7 @@
   // a delta payload.
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMismatch);
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMismatch);
-  EXPECT_EQ(0, payload_state.GetUrlIndex());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
   EXPECT_FALSE(payload_state.ShouldBackoffDownload());
 }
@@ -554,7 +564,7 @@
   MockSystemState mock_system_state;
 
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
-  SetupPayloadStateWith2Urls("Hash8939", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash8939", true, &payload_state, &response);
 
   CheckPayloadBackoffState(&payload_state, 1,  TimeDelta::FromDays(1));
   CheckPayloadBackoffState(&payload_state, 2,  TimeDelta::FromDays(2));
@@ -575,7 +585,7 @@
   MockSystemState mock_system_state;
 
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
-  SetupPayloadStateWith2Urls("Hash8939", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash8939", true, &payload_state, &response);
 
   // Simulate a successful download and see that we are ready to download
   // again without any backoff.
@@ -601,7 +611,7 @@
   int http_total = 0;
 
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
-  SetupPayloadStateWith2Urls("Hash3286", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash3286", true, &payload_state, &response);
 
   // Simulate a previous attempt with in order to set an initial non-zero value
   // for the total bytes downloaded for HTTP.
@@ -617,13 +627,13 @@
 
   // Change the response hash so as to simulate a new response which will
   // reset the current bytes downloaded, but not the total bytes downloaded.
-  SetupPayloadStateWith2Urls("Hash9904", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash9904", true, &payload_state, &response);
 
   // First, simulate successful download of a few bytes over HTTP.
   int first_chunk = 5000000;
   http_total += first_chunk;
   payload_state.DownloadProgress(first_chunk);
-  // Test that first all progress is made on HTTP and none on HTTPs.
+  // Test that first all progress is made on HTTP and none on HTTPS.
   EXPECT_EQ(first_chunk,
             payload_state.GetCurrentBytesDownloaded(kDownloadSourceHttpServer));
   EXPECT_EQ(http_total,
@@ -637,7 +647,7 @@
   ErrorCode error = kErrorCodeDownloadMetadataSignatureMismatch;
   payload_state.UpdateFailed(error);
 
-  // Test that no new progress is made on HTTP and new progress is on HTTPs.
+  // Test that no new progress is made on HTTP and new progress is on HTTPS.
   int second_chunk = 23456789;
   https_total += second_chunk;
   payload_state.DownloadProgress(second_chunk);
@@ -717,7 +727,7 @@
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
 
   // Set the first response.
-  SetupPayloadStateWith2Urls("Hash5823", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash5823", true, &payload_state, &response);
 
   int num_bytes = 10000;
   payload_state.DownloadProgress(num_bytes);
@@ -791,7 +801,7 @@
   // Check that durations are correct for a successful update where
   // time has advanced 7 seconds on the wall clock and 4 seconds on
   // the monotonic clock.
-  SetupPayloadStateWith2Urls("Hash8593", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
   fake_clock.SetWallclockTime(Time::FromInternalValue(8000000));
   fake_clock.SetMonotonicTime(Time::FromInternalValue(6000000));
   payload_state.UpdateSucceeded();
@@ -799,7 +809,7 @@
   EXPECT_EQ(payload_state.GetUpdateDurationUptime().InMicroseconds(), 4000000);
 
   // Check that durations are reset when a new response comes in.
-  SetupPayloadStateWith2Urls("Hash8594", &payload_state, &response);
+  SetupPayloadStateWith2Urls("Hash8594", true, &payload_state, &response);
   EXPECT_EQ(payload_state.GetUpdateDuration().InMicroseconds(), 0);
   EXPECT_EQ(payload_state.GetUpdateDurationUptime().InMicroseconds(), 0);
 
@@ -831,4 +841,68 @@
   EXPECT_TRUE(utils::RecursiveUnlinkDir(temp_dir));
 }
 
+TEST(PayloadStateTest, CandidateUrlsComputedCorrectly) {
+  OmahaResponse response;
+  MockSystemState mock_system_state;
+  PayloadState payload_state;
+
+  // Pretend that this is an offical build so that the HTTP download policy
+  // is honored.
+  EXPECT_CALL(mock_system_state, IsOfficialBuild())
+      .WillRepeatedly(Return(true));
+
+  policy::MockDevicePolicy disable_http_policy;
+  EXPECT_CALL(mock_system_state, device_policy())
+      .WillRepeatedly(Return(&disable_http_policy));
+  EXPECT_CALL(disable_http_policy, GetHttpDownloadsEnabled(_))
+      .WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(true)));
+
+  EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
+
+  // Set the first response.
+  SetupPayloadStateWith2Urls("Hash8433", false, &payload_state, &response);
+
+  // Check that we skip the HTTP URL and use only the HTTPS url.
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+
+  // Advance the URL index to 1 by faking an error.
+  ErrorCode error = kErrorCodeDownloadMetadataSignatureMismatch;
+  payload_state.UpdateFailed(error);
+
+  // Check that we still skip the HTTP URL and use only the HTTPS url.
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
+
+  // Now, slightly change the response and set it again.
+  SetupPayloadStateWith2Urls("Hash2399", false, &payload_state, &response);
+
+  // Check that we still skip the HTTP URL and use only the HTTPS url.
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+
+  // Now, pretend that the HTTP policy is turned on. We want to make sure
+  // the new policy is honored.
+  policy::MockDevicePolicy enable_http_policy;
+  EXPECT_CALL(mock_system_state, device_policy())
+      .WillRepeatedly(Return(&enable_http_policy));
+  EXPECT_CALL(enable_http_policy, GetHttpDownloadsEnabled(_))
+      .WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
+
+  // Now, set the same response using the same hash
+  // so that we can test that the state is reset not because of the
+  // hash but because of the policy change which results in candidate url
+  // list change.
+  SetupPayloadStateWith2Urls("Hash2399", true, &payload_state, &response);
+
+  // Check that we use the HTTP URL now and the failure count is reset.
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+
+  // Fake a failure and see if we're moving over to the HTTPS url and update
+  // the URL switch count properly.
+  payload_state.UpdateFailed(error);
+  EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+}
+
 }
diff --git a/real_system_state.h b/real_system_state.h
index 42d1509..83f3ac1 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -25,6 +25,7 @@
   virtual ~RealSystemState() {}
 
   virtual bool IsOOBEComplete();
+  virtual bool IsOfficialBuild();
 
   virtual inline void set_device_policy(
       const policy::DevicePolicy* device_policy) {
diff --git a/system_state.cc b/system_state.cc
index da18500..2f7d0dd 100644
--- a/system_state.cc
+++ b/system_state.cc
@@ -64,4 +64,8 @@
   return file_util::PathExists(FilePath(kOOBECompletedMarker));
 }
 
+bool RealSystemState::IsOfficialBuild() {
+  return utils::IsOfficialBuild();
+}
+
 }  // namespace chromeos_update_engine
diff --git a/system_state.h b/system_state.h
index 313d670..4f3c98f 100644
--- a/system_state.h
+++ b/system_state.h
@@ -40,6 +40,10 @@
   // False otherwise.
   virtual bool IsOOBEComplete() = 0;
 
+  // Returns true if we're running an official (i.e, non-dev, non-test) build.
+  // False otherwise.
+  virtual bool IsOfficialBuild() = 0;
+
   // Sets or gets the latest device policy.
   virtual void set_device_policy(const policy::DevicePolicy* device_policy) = 0;
   virtual const policy::DevicePolicy* device_policy() const = 0;
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index ff3236c..095de23 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -237,11 +237,14 @@
   EXPECT_CALL(*mock_system_state_.mock_payload_state(), UpdateFailed(_))
       .Times(0);
   OmahaResponse response;
-  response.payload_urls.push_back("http://url");
+  string url1 = "http://url1";
+  response.payload_urls.push_back(url1);
   response.payload_urls.push_back("https://url");
+  EXPECT_CALL(*(mock_system_state_.mock_payload_state()), GetCurrentUrl())
+      .WillRepeatedly(Return(url1));
   mock_system_state_.mock_payload_state()->SetResponse(response);
   attempter_.ScheduleErrorEventAction();
-  EXPECT_EQ(0, mock_system_state_.mock_payload_state()->GetUrlIndex());
+  EXPECT_EQ(url1, mock_system_state_.mock_payload_state()->GetCurrentUrl());
 }
 
 TEST_F(UpdateAttempterTest, ScheduleErrorEventActionTest) {