Collect metrics for android update attempt
am: 90aaa109e3

Change-Id: I2af42cd13eb5127ca4e5c49efa85999121b146bb
diff --git a/Android.mk b/Android.mk
index 0b6ee17..cc06643 100644
--- a/Android.mk
+++ b/Android.mk
@@ -982,6 +982,8 @@
     $(ue_libupdate_engine_android_exported_static_libraries:-host=)
 LOCAL_SHARED_LIBRARIES += \
     $(ue_libupdate_engine_android_exported_shared_libraries:-host=)
+LOCAL_SRC_FILES += \
+    update_attempter_android_unittest.cc
 endif  # local_use_omaha == 1
 ifeq ($(local_use_chrome_network_proxy),1)
 LOCAL_SRC_FILES += \
diff --git a/metrics_utils.cc b/metrics_utils.cc
index 263bacd..5cff293 100644
--- a/metrics_utils.cc
+++ b/metrics_utils.cc
@@ -21,7 +21,8 @@
 #include <base/time/time.h>
 
 #include "update_engine/common/clock_interface.h"
-#include "update_engine/common/prefs_interface.h"
+#include "update_engine/common/constants.h"
+#include "update_engine/common/utils.h"
 #include "update_engine/system_state.h"
 
 using base::Time;
@@ -305,5 +306,76 @@
   return ret;
 }
 
+int64_t GetPersistedValue(const std::string& key, PrefsInterface* prefs) {
+  CHECK(prefs);
+  if (!prefs->Exists(key))
+    return 0;
+
+  int64_t stored_value;
+  if (!prefs->GetInt64(key, &stored_value))
+    return 0;
+
+  if (stored_value < 0) {
+    LOG(ERROR) << key << ": Invalid value (" << stored_value
+               << ") in persisted state. Defaulting to 0";
+    return 0;
+  }
+
+  return stored_value;
+}
+
+void SetNumReboots(int64_t num_reboots, PrefsInterface* prefs) {
+  CHECK(prefs);
+  prefs->SetInt64(kPrefsNumReboots, num_reboots);
+  LOG(INFO) << "Number of Reboots during current update attempt = "
+            << num_reboots;
+}
+
+void SetPayloadAttemptNumber(int64_t payload_attempt_number,
+                             PrefsInterface* prefs) {
+  CHECK(prefs);
+  prefs->SetInt64(kPrefsPayloadAttemptNumber, payload_attempt_number);
+  LOG(INFO) << "Payload Attempt Number = " << payload_attempt_number;
+}
+
+void SetSystemUpdatedMarker(ClockInterface* clock, PrefsInterface* prefs) {
+  CHECK(prefs);
+  CHECK(clock);
+  Time update_finish_time = clock->GetMonotonicTime();
+  prefs->SetInt64(kPrefsSystemUpdatedMarker,
+                  update_finish_time.ToInternalValue());
+  LOG(INFO) << "Updated Marker = " << utils::ToString(update_finish_time);
+}
+
+void SetUpdateTimestampStart(const Time& update_start_time,
+                             PrefsInterface* prefs) {
+  CHECK(prefs);
+  prefs->SetInt64(kPrefsUpdateTimestampStart,
+                  update_start_time.ToInternalValue());
+  LOG(INFO) << "Update Timestamp Start = "
+            << utils::ToString(update_start_time);
+}
+
+bool LoadAndReportTimeToReboot(MetricsReporterInterface* metrics_reporter,
+                               PrefsInterface* prefs,
+                               ClockInterface* clock) {
+  CHECK(prefs);
+  CHECK(clock);
+  int64_t stored_value = GetPersistedValue(kPrefsSystemUpdatedMarker, prefs);
+  if (stored_value == 0)
+    return false;
+
+  Time system_updated_at = Time::FromInternalValue(stored_value);
+  base::TimeDelta time_to_reboot =
+      clock->GetMonotonicTime() - system_updated_at;
+  if (time_to_reboot.ToInternalValue() < 0) {
+    LOG(ERROR) << "time_to_reboot is negative - system_updated_at: "
+               << utils::ToString(system_updated_at);
+    return false;
+  }
+  metrics_reporter->ReportTimeToReboot(time_to_reboot.InMinutes());
+  return true;
+}
+
 }  // namespace metrics_utils
 }  // namespace chromeos_update_engine
diff --git a/metrics_utils.h b/metrics_utils.h
index 2d62dc0..d08cc4a 100644
--- a/metrics_utils.h
+++ b/metrics_utils.h
@@ -17,11 +17,16 @@
 #ifndef UPDATE_ENGINE_METRICS_UTILS_H_
 #define UPDATE_ENGINE_METRICS_UTILS_H_
 
+#include <string>
+
 #include <base/time/time.h>
 
+#include "update_engine/common/clock_interface.h"
 #include "update_engine/common/error_code.h"
+#include "update_engine/common/prefs_interface.h"
 #include "update_engine/connection_utils.h"
 #include "update_engine/metrics_constants.h"
+#include "update_engine/metrics_reporter_interface.h"
 
 namespace chromeos_update_engine {
 
@@ -68,6 +73,33 @@
                              int64_t* storage,
                              base::TimeDelta* out_duration);
 
+// Returns the persisted value from prefs for the given key. It also
+// validates that the value returned is non-negative.
+int64_t GetPersistedValue(const std::string& key, PrefsInterface* prefs);
+
+// Persists the reboot count of the update attempt to |kPrefsNumReboots|.
+void SetNumReboots(int64_t num_reboots, PrefsInterface* prefs);
+
+// Persists the payload attempt number to |kPrefsPayloadAttemptNumber|.
+void SetPayloadAttemptNumber(int64_t payload_attempt_number,
+                             PrefsInterface* prefs);
+
+// Persists the finished time of an update to the |kPrefsSystemUpdatedMarker|.
+void SetSystemUpdatedMarker(ClockInterface* clock, PrefsInterface* prefs);
+
+// Persists the start time of an update to |kPrefsUpdateTimestampStart|.
+void SetUpdateTimestampStart(const base::Time& update_start_time,
+                             PrefsInterface* prefs);
+
+// Called at program startup if the device booted into a new update.
+// The |time_to_reboot| parameter contains the (monotonic-clock) duration
+// from when the update successfully completed (the value in
+// |kPrefsSystemUpdatedMarker|) until the device was booted into the update
+// (current monotonic-clock time).
+bool LoadAndReportTimeToReboot(MetricsReporterInterface* metrics_reporter,
+                               PrefsInterface* prefs,
+                               ClockInterface* clock);
+
 }  // namespace metrics_utils
 }  // namespace chromeos_update_engine
 
diff --git a/payload_state.cc b/payload_state.cc
index 410a0bf..1ec32c5 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -45,6 +45,8 @@
 
 namespace chromeos_update_engine {
 
+using metrics_utils::GetPersistedValue;
+
 const TimeDelta PayloadState::kDurationSlack = TimeDelta::FromSeconds(600);
 
 // We want to upperbound backoffs to 16 days
@@ -247,7 +249,7 @@
   SetNumResponsesSeen(0);
   SetPayloadIndex(0);
 
-  CreateSystemUpdatedMarkerFile();
+  metrics_utils::SetSystemUpdatedMarker(system_state_->clock(), prefs_);
 }
 
 void PayloadState::UpdateFailed(ErrorCode error) {
@@ -762,11 +764,8 @@
 }
 
 void PayloadState::SetNumReboots(uint32_t num_reboots) {
-  CHECK(prefs_);
   num_reboots_ = num_reboots;
-  prefs_->SetInt64(kPrefsNumReboots, num_reboots);
-  LOG(INFO) << "Number of Reboots during current update attempt = "
-            << num_reboots_;
+  metrics_utils::SetNumReboots(num_reboots, prefs_);
 }
 
 void PayloadState::ResetPersistedState() {
@@ -803,24 +802,6 @@
   }
 }
 
-int64_t PayloadState::GetPersistedValue(const string& key) {
-  CHECK(prefs_);
-  if (!prefs_->Exists(key))
-    return 0;
-
-  int64_t stored_value;
-  if (!prefs_->GetInt64(key, &stored_value))
-    return 0;
-
-  if (stored_value < 0) {
-    LOG(ERROR) << key << ": Invalid value (" << stored_value
-               << ") in persisted state. Defaulting to 0";
-    return 0;
-  }
-
-  return stored_value;
-}
-
 string PayloadState::CalculateResponseSignature() {
   string response_sign;
   for (size_t i = 0; i < response_.packages.size(); i++) {
@@ -871,19 +852,18 @@
 }
 
 void PayloadState::LoadPayloadAttemptNumber() {
-  SetPayloadAttemptNumber(GetPersistedValue(kPrefsPayloadAttemptNumber));
+  SetPayloadAttemptNumber(
+      GetPersistedValue(kPrefsPayloadAttemptNumber, prefs_));
 }
 
 void PayloadState::LoadFullPayloadAttemptNumber() {
-  SetFullPayloadAttemptNumber(GetPersistedValue(
-      kPrefsFullPayloadAttemptNumber));
+  SetFullPayloadAttemptNumber(
+      GetPersistedValue(kPrefsFullPayloadAttemptNumber, prefs_));
 }
 
 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_);
+  metrics_utils::SetPayloadAttemptNumber(payload_attempt_number, prefs_);
 }
 
 void PayloadState::SetFullPayloadAttemptNumber(
@@ -910,7 +890,7 @@
 }
 
 void PayloadState::LoadUrlIndex() {
-  SetUrlIndex(GetPersistedValue(kPrefsCurrentUrlIndex));
+  SetUrlIndex(GetPersistedValue(kPrefsCurrentUrlIndex, prefs_));
 }
 
 void PayloadState::SetUrlIndex(uint32_t url_index) {
@@ -925,8 +905,8 @@
 }
 
 void PayloadState::LoadScatteringWaitPeriod() {
-  SetScatteringWaitPeriod(
-      TimeDelta::FromSeconds(GetPersistedValue(kPrefsWallClockWaitPeriod)));
+  SetScatteringWaitPeriod(TimeDelta::FromSeconds(
+      GetPersistedValue(kPrefsWallClockWaitPeriod, prefs_)));
 }
 
 void PayloadState::SetScatteringWaitPeriod(TimeDelta wait_period) {
@@ -943,7 +923,7 @@
 }
 
 void PayloadState::LoadUrlSwitchCount() {
-  SetUrlSwitchCount(GetPersistedValue(kPrefsUrlSwitchCount));
+  SetUrlSwitchCount(GetPersistedValue(kPrefsUrlSwitchCount, prefs_));
 }
 
 void PayloadState::SetUrlSwitchCount(uint32_t url_switch_count) {
@@ -954,7 +934,7 @@
 }
 
 void PayloadState::LoadUrlFailureCount() {
-  SetUrlFailureCount(GetPersistedValue(kPrefsCurrentUrlFailureCount));
+  SetUrlFailureCount(GetPersistedValue(kPrefsCurrentUrlFailureCount, prefs_));
 }
 
 void PayloadState::SetUrlFailureCount(uint32_t url_failure_count) {
@@ -1037,12 +1017,8 @@
 }
 
 void PayloadState::SetUpdateTimestampStart(const Time& value) {
-  CHECK(prefs_);
   update_timestamp_start_ = value;
-  prefs_->SetInt64(kPrefsUpdateTimestampStart,
-                   update_timestamp_start_.ToInternalValue());
-  LOG(INFO) << "Update Timestamp Start = "
-            << utils::ToString(update_timestamp_start_);
+  metrics_utils::SetUpdateTimestampStart(value, prefs_);
 }
 
 void PayloadState::SetUpdateTimestampEnd(const Time& value) {
@@ -1088,7 +1064,7 @@
 }
 
 void PayloadState::LoadNumReboots() {
-  SetNumReboots(GetPersistedValue(kPrefsNumReboots));
+  SetNumReboots(GetPersistedValue(kPrefsNumReboots, prefs_));
 }
 
 void PayloadState::LoadRollbackVersion() {
@@ -1140,7 +1116,7 @@
 
 void PayloadState::LoadCurrentBytesDownloaded(DownloadSource source) {
   string key = GetPrefsKey(kPrefsCurrentBytesDownloaded, source);
-  SetCurrentBytesDownloaded(source, GetPersistedValue(key), true);
+  SetCurrentBytesDownloaded(source, GetPersistedValue(key, prefs_), true);
 }
 
 void PayloadState::SetCurrentBytesDownloaded(
@@ -1164,7 +1140,7 @@
 
 void PayloadState::LoadTotalBytesDownloaded(DownloadSource source) {
   string key = GetPrefsKey(kPrefsTotalBytesDownloaded, source);
-  SetTotalBytesDownloaded(source, GetPersistedValue(key), true);
+  SetTotalBytesDownloaded(source, GetPersistedValue(key, prefs_), true);
 }
 
 void PayloadState::SetTotalBytesDownloaded(
@@ -1188,7 +1164,7 @@
 }
 
 void PayloadState::LoadNumResponsesSeen() {
-  SetNumResponsesSeen(GetPersistedValue(kPrefsNumResponsesSeen));
+  SetNumResponsesSeen(GetPersistedValue(kPrefsNumResponsesSeen, prefs_));
 }
 
 void PayloadState::SetNumResponsesSeen(int num_responses_seen) {
@@ -1229,18 +1205,6 @@
   }
 }
 
-void PayloadState::CreateSystemUpdatedMarkerFile() {
-  CHECK(prefs_);
-  int64_t value = system_state_->clock()->GetWallclockTime().ToInternalValue();
-  prefs_->SetInt64(kPrefsSystemUpdatedMarker, value);
-}
-
-void PayloadState::BootedIntoUpdate(TimeDelta time_to_reboot) {
-  // Send |time_to_reboot| as a UMA stat.
-  system_state_->metrics_reporter()->ReportTimeToReboot(
-      time_to_reboot.InMinutes());
-}
-
 void PayloadState::UpdateEngineStarted() {
   // Flush previous state from abnormal attempt failure, if any.
   ReportAndClearPersistedAttemptMetrics();
@@ -1250,24 +1214,11 @@
   if (!system_state_->system_rebooted())
     return;
 
-  // Figure out if we just booted into a new update
-  if (prefs_->Exists(kPrefsSystemUpdatedMarker)) {
-    int64_t stored_value;
-    if (prefs_->GetInt64(kPrefsSystemUpdatedMarker, &stored_value)) {
-      Time system_updated_at = Time::FromInternalValue(stored_value);
-      if (!system_updated_at.is_null()) {
-        TimeDelta time_to_reboot =
-            system_state_->clock()->GetWallclockTime() - system_updated_at;
-        if (time_to_reboot.ToInternalValue() < 0) {
-          LOG(ERROR) << "time_to_reboot is negative - system_updated_at: "
-                     << utils::ToString(system_updated_at);
-        } else {
-          BootedIntoUpdate(time_to_reboot);
-        }
-      }
-    }
-    prefs_->Delete(kPrefsSystemUpdatedMarker);
-  }
+  // Report time_to_reboot if we booted into a new update.
+  metrics_utils::LoadAndReportTimeToReboot(
+      system_state_->metrics_reporter(), prefs_, system_state_->clock());
+  prefs_->Delete(kPrefsSystemUpdatedMarker);
+
   // Check if it is needed to send metrics about a failed reboot into a new
   // version.
   ReportFailedBootIfNeeded();
@@ -1359,7 +1310,7 @@
 }
 
 void PayloadState::LoadP2PNumAttempts() {
-  SetP2PNumAttempts(GetPersistedValue(kPrefsP2PNumAttempts));
+  SetP2PNumAttempts(GetPersistedValue(kPrefsP2PNumAttempts, prefs_));
 }
 
 Time PayloadState::GetP2PFirstAttemptTimestamp() {
@@ -1376,7 +1327,8 @@
 }
 
 void PayloadState::LoadP2PFirstAttemptTimestamp() {
-  int64_t stored_value = GetPersistedValue(kPrefsP2PFirstAttemptTimestamp);
+  int64_t stored_value =
+      GetPersistedValue(kPrefsP2PFirstAttemptTimestamp, prefs_);
   Time stored_time = Time::FromInternalValue(stored_value);
   SetP2PFirstAttemptTimestamp(stored_time);
 }
diff --git a/payload_state.h b/payload_state.h
index 98f21e8..24e9900 100644
--- a/payload_state.h
+++ b/payload_state.h
@@ -17,6 +17,7 @@
 #ifndef UPDATE_ENGINE_PAYLOAD_STATE_H_
 #define UPDATE_ENGINE_PAYLOAD_STATE_H_
 
+#include <algorithm>
 #include <string>
 #include <vector>
 
@@ -240,10 +241,6 @@
   // reset on a new update.
   void ResetDownloadSourcesOnNewUpdate();
 
-  // Returns the persisted value from prefs_ for the given key. It also
-  // validates that the value returned is non-negative.
-  int64_t GetPersistedValue(const std::string& key);
-
   // Calculates the response "signature", which is basically a string composed
   // of the subset of the fields in the current response that affect the
   // behavior of the PayloadState.
@@ -404,16 +401,7 @@
   // increments num_reboots.
   void UpdateNumReboots();
 
-  // Writes the current wall-clock time to the kPrefsSystemUpdatedMarker
-  // state variable.
-  void CreateSystemUpdatedMarkerFile();
 
-  // Called at program startup if the device booted into a new update.
-  // The |time_to_reboot| parameter contains the (wall-clock) duration
-  // from when the update successfully completed (the value written
-  // into the kPrefsSystemUpdatedMarker state variable) until the device
-  // was booted into the update (current wall-clock time).
-  void BootedIntoUpdate(base::TimeDelta time_to_reboot);
 
   // Loads the |kPrefsP2PFirstAttemptTimestamp| state variable from disk
   // into |p2p_first_attempt_timestamp_|.
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index a69c5ac..c47e389 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -1095,8 +1095,8 @@
   FakePrefs fake_prefs;
 
   // Set the clock to a well-known time (t = 30 seconds).
-  fake_clock.SetWallclockTime(Time::FromInternalValue(
-      30 * Time::kMicrosecondsPerSecond));
+  fake_clock.SetMonotonicTime(
+      Time::FromInternalValue(30 * Time::kMicrosecondsPerSecond));
 
   fake_system_state.set_clock(&fake_clock);
   fake_system_state.set_prefs(&fake_prefs);
@@ -1114,8 +1114,8 @@
   // (t = 500 seconds). We do this by using a new PayloadState object
   // and checking that it emits the right UMA metric with the right
   // value.
-  fake_clock.SetWallclockTime(Time::FromInternalValue(
-      500 * Time::kMicrosecondsPerSecond));
+  fake_clock.SetMonotonicTime(
+      Time::FromInternalValue(500 * Time::kMicrosecondsPerSecond));
   PayloadState payload_state2;
   EXPECT_TRUE(payload_state2.Initialize(&fake_system_state));
 
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index f6b9702..22bb4c2 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -18,8 +18,10 @@
 
 #include <algorithm>
 #include <map>
+#include <memory>
 #include <utility>
 
+#include <android-base/properties.h>
 #include <base/bind.h>
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
@@ -47,6 +49,7 @@
 #endif
 
 using base::Bind;
+using base::Time;
 using base::TimeDelta;
 using base::TimeTicks;
 using std::shared_ptr;
@@ -89,6 +92,7 @@
       boot_control_(boot_control),
       hardware_(hardware),
       processor_(new ActionProcessor()),
+      clock_(new Clock()),
       metrics_reporter_(new MetricsReporterAndroid()) {
   network_selector_ = network::CreateNetworkSelector();
 }
@@ -102,10 +106,12 @@
 void UpdateAttempterAndroid::Init() {
   // In case of update_engine restart without a reboot we need to restore the
   // reboot needed state.
-  if (UpdateCompletedOnThisBoot())
+  if (UpdateCompletedOnThisBoot()) {
     SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
-  else
+  } else {
     SetStatusAndNotify(UpdateStatus::IDLE);
+    UpdatePrefsAndReportUpdateMetricsOnReboot();
+  }
 }
 
 bool UpdateAttempterAndroid::ApplyPayload(
@@ -225,6 +231,10 @@
   // Just in case we didn't update boot flags yet, make sure they're updated
   // before any update processing starts. This will start the update process.
   UpdateBootFlags();
+
+  UpdatePrefsOnUpdateStart(install_plan_.is_resume);
+  // TODO(xunchang) report the metrics for unresumable updates
+
   return true;
 }
 
@@ -262,6 +272,7 @@
       // after resetting to idle state, it doesn't go back to
       // UpdateStatus::UPDATED_NEED_REBOOT state.
       bool ret_value = prefs_->Delete(kPrefsUpdateCompletedOnBootId);
+      ClearMetricsPrefs();
 
       // Update the boot flags so the current slot has higher priority.
       if (!boot_control_->SetActiveBootSlot(boot_control_->GetCurrentSlot()))
@@ -431,22 +442,11 @@
   for (auto observer : daemon_state_->service_observers())
     observer->SendPayloadApplicationComplete(error_code);
 
-  // TODO(xunchang): assign proper values to the metrics.
-  PayloadType payload_type = kNumPayloadTypes;
-  metrics::AttemptResult attempt_result =
-      metrics_utils::GetAttemptResult(error_code);
-  TimeDelta duration;
-  TimeDelta duration_uptime;
-  // Report the android metrics when we terminate the update. Currently we are
-  // reporting error_code only.
-  metrics_reporter_->ReportUpdateAttemptMetrics(nullptr,  // system_state
-                                                0,        // attempt_number
-                                                payload_type,
-                                                duration,
-                                                duration_uptime,
-                                                0,  // payload_size
-                                                attempt_result,
-                                                error_code);
+  CollectAndReportUpdateMetricsOnUpdateFinished(error_code);
+  ClearMetricsPrefs();
+  if (error_code == ErrorCode::kSuccess) {
+    metrics_utils::SetSystemUpdatedMarker(clock_.get(), prefs_);
+  }
 }
 
 void UpdateAttempterAndroid::SetStatusAndNotify(UpdateStatus status) {
@@ -540,4 +540,126 @@
           update_completed_on_boot_id == boot_id);
 }
 
+// Collect and report the android metrics when we terminate the update.
+void UpdateAttempterAndroid::CollectAndReportUpdateMetricsOnUpdateFinished(
+    ErrorCode error_code) {
+  int64_t attempt_number =
+      metrics_utils::GetPersistedValue(kPrefsPayloadAttemptNumber, prefs_);
+  PayloadType payload_type = kPayloadTypeFull;
+  int64_t payload_size = 0;
+  for (const auto& p : install_plan_.payloads) {
+    if (p.type == InstallPayloadType::kDelta)
+      payload_type = kPayloadTypeDelta;
+    payload_size += p.size;
+  }
+
+  metrics::AttemptResult attempt_result =
+      metrics_utils::GetAttemptResult(error_code);
+  Time attempt_start_time = Time::FromInternalValue(
+      metrics_utils::GetPersistedValue(kPrefsUpdateTimestampStart, prefs_));
+  TimeDelta duration_uptime = clock_->GetMonotonicTime() - attempt_start_time;
+
+  metrics_reporter_->ReportUpdateAttemptMetrics(
+      nullptr,  // system_state
+      static_cast<int>(attempt_number),
+      payload_type,
+      TimeDelta(),
+      duration_uptime,
+      payload_size,
+      attempt_result,
+      error_code);
+
+  if (error_code == ErrorCode::kSuccess) {
+    int64_t reboot_count =
+        metrics_utils::GetPersistedValue(kPrefsNumReboots, prefs_);
+    string build_version;
+    prefs_->GetString(kPrefsPreviousVersion, &build_version);
+    metrics_reporter_->ReportSuccessfulUpdateMetrics(
+        static_cast<int>(attempt_number),
+        0,  // update abandoned count
+        payload_type,
+        payload_size,
+        nullptr,  // num bytes downloaded
+        0,        // download overhead percentage
+        duration_uptime,
+        static_cast<int>(reboot_count),
+        0);  // url_switch_count
+  }
+}
+
+void UpdateAttempterAndroid::UpdatePrefsAndReportUpdateMetricsOnReboot() {
+  string current_boot_id;
+  TEST_AND_RETURN(utils::GetBootId(&current_boot_id));
+  // Example: [ro.build.version.incremental]: [4292972]
+  string current_version =
+      android::base::GetProperty("ro.build.version.incremental", "");
+  TEST_AND_RETURN(!current_version.empty());
+
+  // If there's no record of previous version (e.g. due to a data wipe), we
+  // save the info of current boot and skip the metrics report.
+  if (!prefs_->Exists(kPrefsPreviousVersion)) {
+    prefs_->SetString(kPrefsBootId, current_boot_id);
+    prefs_->SetString(kPrefsPreviousVersion, current_version);
+    ClearMetricsPrefs();
+    return;
+  }
+  string previous_version;
+  // update_engine restarted under the same build.
+  // TODO(xunchang) identify and report rollback by checking UpdateMarker.
+  if (prefs_->GetString(kPrefsPreviousVersion, &previous_version) &&
+      previous_version == current_version) {
+    string last_boot_id;
+    bool is_reboot = prefs_->Exists(kPrefsBootId) &&
+                     (prefs_->GetString(kPrefsBootId, &last_boot_id) &&
+                      last_boot_id != current_boot_id);
+    // Increment the reboot number if |kPrefsNumReboots| exists. That pref is
+    // set when we start a new update.
+    if (is_reboot && prefs_->Exists(kPrefsNumReboots)) {
+      prefs_->SetString(kPrefsBootId, current_boot_id);
+      int64_t reboot_count =
+          metrics_utils::GetPersistedValue(kPrefsNumReboots, prefs_);
+      metrics_utils::SetNumReboots(reboot_count + 1, prefs_);
+    }
+    return;
+  }
+
+  // Now that the build version changes, report the update metrics.
+  // TODO(xunchang) check the build version is larger than the previous one.
+  prefs_->SetString(kPrefsBootId, current_boot_id);
+  prefs_->SetString(kPrefsPreviousVersion, current_version);
+
+  bool previous_attempt_exists = prefs_->Exists(kPrefsPayloadAttemptNumber);
+  // |kPrefsPayloadAttemptNumber| should be cleared upon successful update.
+  if (previous_attempt_exists) {
+    metrics_reporter_->ReportAbnormallyTerminatedUpdateAttemptMetrics();
+  }
+
+  metrics_utils::LoadAndReportTimeToReboot(
+      metrics_reporter_.get(), prefs_, clock_.get());
+  ClearMetricsPrefs();
+}
+
+// Save the update start time. Reset the reboot count and attempt number if the
+// update isn't a resume; otherwise increment the attempt number.
+void UpdateAttempterAndroid::UpdatePrefsOnUpdateStart(bool is_resume) {
+  if (!is_resume) {
+    metrics_utils::SetNumReboots(0, prefs_);
+    metrics_utils::SetPayloadAttemptNumber(1, prefs_);
+  } else {
+    int64_t attempt_number =
+        metrics_utils::GetPersistedValue(kPrefsPayloadAttemptNumber, prefs_);
+    metrics_utils::SetPayloadAttemptNumber(attempt_number + 1, prefs_);
+  }
+  Time update_start_time = clock_->GetMonotonicTime();
+  metrics_utils::SetUpdateTimestampStart(update_start_time, prefs_);
+}
+
+void UpdateAttempterAndroid::ClearMetricsPrefs() {
+  CHECK(prefs_);
+  prefs_->Delete(kPrefsNumReboots);
+  prefs_->Delete(kPrefsPayloadAttemptNumber);
+  prefs_->Delete(kPrefsSystemUpdatedMarker);
+  prefs_->Delete(kPrefsUpdateTimestampStart);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/update_attempter_android.h b/update_attempter_android.h
index a347e52..911ab81 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -28,10 +28,12 @@
 #include "update_engine/client_library/include/update_engine/update_status.h"
 #include "update_engine/common/action_processor.h"
 #include "update_engine/common/boot_control_interface.h"
+#include "update_engine/common/clock.h"
 #include "update_engine/common/hardware_interface.h"
 #include "update_engine/common/prefs_interface.h"
 #include "update_engine/daemon_state_interface.h"
 #include "update_engine/metrics_reporter_interface.h"
+#include "update_engine/metrics_utils.h"
 #include "update_engine/network_selector_interface.h"
 #include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/postinstall_runner_action.h"
@@ -87,6 +89,8 @@
   void ProgressUpdate(double progress) override;
 
  private:
+  friend class UpdateAttempterAndroidTest;
+
   // Asynchronously marks the current slot as successful if needed. If already
   // marked as good, CompleteUpdateBootFlags() is called starting the action
   // processor.
@@ -118,6 +122,36 @@
   // Returns whether an update was completed in the current boot.
   bool UpdateCompletedOnThisBoot();
 
+  // Prefs to use for metrics report
+  // |kPrefsPayloadAttemptNumber|: number of update attempts for the current
+  // payload_id.
+  // |KprefsNumReboots|: number of reboots when applying the current update.
+  // |kPrefsSystemUpdatedMarker|: end timestamp of the last successful update.
+  // |kPrefsUpdateTimestampStart|: start timestamp of the current update.
+
+  // Metrics report function to call:
+  //   |ReportUpdateAttemptMetrics|
+  //   |ReportSuccessfulUpdateMetrics|
+  // Prefs to update:
+  //   |kPrefsSystemUpdatedMarker|
+  void CollectAndReportUpdateMetricsOnUpdateFinished(ErrorCode error_code);
+
+  // Metrics report function to call:
+  //   |ReportAbnormallyTerminatedUpdateAttemptMetrics|
+  //   |ReportTimeToRebootMetrics|
+  // Prefs to update:
+  //   |kPrefsBootId|, |kPrefsPreviousVersion|
+  void UpdatePrefsAndReportUpdateMetricsOnReboot();
+
+  // Prefs to update:
+  //   |kPrefsPayloadAttemptNumber|, |kPrefsUpdateTimestampStart|
+  void UpdatePrefsOnUpdateStart(bool is_resume);
+
+  // Prefs to delete:
+  //   |kPrefsNumReboots|, |kPrefsPayloadAttemptNumber|,
+  //   |kPrefsSystemUpdatedMarker|, |kPrefsUpdateTimestampStart|
+  void ClearMetricsPrefs();
+
   DaemonStateInterface* daemon_state_;
 
   // DaemonStateAndroid pointers.
@@ -163,6 +197,8 @@
   // before applying an update to the other slot.
   bool updated_boot_flags_ = false;
 
+  std::unique_ptr<ClockInterface> clock_;
+
   std::unique_ptr<MetricsReporterInterface> metrics_reporter_;
 
   DISALLOW_COPY_AND_ASSIGN(UpdateAttempterAndroid);
diff --git a/update_attempter_android_unittest.cc b/update_attempter_android_unittest.cc
new file mode 100644
index 0000000..6c0718a
--- /dev/null
+++ b/update_attempter_android_unittest.cc
@@ -0,0 +1,150 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/update_attempter_android.h"
+
+#include <memory>
+#include <string>
+
+#include <android-base/properties.h>
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/common/fake_clock.h"
+#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/fake_prefs.h"
+#include "update_engine/common/mock_action_processor.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/daemon_state_android.h"
+#include "update_engine/mock_metrics_reporter.h"
+
+using base::Time;
+using base::TimeDelta;
+using testing::_;
+using update_engine::UpdateStatus;
+
+namespace chromeos_update_engine {
+class UpdateAttempterAndroidTest : public ::testing::Test {
+ protected:
+  UpdateAttempterAndroidTest() = default;
+
+  void SetUp() override {
+    clock_ = new FakeClock();
+    metrics_reporter_ = new testing::NiceMock<MockMetricsReporter>();
+    update_attempter_android_.metrics_reporter_.reset(metrics_reporter_);
+    update_attempter_android_.clock_.reset(clock_);
+    update_attempter_android_.processor_.reset(
+        new testing::NiceMock<MockActionProcessor>());
+  }
+
+  void SetUpdateStatus(update_engine::UpdateStatus status) {
+    update_attempter_android_.status_ = status;
+  }
+
+  UpdateAttempterAndroid update_attempter_android_{
+      &daemon_state_, &prefs_, &boot_control_, &hardware_};
+
+  DaemonStateAndroid daemon_state_;
+  FakePrefs prefs_;
+  FakeBootControl boot_control_;
+  FakeHardware hardware_;
+
+  FakeClock* clock_;
+  testing::NiceMock<MockMetricsReporter>* metrics_reporter_;
+};
+
+TEST_F(UpdateAttempterAndroidTest, UpdatePrefsSameBuildVersionOnInit) {
+  std::string build_version =
+      android::base::GetProperty("ro.build.version.incremental", "");
+  prefs_.SetString(kPrefsPreviousVersion, build_version);
+  prefs_.SetString(kPrefsBootId, "oldboot");
+  prefs_.SetInt64(kPrefsNumReboots, 1);
+
+  EXPECT_CALL(*metrics_reporter_, ReportTimeToReboot(_)).Times(0);
+  update_attempter_android_.Init();
+
+  // Check that the boot_id and reboot_count are updated.
+  std::string boot_id;
+  utils::GetBootId(&boot_id);
+  EXPECT_TRUE(prefs_.Exists(kPrefsBootId));
+  std::string prefs_boot_id;
+  EXPECT_TRUE(prefs_.GetString(kPrefsBootId, &prefs_boot_id));
+  EXPECT_EQ(boot_id, prefs_boot_id);
+
+  EXPECT_TRUE(prefs_.Exists(kPrefsNumReboots));
+  int64_t reboot_count;
+  EXPECT_TRUE(prefs_.GetInt64(kPrefsNumReboots, &reboot_count));
+  EXPECT_EQ(2, reboot_count);
+}
+
+TEST_F(UpdateAttempterAndroidTest, UpdatePrefsBuildVersionChangeOnInit) {
+  prefs_.SetString(kPrefsPreviousVersion, "00001");  // Set the fake version
+  prefs_.SetInt64(kPrefsPayloadAttemptNumber, 1);
+  prefs_.SetInt64(kPrefsSystemUpdatedMarker, 23456);
+
+  EXPECT_CALL(*metrics_reporter_,
+              ReportAbnormallyTerminatedUpdateAttemptMetrics())
+      .Times(1);
+
+  Time now = Time::FromInternalValue(34456);
+  clock_->SetMonotonicTime(now);
+  TimeDelta duration = now - Time::FromInternalValue(23456);
+  EXPECT_CALL(*metrics_reporter_, ReportTimeToReboot(duration.InMinutes()))
+      .Times(1);
+
+  update_attempter_android_.Init();
+  // Check that we reset the metric prefs.
+  EXPECT_FALSE(prefs_.Exists(kPrefsNumReboots));
+  EXPECT_FALSE(prefs_.Exists(kPrefsPayloadAttemptNumber));
+  EXPECT_FALSE(prefs_.Exists(kPrefsUpdateTimestampStart));
+  EXPECT_FALSE(prefs_.Exists(kPrefsSystemUpdatedMarker));
+}
+
+TEST_F(UpdateAttempterAndroidTest, ReportMetricsOnUpdateTerminated) {
+  prefs_.SetInt64(kPrefsNumReboots, 3);
+  prefs_.SetInt64(kPrefsPayloadAttemptNumber, 2);
+  prefs_.SetString(kPrefsPreviousVersion, "56789");
+  prefs_.SetInt64(kPrefsUpdateTimestampStart, 12345);
+
+  Time now = Time::FromInternalValue(22345);
+  clock_->SetMonotonicTime(now);
+  TimeDelta duration = now - Time::FromInternalValue(12345);
+  EXPECT_CALL(
+      *metrics_reporter_,
+      ReportUpdateAttemptMetrics(_,
+                                 2,
+                                 _,
+                                 _,
+                                 duration,
+                                 _,
+                                 metrics::AttemptResult::kUpdateSucceeded,
+                                 ErrorCode::kSuccess))
+      .Times(1);
+  EXPECT_CALL(*metrics_reporter_,
+              ReportSuccessfulUpdateMetrics(2, 0, _, _, _, _, duration, 3, _))
+      .Times(1);
+
+  SetUpdateStatus(UpdateStatus::UPDATE_AVAILABLE);
+  update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+
+  EXPECT_FALSE(prefs_.Exists(kPrefsNumReboots));
+  EXPECT_FALSE(prefs_.Exists(kPrefsPayloadAttemptNumber));
+  EXPECT_FALSE(prefs_.Exists(kPrefsUpdateTimestampStart));
+  EXPECT_TRUE(prefs_.Exists(kPrefsSystemUpdatedMarker));
+}
+
+}  // namespace chromeos_update_engine