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