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 <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
28
29 namespace android {
30 namespace hardware {
31 namespace google {
32 namespace pixel {
33
34 using aidl::android::frameworks::stats::VendorAtom;
35 using aidl::android::frameworks::stats::VendorAtomValue;
36 using android::base::ReadFileToString;
37 using android::hardware::google::pixel::PixelAtoms::BatteryEEPROM;
38
39 #define LINESIZE 71
40 #define LINESIZE_V2 31
41
BatteryEEPROMReporter()42 BatteryEEPROMReporter::BatteryEEPROMReporter() {}
43
checkAndReport(const std::shared_ptr<IStats> & stats_client,const std::string & path)44 void BatteryEEPROMReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client,
45 const std::string &path) {
46 std::string file_contents;
47 std::string history_each;
48
49 const int kSecondsPerMonth = 60 * 60 * 24 * 30;
50 int64_t now = getTimeSecs();
51
52 if ((report_time_ != 0) && (now - report_time_ < kSecondsPerMonth)) {
53 ALOGD("Not upload time. now: %" PRId64 ", pre: %" PRId64, now, report_time_);
54 return;
55 }
56
57 if (!ReadFileToString(path.c_str(), &file_contents)) {
58 ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno));
59 return;
60 }
61 ALOGD("checkAndReport: %s", file_contents.c_str());
62
63 int16_t i, num;
64 struct BatteryHistory hist;
65 const int kHistTotalLen = file_contents.size();
66
67 ALOGD("kHistTotalLen=%d\n", kHistTotalLen);
68
69 if (kHistTotalLen >= (LINESIZE_V2 * BATT_HIST_NUM_MAX_V2)) {
70 struct BatteryHistoryExtend histv2;
71 for (i = 0; i < BATT_HIST_NUM_MAX_V2; i++) {
72 size_t history_offset = i * LINESIZE_V2;
73 if (history_offset > file_contents.size())
74 break;
75 history_each = file_contents.substr(history_offset, LINESIZE_V2);
76 unsigned int data[4];
77
78 /* Format transfer: go/gsx01-eeprom */
79 num = sscanf(history_each.c_str(), "%4" SCNx16 "%4" SCNx16 "%x %x %x %x",
80 &histv2.tempco, &histv2.rcomp0, &data[0], &data[1], &data[2], &data[3]);
81
82 if (histv2.tempco == 0xFFFF && histv2.rcomp0 == 0xFFFF)
83 continue;
84
85 /* Extract each data */
86 uint64_t tmp = (int64_t)data[3] << 48 |
87 (int64_t)data[2] << 32 |
88 (int64_t)data[1] << 16 |
89 data[0];
90
91 /* ignore this data if unreasonable */
92 if (tmp <= 0)
93 continue;
94
95 /* data format/unit in go/gsx01-eeprom#heading=h.finy98ign34p */
96 histv2.timer_h = tmp & 0xFF;
97 histv2.fullcapnom = (tmp >>= 8) & 0x3FF;
98 histv2.fullcaprep = (tmp >>= 10) & 0x3FF;
99 histv2.mixsoc = (tmp >>= 10) & 0x3F;
100 histv2.vfsoc = (tmp >>= 6) & 0x3F;
101 histv2.maxvolt = (tmp >>= 6) & 0xF;
102 histv2.minvolt = (tmp >>= 4) & 0xF;
103 histv2.maxtemp = (tmp >>= 4) & 0xF;
104 histv2.mintemp = (tmp >>= 4) & 0xF;
105 histv2.maxchgcurr = (tmp >>= 4) & 0xF;
106 histv2.maxdischgcurr = (tmp >>= 4) & 0xF;
107
108 /* Mapping to original format to collect data */
109 /* go/pixel-battery-eeprom-atom#heading=h.dcawdjiz2ls6 */
110 hist.tempco = histv2.tempco;
111 hist.rcomp0 = histv2.rcomp0;
112 hist.timer_h = (uint8_t)histv2.timer_h * 5;
113 hist.max_temp = (int8_t)histv2.maxtemp * 3 + 22;
114 hist.min_temp = (int8_t)histv2.mintemp * 3 - 20;
115 hist.min_ibatt = (int16_t)histv2.maxchgcurr * 500 * (-1);
116 hist.max_ibatt = (int16_t)histv2.maxdischgcurr * 500;
117 hist.min_vbatt = (uint16_t)histv2.minvolt * 10 + 2500;
118 hist.max_vbatt = (uint16_t)histv2.maxvolt * 20 + 4200;
119 hist.batt_soc = (uint8_t)histv2.vfsoc * 2;
120 hist.msoc = (uint8_t)histv2.mixsoc * 2;
121 hist.full_cap = (int16_t)histv2.fullcaprep * 125 / 1000;
122 hist.full_rep = (int16_t)histv2.fullcapnom * 125 / 1000;
123 hist.cycle_cnt = (i + 1) * 10;
124
125 reportEvent(stats_client, hist);
126 report_time_ = getTimeSecs();
127 }
128 return;
129 }
130
131 for (i = 0; i < (LINESIZE * BATT_HIST_NUM_MAX); i = i + LINESIZE) {
132 if (i + LINESIZE > kHistTotalLen)
133 break;
134 history_each = file_contents.substr(i, LINESIZE);
135 num = sscanf(history_each.c_str(),
136 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16
137 "%2" SCNx8 "%2" SCNx8 " %2" SCNx8 "%2" SCNx8
138 "%2" SCNx8 "%2" SCNx8 " %2" SCNx8 "%2" SCNx8
139 "%2" SCNx8 "%2" SCNx8 " %4" SCNx16 "%4" SCNx16
140 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16,
141 &hist.cycle_cnt, &hist.full_cap, &hist.esr,
142 &hist.rslow, &hist.batt_temp, &hist.soh,
143 &hist.cc_soc, &hist.cutoff_soc, &hist.msoc,
144 &hist.sys_soc, &hist.reserve, &hist.batt_soc,
145 &hist.min_temp, &hist.max_temp, &hist.max_vbatt,
146 &hist.min_vbatt, &hist.max_ibatt, &hist.min_ibatt,
147 &hist.checksum);
148
149 if (num != kNumBatteryHistoryFields) {
150 ALOGE("Couldn't process %s", history_each.c_str());
151 continue;
152 }
153
154 if (checkLogEvent(hist)) {
155 reportEvent(stats_client, hist);
156 report_time_ = getTimeSecs();
157 }
158 }
159 }
160
getTimeSecs(void)161 int64_t BatteryEEPROMReporter::getTimeSecs(void) {
162 return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
163 }
164
165 /**
166 * @return true if a log should be reported, else false.
167 * Here we use checksum to confirm the data is usable or not.
168 * The checksum mismatch when storage data overflow or corrupt.
169 * We don't need data in such cases.
170 */
checkLogEvent(struct BatteryHistory hist)171 bool BatteryEEPROMReporter::checkLogEvent(struct BatteryHistory hist) {
172 int checksum = 0;
173
174 checksum = hist.cycle_cnt + hist.full_cap + hist.esr + hist.rslow
175 + hist.soh + hist.batt_temp + hist.cutoff_soc + hist.cc_soc
176 + hist.sys_soc + hist.msoc + hist.batt_soc + hist.reserve
177 + hist.max_temp + hist.min_temp + hist.max_vbatt
178 + hist.min_vbatt + hist.max_ibatt + hist.min_ibatt;
179 /* Compare with checksum data */
180 if (checksum == hist.checksum) {
181 return true;
182 } else {
183 return false;
184 }
185 }
186
reportEvent(const std::shared_ptr<IStats> & stats_client,const struct BatteryHistory & hist)187 void BatteryEEPROMReporter::reportEvent(const std::shared_ptr<IStats> &stats_client,
188 const struct BatteryHistory &hist) {
189 // upload atom
190 const std::vector<int> eeprom_history_fields = {
191 BatteryEEPROM::kCycleCntFieldNumber, BatteryEEPROM::kFullCapFieldNumber,
192 BatteryEEPROM::kEsrFieldNumber, BatteryEEPROM::kRslowFieldNumber,
193 BatteryEEPROM::kSohFieldNumber, BatteryEEPROM::kBattTempFieldNumber,
194 BatteryEEPROM::kCutoffSocFieldNumber, BatteryEEPROM::kCcSocFieldNumber,
195 BatteryEEPROM::kSysSocFieldNumber, BatteryEEPROM::kMsocFieldNumber,
196 BatteryEEPROM::kBattSocFieldNumber, BatteryEEPROM::kReserveFieldNumber,
197 BatteryEEPROM::kMaxTempFieldNumber, BatteryEEPROM::kMinTempFieldNumber,
198 BatteryEEPROM::kMaxVbattFieldNumber, BatteryEEPROM::kMinVbattFieldNumber,
199 BatteryEEPROM::kMaxIbattFieldNumber, BatteryEEPROM::kMinIbattFieldNumber,
200 BatteryEEPROM::kChecksumFieldNumber, BatteryEEPROM::kTempcoFieldNumber,
201 BatteryEEPROM::kRcomp0FieldNumber, BatteryEEPROM::kTimerHFieldNumber,
202 BatteryEEPROM::kFullRepFieldNumber};
203
204 ALOGD("reportEvent: cycle_cnt:%d, full_cap:%d, esr:%d, rslow:%d, soh:%d, "
205 "batt_temp:%d, cutoff_soc:%d, cc_soc:%d, sys_soc:%d, msoc:%d, "
206 "batt_soc:%d, reserve:%d, max_temp:%d, min_temp:%d, max_vbatt:%d, "
207 "min_vbatt:%d, max_ibatt:%d, min_ibatt:%d, checksum:%d, full_rep:%d, "
208 "tempco:0x%x, rcomp0:0x%x, timer_h:%d",
209 hist.cycle_cnt, hist.full_cap, hist.esr, hist.rslow, hist.soh, hist.batt_temp,
210 hist.cutoff_soc, hist.cc_soc, hist.sys_soc, hist.msoc, hist.batt_soc, hist.reserve,
211 hist.max_temp, hist.min_temp, hist.max_vbatt, hist.min_vbatt, hist.max_ibatt,
212 hist.min_ibatt, hist.checksum, hist.full_rep, hist.tempco, hist.rcomp0, hist.timer_h);
213
214 std::vector<VendorAtomValue> values(eeprom_history_fields.size());
215 VendorAtomValue val;
216
217 val.set<VendorAtomValue::intValue>(hist.cycle_cnt);
218 values[BatteryEEPROM::kCycleCntFieldNumber - kVendorAtomOffset] = val;
219 val.set<VendorAtomValue::intValue>(hist.full_cap);
220 values[BatteryEEPROM::kFullCapFieldNumber - kVendorAtomOffset] = val;
221 val.set<VendorAtomValue::intValue>(hist.esr);
222 values[BatteryEEPROM::kEsrFieldNumber - kVendorAtomOffset] = val;
223 val.set<VendorAtomValue::intValue>(hist.rslow);
224 values[BatteryEEPROM::kRslowFieldNumber - kVendorAtomOffset] = val;
225 val.set<VendorAtomValue::intValue>(hist.soh);
226 values[BatteryEEPROM::kSohFieldNumber - kVendorAtomOffset] = val;
227 val.set<VendorAtomValue::intValue>(hist.batt_temp);
228 values[BatteryEEPROM::kBattTempFieldNumber - kVendorAtomOffset] = val;
229 val.set<VendorAtomValue::intValue>(hist.cutoff_soc);
230 values[BatteryEEPROM::kCutoffSocFieldNumber - kVendorAtomOffset] = val;
231 val.set<VendorAtomValue::intValue>(hist.cc_soc);
232 values[BatteryEEPROM::kCcSocFieldNumber - kVendorAtomOffset] = val;
233 val.set<VendorAtomValue::intValue>(hist.sys_soc);
234 values[BatteryEEPROM::kSysSocFieldNumber - kVendorAtomOffset] = val;
235 val.set<VendorAtomValue::intValue>(hist.msoc);
236 values[BatteryEEPROM::kMsocFieldNumber - kVendorAtomOffset] = val;
237 val.set<VendorAtomValue::intValue>(hist.batt_soc);
238 values[BatteryEEPROM::kBattSocFieldNumber - kVendorAtomOffset] = val;
239 val.set<VendorAtomValue::intValue>(hist.reserve);
240 values[BatteryEEPROM::kReserveFieldNumber - kVendorAtomOffset] = val;
241 val.set<VendorAtomValue::intValue>(hist.max_temp);
242 values[BatteryEEPROM::kMaxTempFieldNumber - kVendorAtomOffset] = val;
243 val.set<VendorAtomValue::intValue>(hist.min_temp);
244 values[BatteryEEPROM::kMinTempFieldNumber - kVendorAtomOffset] = val;
245 val.set<VendorAtomValue::intValue>(hist.max_vbatt);
246 values[BatteryEEPROM::kMaxVbattFieldNumber - kVendorAtomOffset] = val;
247 val.set<VendorAtomValue::intValue>(hist.min_vbatt);
248 values[BatteryEEPROM::kMinVbattFieldNumber - kVendorAtomOffset] = val;
249 val.set<VendorAtomValue::intValue>(hist.max_ibatt);
250 values[BatteryEEPROM::kMaxIbattFieldNumber - kVendorAtomOffset] = val;
251 val.set<VendorAtomValue::intValue>(hist.min_ibatt);
252 values[BatteryEEPROM::kMinIbattFieldNumber - kVendorAtomOffset] = val;
253 val.set<VendorAtomValue::intValue>(hist.checksum);
254 values[BatteryEEPROM::kChecksumFieldNumber - kVendorAtomOffset] = val;
255 val.set<VendorAtomValue::intValue>(hist.tempco);
256 values[BatteryEEPROM::kTempcoFieldNumber - kVendorAtomOffset] = val;
257 val.set<VendorAtomValue::intValue>(hist.rcomp0);
258 values[BatteryEEPROM::kRcomp0FieldNumber - kVendorAtomOffset] = val;
259 val.set<VendorAtomValue::intValue>(hist.timer_h);
260 values[BatteryEEPROM::kTimerHFieldNumber - kVendorAtomOffset] = val;
261 val.set<VendorAtomValue::intValue>(hist.full_rep);
262 values[BatteryEEPROM::kFullRepFieldNumber - kVendorAtomOffset] = val;
263
264 VendorAtom event = {.reverseDomainName = "",
265 .atomId = PixelAtoms::Atom::kBatteryEeprom,
266 .values = std::move(values)};
267 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
268 if (!ret.isOk())
269 ALOGE("Unable to report BatteryEEPROM to Stats service");
270 }
271
272 } // namespace pixel
273 } // namespace google
274 } // namespace hardware
275 } // namespace android
276