• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,int32_t * val)48 bool BatteryEEPROMReporter::ReadFileToInt(const std::string &path, int32_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 
checkPaths(const std::vector<std::string> & paths)65 std::string BatteryEEPROMReporter::checkPaths(const std::vector<std::string>& paths) {
66     if (paths.empty()) {
67         return ""; // Or throw an exception if appropriate
68     }
69 
70     for (const auto& path : paths) { // Use range-based for loop
71         if (fileExists(path)) {
72             return path;
73         }
74     }
75 
76     return ""; // No path found
77 }
78 
checkAndReport(const std::shared_ptr<IStats> & stats_client,const std::string & path)79 void BatteryEEPROMReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client,
80                                            const std::string &path) {
81     std::string file_contents;
82     std::string history_each;
83     std::string cycle_count;
84 
85     const std::string cycle_count_path(BATTERY_CYCLE_COUNT_PATH);
86     int sparse_index_count = 0;
87 
88     const int kSecondsPerMonth = 60 * 60 * 24 * 30;
89     int64_t now = getTimeSecs();
90 
91     if ((report_time_ != 0) && (now - report_time_ < kSecondsPerMonth)) {
92         ALOGD("Not upload time. now: %" PRId64 ", pre: %" PRId64, now, report_time_);
93         return;
94     }
95 
96     if (!ReadFileToString(path.c_str(), &file_contents)) {
97         ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno));
98         return;
99     }
100 
101     const int kHistTotalLen = file_contents.size();
102     const int kHistTotalNum = kHistTotalLen / LINESIZE;
103     ALOGD("kHistTotalLen=%d, kHistTotalNum=%d\n", kHistTotalLen, kHistTotalNum);
104 
105     /* TODO: wait for pa/2875004 merge
106     if (ReadFileToString(cycle_count_path.c_str(), &cycle_count)) {
107         int cnt;
108 
109         cycle_count = android::base::Trim(cycle_count);
110         if (android::base::ParseInt(cycle_count, &cnt)) {
111             cnt /= 10;
112             if (cnt > kHistTotalNum)
113                 sparse_index_count = cnt % kHistTotalNum;
114         }
115 
116         ALOGD("sparse_index_count %d cnt: %d cycle_count %s\n", sparse_index_count, cnt,
117               cycle_count.c_str());
118     }
119     */
120 
121     struct BatteryEEPROMPipelineRawFormat hist_raw;
122     struct BatteryEEPROMPipeline hist;
123     int16_t i;
124 
125     ReadFileToInt(kBatteryPairingPath, &hist.battery_pairing);
126 
127     for (i = 0; i < kHistTotalNum; i++) {
128         size_t history_offset = i * LINESIZE;
129         if (history_offset + LINESIZE > kHistTotalLen)
130             break;
131         history_each = file_contents.substr(history_offset, LINESIZE);
132         unsigned int data[4];
133 
134         /* Format transfer: go/gsx01-eeprom */
135         int16_t num = sscanf(history_each.c_str(), "%4" SCNx16 "%4" SCNx16 "%x %x %x %x",
136                       &hist_raw.tempco, &hist_raw.rcomp0, &data[0], &data[1], &data[2], &data[3]);
137         if (num <= 0)
138             continue;
139 
140         if (hist_raw.tempco == 0xFFFF && hist_raw.rcomp0 == 0xFFFF)
141             continue;
142 
143         /* Extract each data */
144         uint64_t tmp = (int64_t)data[3] << 48 |
145                        (int64_t)data[2] << 32 |
146                        (int64_t)data[1] << 16 |
147                        data[0];
148 
149         /* ignore this data if unreasonable */
150         if (tmp <= 0)
151             continue;
152 
153         /* data format/unit in go/gsx01-eeprom#heading=h.finy98ign34p */
154         hist_raw.timer_h = tmp & 0xFF;
155         hist_raw.fullcapnom = (tmp >>= 8) & 0x3FF;
156         hist_raw.fullcaprep = (tmp >>= 10) & 0x3FF;
157         hist_raw.mixsoc = (tmp >>= 10) & 0x3F;
158         hist_raw.vfsoc = (tmp >>= 6) & 0x3F;
159         hist_raw.maxvolt = (tmp >>= 6) & 0xF;
160         hist_raw.minvolt = (tmp >>= 4) & 0xF;
161         hist_raw.maxtemp = (tmp >>= 4) & 0xF;
162         hist_raw.mintemp = (tmp >>= 4) & 0xF;
163         hist_raw.maxchgcurr = (tmp >>= 4) & 0xF;
164         hist_raw.maxdischgcurr = (tmp >>= 4) & 0xF;
165 
166         /* Mapping to original format to collect data */
167         /* go/pixel-battery-eeprom-atom#heading=h.dcawdjiz2ls6 */
168         hist.tempco = (int32_t)hist_raw.tempco;
169         hist.rcomp0 = (int32_t)hist_raw.rcomp0;
170         hist.timer_h = (int32_t)hist_raw.timer_h * 5;
171         hist.max_temp = (int32_t)hist_raw.maxtemp * 3 + 22;
172         hist.min_temp = (int32_t)hist_raw.mintemp * 3 - 20;
173         hist.min_ibatt = (int32_t)hist_raw.maxchgcurr * 500 * (-1);
174         hist.max_ibatt = (int32_t)hist_raw.maxdischgcurr * 500;
175         hist.min_vbatt = (int32_t)hist_raw.minvolt * 10 + 2500;
176         hist.max_vbatt = (int32_t)hist_raw.maxvolt * 20 + 4200;
177         hist.batt_soc = (int32_t)hist_raw.vfsoc * 2;
178         hist.msoc = (int32_t)hist_raw.mixsoc * 2;
179         hist.full_cap = (int32_t)hist_raw.fullcaprep * 125 / 1000;
180         hist.full_rep = (int32_t)hist_raw.fullcapnom * 125 / 1000;
181 
182         /* i < sparse_index_count: 20 40 60 80  */
183         if (i < sparse_index_count)
184             hist.cycle_cnt = (i + 1) * 20;
185         else
186             hist.cycle_cnt = (i + sparse_index_count + 1) * 10;
187 
188         reportEvent(stats_client, hist);
189         report_time_ = getTimeSecs();
190     }
191     return;
192 }
193 
getTimeSecs(void)194 int64_t BatteryEEPROMReporter::getTimeSecs(void) {
195     return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
196 }
197 
reportEvent(const std::shared_ptr<IStats> & stats_client,const struct BatteryEEPROMPipeline & hist)198 void BatteryEEPROMReporter::reportEvent(const std::shared_ptr<IStats> &stats_client,
199                                         const struct BatteryEEPROMPipeline &hist) {
200     std::vector<VendorAtomValue> values(kNumEEPROMPipelineFields);
201 
202     ALOGD("reportEvent: cycle_cnt:%d, full_cap:%d, esr:%d, rslow:%d, soh:%d, "
203           "batt_temp:%d, cutoff_soc:%d, cc_soc:%d, sys_soc:%d, msoc:%d, "
204           "batt_soc:%d, reserve:%d, max_temp:%d, min_temp:%d, max_vbatt:%d, "
205           "min_vbatt:%d, max_ibatt:%d, min_ibatt:%d, checksum:%#x, full_rep:%d, "
206           "tempco:%#x, rcomp0:%#x, timer_h:%d, batt_pair:%d",
207           hist.cycle_cnt, hist.full_cap, hist.esr, hist.rslow, hist.soh, hist.batt_temp,
208           hist.cutoff_soc, hist.cc_soc, hist.sys_soc, hist.msoc, hist.batt_soc, hist.reserve,
209           hist.max_temp, hist.min_temp, hist.max_vbatt, hist.min_vbatt, hist.max_ibatt,
210           hist.min_ibatt, hist.checksum, hist.full_rep, hist.tempco, hist.rcomp0, hist.timer_h,
211           hist.battery_pairing);
212 
213     setAtomFieldValue(&values, BatteryEEPROM::kCycleCntFieldNumber, hist.cycle_cnt);
214     setAtomFieldValue(&values, BatteryEEPROM::kFullCapFieldNumber, hist.full_cap);
215     setAtomFieldValue(&values, BatteryEEPROM::kEsrFieldNumber, hist.esr);
216     setAtomFieldValue(&values, BatteryEEPROM::kRslowFieldNumber, hist.rslow);
217     setAtomFieldValue(&values, BatteryEEPROM::kSohFieldNumber, hist.soh);
218     setAtomFieldValue(&values, BatteryEEPROM::kBattTempFieldNumber, hist.batt_temp);
219     setAtomFieldValue(&values, BatteryEEPROM::kCutoffSocFieldNumber, hist.cutoff_soc);
220     setAtomFieldValue(&values, BatteryEEPROM::kCcSocFieldNumber, hist.cc_soc);
221     setAtomFieldValue(&values, BatteryEEPROM::kSysSocFieldNumber, hist.sys_soc);
222     setAtomFieldValue(&values, BatteryEEPROM::kMsocFieldNumber, hist.msoc);
223     setAtomFieldValue(&values, BatteryEEPROM::kBattSocFieldNumber, hist.batt_soc);
224     setAtomFieldValue(&values, BatteryEEPROM::kReserveFieldNumber, hist.reserve);
225     setAtomFieldValue(&values, BatteryEEPROM::kMaxTempFieldNumber, hist.max_temp);
226     setAtomFieldValue(&values, BatteryEEPROM::kMinTempFieldNumber, hist.min_temp);
227     setAtomFieldValue(&values, BatteryEEPROM::kMaxVbattFieldNumber, hist.max_vbatt);
228     setAtomFieldValue(&values, BatteryEEPROM::kMinVbattFieldNumber, hist.min_vbatt);
229     setAtomFieldValue(&values, BatteryEEPROM::kMaxIbattFieldNumber, hist.max_ibatt);
230     setAtomFieldValue(&values, BatteryEEPROM::kMinIbattFieldNumber, hist.min_ibatt);
231     setAtomFieldValue(&values, BatteryEEPROM::kChecksumFieldNumber, hist.checksum);
232     setAtomFieldValue(&values, BatteryEEPROM::kTempcoFieldNumber, hist.tempco);
233     setAtomFieldValue(&values, BatteryEEPROM::kRcomp0FieldNumber, hist.rcomp0);
234     setAtomFieldValue(&values, BatteryEEPROM::kTimerHFieldNumber, hist.timer_h);
235     setAtomFieldValue(&values, BatteryEEPROM::kFullRepFieldNumber, hist.full_rep);
236     setAtomFieldValue(&values, BatteryEEPROM::kBatteryPairingFieldNumber, hist.battery_pairing);
237 
238     VendorAtom event = {.reverseDomainName = "",
239                         .atomId = PixelAtoms::Atom::kBatteryEeprom,
240                         .values = std::move(values)};
241     reportVendorAtom(stats_client, event);
242 }
243 
checkAndReportGMSR(const std::shared_ptr<IStats> & stats_client,const std::vector<std::string> & paths)244 void BatteryEEPROMReporter::checkAndReportGMSR(const std::shared_ptr<IStats> &stats_client,
245                                                const std::vector<std::string> &paths) {
246     struct BatteryEEPROMPipeline gmsr = {.checksum = EvtGMSR};
247     std::string path = checkPaths(paths);
248     std::string file_contents;
249     int16_t num;
250 
251     if (path.empty())
252         return;
253 
254     if (!ReadFileToString(path, &file_contents)) {
255         ALOGE("Unable to read gmsr path: %s - %s", path.c_str(), strerror(errno));
256         return;
257     }
258 
259     num = sscanf(file_contents.c_str(), "rcomp0\t:%x\ntempco\t:%x\nfullcaprep\t:%x\ncycles\t:%x"
260                  "\nfullcapnom\t:%x\nqresidual00\t:%x\nqresidual10\t:%x\nqresidual20\t:%x"
261                  "\nqresidual30\t:%x\ncv_mixcap\t:%x\nhalftime\t:%x",
262                  &gmsr.rcomp0, &gmsr.tempco, &gmsr.full_rep, &gmsr.cycle_cnt, &gmsr.full_cap,
263                  &gmsr.max_vbatt, &gmsr.min_vbatt, &gmsr.max_ibatt, &gmsr.min_ibatt,
264                  &gmsr.esr, &gmsr.rslow);
265     if (num != kNum77759GMSRFields && num != kNum77779GMSRFields) {
266         ALOGE("Couldn't process GMSR. num=%d\n", num);
267         return;
268     }
269 
270     if (gmsr.tempco == 0xFFFF || gmsr.rcomp0 == 0xFFFF || gmsr.full_cap == 0xFFFF) {
271 	    ALOGD("Ignore invalid gmsr");
272 	    return;
273     }
274 
275     reportEvent(stats_client, gmsr);
276 }
277 
checkAndReportMaxfgHistory(const std::shared_ptr<IStats> & stats_client,const std::string & path)278 void BatteryEEPROMReporter::checkAndReportMaxfgHistory(const std::shared_ptr<IStats> &stats_client,
279                                                        const std::string &path) {
280     std::string file_contents;
281     int16_t i;
282 
283     if (path.empty())
284         return;
285 
286     /* not support max17201 */
287     if (!ReadFileToString(path, &file_contents))
288         return;
289 
290     std::string hist_each;
291     const int kHistTotalLen = file_contents.size();
292 
293     ALOGD("checkAndReportMaxfgHistory:size=%d\n%s", kHistTotalLen, file_contents.c_str());
294 
295     for (i = 0; i < kHistTotalLen; i++) {
296         struct BatteryEEPROMPipeline maxfg_hist;
297         uint16_t nQRTable00, nQRTable10, nQRTable20, nQRTable30, nCycles, nFullCapNom;
298         uint16_t nRComp0, nTempCo, nIAvgEmpty, nFullCapRep, nVoltTemp, nMaxMinCurr, nMaxMinVolt;
299         uint16_t nMaxMinTemp, nSOC, nTimerH;
300         int16_t num;
301         size_t hist_offset = i * LINESIZE_MAX17201_HIST;
302 
303         if (hist_offset >= file_contents.size())
304             break;
305 
306         hist_each = file_contents.substr(hist_offset, LINESIZE_MAX17201_HIST);
307         num = sscanf(hist_each.c_str(), "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16
308                      "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16
309                      "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16,
310                      &nQRTable00, &nQRTable10, &nQRTable20, &nQRTable30, &nCycles, &nFullCapNom,
311                      &nRComp0, &nTempCo, &nIAvgEmpty, &nFullCapRep, &nVoltTemp, &nMaxMinCurr,
312                      &nMaxMinVolt, &nMaxMinTemp, &nSOC, &nTimerH);
313 
314         if (num != kNum17201HISTFields) {
315             ALOGE("Couldn't process %s (num=%d)", hist_each.c_str(), num);
316             continue;
317         }
318 
319         /* not assign: nQRTable00, nQRTable10, nQRTable20, nQRTable30 */
320         maxfg_hist.reserve = 0xFF;
321         maxfg_hist.tempco = nTempCo;
322         maxfg_hist.rcomp0 = nRComp0;
323         maxfg_hist.full_rep = nFullCapNom;
324         maxfg_hist.full_cap = nFullCapRep;
325         maxfg_hist.cycle_cnt = nCycles * 16 / 100; // LSB: 16%;
326         maxfg_hist.timer_h = (nTimerH * 32 / 10) / 24; // LSB: 3.2 hours
327         maxfg_hist.batt_soc = (nSOC >> 8) & 0x00FF;
328         maxfg_hist.msoc = nSOC & 0x00FF;
329         maxfg_hist.max_ibatt = ((nMaxMinCurr >> 8) & 0x00FF) * 80;
330         maxfg_hist.min_ibatt = (nMaxMinCurr & 0x00FF) * 80 * (-1);
331         maxfg_hist.max_vbatt = ((nMaxMinVolt >> 8) & 0x00FF) * 20;
332         maxfg_hist.min_vbatt = (nMaxMinVolt & 0x00FF) * 20;
333         maxfg_hist.max_temp = (nMaxMinTemp >> 8) & 0x00FF;
334         maxfg_hist.min_temp = nMaxMinTemp & 0x00FF;
335         maxfg_hist.esr = nIAvgEmpty;
336         maxfg_hist.rslow = nVoltTemp;
337 
338         reportEvent(stats_client, maxfg_hist);
339     }
340 }
341 
checkAndReportFGModelLoading(const std::shared_ptr<IStats> & client,const std::vector<std::string> & paths)342 void BatteryEEPROMReporter::checkAndReportFGModelLoading(const std::shared_ptr<IStats> &client,
343                                                          const std::vector<std::string> &paths) {
344     struct BatteryEEPROMPipeline params = {.full_cap = 0, .esr = 0, .rslow = 0,
345                                     .checksum = EvtModelLoading, };
346     std::string path = checkPaths(paths);
347     std::string file_contents;
348     int num;
349     const char *data;
350 
351     if (path.empty())
352         return;
353 
354     /* not found */
355     if (path.empty())
356         return;
357 
358     if (!ReadFileToString(path, &file_contents)) {
359         ALOGE("Unable to read ModelLoading History path: %s - %s", path.c_str(), strerror(errno));
360         return;
361     }
362 
363     data = file_contents.c_str();
364 
365     num = sscanf(data, "ModelNextUpdate: %x%*[0-9a-f: \n]ATT: %x FAIL: %x",
366                  &params.rslow, &params.full_cap, &params.esr);
367     if (num != 3) {
368         ALOGE("Couldn't process ModelLoading History. num=%d\n", num);
369         return;
370      }
371 
372     /* don't need to report when attempts counter is zero */
373     if (params.full_cap == 0)
374         return;
375 
376     reportEvent(client, params);
377 }
378 
checkAndReportFGLearning(const std::shared_ptr<IStats> & stats_client,const std::vector<std::string> & paths)379 void BatteryEEPROMReporter::checkAndReportFGLearning(const std::shared_ptr<IStats> &stats_client,
380                                                      const std::vector<std::string> &paths) {
381     struct BatteryEEPROMPipeline params = {.checksum = EvtFGLearningHistory};
382     std::string path = checkPaths(paths);
383     struct timespec boot_time;
384     auto format = FormatIgnoreAddr;
385     std::vector<std::vector<uint32_t>> events;
386 
387     if (path.empty())
388         return;
389 
390     clock_gettime(CLOCK_MONOTONIC, &boot_time);
391 
392     readLogbuffer(path, kNumFGLearningFieldsV3, params.checksum, format, last_lh_check_, events);
393     if (events.size() == 0)
394         readLogbuffer(path, kNumFGLearningFieldsV2, params.checksum, format, last_lh_check_, events);
395 
396     for (int event_idx = 0; event_idx < events.size(); event_idx++) {
397         std::vector<uint32_t> &event = events[event_idx];
398         if (event.size() == kNumFGLearningFieldsV2 ||
399             event.size() == kNumFGLearningFieldsV3) {
400             params.full_cap = event[0];                /* fcnom */
401             params.esr = event[1];                     /* dpacc */
402             params.rslow = event[2];                   /* dqacc */
403             params.full_rep = event[3];                /* fcrep */
404             params.msoc = event[4] >> 8;               /* repsoc */
405             params.sys_soc = event[5] >> 8;            /* mixsoc */
406             params.batt_soc = event[6] >> 8;           /* vfsoc */
407             params.min_ibatt = event[7];               /* fstats */
408             params.max_temp = event[8] >> 8;           /* avgtemp */
409             params.min_temp = event[9] >> 8;           /* temp */
410             params.max_ibatt = event[10];              /* qh */
411             params.max_vbatt = event[11];              /* vcell */
412             params.min_vbatt = event[12];              /* avgvcell */
413             params.cycle_cnt = event[13];              /* vfocf */
414             params.rcomp0 = event[14];                 /* rcomp0 */
415             params.tempco = event[15];                 /* tempco */
416             if (event.size() == kNumFGLearningFieldsV3)
417                 params.soh = event[16];                /* unix time */
418         } else {
419             ALOGE("Not support %zu fields for FG learning event", event.size());
420             continue;
421         }
422         reportEvent(stats_client, params);
423     }
424     last_lh_check_ = (unsigned int)boot_time.tv_sec;
425 }
426 
checkAndReportValidation(const std::shared_ptr<IStats> & stats_client,const std::vector<std::string> & paths)427 void BatteryEEPROMReporter::checkAndReportValidation(const std::shared_ptr<IStats> &stats_client,
428                                                      const std::vector<std::string> &paths) {
429     struct BatteryEEPROMPipeline params = {.checksum = EvtHistoryValidation};
430     std::string path = checkPaths(paths);
431     struct timespec boot_time;
432     auto format = FormatIgnoreAddr;
433     std::vector<std::vector<uint32_t>> events;
434 
435     if (path.empty())
436         return;
437 
438     clock_gettime(CLOCK_MONOTONIC, &boot_time);
439 
440     readLogbuffer(path, kNumValidationFields, params.checksum, format, last_hv_check_, events);
441     for (int event_idx = 0; event_idx < events.size(); event_idx++) {
442         std::vector<uint32_t> &event = events[event_idx];
443         if (event.size() == kNumValidationFields) {
444             params.full_cap = event[0]; /* first empty entry */
445             params.esr = event[1];      /* num of entries need to be recovered or fix result */
446             params.rslow = event[2];    /* last cycle count */
447             params.full_rep = event[3]; /* estimate cycle count after recovery */
448             reportEvent(stats_client, params);
449             /* force report history metrics if it was recovered */
450             if (last_hv_check_ != 0) {
451                 report_time_ = 0;
452             }
453         } else {
454             ALOGE("Not support %zu fields for History Validation event", event.size());
455         }
456     }
457     last_hv_check_ = (unsigned int)boot_time.tv_sec;
458 }
459 
460 }  // namespace pixel
461 }  // namespace google
462 }  // namespace hardware
463 }  // namespace android
464