Add Installer.AttemptsCount.Total metric.

This patch adds a new Installer.AttemptsCount.Total metric that
reports the number of payload attempts until the payload is
successfully applied. To achieve this a new variable persisted in
the preferences directory is added. This variable tracks the number
of attempts for the current payload, beign it a full or a delta
payload.

Unfortunatelly, there is a "PayloadAttemptNumber" variable that
tracks the number of attempts for the current payload, but only
if it is a Full payload since this value is used for the exponential
backoff algorithm used only for Full payloads. To overcome this,
this variable is renamed to "FullPayloadAttemptNumber" and the
new variable, tracking both delta and full payloads, is named
"PayloadAttemptNumber". The setter, getter and unittests are
updated as needed, and a new test is introduced to verify the
difference between these two variables when the involved payload
is a Delta payload.

BUG=chromium:226769
TEST=update_engine_unittests

Change-Id: I33d346d27ee8355f1152ebb829621571fe3a4244
Reviewed-on: https://gerrit.chromium.org/gerrit/60951
Reviewed-by: Alex Deymo <deymo@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
diff --git a/constants.cc b/constants.cc
index 7f14ddb..a85eb26 100644
--- a/constants.cc
+++ b/constants.cc
@@ -32,6 +32,7 @@
 const char kPrefsCurrentUrlFailureCount[] = "current-url-failure-count";
 const char kPrefsCurrentUrlIndex[] = "current-url-index";
 const char kPrefsDeltaUpdateFailures[] = "delta-update-failures";
+const char kPrefsFullPayloadAttemptNumber[] = "full-payload-attempt-number";
 const char kPrefsLastActivePingDay[] = "last-active-ping-day";
 const char kPrefsLastRollCallPingDay[] = "last-roll-call-ping-day";
 const char kPrefsManifestMetadataSize[] = "manifest-metadata-size";
diff --git a/constants.h b/constants.h
index c6ffb74..e930772 100644
--- a/constants.h
+++ b/constants.h
@@ -35,6 +35,7 @@
 extern const char kPrefsCurrentUrlFailureCount[];
 extern const char kPrefsCurrentUrlIndex[];
 extern const char kPrefsDeltaUpdateFailures[];
+extern const char kPrefsFullPayloadAttemptNumber[];
 extern const char kPrefsLastActivePingDay[];
 extern const char kPrefsLastRollCallPingDay[];
 extern const char kPrefsManifestMetadataSize[];
diff --git a/mock_payload_state.h b/mock_payload_state.h
index 8959ca1..02db28b 100644
--- a/mock_payload_state.h
+++ b/mock_payload_state.h
@@ -31,7 +31,8 @@
 
   // Getters.
   MOCK_METHOD0(GetResponseSignature, std::string());
-  MOCK_METHOD0(GetPayloadAttemptNumber, uint32_t());
+  MOCK_METHOD0(GetPayloadAttemptNumber, int());
+  MOCK_METHOD0(GetFullPayloadAttemptNumber, int());
   MOCK_METHOD0(GetCurrentUrl, std::string());
   MOCK_METHOD0(GetUrlFailureCount, uint32_t());
   MOCK_METHOD0(GetUrlSwitchCount, uint32_t());
diff --git a/payload_state.cc b/payload_state.cc
index a933ea5..6851fcb 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -26,7 +26,7 @@
 const TimeDelta PayloadState::kDurationSlack = TimeDelta::FromSeconds(600);
 
 // We want to upperbound backoffs to 16 days
-static const uint32_t kMaxBackoffDays = 16;
+static const int kMaxBackoffDays = 16;
 
 // We want to randomize retry attempts after the backoff by +/- 6 hours.
 static const uint32_t kMaxBackoffFuzzMinutes = 12 * 60;
@@ -34,6 +34,7 @@
 PayloadState::PayloadState()
     : prefs_(NULL),
       payload_attempt_number_(0),
+      full_payload_attempt_number_(0),
       url_index_(0),
       url_failure_count_(0),
       url_switch_count_(0) {
@@ -47,6 +48,7 @@
   powerwash_safe_prefs_ = system_state_->powerwash_safe_prefs();
   LoadResponseSignature();
   LoadPayloadAttemptNumber();
+  LoadFullPayloadAttemptNumber();
   LoadUrlIndex();
   LoadUrlFailureCount();
   LoadUrlSwitchCount();
@@ -110,6 +112,7 @@
 void PayloadState::DownloadComplete() {
   LOG(INFO) << "Payload downloaded successfully";
   IncrementPayloadAttemptNumber();
+  IncrementFullPayloadAttemptNumber();
 }
 
 void PayloadState::DownloadProgress(size_t count) {
@@ -157,6 +160,7 @@
   ReportDurationMetrics();
   ReportUpdatesAbandonedCountMetric();
   ReportPayloadTypeMetric();
+  ReportAttemptsCountMetrics();
 
   // Reset the number of responses seen since it counts from the last
   // successful update, e.g. now.
@@ -318,13 +322,19 @@
 }
 
 void PayloadState::IncrementPayloadAttemptNumber() {
+  // Update the payload attempt number for both payload types: full and delta.
+  SetPayloadAttemptNumber(GetPayloadAttemptNumber() + 1);
+}
+
+void PayloadState::IncrementFullPayloadAttemptNumber() {
+  // Update the payload attempt number for full payloads and the backoff time.
   if (response_.is_delta_payload) {
     LOG(INFO) << "Not incrementing payload attempt number for delta payloads";
     return;
   }
 
   LOG(INFO) << "Incrementing the payload attempt number";
-  SetPayloadAttemptNumber(GetPayloadAttemptNumber() + 1);
+  SetFullPayloadAttemptNumber(GetFullPayloadAttemptNumber() + 1);
   UpdateBackoffExpiryTime();
 }
 
@@ -338,7 +348,8 @@
               << "0 as we only have " << candidate_urls_.size()
               << " candidate URL(s)";
     SetUrlIndex(0);
-    IncrementPayloadAttemptNumber();
+  IncrementPayloadAttemptNumber();
+  IncrementFullPayloadAttemptNumber();
   }
 
   // If we have multiple URLs, record that we just switched to another one
@@ -369,20 +380,20 @@
     return;
   }
 
-  if (GetPayloadAttemptNumber() == 0) {
+  if (GetFullPayloadAttemptNumber() == 0) {
     SetBackoffExpiryTime(Time());
     return;
   }
 
   // Since we're doing left-shift below, make sure we don't shift more
-  // than this. E.g. if uint32_t is 4-bytes, don't left-shift more than 30 bits,
+  // than this. E.g. if int is 4-bytes, don't left-shift more than 30 bits,
   // since we don't expect value of kMaxBackoffDays to be more than 100 anyway.
-  uint32_t num_days = 1; // the value to be shifted.
-  const uint32_t kMaxShifts = (sizeof(num_days) * 8) - 2;
+  int num_days = 1; // the value to be shifted.
+  const int kMaxShifts = (sizeof(num_days) * 8) - 2;
 
   // Normal backoff days is 2 raised to (payload_attempt_number - 1).
   // E.g. if payload_attempt_number is over 30, limit power to 30.
-  uint32_t power = min(GetPayloadAttemptNumber() - 1, kMaxShifts);
+  int power = min(GetFullPayloadAttemptNumber() - 1, kMaxShifts);
 
   // The number of days is the minimum of 2 raised to (payload_attempt_number
   // - 1) or kMaxBackoffDays.
@@ -543,6 +554,7 @@
 
 void PayloadState::ResetPersistedState() {
   SetPayloadAttemptNumber(0);
+  SetFullPayloadAttemptNumber(0);
   SetUrlIndex(0);
   SetUrlFailureCount(0);
   SetUrlSwitchCount(0);
@@ -639,13 +651,27 @@
                                             false));
 }
 
-void PayloadState::SetPayloadAttemptNumber(uint32_t payload_attempt_number) {
+void PayloadState::LoadFullPayloadAttemptNumber() {
+  SetFullPayloadAttemptNumber(GetPersistedValue(kPrefsFullPayloadAttemptNumber,
+                                            false));
+}
+
+void PayloadState::SetPayloadAttemptNumber(int payload_attempt_number) {
   CHECK(prefs_);
   payload_attempt_number_ = payload_attempt_number;
   LOG(INFO) << "Payload Attempt Number = " << payload_attempt_number_;
   prefs_->SetInt64(kPrefsPayloadAttemptNumber, payload_attempt_number_);
 }
 
+void PayloadState::SetFullPayloadAttemptNumber(
+    int full_payload_attempt_number) {
+  CHECK(prefs_);
+  full_payload_attempt_number_ = full_payload_attempt_number;
+  LOG(INFO) << "Full Payload Attempt Number = " << full_payload_attempt_number_;
+  prefs_->SetInt64(kPrefsFullPayloadAttemptNumber,
+      full_payload_attempt_number_);
+}
+
 void PayloadState::LoadUrlIndex() {
   SetUrlIndex(GetPersistedValue(kPrefsCurrentUrlIndex, false));
 }
@@ -900,6 +926,21 @@
             << " for metric " <<  metric;
 }
 
+void PayloadState::ReportAttemptsCountMetrics() {
+  string metric;
+  int total_attempts = GetPayloadAttemptNumber();
+
+  metric = "Installer.AttemptsCount.Total";
+  system_state_->metrics_lib()->SendToUMA(
+       metric,
+       total_attempts,
+       1,      // min
+       50,     // max
+       kNumDefaultUmaBuckets);
+  LOG(INFO) << "Uploading " << total_attempts
+            << " for metric " <<  metric;
+}
+
 string PayloadState::GetPrefsKey(const string& prefix, DownloadSource source) {
   return prefix + "-from-" + utils::ToString(source);
 }
diff --git a/payload_state.h b/payload_state.h
index c4651e9..8c3133c 100644
--- a/payload_state.h
+++ b/payload_state.h
@@ -46,7 +46,11 @@
     return response_signature_;
   }
 
-  virtual inline uint32_t GetPayloadAttemptNumber() {
+  virtual inline int GetFullPayloadAttemptNumber() {
+    return full_payload_attempt_number_;
+  }
+
+  virtual inline int GetPayloadAttemptNumber() {
     return payload_attempt_number_;
   }
 
@@ -93,9 +97,12 @@
   }
 
  private:
+  // Increments the payload attempt number used for metrics.
+  void IncrementPayloadAttemptNumber();
+
   // Increments the payload attempt number which governs the backoff behavior
   // at the time of the next update check.
-  void IncrementPayloadAttemptNumber();
+  void IncrementFullPayloadAttemptNumber();
 
   // Advances the current URL index to the next available one. If all URLs have
   // been exhausted during the current payload download attempt (as indicated
@@ -137,6 +144,9 @@
   // Reports the metric related to the applied payload type.
   void ReportPayloadTypeMetric();
 
+  // Reports the various metrics related to update attempts counts.
+  void ReportAttemptsCountMetrics();
+
   // Resets all the persisted state values which are maintained relative to the
   // current response signature. The response signature itself is not reset.
   void ResetPersistedState();
@@ -166,10 +176,19 @@
   // Initializes the payload attempt number from the persisted state.
   void LoadPayloadAttemptNumber();
 
+  // Initializes the payload attempt number for full payloads from the persisted
+  // state.
+  void LoadFullPayloadAttemptNumber();
+
   // Sets the payload attempt number to the given value. Also persists the
   // value being set so that we resume from the same value in case of a process
   // restart.
-  void SetPayloadAttemptNumber(uint32_t payload_attempt_number);
+  void SetPayloadAttemptNumber(int payload_attempt_number);
+
+  // Sets the payload attempt number for full updates to the given value. Also
+  // persists the value being set so that we resume from the same value in case
+  // of a process restart.
+  void SetFullPayloadAttemptNumber(int payload_attempt_number);
 
   // Initializes the current URL index from the persisted state.
   void LoadUrlIndex();
@@ -330,12 +349,19 @@
   // process restart.
   std::string response_signature_;
 
+  // The number of times we've tried to download the payload. This is
+  // incremented each time we download the payload successsfully or when we
+  // exhaust all failure limits for all URLs and are about to wrap around back
+  // to the first URL.  Each update to this value is persisted so we resume from
+  // the same value in case of a process restart.
+  int payload_attempt_number_;
+
   // The number of times we've tried to download the payload in full. This is
   // incremented each time we download the payload in full successsfully or
   // when we exhaust all failure limits for all URLs and are about to wrap
   // around back to the first URL.  Each update to this value is persisted so
   // we resume from the same value in case of a process restart.
-  uint32_t payload_attempt_number_;
+  int full_payload_attempt_number_;
 
   // The index of the current URL.  This type is different from the one in the
   // accessor methods because PrefsInterface supports only int64_t but we want
diff --git a/payload_state_interface.h b/payload_state_interface.h
index 54c7c9f..4323b4f 100644
--- a/payload_state_interface.h
+++ b/payload_state_interface.h
@@ -70,7 +70,11 @@
   virtual std::string GetResponseSignature() = 0;
 
   // Returns the payload attempt number.
-  virtual uint32_t GetPayloadAttemptNumber() = 0;
+  virtual int GetPayloadAttemptNumber() = 0;
+
+  // Returns the payload attempt number of the attempted full payload. Returns
+  // 0 for delta payloads.
+  virtual int GetFullPayloadAttemptNumber() = 0;
 
   // Returns the current URL. Returns an empty string if there's no valid URL.
   virtual std::string GetCurrentUrl() = 0;
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index 9bd4c08..f94b756 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -101,6 +101,8 @@
   EXPECT_CALL(*prefs, SetInt64(_,_)).Times(AnyNumber());
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
     .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, 0)).Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0)).Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
@@ -145,6 +147,8 @@
   EXPECT_CALL(*prefs, SetInt64(_,_)).Times(AnyNumber());
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
     .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, 0))
     .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0))
@@ -194,6 +198,8 @@
   EXPECT_CALL(*prefs, SetInt64(_,_)).Times(AnyNumber());
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
     .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, 0))
     .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0))
@@ -240,6 +246,10 @@
     .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 1))
     .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 1))
+    .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, _)).Times(AtLeast(2));
 
   // Reboots will be set
@@ -328,6 +338,13 @@
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 2))
     .Times(AtLeast(1));
 
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(2));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 1))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 2))
+    .Times(AtLeast(1));
+
   EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, _)).Times(AtLeast(4));
 
   EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0)).Times(AtLeast(4));
@@ -364,6 +381,7 @@
   // This should advance the URL index.
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMismatch);
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
@@ -371,6 +389,7 @@
   // This should advance the failure count only.
   payload_state.UpdateFailed(kErrorCodeDownloadTransferError);
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(1, payload_state.GetUrlFailureCount());
   EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
@@ -378,6 +397,7 @@
   // This should advance the failure count only.
   payload_state.UpdateFailed(kErrorCodeDownloadTransferError);
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(2, payload_state.GetUrlFailureCount());
   EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
@@ -388,6 +408,7 @@
   // attempt number to be incremented.
   payload_state.UpdateFailed(kErrorCodeDownloadTransferError);
   EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(2, payload_state.GetUrlSwitchCount());
@@ -396,6 +417,7 @@
   // This should advance the URL index.
   payload_state.UpdateFailed(kErrorCodePayloadHashMismatchError);
   EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(3, payload_state.GetUrlSwitchCount());
@@ -405,6 +427,7 @@
   // wrap-around of URL index.
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMissingError);
   EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(2, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(4, payload_state.GetUrlSwitchCount());
@@ -414,6 +437,7 @@
   payload_state.UpdateFailed(static_cast<ErrorCode>(
       kErrorCodeOmahaRequestHTTPResponseBase + 404));
   EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(2, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(1, payload_state.GetUrlFailureCount());
   EXPECT_EQ(4, payload_state.GetUrlSwitchCount());
@@ -423,6 +447,7 @@
   // afterwards.
   payload_state.DownloadProgress(progress_bytes);
   EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(2, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(4, payload_state.GetUrlSwitchCount());
@@ -434,14 +459,16 @@
 
   // 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.GetFullPayloadAttemptNumber());
   EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
   EXPECT_FALSE(payload_state.ShouldBackoffDownload());
 }
 
-TEST(PayloadStateTest, PayloadAttemptNumberIncreasesOnSuccessfulDownload) {
+TEST(PayloadStateTest, PayloadAttemptNumberIncreasesOnSuccessfulFullDownload) {
   OmahaResponse response;
+  response.is_delta_payload = false;
   PayloadState payload_state;
   MockSystemState mock_system_state;
   NiceMock<PrefsMock>* prefs = mock_system_state.mock_prefs();
@@ -452,6 +479,11 @@
   EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 1))
     .Times(AtLeast(1));
 
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 1))
+    .Times(AtLeast(1));
+
   EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, _))
     .Times(AtLeast(2));
 
@@ -466,8 +498,50 @@
 
   // This should just advance the payload attempt number;
   EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
   payload_state.DownloadComplete();
   EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
+  EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
+  EXPECT_EQ(0, payload_state.GetUrlFailureCount());
+  EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
+}
+
+TEST(PayloadStateTest, PayloadAttemptNumberIncreasesOnSuccessfulDeltaDownload) {
+  OmahaResponse response;
+  response.is_delta_payload = true;
+  PayloadState payload_state;
+  MockSystemState mock_system_state;
+  NiceMock<PrefsMock>* prefs = mock_system_state.mock_prefs();
+
+  EXPECT_CALL(*prefs, SetInt64(_,_)).Times(AnyNumber());
+  EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsPayloadAttemptNumber, 1))
+    .Times(AtLeast(1));
+
+  // kPrefsFullPayloadAttemptNumber is not incremented for delta payloads.
+  EXPECT_CALL(*prefs, SetInt64(kPrefsFullPayloadAttemptNumber, 0))
+    .Times(AtLeast(1));
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, _))
+    .Times(1);
+
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0))
+    .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlFailureCount, 0))
+    .Times(AtLeast(1));
+
+  EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
+
+  SetupPayloadStateWith2Urls("Hash8593", true, &payload_state, &response);
+
+  // This should just advance the payload attempt number;
+  EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
+  payload_state.DownloadComplete();
+  EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
@@ -487,6 +561,7 @@
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMismatch);
   payload_state.UpdateFailed(kErrorCodeDownloadTransferError);
   EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_EQ("https://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(1, payload_state.GetUrlFailureCount());
   EXPECT_EQ(1, payload_state.GetUrlSwitchCount());
@@ -500,6 +575,8 @@
   EXPECT_CALL(*prefs2, GetInt64(_,_)).Times(AtLeast(1));
   EXPECT_CALL(*prefs2, GetInt64(kPrefsPayloadAttemptNumber, _))
     .Times(AtLeast(1));
+  EXPECT_CALL(*prefs2, GetInt64(kPrefsFullPayloadAttemptNumber, _))
+    .Times(AtLeast(1));
   EXPECT_CALL(*prefs2, GetInt64(kPrefsCurrentUrlIndex, _))
       .WillRepeatedly(DoAll(SetArgumentPointee<1>(2), Return(true)));
   EXPECT_CALL(*prefs2, GetInt64(kPrefsCurrentUrlFailureCount, _))
@@ -517,6 +594,7 @@
   // 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.GetFullPayloadAttemptNumber());
   EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
   EXPECT_EQ(0, payload_state.GetUrlFailureCount());
   EXPECT_EQ(0, payload_state.GetUrlSwitchCount());
@@ -534,7 +612,8 @@
   // Simulate a successful download and see that we're ready to download
   // again without any backoff as this is a delta payload.
   payload_state.DownloadComplete();
-  EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_FALSE(payload_state.ShouldBackoffDownload());
 
   // Simulate two failures (enough to cause payload backoff) and check
@@ -543,7 +622,8 @@
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMismatch);
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMismatch);
   EXPECT_EQ("http://test", payload_state.GetCurrentUrl());
-  EXPECT_EQ(0, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(0, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_FALSE(payload_state.ShouldBackoffDownload());
 }
 
@@ -551,7 +631,8 @@
                                      int expected_attempt_number,
                                      TimeDelta expected_days) {
   payload_state->DownloadComplete();
-  EXPECT_EQ(expected_attempt_number, payload_state->GetPayloadAttemptNumber());
+  EXPECT_EQ(expected_attempt_number,
+      payload_state->GetFullPayloadAttemptNumber());
   EXPECT_TRUE(payload_state->ShouldBackoffDownload());
   Time backoff_expiry_time = payload_state->GetBackoffExpiryTime();
   // Add 1 hour extra to the 6 hour fuzz check to tolerate edge cases.
@@ -598,6 +679,7 @@
   // again without any backoff.
   payload_state.DownloadComplete();
   EXPECT_EQ(1, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(1, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_FALSE(payload_state.ShouldBackoffDownload());
 
   // Test again, this time by simulating two errors that would cause
@@ -606,6 +688,7 @@
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMismatch);
   payload_state.UpdateFailed(kErrorCodeDownloadMetadataSignatureMismatch);
   EXPECT_EQ(2, payload_state.GetPayloadAttemptNumber());
+  EXPECT_EQ(2, payload_state.GetFullPayloadAttemptNumber());
   EXPECT_FALSE(payload_state.ShouldBackoffDownload());
 }
 
@@ -716,6 +799,8 @@
       "Installer.DownloadOverheadPercentage", 542, _, _, _));
   EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendEnumToUMA(
       "Installer.PayloadFormat", kPayloadTypeFull, kNumPayloadTypes));
+  EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+      "Installer.AttemptsCount.Total", 1, _, _, _));
 
   payload_state.UpdateSucceeded();
 
@@ -1054,7 +1139,8 @@
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
   SetupPayloadStateWith2Urls("Hash6437", true, &payload_state, &response);
 
-  // Mock the request to a request where the delta was disabled.
+  // Mock the request to a request where the delta is enabled, although the
+  // result is full.
   OmahaRequestParams params(&mock_system_state);
   params.set_delta_okay(true);
   mock_system_state.set_request_params(&params);