1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "pixelstats: BatteryEEPROM"
18 #define BATTERY_CYCLE_COUNT_PATH "/sys/class/power_supply/battery/cycle_count"
19 
20 #include <log/log.h>
21 #include <time.h>
22 #include <utils/Timers.h>
23 #include <cinttypes>
24 #include <cmath>
25 
26 #include <android-base/file.h>
27 #include <android-base/parseint.h>
28 #include <android-base/strings.h>
29 #include <pixelstats/BatteryEEPROMReporter.h>
30 #include <pixelstats/StatsHelper.h>
31 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
32 
33 namespace android {
34 namespace hardware {
35 namespace google {
36 namespace pixel {
37 
38 using aidl::android::frameworks::stats::VendorAtom;
39 using aidl::android::frameworks::stats::VendorAtomValue;
40 using android::base::ReadFileToString;
41 using android::hardware::google::pixel::PixelAtoms::BatteryEEPROM;
42 
43 #define LINESIZE 31
44 #define LINESIZE_MAX17201_HIST 80
45 
BatteryEEPROMReporter()46 BatteryEEPROMReporter::BatteryEEPROMReporter() {}
47 
ReadFileToInt(const std::string & path,int16_t * val)48 bool BatteryEEPROMReporter::ReadFileToInt(const std::string &path, int16_t *val) {
49     std::string file_contents;
50 
51     if (!ReadFileToString(path.c_str(), &file_contents)) {
52         ALOGI("Unable to read %s - %s", path.c_str(), strerror(errno));
53         return false;
54     }
55 
56     file_contents = android::base::Trim(file_contents);
57     if (!android::base::ParseInt(file_contents, val)) {
58         ALOGI("Unable to convert %s to int - %s", path.c_str(), strerror(errno));
59         return false;
60     }
61 
62     return true;
63 }
64 
setAtomFieldValue(std::vector<VendorAtomValue> * values,int offset,int content)65 void BatteryEEPROMReporter::setAtomFieldValue(std::vector<VendorAtomValue> *values, int offset,
66                                               int content) {
67     std::vector<VendorAtomValue> &val = *values;
68 
69     if (offset - kVendorAtomOffset < val.size())
70         val[offset - kVendorAtomOffset].set<VendorAtomValue::intValue>(content);
71 }
72 
checkAndReport(const std::shared_ptr<IStats> & stats_client,const std::string & path)73 void BatteryEEPROMReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client,
74                                            const std::string &path) {
75     std::string file_contents;
76     std::string history_each;
77     std::string cycle_count;
78 
79     const std::string cycle_count_path(BATTERY_CYCLE_COUNT_PATH);
80     int sparse_index_count = 0;
81 
82     const int kSecondsPerMonth = 60 * 60 * 24 * 30;
83     int64_t now = getTimeSecs();
84 
85     if ((report_time_ != 0) && (now - report_time_ < kSecondsPerMonth)) {
86         ALOGD("Not upload time. now: %" PRId64 ", pre: %" PRId64, now, report_time_);
87         return;
88     }
89 
90     if (!ReadFileToString(path.c_str(), &file_contents)) {
91         ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno));
92         return;
93     }
94 
95     const int kHistTotalLen = file_contents.size();
96     const int kHistTotalNum = kHistTotalLen / LINESIZE;
97     ALOGD("kHistTotalLen=%d, kHistTotalNum=%d\n", kHistTotalLen, kHistTotalNum);
98 
99     /* TODO: wait for pa/2875004 merge
100     if (ReadFileToString(cycle_count_path.c_str(), &cycle_count)) {
101         int cnt;
102 
103         cycle_count = android::base::Trim(cycle_count);
104         if (android::base::ParseInt(cycle_count, &cnt)) {
105             cnt /= 10;
106             if (cnt > kHistTotalNum)
107                 sparse_index_count = cnt % kHistTotalNum;
108         }
109 
110         ALOGD("sparse_index_count %d cnt: %d cycle_count %s\n", sparse_index_count, cnt,
111               cycle_count.c_str());
112     }
113     */
114 
115     struct BatteryHistoryRawFormat hist_raw;
116     struct BatteryHistory hist;
117     int16_t i;
118 
119     ReadFileToInt(kBatteryPairingPath, &hist.battery_pairing);
120 
121     for (i = 0; i < kHistTotalNum; i++) {
122         size_t history_offset = i * LINESIZE;
123         if (history_offset + LINESIZE > kHistTotalLen)
124             break;
125         history_each = file_contents.substr(history_offset, LINESIZE);
126         unsigned int data[4];
127 
128         /* Format transfer: go/gsx01-eeprom */
129         int16_t num = sscanf(history_each.c_str(), "%4" SCNx16 "%4" SCNx16 "%x %x %x %x",
130                       &hist_raw.tempco, &hist_raw.rcomp0, &data[0], &data[1], &data[2], &data[3]);
131         if (num <= 0)
132             continue;
133 
134         if (hist_raw.tempco == 0xFFFF && hist_raw.rcomp0 == 0xFFFF)
135             continue;
136 
137         /* Extract each data */
138         uint64_t tmp = (int64_t)data[3] << 48 |
139                        (int64_t)data[2] << 32 |
140                        (int64_t)data[1] << 16 |
141                        data[0];
142 
143         /* ignore this data if unreasonable */
144         if (tmp <= 0)
145             continue;
146 
147         /* data format/unit in go/gsx01-eeprom#heading=h.finy98ign34p */
148         hist_raw.timer_h = tmp & 0xFF;
149         hist_raw.fullcapnom = (tmp >>= 8) & 0x3FF;
150         hist_raw.fullcaprep = (tmp >>= 10) & 0x3FF;
151         hist_raw.mixsoc = (tmp >>= 10) & 0x3F;
152         hist_raw.vfsoc = (tmp >>= 6) & 0x3F;
153         hist_raw.maxvolt = (tmp >>= 6) & 0xF;
154         hist_raw.minvolt = (tmp >>= 4) & 0xF;
155         hist_raw.maxtemp = (tmp >>= 4) & 0xF;
156         hist_raw.mintemp = (tmp >>= 4) & 0xF;
157         hist_raw.maxchgcurr = (tmp >>= 4) & 0xF;
158         hist_raw.maxdischgcurr = (tmp >>= 4) & 0xF;
159 
160         /* Mapping to original format to collect data */
161         /* go/pixel-battery-eeprom-atom#heading=h.dcawdjiz2ls6 */
162         hist.tempco = hist_raw.tempco;
163         hist.rcomp0 = hist_raw.rcomp0;
164         hist.timer_h = (uint8_t)hist_raw.timer_h * 5;
165         hist.max_temp = (int8_t)hist_raw.maxtemp * 3 + 22;
166         hist.min_temp = (int8_t)hist_raw.mintemp * 3 - 20;
167         hist.min_ibatt = (int16_t)hist_raw.maxchgcurr * 500 * (-1);
168         hist.max_ibatt = (int16_t)hist_raw.maxdischgcurr * 500;
169         hist.min_vbatt = (uint16_t)hist_raw.minvolt * 10 + 2500;
170         hist.max_vbatt = (uint16_t)hist_raw.maxvolt * 20 + 4200;
171         hist.batt_soc = (uint8_t)hist_raw.vfsoc * 2;
172         hist.msoc = (uint8_t)hist_raw.mixsoc * 2;
173         hist.full_cap = (int16_t)hist_raw.fullcaprep * 125 / 1000;
174         hist.full_rep = (int16_t)hist_raw.fullcapnom * 125 / 1000;
175 
176         /* i < sparse_index_count: 20 40 60 80  */
177         if (i < sparse_index_count)
178             hist.cycle_cnt = (i + 1) * 20;
179         else
180             hist.cycle_cnt = (i + sparse_index_count + 1) * 10;
181 
182         reportEvent(stats_client, hist);
183         report_time_ = getTimeSecs();
184     }
185     return;
186 }
187 
getTimeSecs(void)188 int64_t BatteryEEPROMReporter::getTimeSecs(void) {
189     return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
190 }
191 
192 /**
193  * @return true if a log should be reported, else false.
194  * Here we use checksum to confirm the data is usable or not.
195  * The checksum mismatch when storage data overflow or corrupt.
196  * We don't need data in such cases.
197  */
checkLogEvent(struct BatteryHistory hist)198 bool BatteryEEPROMReporter::checkLogEvent(struct BatteryHistory hist) {
199     int checksum = 0;
200 
201     checksum = hist.cycle_cnt + hist.full_cap + hist.esr + hist.rslow
202                 + hist.soh + hist.batt_temp + hist.cutoff_soc + hist.cc_soc
203                 + hist.sys_soc + hist.msoc + hist.batt_soc + hist.reserve
204                 + hist.max_temp + hist.min_temp + hist.max_vbatt
205                 + hist.min_vbatt + hist.max_ibatt + hist.min_ibatt;
206     /* Compare with checksum data */
207     if (checksum == hist.checksum) {
208         return true;
209     } else {
210         return false;
211     }
212 }
213 
reportEvent(const std::shared_ptr<IStats> & stats_client,const struct BatteryHistory & hist)214 void BatteryEEPROMReporter::reportEvent(const std::shared_ptr<IStats> &stats_client,
215                                         const struct BatteryHistory &hist) {
216     // upload atom
217     const std::vector<int> eeprom_history_fields = {
218             BatteryEEPROM::kCycleCntFieldNumber,  BatteryEEPROM::kFullCapFieldNumber,
219             BatteryEEPROM::kEsrFieldNumber,       BatteryEEPROM::kRslowFieldNumber,
220             BatteryEEPROM::kSohFieldNumber,       BatteryEEPROM::kBattTempFieldNumber,
221             BatteryEEPROM::kCutoffSocFieldNumber, BatteryEEPROM::kCcSocFieldNumber,
222             BatteryEEPROM::kSysSocFieldNumber,    BatteryEEPROM::kMsocFieldNumber,
223             BatteryEEPROM::kBattSocFieldNumber,   BatteryEEPROM::kReserveFieldNumber,
224             BatteryEEPROM::kMaxTempFieldNumber,   BatteryEEPROM::kMinTempFieldNumber,
225             BatteryEEPROM::kMaxVbattFieldNumber,  BatteryEEPROM::kMinVbattFieldNumber,
226             BatteryEEPROM::kMaxIbattFieldNumber,  BatteryEEPROM::kMinIbattFieldNumber,
227             BatteryEEPROM::kChecksumFieldNumber,  BatteryEEPROM::kTempcoFieldNumber,
228             BatteryEEPROM::kRcomp0FieldNumber,    BatteryEEPROM::kTimerHFieldNumber,
229             BatteryEEPROM::kFullRepFieldNumber,   BatteryEEPROM::kBatteryPairingFieldNumber};
230 
231     ALOGD("reportEvent: cycle_cnt:%d, full_cap:%d, esr:%d, rslow:%d, soh:%d, "
232           "batt_temp:%d, cutoff_soc:%d, cc_soc:%d, sys_soc:%d, msoc:%d, "
233           "batt_soc:%d, reserve:%d, max_temp:%d, min_temp:%d, max_vbatt:%d, "
234           "min_vbatt:%d, max_ibatt:%d, min_ibatt:%d, checksum:%#x, full_rep:%d, "
235           "tempco:%#x, rcomp0:%#x, timer_h:%d, batt_pair:%d",
236           hist.cycle_cnt, hist.full_cap, hist.esr, hist.rslow, hist.soh, hist.batt_temp,
237           hist.cutoff_soc, hist.cc_soc, hist.sys_soc, hist.msoc, hist.batt_soc, hist.reserve,
238           hist.max_temp, hist.min_temp, hist.max_vbatt, hist.min_vbatt, hist.max_ibatt,
239           hist.min_ibatt, hist.checksum, hist.full_rep, hist.tempco, hist.rcomp0, hist.timer_h,
240           hist.battery_pairing);
241 
242     std::vector<VendorAtomValue> values(eeprom_history_fields.size());
243     VendorAtomValue val;
244 
245     val.set<VendorAtomValue::intValue>(hist.cycle_cnt);
246     values[BatteryEEPROM::kCycleCntFieldNumber - kVendorAtomOffset] = val;
247     val.set<VendorAtomValue::intValue>(hist.full_cap);
248     values[BatteryEEPROM::kFullCapFieldNumber - kVendorAtomOffset] = val;
249     val.set<VendorAtomValue::intValue>(hist.esr);
250     values[BatteryEEPROM::kEsrFieldNumber - kVendorAtomOffset] = val;
251     val.set<VendorAtomValue::intValue>(hist.rslow);
252     values[BatteryEEPROM::kRslowFieldNumber - kVendorAtomOffset] = val;
253     val.set<VendorAtomValue::intValue>(hist.soh);
254     values[BatteryEEPROM::kSohFieldNumber - kVendorAtomOffset] = val;
255     val.set<VendorAtomValue::intValue>(hist.batt_temp);
256     values[BatteryEEPROM::kBattTempFieldNumber - kVendorAtomOffset] = val;
257     val.set<VendorAtomValue::intValue>(hist.cutoff_soc);
258     values[BatteryEEPROM::kCutoffSocFieldNumber - kVendorAtomOffset] = val;
259     val.set<VendorAtomValue::intValue>(hist.cc_soc);
260     values[BatteryEEPROM::kCcSocFieldNumber - kVendorAtomOffset] = val;
261     val.set<VendorAtomValue::intValue>(hist.sys_soc);
262     values[BatteryEEPROM::kSysSocFieldNumber - kVendorAtomOffset] = val;
263     val.set<VendorAtomValue::intValue>(hist.msoc);
264     values[BatteryEEPROM::kMsocFieldNumber - kVendorAtomOffset] = val;
265     val.set<VendorAtomValue::intValue>(hist.batt_soc);
266     values[BatteryEEPROM::kBattSocFieldNumber - kVendorAtomOffset] = val;
267     val.set<VendorAtomValue::intValue>(hist.reserve);
268     values[BatteryEEPROM::kReserveFieldNumber - kVendorAtomOffset] = val;
269     val.set<VendorAtomValue::intValue>(hist.max_temp);
270     values[BatteryEEPROM::kMaxTempFieldNumber - kVendorAtomOffset] = val;
271     val.set<VendorAtomValue::intValue>(hist.min_temp);
272     values[BatteryEEPROM::kMinTempFieldNumber - kVendorAtomOffset] = val;
273     val.set<VendorAtomValue::intValue>(hist.max_vbatt);
274     values[BatteryEEPROM::kMaxVbattFieldNumber - kVendorAtomOffset] = val;
275     val.set<VendorAtomValue::intValue>(hist.min_vbatt);
276     values[BatteryEEPROM::kMinVbattFieldNumber - kVendorAtomOffset] = val;
277     val.set<VendorAtomValue::intValue>(hist.max_ibatt);
278     values[BatteryEEPROM::kMaxIbattFieldNumber - kVendorAtomOffset] = val;
279     val.set<VendorAtomValue::intValue>(hist.min_ibatt);
280     values[BatteryEEPROM::kMinIbattFieldNumber - kVendorAtomOffset] = val;
281     val.set<VendorAtomValue::intValue>(hist.checksum);
282     values[BatteryEEPROM::kChecksumFieldNumber - kVendorAtomOffset] = val;
283     val.set<VendorAtomValue::intValue>(hist.tempco);
284     values[BatteryEEPROM::kTempcoFieldNumber - kVendorAtomOffset] = val;
285     val.set<VendorAtomValue::intValue>(hist.rcomp0);
286     values[BatteryEEPROM::kRcomp0FieldNumber - kVendorAtomOffset] = val;
287     val.set<VendorAtomValue::intValue>(hist.timer_h);
288     values[BatteryEEPROM::kTimerHFieldNumber - kVendorAtomOffset] = val;
289     val.set<VendorAtomValue::intValue>(hist.full_rep);
290     values[BatteryEEPROM::kFullRepFieldNumber - kVendorAtomOffset] = val;
291     val.set<VendorAtomValue::intValue>(hist.battery_pairing);
292     values[BatteryEEPROM::kBatteryPairingFieldNumber - kVendorAtomOffset] = val;
293 
294     VendorAtom event = {.reverseDomainName = "",
295                         .atomId = PixelAtoms::Atom::kBatteryEeprom,
296                         .values = std::move(values)};
297     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
298     if (!ret.isOk())
299         ALOGE("Unable to report BatteryEEPROM to Stats service");
300 }
301 
reportEventInt32(const std::shared_ptr<IStats> & stats_client,const struct BatteryHistoryInt32 & hist)302 void BatteryEEPROMReporter::reportEventInt32(const std::shared_ptr<IStats> &stats_client,
303                                              const struct BatteryHistoryInt32 &hist) {
304     std::vector<VendorAtomValue> values(23);
305 
306     ALOGD("reportEvent: cycle_cnt:%d, full_cap:%d, esr:%d, rslow:%d, soh:%d, "
307           "batt_temp:%d, cutoff_soc:%d, cc_soc:%d, sys_soc:%d, msoc:%d, "
308           "batt_soc:%d, reserve:%d, max_temp:%d, min_temp:%d, max_vbatt:%d, "
309           "min_vbatt:%d, max_ibatt:%d, min_ibatt:%d, checksum:%#x, full_rep:%d, "
310           "tempco:%#x, rcomp0:%#x, timer_h:%d",
311           hist.cycle_cnt, hist.full_cap, hist.esr, hist.rslow, hist.soh, hist.batt_temp,
312           hist.cutoff_soc, hist.cc_soc, hist.sys_soc, hist.msoc, hist.batt_soc, hist.reserve,
313           hist.max_temp, hist.min_temp, hist.max_vbatt, hist.min_vbatt, hist.max_ibatt,
314           hist.min_ibatt, hist.checksum, hist.full_rep, hist.tempco, hist.rcomp0, hist.timer_h);
315 
316     setAtomFieldValue(&values, BatteryEEPROM::kCycleCntFieldNumber, hist.cycle_cnt);
317     setAtomFieldValue(&values, BatteryEEPROM::kFullCapFieldNumber, hist.full_cap);
318     setAtomFieldValue(&values, BatteryEEPROM::kEsrFieldNumber, hist.esr);
319     setAtomFieldValue(&values, BatteryEEPROM::kRslowFieldNumber, hist.rslow);
320     setAtomFieldValue(&values, BatteryEEPROM::kSohFieldNumber, hist.soh);
321     setAtomFieldValue(&values, BatteryEEPROM::kBattTempFieldNumber, hist.batt_temp);
322     setAtomFieldValue(&values, BatteryEEPROM::kCutoffSocFieldNumber, hist.cutoff_soc);
323     setAtomFieldValue(&values, BatteryEEPROM::kCcSocFieldNumber, hist.cc_soc);
324     setAtomFieldValue(&values, BatteryEEPROM::kSysSocFieldNumber, hist.sys_soc);
325     setAtomFieldValue(&values, BatteryEEPROM::kMsocFieldNumber, hist.msoc);
326     setAtomFieldValue(&values, BatteryEEPROM::kBattSocFieldNumber, hist.batt_soc);
327     setAtomFieldValue(&values, BatteryEEPROM::kReserveFieldNumber, hist.reserve);
328     setAtomFieldValue(&values, BatteryEEPROM::kMaxTempFieldNumber, hist.max_temp);
329     setAtomFieldValue(&values, BatteryEEPROM::kMinTempFieldNumber, hist.min_temp);
330     setAtomFieldValue(&values, BatteryEEPROM::kMaxVbattFieldNumber, hist.max_vbatt);
331     setAtomFieldValue(&values, BatteryEEPROM::kMinVbattFieldNumber, hist.min_vbatt);
332     setAtomFieldValue(&values, BatteryEEPROM::kMaxIbattFieldNumber, hist.max_ibatt);
333     setAtomFieldValue(&values, BatteryEEPROM::kMinIbattFieldNumber, hist.min_ibatt);
334     setAtomFieldValue(&values, BatteryEEPROM::kChecksumFieldNumber, hist.checksum);
335     setAtomFieldValue(&values, BatteryEEPROM::kTempcoFieldNumber, hist.tempco);
336     setAtomFieldValue(&values, BatteryEEPROM::kRcomp0FieldNumber, hist.rcomp0);
337     setAtomFieldValue(&values, BatteryEEPROM::kTimerHFieldNumber, hist.timer_h);
338     setAtomFieldValue(&values, BatteryEEPROM::kFullRepFieldNumber, hist.full_rep);
339 
340     VendorAtom event = {.reverseDomainName = "",
341                         .atomId = PixelAtoms::Atom::kBatteryEeprom,
342                         .values = std::move(values)};
343     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
344     if (!ret.isOk())
345         ALOGE("Unable to report BatteryEEPROM to Stats service");
346 }
347 
checkAndReportGMSR(const std::shared_ptr<IStats> & stats_client,const std::vector<std::string> & paths)348 void BatteryEEPROMReporter::checkAndReportGMSR(const std::shared_ptr<IStats> &stats_client,
349                                                const std::vector<std::string> &paths) {
350     struct BatteryHistory gmsr = {.checksum = EvtGMSR};
351     std::string file_contents;
352     std::string path;
353     int16_t num;
354 
355     if (paths.empty())
356         return;
357 
358     for (int i = 0; i < paths.size(); i++) {
359         if (fileExists(paths[i])) {
360             path = paths[i];
361             break;
362         }
363     }
364 
365     if (!ReadFileToString(path, &file_contents)) {
366         ALOGE("Unable to read gmsr path: %s - %s", path.c_str(), strerror(errno));
367         return;
368     }
369 
370     num = sscanf(file_contents.c_str(),  "rcomp0\t:%4" SCNx16 "\ntempco\t:%4" SCNx16
371                  "\nfullcaprep\t:%4" SCNx16 "\ncycles\t:%4" SCNx16 "\nfullcapnom\t:%4" SCNx16
372                  "\nqresidual00\t:%4" SCNx16 "\nqresidual10\t:%4" SCNx16
373                  "\nqresidual20\t:%4" SCNx16 "\nqresidual30\t:%4" SCNx16
374                  "\ncv_mixcap\t:%4" SCNx16 "\nhalftime\t:%4" SCNx16,
375                  &gmsr.rcomp0, &gmsr.tempco, &gmsr.full_rep, &gmsr.cycle_cnt, &gmsr.full_cap,
376                  &gmsr.max_vbatt, &gmsr.min_vbatt, &gmsr.max_ibatt, &gmsr.min_ibatt,
377                  &gmsr.esr, &gmsr.rslow);
378     if (num != kNum77759GMSRFields && num != kNum77779GMSRFields) {
379         ALOGE("Couldn't process GMSR. num=%d\n", num);
380         return;
381     }
382 
383     if (gmsr.tempco == 0xFFFF || gmsr.rcomp0 == 0xFFFF || gmsr.full_cap == 0xFFFF) {
384 	    ALOGD("Ignore invalid gmsr");
385 	    return;
386     }
387 
388     reportEvent(stats_client, gmsr);
389 }
390 
checkAndReportMaxfgHistory(const std::shared_ptr<IStats> & stats_client,const std::string & path)391 void BatteryEEPROMReporter::checkAndReportMaxfgHistory(const std::shared_ptr<IStats> &stats_client,
392                                                        const std::string &path) {
393     std::string file_contents;
394     int16_t i;
395 
396     if (path.empty())
397         return;
398 
399     if (!ReadFileToString(path, &file_contents)) {
400         ALOGD("Unable to read maxfg_hist path: %s - %s", path.c_str(), strerror(errno));
401         return;
402     }
403 
404     std::string hist_each;
405     const int kHistTotalLen = file_contents.size();
406 
407     ALOGD("checkAndReportMaxfgHistory:size=%d\n%s", kHistTotalLen, file_contents.c_str());
408 
409     for (i = 0; i < kHistTotalLen; i++) {
410         struct BatteryHistory maxfg_hist;
411         uint16_t nQRTable00, nQRTable10, nQRTable20, nQRTable30, nCycles, nFullCapNom;
412         uint16_t nRComp0, nTempCo, nIAvgEmpty, nFullCapRep, nVoltTemp, nMaxMinCurr, nMaxMinVolt;
413         uint16_t nMaxMinTemp, nSOC, nTimerH;
414         int16_t num;
415         size_t hist_offset = i * LINESIZE_MAX17201_HIST;
416 
417         if (hist_offset >= file_contents.size())
418             break;
419 
420         hist_each = file_contents.substr(hist_offset, LINESIZE_MAX17201_HIST);
421         num = sscanf(hist_each.c_str(), "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16
422                      "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16
423                      "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16,
424                      &nQRTable00, &nQRTable10, &nQRTable20, &nQRTable30, &nCycles, &nFullCapNom,
425                      &nRComp0, &nTempCo, &nIAvgEmpty, &nFullCapRep, &nVoltTemp, &nMaxMinCurr,
426                      &nMaxMinVolt, &nMaxMinTemp, &nSOC, &nTimerH);
427 
428         if (num != kNum17201HISTFields) {
429             ALOGE("Couldn't process %s (num=%d)", hist_each.c_str(), num);
430             continue;
431         }
432 
433         /* not assign: nQRTable00, nQRTable10, nQRTable20, nQRTable30 */
434         maxfg_hist.reserve = 0xFF;
435         maxfg_hist.tempco = nTempCo;
436         maxfg_hist.rcomp0 = nRComp0;
437         maxfg_hist.full_rep = nFullCapNom;
438         maxfg_hist.full_cap = nFullCapRep;
439         maxfg_hist.cycle_cnt = nCycles * 16 / 100; // LSB: 16%;
440         maxfg_hist.timer_h = (nTimerH * 32 / 10) / 24; // LSB: 3.2 hours
441         maxfg_hist.batt_soc = (nSOC >> 8) & 0x00FF;
442         maxfg_hist.msoc = nSOC & 0x00FF;
443         maxfg_hist.max_ibatt = ((nMaxMinCurr >> 8) & 0x00FF) * 80;
444         maxfg_hist.min_ibatt = (nMaxMinCurr & 0x00FF) * 80 * (-1);
445         maxfg_hist.max_vbatt = ((nMaxMinVolt >> 8) & 0x00FF) * 20;
446         maxfg_hist.min_vbatt = (nMaxMinVolt & 0x00FF) * 20;
447         maxfg_hist.max_temp = (nMaxMinTemp >> 8) & 0x00FF;
448         maxfg_hist.min_temp = nMaxMinTemp & 0x00FF;
449         maxfg_hist.esr = nIAvgEmpty;
450         maxfg_hist.rslow = nVoltTemp;
451 
452         reportEvent(stats_client, maxfg_hist);
453     }
454 }
455 
checkAndReportFGModelLoading(const std::shared_ptr<IStats> & client,const std::vector<std::string> & paths)456 void BatteryEEPROMReporter::checkAndReportFGModelLoading(const std::shared_ptr<IStats> &client,
457                                                          const std::vector<std::string> &paths) {
458     struct BatteryHistory params = {.full_cap = 0, .esr = 0, .rslow = 0,
459                                     .checksum = EvtModelLoading, };
460     std::string file_contents;
461     std::string path;
462     int num;
463     const char *data;
464 
465     if (paths.empty())
466         return;
467 
468     for (int i = 0; i < paths.size(); i++) {
469         if (fileExists(paths[i])) {
470             path = paths[i];
471             break;
472         }
473     }
474 
475     /* not found */
476     if (path.empty())
477         return;
478 
479     if (!ReadFileToString(path, &file_contents)) {
480         ALOGE("Unable to read ModelLoading History path: %s - %s", path.c_str(), strerror(errno));
481         return;
482     }
483 
484     data = file_contents.c_str();
485 
486     num = sscanf(data, "ModelNextUpdate: %" SCNu16 "%*[0-9a-f: \n]ATT: %" SCNu16 " FAIL: %" SCNu16,
487                  &params.rslow, &params.full_cap, &params.esr);
488     if (num != 3) {
489         ALOGE("Couldn't process ModelLoading History. num=%d\n", num);
490         return;
491      }
492 
493     /* don't need to report when attempts counter is zero */
494     if (params.full_cap == 0)
495         return;
496 
497     reportEvent(client, params);
498 }
499 
checkAndReportFGLearning(const std::shared_ptr<IStats> & stats_client,const std::vector<std::string> & paths)500 void BatteryEEPROMReporter::checkAndReportFGLearning(const std::shared_ptr<IStats> &stats_client,
501                                                      const std::vector<std::string> &paths) {
502     struct BatteryHistoryInt32 params = {.checksum = EvtFGLearningHistory};
503     std::string path;
504     struct timespec boot_time;
505     auto format = FormatIgnoreAddr;
506     std::vector<std::vector<uint32_t>> events;
507 
508     if (paths.empty())
509         return;
510 
511     for (int i = 0; i < paths.size(); i++) {
512         if (fileExists(paths[i])) {
513             path = paths[i];
514             break;
515         }
516     }
517 
518     /* not found */
519     if (path.empty())
520         return;
521 
522     clock_gettime(CLOCK_MONOTONIC, &boot_time);
523 
524     readLogbuffer(path, kNumFGLearningFieldsV3, params.checksum, format, last_lh_check_, events);
525     if (events.size() == 0)
526         readLogbuffer(path, kNumFGLearningFieldsV2, params.checksum, format, last_lh_check_, events);
527 
528     for (int event_idx = 0; event_idx < events.size(); event_idx++) {
529         std::vector<uint32_t> &event = events[event_idx];
530         if (event.size() == kNumFGLearningFieldsV2 ||
531             event.size() == kNumFGLearningFieldsV3) {
532             params.full_cap = event[0];                /* fcnom */
533             params.esr = event[1];                     /* dpacc */
534             params.rslow = event[2];                   /* dqacc */
535             params.full_rep = event[3];                /* fcrep */
536             params.msoc = (uint8_t)(event[4] >> 8);    /* repsoc */
537             params.sys_soc = (uint8_t)(event[5] >> 8); /* mixsoc */
538             params.batt_soc = (uint8_t)(event[6] >> 8);/* vfsoc */
539             params.min_ibatt = event[7];               /* fstats */
540             params.max_temp = (int8_t)(event[8] >> 8); /* avgtemp */
541             params.min_temp = (int8_t)(event[9] >> 8); /* temp */
542             params.max_ibatt = event[10];              /* qh */
543             params.max_vbatt = event[11];              /* vcell */
544             params.min_vbatt = event[12];              /* avgvcell */
545             params.cycle_cnt = event[13];              /* vfocf */
546             params.rcomp0 = event[14];                 /* rcomp0 */
547             params.tempco = event[15];                 /* tempco */
548             if (event.size() == kNumFGLearningFieldsV3)
549                 params.soh = event[16];                /* unix time */
550         } else {
551             ALOGE("Not support %zu fields for FG learning event", event.size());
552             continue;
553         }
554         reportEventInt32(stats_client, params);
555     }
556     last_lh_check_ = (unsigned int)boot_time.tv_sec;
557 }
558 
checkAndReportValidation(const std::shared_ptr<IStats> & stats_client,const std::vector<std::string> & paths)559 void BatteryEEPROMReporter::checkAndReportValidation(const std::shared_ptr<IStats> &stats_client,
560                                                      const std::vector<std::string> &paths) {
561     struct BatteryHistoryInt32 params = {.checksum = EvtHistoryValidation};
562     std::string path;
563     struct timespec boot_time;
564     auto format = FormatIgnoreAddr;
565     std::vector<std::vector<uint32_t>> events;
566 
567     if (paths.empty())
568         return;
569 
570     for (int i = 0; i < paths.size(); i++) {
571         if (fileExists(paths[i])) {
572             path = paths[i];
573             break;
574         }
575     }
576 
577     /* not found */
578     if (path.empty())
579         return;
580 
581     clock_gettime(CLOCK_MONOTONIC, &boot_time);
582 
583     readLogbuffer(path, kNumValidationFields, params.checksum, format, last_hv_check_, events);
584     for (int event_idx = 0; event_idx < events.size(); event_idx++) {
585         std::vector<uint32_t> &event = events[event_idx];
586         if (event.size() == kNumValidationFields) {
587             params.full_cap = event[0]; /* first empty entry */
588             params.esr = event[1];      /* num of entries need to be recovered or fix result */
589             params.rslow = event[2];    /* last cycle count */
590             params.full_rep = event[3]; /* estimate cycle count after recovery */
591             reportEventInt32(stats_client, params);
592             /* force report history metrics if it was recovered */
593             if (last_hv_check_ != 0) {
594                 report_time_ = 0;
595             }
596         } else {
597             ALOGE("Not support %zu fields for History Validation event", event.size());
598         }
599     }
600     last_hv_check_ = (unsigned int)boot_time.tv_sec;
601 }
602 
603 }  // namespace pixel
604 }  // namespace google
605 }  // namespace hardware
606 }  // namespace android
607