bootstat: Handle v1 record files which do not contain file contents.

Bug: 27836969
Change-Id: I18fcdab7ca32d00af3f8827f42d47868492ed719
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 5d1fae9..ef4f68e 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -55,8 +55,11 @@
     return false;
   }
 
-  int32_t value = std::stoi(content);
-  bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+  // Ignore existing bootstat records (which do not contain file content).
+  if (!content.empty()) {
+    int32_t value = std::stoi(content);
+    bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+  }
 
   return true;
 }
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
index 4d5deee..a2b8318 100644
--- a/bootstat/boot_event_record_store.h
+++ b/bootstat/boot_event_record_store.h
@@ -55,6 +55,7 @@
   FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
   FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue);
   FRIEND_TEST(BootEventRecordStoreTest, GetBootEvent);
+  FRIEND_TEST(BootEventRecordStoreTest, GetBootEventNoFileContent);
 
   // Sets the filesystem path of the record store.
   void SetStorePath(const std::string& path);
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 343f9d0..d4f0755 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -17,12 +17,16 @@
 #include "boot_event_record_store.h"
 
 #include <dirent.h>
+#include <fcntl.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <cstdint>
 #include <cstdlib>
+#include <fstream>
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/test_utils.h>
+#include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
 #include "uptime_parser.h"
@@ -31,6 +35,36 @@
 
 namespace {
 
+// Creates a fake boot event record file at |record_path| containing the boot
+// record |value|. This method is necessary as truncating a
+// BootEventRecordStore-created file would modify the mtime, which would alter
+// the value of the record.
+bool CreateEmptyBootEventRecord(const std::string& record_path, int32_t value) {
+  android::base::unique_fd record_fd(creat(record_path.c_str(), S_IRUSR | S_IWUSR));
+  if (record_fd.get() == -1) {
+    return false;
+  }
+
+  // Writing the value as content in the record file is a debug measure to
+  // ensure the validity of the file mtime value, i.e., to check that the record
+  // file mtime values are not changed once set.
+  // TODO(jhawkins): Remove this block.
+  if (!android::base::WriteStringToFd(std::to_string(value), record_fd.get())) {
+    return false;
+  }
+
+  // Set the |mtime| of the file to store the value of the boot event while
+  // preserving the |atime|.
+  struct timespec atime = {/* tv_sec */ 0, /* tv_usec */ UTIME_OMIT};
+  struct timespec mtime = {/* tv_sec */ value, /* tv_usec */ 0};
+  const struct timespec times[] = {atime, mtime};
+  if (futimens(record_fd.get(), times) != 0) {
+    return false;
+  }
+
+  return true;
+}
+
 // Returns true if the time difference between |a| and |b| is no larger
 // than 10 seconds.  This allow for a relatively large fuzz when comparing
 // two timestamps taken back-to-back.
@@ -178,4 +212,19 @@
 
   // Null |record|.
   EXPECT_DEATH(store.GetBootEvent("carboniferous", nullptr), std::string());
-}
\ No newline at end of file
+}
+
+// Tests that the BootEventRecordStore is capable of handling an older record
+// protocol which does not contain file contents.
+TEST_F(BootEventRecordStoreTest, GetBootEventNoFileContent) {
+  BootEventRecordStore store;
+  store.SetStorePath(GetStorePathForTesting());
+
+  EXPECT_TRUE(CreateEmptyBootEventRecord(store.GetBootEventPath("devonian"), 2718));
+
+  BootEventRecordStore::BootEventRecord record;
+  bool result = store.GetBootEvent("devonian", &record);
+  EXPECT_EQ(true, result);
+  EXPECT_EQ("devonian", record.first);
+  EXPECT_EQ(2718, record.second);
+}