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