Implement battery EEPROM atom

Bug: 160202146
Test: Ensure that logs are accurately reported via statsd
with 'cmd stats print-logs' and 'logcat | grep <atom ID>'

Merged-In: I1b41c60ed9728a51abda55e297c21b516772a264
Change-Id: I1b41c60ed9728a51abda55e297c21b516772a264
(cherry picked from commit 46bfff783aac6c3dd728d9b2404e4672d9e65d3f)
diff --git a/pixelstats/Android.bp b/pixelstats/Android.bp
index 7f2ee61..94fa064 100644
--- a/pixelstats/Android.bp
+++ b/pixelstats/Android.bp
@@ -49,6 +49,7 @@
     "UeventListener.cpp",
     "WlcReporter.cpp",
     "BatteryCapacityReporter.cpp",
+    "BatteryEEPROMReporter.cpp",
   ],
   cflags: [
     "-Wall",
diff --git a/pixelstats/BatteryEEPROMReporter.cpp b/pixelstats/BatteryEEPROMReporter.cpp
new file mode 100644
index 0000000..8bbfcf1
--- /dev/null
+++ b/pixelstats/BatteryEEPROMReporter.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "pixelstats: BatteryEEPROM"
+
+#include <log/log.h>
+#include <time.h>
+#include <utils/Timers.h>
+#include <cmath>
+#include <inttypes.h>
+
+#include <android-base/file.h>
+
+#include <android/frameworks/stats/1.0/IStats.h>
+#include <pixelstats/BatteryEEPROMReporter.h>
+
+#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+
+using android::base::ReadFileToString;
+using android::frameworks::stats::V1_0::IStats;
+using android::frameworks::stats::V1_0::VendorAtom;
+using android::hardware::google::pixel::PixelAtoms::BatteryEEPROM;
+
+#define LINESIZE 71
+
+BatteryEEPROMReporter::BatteryEEPROMReporter() {}
+
+void BatteryEEPROMReporter::checkAndReport(const std::string &path) {
+    std::string file_contents;
+    std::string history_each;
+
+    const int kSecondsPerMonth = 60 * 60 * 24 * 30;
+    int64_t now = getTimeSecs();
+
+    if ((report_time_ != 0) && (now - report_time_ < kSecondsPerMonth)) {
+        ALOGD("Not upload time. now:%ld, pre:%ld", now, report_time_);
+        return;
+    }
+
+    if (!ReadFileToString(path.c_str(), &file_contents)) {
+        ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno));
+        return;
+    }
+    ALOGD("checkAndReport: %s", file_contents.c_str());
+
+    int16_t i, num;
+    struct BatteryHistory hist;
+    const int kHistTotalLen = strlen(file_contents.c_str());
+
+    for (i = 0; i < (LINESIZE * BATT_HIST_NUM_MAX); i = i + LINESIZE) {
+        if (i + LINESIZE > kHistTotalLen)
+            break;
+        history_each = file_contents.substr(i, LINESIZE);
+        num = sscanf(history_each.c_str(),
+                   "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16
+                   "%2" SCNx8 "%2" SCNx8 " %2" SCNx8 "%2" SCNx8
+                   "%2" SCNx8 "%2" SCNx8 " %2" SCNx8 "%2" SCNx8
+                   "%2" SCNx8 "%2" SCNx8 " %4" SCNx16 "%4" SCNx16
+                   "%4" SCNx16 "%4" SCNx16 "%4" SCNx16,
+                   &hist.cycle_cnt, &hist.full_cap, &hist.esr,
+                   &hist.rslow, &hist.soh, &hist.batt_temp,
+                   &hist.cutoff_soc, &hist.cc_soc, &hist.sys_soc,
+                   &hist.msoc, &hist.batt_soc, &hist.reserve,
+                   &hist.max_temp, &hist.min_temp, &hist.max_vbatt,
+                   &hist.min_vbatt, &hist.max_ibatt, &hist.min_ibatt,
+                   &hist.checksum);
+
+        if (num != kNumBatteryHistoryFields) {
+            ALOGE("Couldn't process %s", history_each.c_str());
+            continue;
+        }
+
+        if (checkLogEvent(hist)) {
+            reportEvent(hist);
+            report_time_ = getTimeSecs();
+        }
+    }
+}
+
+int64_t BatteryEEPROMReporter::getTimeSecs(void) {
+    return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
+}
+
+/**
+ * @return true if a log should be reported, else false.
+ * Here we use checksum to confirm the data is usable or not.
+ * The checksum mismatch when storage data overflow or corrupt.
+ * We don't need data in such cases.
+ */
+bool BatteryEEPROMReporter::checkLogEvent(struct BatteryHistory hist) {
+    int checksum = 0;
+
+    checksum = hist.cycle_cnt + hist.full_cap + hist.esr + hist.rslow
+                + hist.soh + hist.batt_temp + hist.cutoff_soc + hist.cc_soc
+                + hist.sys_soc + hist.msoc + hist.batt_soc + hist.reserve
+                + hist.max_temp + hist.min_temp + hist.max_vbatt
+                + hist.min_vbatt + hist.max_ibatt + hist.min_ibatt;
+    /* Compare with checksum data */
+    if (checksum == hist.checksum) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void BatteryEEPROMReporter::reportEvent(struct BatteryHistory hist) {
+       sp<IStats> stats_client = IStats::tryGetService();
+       // upload atom
+       std::vector<int> eeprom_history_fields = {BatteryEEPROM::kCycleCntFieldNumber,
+                                                 BatteryEEPROM::kFullCapFieldNumber,
+                                                 BatteryEEPROM::kEsrFieldNumber,
+                                                 BatteryEEPROM::kRslowFieldNumber,
+                                                 BatteryEEPROM::kSohFieldNumber,
+                                                 BatteryEEPROM::kBattTempFieldNumber,
+                                                 BatteryEEPROM::kCutoffSocFieldNumber,
+                                                 BatteryEEPROM::kCcSocFieldNumber,
+                                                 BatteryEEPROM::kSysSocFieldNumber,
+                                                 BatteryEEPROM::kMsocFieldNumber,
+                                                 BatteryEEPROM::kBattSocFieldNumber,
+                                                 BatteryEEPROM::kReserveFieldNumber,
+                                                 BatteryEEPROM::kMaxTempFieldNumber,
+                                                 BatteryEEPROM::kMinTempFieldNumber,
+                                                 BatteryEEPROM::kMaxVbattFieldNumber,
+                                                 BatteryEEPROM::kMinVbattFieldNumber,
+                                                 BatteryEEPROM::kMaxIbattFieldNumber,
+                                                 BatteryEEPROM::kMinIbattFieldNumber,
+                                                 BatteryEEPROM::kChecksumFieldNumber};
+       std::vector<VendorAtom::Value> values(eeprom_history_fields.size());
+       VendorAtom::Value val;
+
+       ALOGD("reportEvent: cycle_cnt:%d, full_cap:%d, esr:%d, rslow:%d, soh:%d, "
+             "batt_temp:%d, cutoff_soc:%d, cc_soc:%d, sys_soc:%d, msoc:%d, "
+             "batt_soc:%d, reserve:%d, max_temp:%d, min_temp:%d, max_vbatt:%d, "
+             "min_vbatt:%d, max_ibatt:%d, min_ibatt:%d, checksum:%d",
+             hist.cycle_cnt, hist.full_cap, hist.esr, hist.rslow, hist.soh,
+             hist.batt_temp, hist.cutoff_soc, hist.cc_soc, hist.sys_soc,
+             hist.msoc, hist.batt_soc, hist.reserve, hist.max_temp,
+             hist.min_temp, hist.max_vbatt, hist.min_vbatt, hist.max_ibatt,
+             hist.min_ibatt, hist.checksum);
+
+       val.intValue(hist.cycle_cnt);
+       values[BatteryEEPROM::kCycleCntFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.full_cap);
+       values[BatteryEEPROM::kFullCapFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.esr);
+       values[BatteryEEPROM::kEsrFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.rslow);
+       values[BatteryEEPROM::kRslowFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.soh);
+       values[BatteryEEPROM::kSohFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.batt_temp);
+       values[BatteryEEPROM::kBattTempFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.cutoff_soc);
+       values[BatteryEEPROM::kCutoffSocFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.cc_soc);
+       values[BatteryEEPROM::kCcSocFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.sys_soc);
+       values[BatteryEEPROM::kSysSocFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.msoc);
+       values[BatteryEEPROM::kMsocFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.batt_soc);
+       values[BatteryEEPROM::kBattSocFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.reserve);
+       values[BatteryEEPROM::kReserveFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.max_temp);
+       values[BatteryEEPROM::kMaxTempFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.min_temp);
+       values[BatteryEEPROM::kMinTempFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.max_vbatt);
+       values[BatteryEEPROM::kMaxVbattFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.min_vbatt);
+       values[BatteryEEPROM::kMinTempFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.max_ibatt);
+       values[BatteryEEPROM::kMaxIbattFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.min_ibatt);
+       values[BatteryEEPROM::kMinIbattFieldNumber - kVendorAtomOffset] = val;
+       val.intValue(hist.checksum);
+       values[BatteryEEPROM::kChecksumFieldNumber - kVendorAtomOffset] = val;
+
+       VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
+                        .atomId = PixelAtoms::Ids::BATTERY_EEPROM,
+                        .values = values};
+       Return<void> ret = stats_client->reportVendorAtom(event);
+       if (!ret.isOk())
+           ALOGE("Unable to report BatteryEEPROM to Stats service");
+}
+
+
+}  // namespace pixel
+}  // namespace google
+}  // namespace hardware
+}  // namespace android
diff --git a/pixelstats/SysfsCollector.cpp b/pixelstats/SysfsCollector.cpp
index bf1e3d5..29b3509 100644
--- a/pixelstats/SysfsCollector.cpp
+++ b/pixelstats/SysfsCollector.cpp
@@ -72,7 +72,8 @@
       kF2fsStatsPath(sysfs_paths.F2fsStatsPath),
       kUserdataBlockProp(sysfs_paths.UserdataBlockProp),
       kZramMmStatPath("/sys/block/zram0/mm_stat"),
-      kZramBdStatPath("/sys/block/zram0/bd_stat") {}
+      kZramBdStatPath("/sys/block/zram0/bd_stat"),
+      kEEPROMPath(sysfs_paths.EEPROMPath) {}
 
 bool SysfsCollector::ReadFileToInt(const std::string &path, int *val) {
     return ReadFileToInt(path.c_str(), val);
@@ -126,6 +127,18 @@
 }
 
 /**
+ * Read the contents of kEEPROMPath and report them.
+ */
+void SysfsCollector::logBatteryEEPROM() {
+    if (kEEPROMPath == nullptr || strlen(kEEPROMPath) == 0) {
+        ALOGV("Battery EEPROM path not specified");
+        return;
+    }
+
+    battery_EEPROM_reporter_.checkAndReport(kEEPROMPath);
+}
+
+/**
  * Check the codec for failures over the past 24hr.
  */
 void SysfsCollector::logCodecFailed() {
@@ -599,6 +612,7 @@
     logUFSLifetime();
     logF2fsStats();
     logZramStats();
+    logBatteryEEPROM();
 
     stats_.clear();
 }
diff --git a/pixelstats/include/pixelstats/BatteryEEPROMReporter.h b/pixelstats/include/pixelstats/BatteryEEPROMReporter.h
new file mode 100644
index 0000000..e3ff997
--- /dev/null
+++ b/pixelstats/include/pixelstats/BatteryEEPROMReporter.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_GOOGLE_PIXEL_PIXELSTATS_BATTERYEEPROMREPORTER_H
+#define HARDWARE_GOOGLE_PIXEL_PIXELSTATS_BATTERYEEPROMREPORTER_H
+
+namespace android {
+namespace hardware {
+namespace google {
+namespace pixel {
+
+// The storage for save whole history is 928 byte
+// each history contains 19 items with total size 28 byte
+// hence the history number is 928/28~33
+#define BATT_HIST_NUM_MAX	33
+
+/**
+ * A class to upload battery EEPROM metrics
+ */
+class BatteryEEPROMReporter {
+  public:
+    BatteryEEPROMReporter();
+    void checkAndReport(const std::string &path);
+
+  private:
+    // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so
+    // store everything in the values array at the index of the field number
+    // -2.
+    const int kVendorAtomOffset = 2;
+
+    struct BatteryHistory {
+        /* The cycle count number; record of charge/discharge times */
+        uint16_t cycle_cnt;
+        /* The current full capacity of the battery under nominal conditions */
+        uint16_t full_cap;
+        /* The battery equivalent series resistance */
+        uint16_t esr;
+        /* Battery resistance related to temperature change */
+        uint16_t rslow;
+        /* Battery health indicator reflecting the battery age state */
+        uint8_t soh;
+        /* The battery temperature */
+        int8_t batt_temp;
+
+        /* Battery state of charge (SOC) shutdown point */
+        uint8_t cutoff_soc;
+        /* Raw battery state of charge (SOC), based on battery current (CC = Coulomb Counter) */
+        uint8_t cc_soc;
+        /* Estimated battery state of charge (SOC) from batt_soc with endpoint limiting (0% and 100%) */
+        uint8_t sys_soc;
+        /* Filtered monotonic SOC, handles situations where the cutoff_soc is increased and
+         * then decreased from the battery physical properties
+         */
+        uint8_t msoc;
+        /* Estimated SOC derived from cc_soc that provides voltage loop feedback correction using
+         * battery voltage, current, and status values
+         */
+        uint8_t batt_soc;
+
+        /* Field used for data padding in the EEPROM data */
+        uint8_t reserve;
+
+        /* The maximum battery temperature ever seen */
+        int8_t max_temp;
+        /* The minimum battery temperature ever seen */
+        int8_t min_temp;
+        /* The maximum battery voltage ever seen */
+        uint16_t max_vbatt;
+        /* The minimum battery voltage ever seen */
+        uint16_t min_vbatt;
+        /* The maximum battery current ever seen */
+        int16_t max_ibatt;
+        /* The minimum battery current ever seen */
+        int16_t min_ibatt;
+        /* Field used to verify the integrity of the EEPROM data */
+        uint16_t checksum;
+    };
+    /* The number of elements in struct BatteryHistory */
+    const int kNumBatteryHistoryFields = 19;
+
+    int64_t report_time_ = 0;
+    int64_t getTimeSecs();
+
+    bool checkLogEvent(struct BatteryHistory hist);
+    void reportEvent(struct BatteryHistory hist);
+};
+
+}  // namespace pixel
+}  // namespace google
+}  // namespace hardware
+}  // namespace android
+
+#endif  // HARDWARE_GOOGLE_PIXEL_PIXELSTATS_BATTERYEEPROMREPORTER_H
diff --git a/pixelstats/include/pixelstats/SysfsCollector.h b/pixelstats/include/pixelstats/SysfsCollector.h
index c6cc608..bbe116f 100644
--- a/pixelstats/include/pixelstats/SysfsCollector.h
+++ b/pixelstats/include/pixelstats/SysfsCollector.h
@@ -20,6 +20,8 @@
 #include <android/frameworks/stats/1.0/IStats.h>
 #include <utils/StrongPointer.h>
 
+#include "BatteryEEPROMReporter.h"
+
 using android::sp;
 using android::frameworks::stats::V1_0::IStats;
 using android::frameworks::stats::V1_0::SlowIo;
@@ -50,6 +52,7 @@
         const char *const UserdataBlockProp;
         const char *const ZramMmStatPath;
         const char *const ZramBdStatPath;
+        const char *const EEPROMPath;
     };
 
     SysfsCollector(const struct SysfsPaths &paths);
@@ -71,6 +74,7 @@
     void logF2fsStats();
     void logZramStats();
     void logBootStats();
+    void logBatteryEEPROM();
 
     void reportSlowIoFromFile(const char *path, const SlowIo::IoOperation &operation_s);
     void reportZramMmStat();
@@ -96,8 +100,11 @@
     const char *const kUserdataBlockProp;
     const char *const kZramMmStatPath;
     const char *const kZramBdStatPath;
+    const char *const kEEPROMPath;
     sp<IStats> stats_;
 
+    BatteryEEPROMReporter battery_EEPROM_reporter_;
+
     // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so
     // store everything in the values array at the index of the field number
     // -2.
diff --git a/pixelstats/pixelatoms.proto b/pixelstats/pixelatoms.proto
index 857e66f..a920783 100644
--- a/pixelstats/pixelatoms.proto
+++ b/pixelstats/pixelatoms.proto
@@ -46,6 +46,8 @@
     DEVICE_ORIENTATION = 105009;
     FG_CAPACITY = 105010;
     PD_VID_PID = 105011;
+    BATTERY_EEPROM = 105012;
+
     // AOSP atom ID range ends at 109999
 }
 
@@ -257,3 +259,51 @@
     /* Product ID of wired charger */
     optional int32 pid = 3;
 }
+
+message BatteryEEPROM {
+    /* The cycle count number; record of charge/discharge times */
+    optional int32 cycle_cnt = 2;
+    /* The current full capacity of the battery under nominal conditions */
+    optional int32 full_cap = 3;
+    /* The battery equivalent series resistance */
+    optional int32 esr = 4;
+    /* Battery resistance related to temperature change */
+    optional int32 rslow = 5;
+    /* Battery health indicator reflecting the battery age state */
+    optional int32 soh = 6;
+    /* The battery temperature */
+    optional int32 batt_temp = 7;
+
+    /* Battery state of charge (SOC) shutdown point */
+    optional int32 cutoff_soc = 8;
+    /* Raw battery state of charge (SOC), based on battery current (CC = Coulomb Counter) */
+    optional int32 cc_soc = 9;
+    /* Estimated battery state of charge (SOC) from batt_soc with endpoint limiting (0% and 100%) */
+    optional int32 sys_soc = 10;
+    /* Filtered monotonic SOC, handles situations where the cutoff_soc is increased and
+     * then decreased from the battery physical properties
+     */
+    optional int32 msoc = 11;
+    /* Estimated SOC derived from cc_soc that provides voltage loop feedback correction using
+     * battery voltage, current, and status values
+     */
+    optional int32 batt_soc = 12;
+
+    /* Field used for data padding in the EEPROM data */
+    optional int32 reserve = 13;
+
+    /* The maximum battery temperature ever seen */
+    optional int32 max_temp = 14;
+    /* The minimum battery temperature ever seen */
+    optional int32 min_temp = 15;
+    /* The maximum battery voltage ever seen */
+    optional int32 max_vbatt = 16;
+    /* The minimum battery voltage ever seen */
+    optional int32 min_vbatt = 17;
+    /* The maximum battery current ever seen */
+    optional int32 max_ibatt = 18;
+    /* The minimum battery current ever seen */
+    optional int32 min_ibatt = 19;
+    /* Field used to verify the integrity of the EEPROM data */
+    optional int32 checksum = 20;
+}