diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp index 22a67bb3a..8282b406b 100644 --- a/bootstat/boot_event_record_store.cpp +++ b/bootstat/boot_event_record_store.cpp @@ -50,7 +50,7 @@ BootEventRecordStore::BootEventRecordStore() { SetStorePath(BOOTSTAT_DATA_DIR); } -void BootEventRecordStore::AddBootEvent(const std::string& name) { +void BootEventRecordStore::AddBootEvent(const std::string& event) { std::string uptime_str; if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) { LOG(ERROR) << "Failed to read /proc/uptime"; @@ -58,15 +58,15 @@ void BootEventRecordStore::AddBootEvent(const std::string& name) { // Cast intentionally rounds down. int32_t uptime = static_cast(strtod(uptime_str.c_str(), NULL)); - AddBootEventWithValue(name, uptime); + AddBootEventWithValue(event, uptime); } // The implementation of AddBootEventValue makes use of the mtime file // attribute to store the value associated with a boot event in order to // optimize on-disk size requirements and small-file thrashing. void BootEventRecordStore::AddBootEventWithValue( - const std::string& name, int32_t value) { - std::string record_path = GetBootEventPath(name); + const std::string& event, int32_t value) { + std::string record_path = GetBootEventPath(event); if (creat(record_path.c_str(), S_IRUSR | S_IWUSR) == -1) { PLOG(ERROR) << "Failed to create " << record_path; } @@ -86,6 +86,22 @@ void BootEventRecordStore::AddBootEventWithValue( } } +bool BootEventRecordStore::GetBootEvent( + const std::string& event, BootEventRecord* record) const { + CHECK_NE(static_cast(nullptr), record); + CHECK(!event.empty()); + + const std::string record_path = GetBootEventPath(event); + int32_t uptime; + if (!ParseRecordEventTime(record_path, &uptime)) { + LOG(ERROR) << "Failed to parse boot time record: " << record_path; + return false; + } + + *record = std::make_pair(event, uptime); + return true; +} + std::vector BootEventRecordStore:: GetAllBootEvents() const { std::vector events; @@ -104,14 +120,13 @@ std::vector BootEventRecordStore:: } const std::string event = entry->d_name; - const std::string record_path = GetBootEventPath(event); - int32_t uptime; - if (!ParseRecordEventTime(record_path, &uptime)) { - LOG(ERROR) << "Failed to parse boot time record: " << record_path; + BootEventRecord record; + if (!GetBootEvent(event, &record)) { + LOG(ERROR) << "Failed to parse boot time event: " << event; continue; } - events.push_back(std::make_pair(event, uptime)); + events.push_back(record); } return events; diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h index d1b7835ab..4d5deee51 100644 --- a/bootstat/boot_event_record_store.h +++ b/bootstat/boot_event_record_store.h @@ -34,12 +34,16 @@ class BootEventRecordStore { BootEventRecordStore(); - // Persists the boot event named |name| in the record store. - void AddBootEvent(const std::string& name); + // Persists the boot |event| in the record store. + void AddBootEvent(const std::string& event); - // Persists the boot event named |name| with the associated |value| in the - // record store. - void AddBootEventWithValue(const std::string& name, int32_t value); + // Persists the boot |event| with the associated |value| in the record store. + void AddBootEventWithValue(const std::string& event, int32_t value); + + // Queries the named boot |event|. |record| must be non-null. |record| + // contains the boot event data on success. Returns true iff the query is + // successful. + bool GetBootEvent(const std::string& event, BootEventRecord* record) const; // Returns a list of all of the boot events persisted in the record store. std::vector GetAllBootEvents() const; @@ -50,6 +54,7 @@ class BootEventRecordStore { FRIEND_TEST(BootEventRecordStoreTest, AddSingleBootEvent); FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents); FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue); + FRIEND_TEST(BootEventRecordStoreTest, GetBootEvent); // 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 3e6d70674..0d7bbb0b8 100644 --- a/bootstat/boot_event_record_store_test.cpp +++ b/bootstat/boot_event_record_store_test.cpp @@ -165,4 +165,27 @@ TEST_F(BootEventRecordStoreTest, AddBootEventWithValue) { ASSERT_EQ(1U, events.size()); EXPECT_EQ("permian", events[0].first); EXPECT_EQ(42, events[0].second); +} + +TEST_F(BootEventRecordStoreTest, GetBootEvent) { + BootEventRecordStore store; + store.SetStorePath(GetStorePathForTesting()); + + // Event does not exist. + BootEventRecordStore::BootEventRecord record; + bool result = store.GetBootEvent("nonexistent", &record); + EXPECT_EQ(false, result); + + // Empty path. + EXPECT_DEATH(store.GetBootEvent(std::string(), &record), std::string()); + + // Success case. + store.AddBootEventWithValue("carboniferous", 314); + result = store.GetBootEvent("carboniferous", &record); + EXPECT_EQ(true, result); + EXPECT_EQ("carboniferous", record.first); + EXPECT_EQ(314, record.second); + + // Null |record|. + EXPECT_DEATH(store.GetBootEvent("carboniferous", nullptr), std::string()); } \ No newline at end of file diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp index 1d16f69e2..0c49f8217 100644 --- a/bootstat/bootstat.cpp +++ b/bootstat/bootstat.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -155,6 +156,32 @@ void RecordBootReason() { boot_event_store.AddBootEventWithValue("boot_reason", boot_reason); } +// Records two metrics related to the user resetting a device: the time at +// which the device is reset, and the time since the user last reset the +// device. The former is only set once per-factory reset. +void RecordFactoryReset() { + BootEventRecordStore boot_event_store; + BootEventRecordStore::BootEventRecord record; + + time_t current_time_utc = time(nullptr); + + // The factory_reset boot event does not exist after the device is reset, so + // use this signal to mark the time of the factory reset. + if (!boot_event_store.GetBootEvent("factory_reset", &record)) { + boot_event_store.AddBootEventWithValue("factory_reset", current_time_utc); + boot_event_store.AddBootEventWithValue("time_since_factory_reset", 0); + return; + } + + // Calculate and record the difference in time between now and the + // factory_reset time. + time_t factory_reset_utc = record.second; + time_t time_since_factory_reset = difftime(current_time_utc, + factory_reset_utc); + boot_event_store.AddBootEventWithValue("time_since_factory_reset", + time_since_factory_reset); +} + } // namespace int main(int argc, char **argv) { @@ -165,12 +192,14 @@ int main(int argc, char **argv) { int option_index = 0; static const char boot_reason_str[] = "record_boot_reason"; + static const char factory_reset_str[] = "record_factory_reset"; static const struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "log", no_argument, NULL, 'l' }, { "print", no_argument, NULL, 'p' }, { "record", required_argument, NULL, 'r' }, { boot_reason_str, no_argument, NULL, 0 }, + { factory_reset_str, no_argument, NULL, 0 }, { NULL, 0, NULL, 0 } }; @@ -182,6 +211,8 @@ int main(int argc, char **argv) { const std::string option_name = long_options[option_index].name; if (option_name == boot_reason_str) { RecordBootReason(); + } else if (option_name == factory_reset_str) { + RecordFactoryReset(); } else { LOG(ERROR) << "Invalid option: " << option_name; }