Add update reboot metric to the update engine.

This change add the Installer.UpdateNumReboots metric.

This records the number of reboots that occurred while an update was being
attempted. It uses a marker file stored in tmp to discover whether or not
it's already recorded the reboot.

BUG=chromium:226766
TEST=Unittests | ran an update on a test machine and rebooted/resumed and
checked about:histograms to confirm numbers. Also restart update-engine to
verify it didn't double count that.

Change-Id: I5d2af9d5b62a9d974c7c6243a89cb3359051b650
Reviewed-on: https://gerrit.chromium.org/gerrit/47710
Tested-by: Chris Sosa <sosa@chromium.org>
Commit-Queue: Chris Sosa <sosa@chromium.org>
Reviewed-by: Chris Sosa <sosa@chromium.org>
diff --git a/constants.cc b/constants.cc
index 0bc95f7..3609a02 100644
--- a/constants.cc
+++ b/constants.cc
@@ -9,8 +9,12 @@
 const char kPowerwashMarkerFile[] =
   "/mnt/stateful_partition/factory_install_reset";
 
+const char kSystemRebootedMarkerFile[] = "/tmp/update_engine_update_recorded";
+
 const char kPowerwashCommand[] = "safe fast\n";
 
+const char kStatefulPartition[] = "/mnt/stateful_partition";
+
 // Constants defining keys for the persisted state of update engine.
 const char kPrefsBackoffExpiryTime[] = "backoff-expiry-time";
 const char kPrefsCertificateReportToSendDownload[] =
@@ -25,6 +29,7 @@
 const char kPrefsLastActivePingDay[] = "last-active-ping-day";
 const char kPrefsLastRollCallPingDay[] = "last-roll-call-ping-day";
 const char kPrefsManifestMetadataSize[] = "manifest-metadata-size";
+const char kPrefsNumReboots[] = "num-reboots";
 const char kPrefsPayloadAttemptNumber[] = "payload-attempt-number";
 const char kPrefsPreviousVersion[] = "previous-version";
 const char kPrefsResumedUpdateFailures[] = "resumed-update-failures";
diff --git a/constants.h b/constants.h
index 3b8a918..e97ec90 100644
--- a/constants.h
+++ b/constants.h
@@ -11,9 +11,15 @@
 // completes successfully so that the device is powerwashed on next reboot.
 extern const char kPowerwashMarkerFile[];
 
+// Path to the marker file we use to indicate we've recorded a system reboot.
+extern const char kSystemRebootedMarkerFile[];
+
 // The contents of the powerwash marker file.
 extern const char kPowerwashCommand[];
 
+// Path to the stateful partition on the root filesystem.
+extern const char kStatefulPartition[];
+
 // Constants related to preferences.
 extern const char kPrefsBackoffExpiryTime[];
 extern const char kPrefsCertificateReportToSendDownload[];
@@ -26,6 +32,7 @@
 extern const char kPrefsLastActivePingDay[];
 extern const char kPrefsLastRollCallPingDay[];
 extern const char kPrefsManifestMetadataSize[];
+extern const char kPrefsNumReboots[];
 extern const char kPrefsPayloadAttemptNumber[];
 extern const char kPrefsPreviousVersion[];
 extern const char kPrefsResumedUpdateFailures[];
diff --git a/integration_unittest.cc b/integration_unittest.cc
index f42b89e..e54c0e2 100644
--- a/integration_unittest.cc
+++ b/integration_unittest.cc
@@ -7,6 +7,7 @@
 #include <glib.h>
 #include <pthread.h>
 #include <gtest/gtest.h>
+#include "update_engine/constants.h"
 #include "update_engine/download_action.h"
 #include "update_engine/install_action.h"
 #include "update_engine/libcurl_http_fetcher.h"
@@ -134,7 +135,7 @@
   ASSERT_EQ(0, system("rm -f /tmp/update_engine_test_postinst_out.txt"));
   ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
   ASSERT_EQ(0, system((string("mkdir -p ") + kTestDir +
-                       utils::kStatefulPartition +
+                       kStatefulPartition +
                        "/etc").c_str()));
   ASSERT_TRUE(WriteFileString(string(kTestDir) + "/etc/lsb-release",
                               "GOOGLE_RELEASE=0.2.0.0\n"
diff --git a/mock_payload_state.h b/mock_payload_state.h
index fe744de..88578b5 100644
--- a/mock_payload_state.h
+++ b/mock_payload_state.h
@@ -21,6 +21,7 @@
   MOCK_METHOD1(SetResponse, void(const OmahaResponse& response));
   MOCK_METHOD0(DownloadComplete, void());
   MOCK_METHOD1(DownloadProgress, void(size_t count));
+  MOCK_METHOD0(UpdateResumed, void());
   MOCK_METHOD0(UpdateRestarted, void());
   MOCK_METHOD0(UpdateSucceeded, void());
   MOCK_METHOD1(UpdateFailed, void(ActionExitCode error));
@@ -37,6 +38,7 @@
   MOCK_METHOD0(GetUpdateDurationUptime, base::TimeDelta());
   MOCK_METHOD1(GetCurrentBytesDownloaded, uint64_t(DownloadSource source));
   MOCK_METHOD1(GetTotalBytesDownloaded, uint64_t(DownloadSource source));
+  MOCK_METHOD0(GetNumReboots, uint32_t());
 };
 
 }  // namespace chromeos_update_engine
diff --git a/mock_system_state.h b/mock_system_state.h
index b7f9211..ac93d16 100644
--- a/mock_system_state.h
+++ b/mock_system_state.h
@@ -32,6 +32,7 @@
 
   MOCK_METHOD1(set_device_policy, void(const policy::DevicePolicy*));
   MOCK_CONST_METHOD0(device_policy, const policy::DevicePolicy*());
+  MOCK_METHOD0(system_rebooted, bool());
 
   inline virtual ConnectionManager* connection_manager() {
     return connection_manager_;
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index e550070..c3a3f1b 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -1479,7 +1479,7 @@
   const string kTestDir = "omaha_request_action-test";
   ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
   ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir +
-                        utils::kStatefulPartition + "/etc"));
+                      kStatefulPartition + "/etc"));
   vector<char> post_data;
   NiceMock<PrefsMock> prefs;
   ASSERT_TRUE(WriteFileString(
@@ -1488,7 +1488,7 @@
       "CHROMEOS_BOARD_APPID={22222222-2222-2222-2222-222222222222}\n"
       "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
   ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      kTestDir + kStatefulPartition + "/etc/lsb-release",
       "CHROMEOS_IS_POWERWASH_ALLOWED=true\n"
       "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
   OmahaRequestParams params = kDefaultTestParams;
@@ -1519,7 +1519,7 @@
   const string kTestDir = "omaha_request_action-test";
   ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
   ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir +
-                        utils::kStatefulPartition + "/etc"));
+                      kStatefulPartition + "/etc"));
   vector<char> post_data;
   NiceMock<PrefsMock> prefs;
   ASSERT_TRUE(WriteFileString(
@@ -1528,7 +1528,7 @@
       "CHROMEOS_BOARD_APPID={22222222-2222-2222-2222-222222222222}\n"
       "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
   ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      kTestDir + kStatefulPartition + "/etc/lsb-release",
       "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
   OmahaRequestParams params = kDefaultTestParams;
   params.set_root(string("./") + kTestDir);
diff --git a/omaha_request_params.cc b/omaha_request_params.cc
index 591a6f6..f30e771 100644
--- a/omaha_request_params.cc
+++ b/omaha_request_params.cc
@@ -15,6 +15,7 @@
 #include <base/file_util.h>
 #include <policy/device_policy.h>
 
+#include "update_engine/constants.h"
 #include "update_engine/simple_key_value_store.h"
 #include "update_engine/system_state.h"
 #include "update_engine/utils.h"
@@ -132,7 +133,7 @@
   }
 
   TEST_AND_RETURN_FALSE(IsValidChannel(new_target_channel));
-  FilePath kFile(root_ + utils::kStatefulPartition + "/etc/lsb-release");
+  FilePath kFile(root_ + kStatefulPartition + "/etc/lsb-release");
   string file_data;
   map<string, string> data;
   if (file_util::ReadFileToString(kFile, &file_data)) {
@@ -213,7 +214,7 @@
                                        bool stateful_override) const {
   vector<string> files;
   if (stateful_override) {
-    files.push_back(string(utils::kStatefulPartition) + "/etc/lsb-release");
+    files.push_back(string(kStatefulPartition) + "/etc/lsb-release");
   }
   files.push_back("/etc/lsb-release");
   for (vector<string>::const_iterator it = files.begin();
diff --git a/omaha_request_params_unittest.cc b/omaha_request_params_unittest.cc
index 31ccd66..c12abcb 100644
--- a/omaha_request_params_unittest.cc
+++ b/omaha_request_params_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/file_util.h"
 #include "gtest/gtest.h"
+#include "update_engine/constants.h"
 #include "update_engine/install_plan.h"
 #include "update_engine/mock_system_state.h"
 #include "update_engine/omaha_request_params.h"
@@ -31,7 +32,7 @@
   virtual void SetUp() {
     ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
     ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir +
-                        utils::kStatefulPartition + "/etc"));
+                        kStatefulPartition + "/etc"));
     // Create a fresh copy of the params for each test, so there's no
     // unintended reuse of state across tests.
     MockSystemState mock_system_state;
@@ -258,7 +259,7 @@
       "CHROMEOS_RELEASE_TRACK=dev-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
   ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      kTestDir + kStatefulPartition + "/etc/lsb-release",
       "CHROMEOS_RELEASE_BOARD=x86-generic\n"
       "CHROMEOS_RELEASE_TRACK=beta-channel\n"
       "CHROMEOS_AUSERVER=https://www.google.com"));
@@ -286,7 +287,7 @@
       "CHROMEOS_RELEASE_TRACK=dev-channel\n"
       "CHROMEOS_AUSERVER=https://www.google.com"));
   ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      kTestDir + kStatefulPartition + "/etc/lsb-release",
       "CHROMEOS_RELEASE_BOARD=x86-generic\n"
       "CHROMEOS_RELEASE_TRACK=stable-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
@@ -312,7 +313,7 @@
       "CHROMEOS_RELEASE_TRACK=dev-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
   ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      kTestDir + kStatefulPartition + "/etc/lsb-release",
       "CHROMEOS_RELEASE_BOARD=x86-generic\n"
       "CHROMEOS_RELEASE_TRACK=dev-channel"));
   MockSystemState mock_system_state;
@@ -474,7 +475,7 @@
 
   // Set a different channel in stateful LSB release.
   ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      kTestDir + kStatefulPartition + "/etc/lsb-release",
       "CHROMEOS_RELEASE_TRACK=stable-channel\n"
       "CHROMEOS_IS_POWERWASH_ALLOWED=true\n"));
 
@@ -518,7 +519,7 @@
       "CHROMEOS_RELEASE_TRACK=canary-channel\n"
       "CHROMEOS_AUSERVER=http://www.google.com"));
   ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      kTestDir + kStatefulPartition + "/etc/lsb-release",
       "CHROMEOS_RELEASE_BOARD=x86-generic\n"
       "CHROMEOS_RELEASE_TRACK=stable-channel\n"
       "CHROMEOS_AUSERVER=https://www.google.com"));
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index af47289..f494aa0 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -53,7 +53,9 @@
   install_plan_.hash_checks_mandatory = AreHashChecksMandatory(response);
   install_plan_.is_resume =
       DeltaPerformer::CanResumeUpdate(system_state_->prefs(), response.hash);
-  if (!install_plan_.is_resume) {
+  if (install_plan_.is_resume) {
+    system_state_->payload_state()->UpdateResumed();
+  } else {
     system_state_->payload_state()->UpdateRestarted();
     LOG_IF(WARNING, !DeltaPerformer::ResetUpdateProgress(
         system_state_->prefs(), false))
@@ -84,7 +86,7 @@
   // hacky solution but should be OK for now.
   //
   // TODO(petkov): Rearchitect this to avoid communication through a
-  // file. Ideallly, we would include this information in D-Bus's GetStatus
+  // file. Ideally, we would include this information in D-Bus's GetStatus
   // method and UpdateStatus signal. A potential issue is that update_engine may
   // be unresponsive during an update download.
   utils::WriteFile(kDeadlineFile,
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index 88ac0c4..cb27f82 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -240,12 +240,12 @@
   const string kTestDir = "omaha_response_handler_action-test";
   ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
   ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir +
-                        utils::kStatefulPartition + "/etc"));
+                      kStatefulPartition + "/etc"));
   ASSERT_TRUE(WriteFileString(
       kTestDir + "/etc/lsb-release",
       "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
   ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      kTestDir + kStatefulPartition + "/etc/lsb-release",
       "CHROMEOS_IS_POWERWASH_ALLOWED=true\n"
       "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
 
@@ -277,12 +277,12 @@
   const string kTestDir = "omaha_response_handler_action-test";
   ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir + "/etc"));
   ASSERT_EQ(0, System(string("mkdir -p ") + kTestDir +
-                        utils::kStatefulPartition + "/etc"));
+                      kStatefulPartition + "/etc"));
   ASSERT_TRUE(WriteFileString(
       kTestDir + "/etc/lsb-release",
       "CHROMEOS_RELEASE_TRACK=stable-channel\n"));
   ASSERT_TRUE(WriteFileString(
-      kTestDir + utils::kStatefulPartition + "/etc/lsb-release",
+      kTestDir + kStatefulPartition + "/etc/lsb-release",
       "CHROMEOS_RELEASE_TRACK=canary-channel\n"));
 
   MockSystemState mock_system_state;
diff --git a/payload_state.cc b/payload_state.cc
index 2a26722..bae21b9 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -58,6 +58,7 @@
     LoadCurrentBytesDownloaded(source);
     LoadTotalBytesDownloaded(source);
   }
+  LoadNumReboots();
   return true;
 }
 
@@ -124,9 +125,15 @@
   SetUrlFailureCount(0);
 }
 
+void PayloadState::UpdateResumed() {
+  LOG(INFO) << "Resuming an update that was previously started.";
+  UpdateNumReboots();
+}
+
 void PayloadState::UpdateRestarted() {
   LOG(INFO) << "Starting a new update";
   ResetDownloadSourcesOnNewUpdate();
+  SetNumReboots(0);
 }
 
 void PayloadState::UpdateSucceeded() {
@@ -135,6 +142,7 @@
   SetUpdateTimestampEnd(Time::Now());
   ReportBytesDownloadedMetrics();
   ReportUpdateUrlSwitchesMetric();
+  ReportRebootMetrics();
 }
 
 void PayloadState::UpdateFailed(ActionExitCode error) {
@@ -435,6 +443,39 @@
        kNumDefaultUmaBuckets);
 }
 
+void PayloadState::ReportRebootMetrics() {
+  // Report the number of num_reboots.
+  string metric = "Installer.UpdateNumReboots";
+  uint32_t num_reboots = GetNumReboots();
+  LOG(INFO) << "Uploading reboot count of " << num_reboots << " for metric "
+            <<  metric;
+  system_state_->metrics_lib()->SendToUMA(
+      metric,
+      static_cast<int>(num_reboots),  // sample
+      0,  // min = 0.
+      50,  // max
+      25);  // buckets
+  SetNumReboots(0);
+}
+
+void PayloadState::UpdateNumReboots() {
+  // We only update the reboot count when the system has been detected to have
+  // been rebooted.
+  if (!system_state_->system_rebooted()) {
+    return;
+  }
+
+  SetNumReboots(GetNumReboots() + 1);
+}
+
+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_;
+}
+
 void PayloadState::ResetPersistedState() {
   SetPayloadAttemptNumber(0);
   SetUrlIndex(0);
@@ -686,6 +727,10 @@
   SetUpdateDurationUptime(stored_delta);
 }
 
+void PayloadState::LoadNumReboots() {
+  SetNumReboots(GetPersistedValue(kPrefsNumReboots));
+}
+
 void PayloadState::SetUpdateDurationUptimeExtended(const TimeDelta& value,
                                                    const Time& timestamp,
                                                    bool use_logging) {
diff --git a/payload_state.h b/payload_state.h
index 5427c74..43fe5cc 100644
--- a/payload_state.h
+++ b/payload_state.h
@@ -35,6 +35,7 @@
   virtual void SetResponse(const OmahaResponse& response);
   virtual void DownloadComplete();
   virtual void DownloadProgress(size_t count);
+  virtual void UpdateResumed();
   virtual void UpdateRestarted();
   virtual void UpdateSucceeded();
   virtual void UpdateFailed(ActionExitCode error);
@@ -76,6 +77,10 @@
     return source < kNumDownloadSources ? total_bytes_downloaded_[source] : 0;
   }
 
+  virtual inline uint32_t GetNumReboots() {
+    return num_reboots_;
+  }
+
  private:
   // Increments the payload attempt number which governs the backoff behavior
   // at the time of the next update check.
@@ -112,6 +117,9 @@
   // Reports the metric related to number of URL switches.
   void ReportUpdateUrlSwitchesMetric();
 
+  // Reports the various metrics related to rebooting during an update.
+  void ReportRebootMetrics();
+
   // Resets all the persisted state values which are maintained relative to the
   // current response signature. The response signature itself is not reset.
   void ResetPersistedState();
@@ -235,6 +243,18 @@
   // The global state of the system.
   SystemState* system_state_;
 
+  // Initializes |num_reboots_| from the persisted state.
+  void LoadNumReboots();
+
+  // Sets |num_reboots| for the update attempt. Also persists the
+  // value being set so that we resume from the same value in case of a process
+  // restart.
+  void SetNumReboots(uint32_t num_reboots);
+
+  // Checks to see if the device rebooted since the last call and if so
+  // increments num_reboots.
+  void UpdateNumReboots();
+
   // Interface object with which we read/write persisted state. This must
   // be set by calling the Initialize method before calling any other method.
   PrefsInterface* prefs_;
@@ -276,6 +296,11 @@
   // data we read from the socket.
   DownloadSource current_download_source_;
 
+  // The number of system reboots during an update attempt. Technically since
+  // we don't go out of our way to not update it when not attempting an update,
+  // also records the number of reboots before the next update attempt starts.
+  uint32_t num_reboots_;
+
   // The timestamp until which we've to wait before attempting to download the
   // payload again, so as to backoff repeated downloads.
   base::Time backoff_expiry_time_;
diff --git a/payload_state_interface.h b/payload_state_interface.h
index 76b847a..e092290 100644
--- a/payload_state_interface.h
+++ b/payload_state_interface.h
@@ -40,6 +40,9 @@
   // able to make forward progress with the current URL.
   virtual void DownloadProgress(size_t count) = 0;
 
+  // This method should be called every time we resume an update attempt.
+  virtual void UpdateResumed() = 0;
+
   // This method should be called every time we begin a new update. This method
   // should not be called when we resume an update from the previously
   // downloaded point. This is used to reset the metrics for each new update.
@@ -99,6 +102,9 @@
   // source since the the last successful update. This is used to compute the
   // overhead we incur.
   virtual uint64_t GetTotalBytesDownloaded(DownloadSource source) = 0;
+
+  // Returns the reboot count for this update attempt.
+  virtual uint32_t GetNumReboots() = 0;
  };
 
 }  // namespace chromeos_update_engine
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index a0f5a42..2be5896 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -4,6 +4,8 @@
 
 #include <glib.h>
 
+#include "base/file_path.h"
+#include "base/file_util.h"
 #include "base/stringprintf.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -99,6 +101,7 @@
     .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttp, 0))
     .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);
@@ -143,6 +146,8 @@
     .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttp, 0))
     .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);
@@ -185,6 +190,8 @@
     .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kCurrentBytesDownloadedFromHttp, 0))
     .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);
@@ -219,6 +226,9 @@
     .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsBackoffExpiryTime, _)).Times(AtLeast(2));
 
+  // Reboots will be set
+  EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, _)).Times(AtLeast(1));
+
   // Url index should go from 0 to 1 twice.
   EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 0)).Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kPrefsCurrentUrlIndex, 1)).Times(AtLeast(1));
@@ -325,6 +335,8 @@
     .Times(AtLeast(1));
   EXPECT_CALL(*prefs, SetInt64(kTotalBytesDownloadedFromHttp, progress_bytes))
     .Times(AtLeast(1));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 0))
+      .Times(AtLeast(1));
 
   EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
 
@@ -638,6 +650,9 @@
   EXPECT_EQ(https_total,
             payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpsServer));
 
+  // Don't care about other metrics in this test.
+  EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
+      _,_,_,_,_)).Times(AtLeast(0));
   EXPECT_CALL(*mock_system_state.mock_metrics_lib(), SendToUMA(
       "Installer.SuccessfulMBsDownloadedFromHttpServer",
       http_chunk / kNumBytesInOneMiB, _, _, _));
@@ -696,6 +711,32 @@
             payload_state.GetTotalBytesDownloaded(kDownloadSourceHttpServer));
 }
 
+TEST(PayloadStateTest, NumRebootsIncrementsCorrectly) {
+  MockSystemState mock_system_state;
+  PayloadState payload_state;
 
+  NiceMock<PrefsMock>* prefs = mock_system_state.mock_prefs();
+  EXPECT_CALL(*prefs, SetInt64(_,_)).Times(AtLeast(0));
+  EXPECT_CALL(*prefs, SetInt64(kPrefsNumReboots, 1)).Times(AtLeast(1));
+
+  EXPECT_TRUE(payload_state.Initialize(&mock_system_state));
+
+  payload_state.UpdateRestarted();
+  EXPECT_EQ(0, payload_state.GetNumReboots());
+
+  EXPECT_CALL(mock_system_state, system_rebooted()).WillOnce(Return(true));
+  payload_state.UpdateResumed();
+  // Num reboots should be incremented because system rebooted detected.
+  EXPECT_EQ(1, payload_state.GetNumReboots());
+
+  EXPECT_CALL(mock_system_state, system_rebooted()).WillOnce(Return(false));
+  payload_state.UpdateResumed();
+  // Num reboots should now be 1 as reboot was not detected.
+  EXPECT_EQ(1, payload_state.GetNumReboots());
+
+  // Restart the update again to verify we set the num of reboots back to 0.
+  payload_state.UpdateRestarted();
+  EXPECT_EQ(0, payload_state.GetNumReboots());
+}
 
 }
diff --git a/postinstall_runner_action.cc b/postinstall_runner_action.cc
index 934b461..802a2cd 100644
--- a/postinstall_runner_action.cc
+++ b/postinstall_runner_action.cc
@@ -8,7 +8,7 @@
 #include <stdlib.h>
 #include <vector>
 
-#include "update_engine/constants.h"
+#include "update_engine/action_processor.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/utils.h"
 
diff --git a/postinstall_runner_action_unittest.cc b/postinstall_runner_action_unittest.cc
index 81f4420..85954dd 100644
--- a/postinstall_runner_action_unittest.cc
+++ b/postinstall_runner_action_unittest.cc
@@ -98,7 +98,7 @@
   // True if the post-install action is expected to succeed.
   bool should_succeed = do_losetup && !err_code;
 
-  const string mountpoint(string(utils::kStatefulPartition) +
+  const string mountpoint(string(kStatefulPartition) +
                           "/au_destination");
 
   string cwd;
diff --git a/real_system_state.h b/real_system_state.h
index ecb2fc0..72a07ad 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -64,6 +64,10 @@
     return &request_params_;
   }
 
+  virtual inline bool system_rebooted(){
+    return system_rebooted_;
+  }
+
   // Initializes this concrete object. Other methods should be invoked only
   // if the object has been initialized successfully.
   bool Initialize(bool enable_gpio);
@@ -101,6 +105,11 @@
 
   // Common parameters for all Omaha requests.
   OmahaRequestParams request_params_;
+
+  // If true, this is the first instance of the update engine since the system
+  // rebooted. Important for tracking whether you are running instance of the
+  // update engine on first boot or due to a crash/restart.
+  bool system_rebooted_;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/system_state.cc b/system_state.cc
index 432b99f..da18500 100644
--- a/system_state.cc
+++ b/system_state.cc
@@ -5,6 +5,7 @@
 #include <base/file_util.h>
 
 #include "update_engine/real_system_state.h"
+#include "update_engine/utils.h"
 
 namespace chromeos_update_engine {
 
@@ -14,7 +15,8 @@
 RealSystemState::RealSystemState()
     : device_policy_(NULL),
       connection_manager_(this),
-      request_params_(this) {}
+      request_params_(this),
+      system_rebooted_(false) {}
 
 bool RealSystemState::Initialize(bool enable_gpio) {
   metrics_lib_.Init();
@@ -24,6 +26,14 @@
     return false;
   }
 
+  if (!utils::FileExists(kSystemRebootedMarkerFile)) {
+    if (!utils::WriteFile(kSystemRebootedMarkerFile, "", 0)) {
+      LOG(ERROR) << "Could not create reboot marker file";
+      return false;
+    }
+    system_rebooted_ = true;
+  }
+
   if (!payload_state_.Initialize(this))
     return false;
 
diff --git a/system_state.h b/system_state.h
index 4b30d1a..6f06a34 100644
--- a/system_state.h
+++ b/system_state.h
@@ -64,6 +64,11 @@
   // Returns a pointer to the object that stores the parameters that are
   // common to all Omaha requests.
   virtual OmahaRequestParams* request_params() = 0;
+
+  // If true, this is the first instance of the update engine since the system
+  // restarted. Important for tracking whether you are running instance of the
+  // update engine on first boot or due to a crash/restart.
+  virtual bool system_rebooted() = 0;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/update_attempter.cc b/update_attempter.cc
index 1360865..9f01ab9 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -639,6 +639,7 @@
     prefs_->Delete(kPrefsUpdateFirstSeenAt);
     prefs_->Delete(kPrefsUpdateTimestampStart);
     prefs_->Delete(kPrefsUpdateDurationUptime);
+    LOG(INFO) << "Update successfully applied, waiting to reboot.";
 
     SetStatusAndNotify(UPDATE_STATUS_UPDATED_NEED_REBOOT,
                        kUpdateNoticeUnspecified);
diff --git a/utils.cc b/utils.cc
index 3853b94..f0cea0d 100644
--- a/utils.cc
+++ b/utils.cc
@@ -59,7 +59,6 @@
 namespace utils {
 
 static const char kDevImageMarker[] = "/root/.dev_mode";
-const char* const kStatefulPartition = "/mnt/stateful_partition";
 
 // Cgroup container is created in update-engine's upstart script located at
 // /etc/init/update-engine.conf.
diff --git a/utils.h b/utils.h
index ef42745..72c6ea1 100644
--- a/utils.h
+++ b/utils.h
@@ -197,8 +197,6 @@
   HexDumpArray(reinterpret_cast<const unsigned char*>(&vect[0]), vect.size());
 }
 
-extern const char* const kStatefulPartition;
-
 bool StringHasSuffix(const std::string& str, const std::string& suffix);
 bool StringHasPrefix(const std::string& str, const std::string& prefix);