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