Add D-Bus method to get the duration since an update completed.

This new API can be used by Chrome to automatically reboot the device
at N hours after updating. This is implemented as a D-Bus method that
returns the number of micro-seconds on the wall-clock since the update
completed. If the device has not updated, the D-Bus method returns an
error.

For robustness, durations are measured using the CLOCK_BOOTTIME clock
instead of the usual CLOCK_REALTIME clock. This avoids interference
with NTP adjustments, the RTC clock being wrong and other things.

BUG=chromium:218192
TEST=New unit test + unit tests pass + manual test on a device using
    the gdbus(1) command as the chronos user.

Change-Id: I51d44d69afe2d3024bb0780916c3c4e3f8ebb19e
Reviewed-on: https://chromium-review.googlesource.com/173032
Reviewed-by: David Zeuthen <zeuthen@chromium.org>
Commit-Queue: David Zeuthen <zeuthen@chromium.org>
Tested-by: David Zeuthen <zeuthen@chromium.org>
diff --git a/update_attempter.cc b/update_attempter.cc
index 69a625d..eb57644 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -9,8 +9,11 @@
 #include <vector>
 
 #include <base/file_util.h>
+#include <base/logging.h>
 #include <base/rand_util.h>
+#include <base/stringprintf.h>
 #include <chromeos/dbus/service_constants.h>
+
 #include <glib.h>
 #include <metrics/metrics_library.h>
 #include <policy/libpolicy.h>
@@ -38,8 +41,10 @@
 #include "update_engine/update_check_scheduler.h"
 #include "update_engine/utils.h"
 
+using base::Time;
 using base::TimeDelta;
 using base::TimeTicks;
+using base::StringPrintf;
 using google::protobuf::NewPermanentCallback;
 using std::make_pair;
 using std::tr1::shared_ptr;
@@ -799,6 +804,18 @@
   return true;
 }
 
+void UpdateAttempter::WriteUpdateCompletedMarker() {
+  if (update_completed_marker_.empty())
+    return;
+
+  int64_t value = system_state_->clock()->GetBootTime().ToInternalValue();
+  string contents = StringPrintf("%" PRIi64, value);
+
+  utils::WriteFile(update_completed_marker_.c_str(),
+                   contents.c_str(),
+                   contents.length());
+}
+
 // Delegate methods:
 void UpdateAttempter::ProcessingDone(const ActionProcessor* processor,
                                      ErrorCode code) {
@@ -825,8 +842,7 @@
   }
 
   if (code == kErrorCodeSuccess) {
-    if (!update_completed_marker_.empty())
-      utils::WriteFile(update_completed_marker_.c_str(), "", 0);
+    WriteUpdateCompletedMarker();
     prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
     prefs_->SetString(kPrefsPreviousVersion,
                       omaha_request_params_->app_version());
@@ -1423,4 +1439,24 @@
   return true;
 }
 
+bool UpdateAttempter::GetBootTimeAtUpdate(base::Time *out_boot_time) {
+  if (update_completed_marker_.empty())
+    return false;
+
+  string contents;
+  if (!utils::ReadFile(update_completed_marker_, &contents))
+    return false;
+
+  char *endp;
+  int64_t stored_value = strtoll(contents.c_str(), &endp, 10);
+  if (*endp != '\0') {
+    LOG(ERROR) << "Error parsing file " << update_completed_marker_ << " "
+               << "with content '" << contents << "'";
+    return false;
+  }
+
+  *out_boot_time = Time::FromInternalValue(stored_value);
+  return true;
+}
+
 }  // namespace chromeos_update_engine