storaged: account on/off charger per uid io usage

Register a listener to batteryproperties service for charger stats
change.

Aggregate IO usage based on charger stats in a collection window.

Bug: 33086174
Bug: 34198239
Change-Id: Ibe306c9c3ff8b8ada6be034aa8511268cb9a9b1c
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index 93c9df4..b46d09a 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -49,20 +49,19 @@
     return true;
 }
 
-void uid_monitor::set_last_uids(std::unordered_map<uint32_t, struct uid_info>&& uids,
-            uint64_t ts)
+std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
 {
-    last_uids = uids;
-    last_report_ts = ts;
-}
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    return get_uid_io_stats_locked();
+};
 
-std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uids()
+std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked()
 {
-    std::unordered_map<uint32_t, struct uid_info> uids;
+    std::unordered_map<uint32_t, struct uid_info> uid_io_stats;
     std::string buffer;
     if (!android::base::ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
         PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
-        return uids;
+        return uid_io_stats;
     }
 
     std::stringstream ss(buffer);
@@ -70,144 +69,167 @@
     bool refresh_uid = false;
 
     while (ss >> u.uid) {
-        ss >> u.io[UID_FOREGROUND].rchar >> u.io[UID_FOREGROUND].wchar
-           >> u.io[UID_FOREGROUND].read_bytes >> u.io[UID_FOREGROUND].write_bytes
-           >> u.io[UID_BACKGROUND].rchar >> u.io[UID_BACKGROUND].wchar
-           >> u.io[UID_BACKGROUND].read_bytes >> u.io[UID_BACKGROUND].write_bytes;
+        ss >> u.io[FOREGROUND].rchar >> u.io[FOREGROUND].wchar
+           >> u.io[FOREGROUND].read_bytes >> u.io[FOREGROUND].write_bytes
+           >> u.io[BACKGROUND].rchar >> u.io[BACKGROUND].wchar
+           >> u.io[BACKGROUND].read_bytes >> u.io[BACKGROUND].write_bytes;
 
         if (!ss.good()) {
             ss.clear(std::ios_base::badbit);
             break;
         }
 
-        if (last_uids.find(u.uid) == last_uids.end()) {
+        if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
             refresh_uid = true;
             u.name = std::to_string(u.uid);
         } else {
-            u.name = last_uids[u.uid].name;
+            u.name = last_uid_io_stats[u.uid].name;
         }
-        uids[u.uid] = u;
+        uid_io_stats[u.uid] = u;
     }
 
     if (!ss.eof() || ss.bad()) {
-        uids.clear();
+        uid_io_stats.clear();
         LOG_TO(SYSTEM, ERROR) << "read UID IO stats failed";
     }
 
     if (refresh_uid) {
-        packagelist_parse(packagelist_parse_cb, &uids);
+        packagelist_parse(packagelist_parse_cb, &uid_io_stats);
     }
 
-    return uids;
+    return uid_io_stats;
 }
 
-static const int MAX_UID_EVENTS_SIZE = 1000 * 48; // 1000 uids in 48 hours
+static const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
 
-void uid_monitor::add_events(const std::vector<struct uid_event>& new_events,
-                             uint64_t curr_ts)
+static inline int records_size(
+    const std::map<uint64_t, std::vector<struct uid_record>>& records)
 {
-    std::unique_ptr<lock_t> lock(new lock_t(&events_lock));
-
-    // remove events more than 5 days old
-    struct uid_event first_event;
-    first_event.ts = curr_ts / SEC_TO_USEC - 5 * DAY_TO_SEC;
-    auto it = std::upper_bound(events.begin(), events.end(), first_event);
-    events.erase(events.begin(), it);
-
-    // make some room for new events
-    int overflow = events.size() + new_events.size() - MAX_UID_EVENTS_SIZE;
-    if (overflow > 0)
-        events.erase(events.begin(), events.begin() + overflow);
-
-    events.insert(events.end(), new_events.begin(), new_events.end());
+    int count = 0;
+    for (auto const& it : records) {
+        count += it.second.size();
+    }
+    return count;
 }
 
-std::vector<struct uid_event> uid_monitor::dump_events(int hours)
+static struct uid_io_usage zero_io_usage;
+
+void uid_monitor::add_records_locked(uint64_t curr_ts)
 {
-    std::unique_ptr<lock_t> lock(new lock_t(&events_lock));
-    std::vector<struct uid_event> dump_events;
-    struct timespec ts;
-
-    if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
-        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
-        return dump_events;
+    // remove records more than 5 days old
+    if (curr_ts > 5 * DAY_TO_SEC) {
+        auto it = records.lower_bound(curr_ts - 5 * DAY_TO_SEC);
+        records.erase(records.begin(), it);
     }
 
-    struct uid_event first_event;
-    if (hours == 0) {
-        first_event.ts = 0; // dump all events
-    } else {
-        first_event.ts = ts.tv_sec - (uint64_t)hours * HOUR_TO_SEC;
+    std::vector<struct uid_record> new_records;
+    for (const auto& p : curr_io_stats) {
+        struct uid_record record = {};
+        record.name = p.first;
+        record.ios = p.second;
+        if (memcmp(&record.ios, &zero_io_usage, sizeof(struct uid_io_usage))) {
+            new_records.push_back(record);
+        }
     }
-    auto it = std::upper_bound(events.begin(), events.end(), first_event);
 
-    dump_events.assign(it, events.end());
+    curr_io_stats.clear();
 
-    return dump_events;
+    if (new_records.empty())
+      return;
+
+    // make some room for new records
+    int overflow = records_size(records) +
+        new_records.size() - MAX_UID_RECORDS_SIZE;
+    while (overflow > 0 && records.size() > 0) {
+        overflow -= records[0].size();
+        records.erase(records.begin());
+    }
+
+    records[curr_ts].insert(records[curr_ts].end(),
+        new_records.begin(), new_records.end());
+}
+
+std::map<uint64_t, std::vector<struct uid_record>> uid_monitor::dump(int hours)
+{
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+
+    std::map<uint64_t, std::vector<struct uid_record>> dump_records;
+    uint64_t first_ts = 0;
+
+    if (hours != 0) {
+        first_ts = time(NULL) - (uint64_t)hours * HOUR_TO_SEC;
+    }
+
+    dump_records.insert(records.lower_bound(first_ts), records.end());
+
+    return dump_records;
+}
+
+void uid_monitor::update_curr_io_stats_locked()
+{
+    std::unordered_map<uint32_t, struct uid_info> uid_io_stats =
+        get_uid_io_stats_locked();
+    if (uid_io_stats.empty()) {
+        return;
+    }
+
+    for (const auto& it : uid_io_stats) {
+        const struct uid_info& uid = it.second;
+
+        if (curr_io_stats.find(uid.name) == curr_io_stats.end()) {
+          curr_io_stats[uid.name] = {};
+        }
+
+        struct uid_io_usage& usage = curr_io_stats[uid.name];
+        usage.bytes[READ][FOREGROUND][charger_stat] +=
+            uid.io[FOREGROUND].read_bytes -
+            last_uid_io_stats[uid.uid].io[FOREGROUND].read_bytes;
+        usage.bytes[READ][BACKGROUND][charger_stat] +=
+            uid.io[BACKGROUND].read_bytes -
+            last_uid_io_stats[uid.uid].io[BACKGROUND].read_bytes;
+        usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+            uid.io[FOREGROUND].write_bytes -
+            last_uid_io_stats[uid.uid].io[FOREGROUND].write_bytes;
+        usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+            uid.io[BACKGROUND].write_bytes -
+            last_uid_io_stats[uid.uid].io[BACKGROUND].write_bytes;;
+    }
+
+    last_uid_io_stats = uid_io_stats;
 }
 
 void uid_monitor::report()
 {
-    struct timespec ts;
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
 
-    // Use monotonic to exclude suspend time so that we measure IO bytes/sec
-    // when system is running.
-    if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
-        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+    update_curr_io_stats_locked();
+    add_records_locked(time(NULL));
+}
+
+void uid_monitor::set_charger_state(charger_stat_t stat)
+{
+    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+
+    if (charger_stat == stat) {
         return;
     }
 
-    uint64_t curr_ts = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
-    uint64_t ts_delta = curr_ts - last_report_ts;
-    uint64_t adjusted_threshold = threshold * ((double)ts_delta / interval / NS_PER_SEC);
+    update_curr_io_stats_locked();
+    charger_stat = stat;
+}
 
-    std::unordered_map<uint32_t, struct uid_info> uids = get_uids();
-    if (uids.empty()) {
-        return;
-    }
-
-    std::vector<struct uid_event> new_events;
-    for (const auto& it : uids) {
-        const struct uid_info& uid = it.second;
-        struct uid_event event;
-
-        event.ts = ts.tv_sec;
-        event.name = uid.name;
-        event.fg_read_bytes = uid.io[UID_FOREGROUND].read_bytes -
-            last_uids[uid.uid].io[UID_FOREGROUND].read_bytes;;
-        event.fg_write_bytes = uid.io[UID_FOREGROUND].write_bytes -
-            last_uids[uid.uid].io[UID_FOREGROUND].write_bytes;;
-        event.bg_read_bytes = uid.io[UID_BACKGROUND].read_bytes -
-            last_uids[uid.uid].io[UID_BACKGROUND].read_bytes;;
-        event.bg_write_bytes = uid.io[UID_BACKGROUND].write_bytes -
-            last_uids[uid.uid].io[UID_BACKGROUND].write_bytes;;
-
-        if (event.fg_read_bytes + event.fg_write_bytes +
-            event.bg_read_bytes + event.bg_write_bytes == 0) {
-            continue;
-        }
-
-        new_events.push_back(event);
-    }
-
-    add_events(new_events, curr_ts);
-    set_last_uids(std::move(uids), curr_ts);
+void uid_monitor::init(charger_stat_t stat)
+{
+    charger_stat = stat;
+    last_uid_io_stats = get_uid_io_stats();
 }
 
 uid_monitor::uid_monitor()
 {
-    struct timespec ts;
-
-    if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
-        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
-        return;
-    }
-    last_report_ts = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
-
-    sem_init(&events_lock, 0, 1);
+    sem_init(&um_lock, 0, 1);
 }
 
 uid_monitor::~uid_monitor()
 {
-    sem_destroy(&events_lock);
+    sem_destroy(&um_lock);
 }