1 /*
2 * Copyright (C) 2021 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-uevent: ChargeStatsReporter"
18
19 #include <android-base/file.h>
20 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
21 #include <log/log.h>
22 #include <pixelstats/ChargeStatsReporter.h>
23 #include <pixelstats/StatsHelper.h>
24 #include <time.h>
25 #include <utils/Timers.h>
26
27 #include <cmath>
28
29 namespace android {
30 namespace hardware {
31 namespace google {
32 namespace pixel {
33
34 using aidl::android::frameworks::stats::IStats;
35 using aidl::android::frameworks::stats::VendorAtom;
36 using aidl::android::frameworks::stats::VendorAtomValue;
37 using android::base::ReadFileToString;
38 using android::base::WriteStringToFile;
39 using android::hardware::google::pixel::PixelAtoms::ChargeStats;
40 using android::hardware::google::pixel::PixelAtoms::VoltageTierStats;
41
42 #define DURATION_FILTER_SECS 15
43 #define CHG_STATS_FMT "%d,%d,%d, %d,%d,%d,%d %d %d,%d, %d,%d"
44 #define WLC_ASTATS_FMT "A:%d,%d,%d,%d"
45 #define WLC_DSTATS_FMT "D:%x,%x,%x,%x,%x, %x,%x"
46
ChargeStatsReporter()47 ChargeStatsReporter::ChargeStatsReporter() {}
48
getTimeSecs(void)49 int64_t ChargeStatsReporter::getTimeSecs(void) {
50 return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
51 }
52
ReportChargeStats(const std::shared_ptr<IStats> & stats_client,const std::string line,const std::string wline_at,const std::string wline_ac,const std::string pca_line)53 void ChargeStatsReporter::ReportChargeStats(const std::shared_ptr<IStats> &stats_client,
54 const std::string line, const std::string wline_at,
55 const std::string wline_ac,
56 const std::string pca_line) {
57 int charge_stats_fields[] = {
58 ChargeStats::kAdapterTypeFieldNumber,
59 ChargeStats::kAdapterVoltageFieldNumber,
60 ChargeStats::kAdapterAmperageFieldNumber,
61 ChargeStats::kSsocInFieldNumber,
62 ChargeStats::kVoltageInFieldNumber,
63 ChargeStats::kSsocOutFieldNumber,
64 ChargeStats::kVoltageOutFieldNumber,
65 ChargeStats::kChargeCapacityFieldNumber,
66 ChargeStats::kCsiAggregateStatusFieldNumber,
67 ChargeStats::kCsiAggregateTypeFieldNumber,
68 ChargeStats::kAdapterCapabilities0FieldNumber,
69 ChargeStats::kAdapterCapabilities1FieldNumber,
70 ChargeStats::kAdapterCapabilities2FieldNumber,
71 ChargeStats::kAdapterCapabilities3FieldNumber,
72 ChargeStats::kAdapterCapabilities4FieldNumber,
73 ChargeStats::kReceiverState0FieldNumber,
74 ChargeStats::kReceiverState1FieldNumber,
75 ChargeStats::kAacrAlgoFieldNumber,
76 ChargeStats::kAacpVersionFieldNumber,
77 ChargeStats::kAaccFieldNumber,
78 };
79 const int32_t chg_fields_size = std::size(charge_stats_fields);
80 static_assert(chg_fields_size == 20, "Unexpected charge stats fields size");
81 const int32_t wlc_fields_size = 7;
82 std::vector<VendorAtomValue> values(chg_fields_size);
83 VendorAtomValue val;
84 int32_t i = 0, tmp[chg_fields_size] = {0};
85 int32_t pca_ac[2] = {0}, pca_rs[5] = {0}, stats_size;
86 std::string pdo_line, file_contents;
87 std::istringstream ss;
88
89 ALOGD("processing %s", line.c_str());
90
91 stats_size = sscanf(line.c_str(), CHG_STATS_FMT, &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4],
92 &tmp[5], &tmp[6], &tmp[7], &tmp[8], &tmp[9], &tmp[18], &tmp[19]);
93 if (stats_size != kNumChgStatsFormat00Fields && stats_size != kNumChgStatsFormat01Fields &&
94 stats_size != kNumChgStatsFormat02Fields && stats_size != kNumChgStatsFormat03Fields) {
95 ALOGE("Couldn't process %s (stats_size: %d)", line.c_str(), stats_size);
96 return;
97 }
98
99 if (!wline_at.empty()) {
100 int32_t type = 0, soc = 0, voltage = 0, current = 0;
101 ALOGD("wlc: processing %s", wline_at.c_str());
102 if (sscanf(wline_at.c_str(), WLC_ASTATS_FMT, &type, &soc, &voltage, ¤t) != 4) {
103 ALOGE("Couldn't process %s", wline_at.c_str());
104 } else {
105 tmp[0] = wireless_charge_stats_.TranslateSysModeToAtomValue(type);
106 tmp[1] = voltage;
107 tmp[2] = current;
108 ALOGD("wlc: processing %s", wline_ac.c_str());
109 if (sscanf(wline_ac.c_str(), WLC_DSTATS_FMT, &tmp[10], &tmp[11], &tmp[12],
110 &tmp[13], &tmp[14], &tmp[15], &tmp[16]) != 7)
111 ALOGE("Couldn't process %s", wline_ac.c_str());
112 }
113 }
114
115 if (!pca_line.empty()) {
116 ALOGD("pca: processing %s", pca_line.c_str());
117 if (sscanf(pca_line.c_str(), "D:%x,%x %x,%x,%x,%x,%x", &pca_ac[0], &pca_ac[1], &pca_rs[0],
118 &pca_rs[1], &pca_rs[2], &pca_rs[3], &pca_rs[4]) != 7) {
119 ALOGE("Couldn't process %s", pca_line.c_str());
120 } else {
121 tmp[12] = pca_rs[2];
122 tmp[13] = pca_rs[3];
123 tmp[14] = pca_rs[4];
124 tmp[16] = pca_rs[1];
125 if (wline_at.empty()) {
126 /* force adapter type to PPS when pca log is available, but not wlc */
127 tmp[0] = PixelAtoms::ChargeStats::ADAPTER_TYPE_USB_PD_PPS;
128 tmp[10] = pca_ac[0];
129 tmp[11] = pca_ac[1];
130 tmp[15] = pca_rs[0];
131 }
132 }
133 }
134
135 if (ReadFileToString(kGChargerMetricsPath.c_str(), &file_contents)) {
136 ss.str(file_contents);
137 while (std::getline(ss, pdo_line)) {
138 if (sscanf(pdo_line.c_str(), "D:%x,%x,%x,%x,%x,%x,%x", &pca_ac[0], &pca_ac[1],
139 &pca_rs[0], &pca_rs[1], &pca_rs[2], &pca_rs[3], &pca_rs[4]) != 7) {
140 continue;
141 } else {
142 ALOGD("processed %s, apdo:%d, pdo:%d", pdo_line.c_str(), pca_ac[1], pca_rs[4]);
143 tmp[15] = pca_ac[1]; /* APDO */
144 tmp[16] = pca_rs[4]; /* PDO */
145 break;
146 }
147 }
148 }
149
150 if (ReadFileToString(kGAacrAlgoPath.c_str(), &file_contents)) {
151 ss.str(file_contents);
152 ss >> tmp[17];
153 }
154
155 for (i = 0; i < chg_fields_size; i++) {
156 val.set<VendorAtomValue::intValue>(tmp[i]);
157 values[charge_stats_fields[i] - kVendorAtomOffset] = val;
158 }
159
160 VendorAtom event = {.reverseDomainName = "",
161 .atomId = PixelAtoms::Atom::kChargeStats,
162 .values = std::move(values)};
163 reportVendorAtom(stats_client, event);
164 }
165
ReportVoltageTierStats(const std::shared_ptr<IStats> & stats_client,const char * line,const bool has_wireless=false,const std::string & wfile_contents="")166 void ChargeStatsReporter::ReportVoltageTierStats(const std::shared_ptr<IStats> &stats_client,
167 const char *line, const bool has_wireless = false,
168 const std::string &wfile_contents = "") {
169 int voltage_tier_stats_fields[] = {
170 VoltageTierStats::kVoltageTierFieldNumber,
171 VoltageTierStats::kSocInFieldNumber, /* retrieved via ssoc_tmp */
172 VoltageTierStats::kCcInFieldNumber,
173 VoltageTierStats::kTempInFieldNumber,
174 VoltageTierStats::kTimeFastSecsFieldNumber,
175 VoltageTierStats::kTimeTaperSecsFieldNumber,
176 VoltageTierStats::kTimeOtherSecsFieldNumber,
177 VoltageTierStats::kTempMinFieldNumber,
178 VoltageTierStats::kTempAvgFieldNumber,
179 VoltageTierStats::kTempMaxFieldNumber,
180 VoltageTierStats::kIbattMinFieldNumber,
181 VoltageTierStats::kIbattAvgFieldNumber,
182 VoltageTierStats::kIbattMaxFieldNumber,
183 VoltageTierStats::kIclMinFieldNumber,
184 VoltageTierStats::kIclAvgFieldNumber,
185 VoltageTierStats::kIclMaxFieldNumber,
186 VoltageTierStats::kMinAdapterPowerOutFieldNumber,
187 VoltageTierStats::kTimeAvgAdapterPowerOutFieldNumber,
188 VoltageTierStats::kMaxAdapterPowerOutFieldNumber,
189 VoltageTierStats::kChargingOperatingPointFieldNumber};
190
191 const int32_t vtier_fields_size = std::size(voltage_tier_stats_fields);
192 static_assert(vtier_fields_size == 20, "Unexpected voltage tier stats fields size");
193 const int32_t wlc_fields_size = 4;
194 std::vector<VendorAtomValue> values(vtier_fields_size);
195 VendorAtomValue val;
196 float ssoc_tmp;
197 int32_t i = 0, tmp[vtier_fields_size - 1] = {0}, /* ssoc_tmp is not saved in this array */
198 fields_size = (vtier_fields_size - wlc_fields_size);
199
200 if (sscanf(line, "%d, %f,%d,%d, %d,%d,%d, %d,%d,%d, %d,%d,%d, %d,%d,%d", &tmp[0], &ssoc_tmp,
201 &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5], &tmp[6], &tmp[7], &tmp[8], &tmp[9],
202 &tmp[10], &tmp[11], &tmp[12], &tmp[13], &tmp[14]) != 16) {
203 /* If format isn't as expected, then ignore line on purpose */
204 return;
205 }
206
207 if (has_wireless) {
208 wireless_charge_stats_.CalculateWirelessChargeStats(static_cast<int>(ssoc_tmp),
209 wfile_contents);
210 tmp[15] = wireless_charge_stats_.pout_min_;
211 tmp[16] = wireless_charge_stats_.pout_avg_;
212 tmp[17] = wireless_charge_stats_.pout_max_;
213 tmp[18] = wireless_charge_stats_.of_freq_;
214 fields_size = vtier_fields_size; /* include wlc stats */
215 }
216
217 ALOGD("VoltageTierStats: processed %s", line);
218 val.set<VendorAtomValue::intValue>(tmp[0]);
219 values[voltage_tier_stats_fields[0] - kVendorAtomOffset] = val;
220 val.set<VendorAtomValue::floatValue>(ssoc_tmp);
221 values[voltage_tier_stats_fields[1] - kVendorAtomOffset] = val;
222 for (i = 2; i < fields_size; i++) {
223 val.set<VendorAtomValue::intValue>(tmp[i - 1]);
224 values[voltage_tier_stats_fields[i] - kVendorAtomOffset] = val;
225 }
226
227 VendorAtom event = {.reverseDomainName = "",
228 .atomId = PixelAtoms::Atom::kVoltageTierStats,
229 .values = std::move(values)};
230 reportVendorAtom(stats_client, event);
231 }
232
233 /**
234 * b/223664185
235 * Adds a rolling window filter to charge stats. If the time has expired, there will be a new log
236 * event.
237 *
238 * This helps ensure that we throttle stats even if there is an intermittent disconnect, while still
239 * retaining some stats on the disconnect.
240 */
shouldReportEvent(void)241 bool ChargeStatsReporter::shouldReportEvent(void) {
242 const int64_t current_time = getTimeSecs();
243 if (current_time == 0) {
244 ALOGE("Current boot time is zero!");
245 return false;
246 }
247
248 if (log_event_time_secs_ == 0 || log_event_time_secs_ + DURATION_FILTER_SECS < current_time) {
249 log_event_time_secs_ = current_time;
250 return true;
251 }
252
253 return false;
254 }
255
checkAndReport(const std::shared_ptr<IStats> & stats_client,const std::string & path)256 void ChargeStatsReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client,
257 const std::string &path) {
258 std::string file_contents, line, wfile_contents, wline_at, wline_ac, pca_file_contents,
259 pca_line, thermal_file_contents, gcharger_file_contents, gdbatt_file_contents;
260 std::istringstream ss;
261 bool has_wireless, has_pca, has_thermal, has_gcharger, has_dual_batt;
262
263 if (!ReadFileToString(path.c_str(), &file_contents)) {
264 ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno));
265 return;
266 }
267
268 ss.str(file_contents);
269
270 if (!std::getline(ss, line)) {
271 ALOGE("Unable to read first line");
272 return;
273 }
274
275 if (!WriteStringToFile("0", path.c_str())) {
276 ALOGE("Couldn't clear %s - %s", path.c_str(), strerror(errno));
277 }
278
279 if (!shouldReportEvent()) {
280 ALOGW("Too many log events; event ignored.");
281 return;
282 }
283
284 has_pca = pca_charge_stats_.CheckPcaContentsAndAck(&pca_file_contents);
285 if (has_pca) {
286 std::istringstream pca_ss;
287
288 pca_ss.str(pca_file_contents);
289 std::getline(pca_ss, pca_line);
290 }
291
292 has_wireless = wireless_charge_stats_.CheckWirelessContentsAndAck(&wfile_contents);
293 if (has_wireless) {
294 std::istringstream wss;
295
296 /* there are two lines in the head, A: ...(Adapter Type) and D: ...(Adapter Capabilities) */
297 wss.str(wfile_contents);
298 std::getline(wss, wline_at);
299 std::getline(wss, wline_ac);
300
301 /* reset initial tier soc */
302 wireless_charge_stats_.tier_soc_ = 0;
303 }
304
305 ReportChargeStats(stats_client, line, wline_at, wline_ac, pca_line);
306
307 while (std::getline(ss, line)) {
308 ReportVoltageTierStats(stats_client, line.c_str(), has_wireless, wfile_contents);
309 }
310
311 has_thermal = checkContentsAndAck(&thermal_file_contents, kThermalChargeMetricsPath);
312 if (has_thermal) {
313 std::istringstream wss;
314 wss.str(thermal_file_contents);
315 while (std::getline(wss, line)) {
316 ReportVoltageTierStats(stats_client, line.c_str());
317 }
318 }
319
320 has_gcharger = checkContentsAndAck(&gcharger_file_contents, kGChargerMetricsPath);
321 if (has_gcharger) {
322 std::istringstream wss;
323 wss.str(gcharger_file_contents);
324 while (std::getline(wss, line)) {
325 ReportVoltageTierStats(stats_client, line.c_str());
326 }
327 }
328
329 has_dual_batt = checkContentsAndAck(&gdbatt_file_contents, kGDualBattMetricsPath);
330 if (has_dual_batt) {
331 std::istringstream wss;
332 wss.str(gdbatt_file_contents);
333 while (std::getline(wss, line)) {
334 ReportVoltageTierStats(stats_client, line.c_str());
335 }
336 }
337 }
338
checkContentsAndAck(std::string * file_contents,const std::string & path)339 bool ChargeStatsReporter::checkContentsAndAck(std::string *file_contents, const std::string &path) {
340 if (!ReadFileToString(path.c_str(), file_contents)) {
341 return false;
342 }
343
344 if (!WriteStringToFile("0", path.c_str())) {
345 ALOGE("Couldn't clear %s - %s", path.c_str(), strerror(errno));
346 return false;
347 }
348 return true;
349 }
350
351 } // namespace pixel
352 } // namespace google
353 } // namespace hardware
354 } // namespace android
355