• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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: BrownoutDetected"
18 
19 #include <aidl/android/frameworks/stats/IStats.h>
20 #include <android-base/file.h>
21 #include <android-base/parseint.h>
22 #include <android-base/properties.h>
23 #include <android-base/stringprintf.h>
24 #include <android-base/strings.h>
25 #include <android/binder_manager.h>
26 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
27 #include <pixelstats/BrownoutDetectedReporter.h>
28 #include <time.h>
29 #include <utils/Log.h>
30 
31 #include <map>
32 #include <regex>
33 
34 namespace android {
35 namespace hardware {
36 namespace google {
37 namespace pixel {
38 
39 using aidl::android::frameworks::stats::IStats;
40 using aidl::android::frameworks::stats::VendorAtom;
41 using aidl::android::frameworks::stats::VendorAtomValue;
42 using android::base::ReadFileToString;
43 using android::hardware::google::pixel::PixelAtoms::BrownoutDetected;
44 
45 #define READING_IDX 2
46 #define KEY_IDX 0
47 #define DEFAULT_BATTERY_TEMP 9999999
48 #define DEFAULT_BATTERY_SOC 100
49 #define DEFAULT_BATTERY_VOLT 5000000
50 #define ONE_SECOND_IN_US 1000000
51 
52 const std::regex kTimestampPattern("^\\S+\\s[0-9]+:[0-9]+:[0-9]+\\S+$");
53 const std::regex kIrqPattern("^(\\S+)\\striggered\\sat\\s\\S+$");
54 const std::regex kOdpmPattern("^CH\\d+\\[(\\S+)\\],\\s(\\d+)$");
55 const std::regex kDvfsPattern("^([A-Z1-9]+):(\\d+)$");
56 const std::regex kFgPattern("^(voltage_now):(\\d+)$");
57 const std::regex kBatteryTempPattern("^(battery):(\\d+)$");
58 const std::regex kBatteryCyclePattern("^(battery_cycle):(\\d+)$");
59 const std::regex kBatterySocPattern("^(soc):(\\d+)$");
60 const std::regex kAlreadyUpdatedPattern("^(LASTMEAL_UPDATED)$");
61 
62 const std::map<std::string, int> kBrownoutReason = {{"uvlo,pmic,if", BrownoutDetected::UVLO_IF},
63                                                     {"ocp,pmic,if", BrownoutDetected::OCP_IF},
64                                                     {"uvlo,pmic,main", BrownoutDetected::UVLO_MAIN},
65                                                     {"uvlo,pmic,sub", BrownoutDetected::UVLO_SUB},
66                                                     {"ocp,buck1m", BrownoutDetected::OCP_B1M},
67                                                     {"ocp,buck2m", BrownoutDetected::OCP_B2M},
68                                                     {"ocp,buck3m", BrownoutDetected::OCP_B3M},
69                                                     {"ocp,buck4m", BrownoutDetected::OCP_B4M},
70                                                     {"ocp,buck5m", BrownoutDetected::OCP_B5M},
71                                                     {"ocp,buck6m", BrownoutDetected::OCP_B6M},
72                                                     {"ocp,buck7m", BrownoutDetected::OCP_B7M},
73                                                     {"ocp,buck8m", BrownoutDetected::OCP_B8M},
74                                                     {"ocp,buck9m", BrownoutDetected::OCP_B9M},
75                                                     {"ocp,buck10m", BrownoutDetected::OCP_B10M},
76                                                     {"ocp,buck1s", BrownoutDetected::OCP_B1S},
77                                                     {"ocp,buck2s", BrownoutDetected::OCP_B2S},
78                                                     {"ocp,buck3s", BrownoutDetected::OCP_B3S},
79                                                     {"ocp,buck4s", BrownoutDetected::OCP_B4S},
80                                                     {"ocp,buck5s", BrownoutDetected::OCP_B5S},
81                                                     {"ocp,buck6s", BrownoutDetected::OCP_B6S},
82                                                     {"ocp,buck7s", BrownoutDetected::OCP_B7S},
83                                                     {"ocp,buck8s", BrownoutDetected::OCP_B8S},
84                                                     {"ocp,buck9s", BrownoutDetected::OCP_B9S},
85                                                     {"ocp,buck10s", BrownoutDetected::OCP_B10S},
86                                                     {"ocp,buckas", BrownoutDetected::OCP_BAS},
87                                                     {"ocp,buckbs", BrownoutDetected::OCP_BBS},
88                                                     {"ocp,buckcs", BrownoutDetected::OCP_BCS},
89                                                     {"ocp,buckds", BrownoutDetected::OCP_BDS}};
90 
updateIfFound(std::string line,std::regex pattern,int * current_value,Update flag)91 bool BrownoutDetectedReporter::updateIfFound(std::string line, std::regex pattern,
92                                              int *current_value, Update flag) {
93     bool found = false;
94     std::smatch pattern_match;
95     if (std::regex_match(line, pattern_match, pattern)) {
96         if (pattern_match.size() < (READING_IDX + 1)) {
97             return found;
98         }
99         found = true;
100         int reading = std::stoi(pattern_match[READING_IDX].str());
101         if (flag == kUpdateMax) {
102             if (*current_value < reading) {
103                 *current_value = reading;
104             }
105         } else {
106             if (*current_value > reading) {
107                 *current_value = reading;
108             }
109         }
110     }
111     return found;
112 }
113 
setAtomFieldValue(std::vector<VendorAtomValue> * values,int offset,int content)114 void BrownoutDetectedReporter::setAtomFieldValue(std::vector<VendorAtomValue> *values, int offset,
115                                                  int content) {
116     std::vector<VendorAtomValue> &val = *values;
117     if (offset - kVendorAtomOffset < val.size()) {
118         val[offset - kVendorAtomOffset].set<VendorAtomValue::intValue>(content);
119     }
120 }
121 
uploadData(const std::shared_ptr<IStats> & stats_client,const struct BrownoutDetectedInfo max_value)122 void BrownoutDetectedReporter::uploadData(const std::shared_ptr<IStats> &stats_client,
123                                           const struct BrownoutDetectedInfo max_value) {
124     // Load values array
125     VendorAtomValue tmp;
126     std::vector<VendorAtomValue> values(37);
127     setAtomFieldValue(&values, BrownoutDetected::kTriggeredIrqFieldNumber,
128                       max_value.triggered_irq_);
129     setAtomFieldValue(&values, BrownoutDetected::kTriggeredTimestampFieldNumber,
130                       max_value.triggered_timestamp_);
131     setAtomFieldValue(&values, BrownoutDetected::kBatteryTempFieldNumber, max_value.battery_temp_);
132     setAtomFieldValue(&values, BrownoutDetected::kBatterySocFieldNumber,
133                       100 - max_value.battery_soc_);
134     setAtomFieldValue(&values, BrownoutDetected::kBatteryCycleFieldNumber,
135                       max_value.battery_cycle_);
136     setAtomFieldValue(&values, BrownoutDetected::kVoltageNowFieldNumber, max_value.voltage_now_);
137 
138     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel01FieldNumber,
139                       max_value.odpm_value_[0]);
140     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel02FieldNumber,
141                       max_value.odpm_value_[1]);
142     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel03FieldNumber,
143                       max_value.odpm_value_[2]);
144     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel04FieldNumber,
145                       max_value.odpm_value_[3]);
146     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel05FieldNumber,
147                       max_value.odpm_value_[4]);
148     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel06FieldNumber,
149                       max_value.odpm_value_[5]);
150     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel07FieldNumber,
151                       max_value.odpm_value_[6]);
152     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel08FieldNumber,
153                       max_value.odpm_value_[7]);
154     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel09FieldNumber,
155                       max_value.odpm_value_[8]);
156     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel10FieldNumber,
157                       max_value.odpm_value_[9]);
158     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel11FieldNumber,
159                       max_value.odpm_value_[10]);
160     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel12FieldNumber,
161                       max_value.odpm_value_[11]);
162     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel13FieldNumber,
163                       max_value.odpm_value_[12]);
164     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel14FieldNumber,
165                       max_value.odpm_value_[13]);
166     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel15FieldNumber,
167                       max_value.odpm_value_[14]);
168     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel16FieldNumber,
169                       max_value.odpm_value_[15]);
170     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel17FieldNumber,
171                       max_value.odpm_value_[16]);
172     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel18FieldNumber,
173                       max_value.odpm_value_[17]);
174     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel19FieldNumber,
175                       max_value.odpm_value_[18]);
176     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel20FieldNumber,
177                       max_value.odpm_value_[19]);
178     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel21FieldNumber,
179                       max_value.odpm_value_[20]);
180     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel22FieldNumber,
181                       max_value.odpm_value_[21]);
182     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel23FieldNumber,
183                       max_value.odpm_value_[22]);
184     setAtomFieldValue(&values, BrownoutDetected::kOdpmChannel24FieldNumber,
185                       max_value.odpm_value_[23]);
186 
187     setAtomFieldValue(&values, BrownoutDetected::kDvfsChannel1FieldNumber,
188                       max_value.dvfs_value_[0]);
189     setAtomFieldValue(&values, BrownoutDetected::kDvfsChannel2FieldNumber,
190                       max_value.dvfs_value_[1]);
191     setAtomFieldValue(&values, BrownoutDetected::kDvfsChannel3FieldNumber,
192                       max_value.dvfs_value_[2]);
193     setAtomFieldValue(&values, BrownoutDetected::kDvfsChannel4FieldNumber,
194                       max_value.dvfs_value_[3]);
195     setAtomFieldValue(&values, BrownoutDetected::kDvfsChannel5FieldNumber,
196                       max_value.dvfs_value_[4]);
197     setAtomFieldValue(&values, BrownoutDetected::kDvfsChannel6FieldNumber,
198                       max_value.dvfs_value_[5]);
199     setAtomFieldValue(&values, BrownoutDetected::kBrownoutReasonFieldNumber,
200                       max_value.brownout_reason_);
201 
202     // Send vendor atom to IStats HAL
203     VendorAtom event = {.reverseDomainName = "",
204                         .atomId = PixelAtoms::Atom::kBrownoutDetected,
205                         .values = std::move(values)};
206     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
207     if (!ret.isOk())
208         ALOGE("Unable to report ChargeStats to Stats service");
209 }
210 
parseTimestamp(std::string timestamp)211 long BrownoutDetectedReporter::parseTimestamp(std::string timestamp) {
212     struct tm triggeredTimestamp = {};
213     std::string timestampFormat = "%Y-%m-%d %H:%M:%S";
214     if (strptime(timestamp.substr(0, 19).c_str(), timestampFormat.c_str(), &triggeredTimestamp)) {
215         auto logFileTime = std::chrono::system_clock::from_time_t(mktime(&triggeredTimestamp));
216         return logFileTime.time_since_epoch().count() / ONE_SECOND_IN_US;
217     }
218     return 0;
219 }
220 
logBrownout(const std::shared_ptr<IStats> & stats_client,const std::string & logFilePath,const std::string & brownoutReasonProp)221 void BrownoutDetectedReporter::logBrownout(const std::shared_ptr<IStats> &stats_client,
222                                            const std::string &logFilePath,
223                                            const std::string &brownoutReasonProp) {
224     std::string logFile;
225     if (!android::base::ReadFileToString(logFilePath, &logFile)) {
226         return;
227     }
228     std::istringstream content(logFile);
229     std::string line;
230     struct BrownoutDetectedInfo max_value = {};
231     max_value.voltage_now_ = DEFAULT_BATTERY_VOLT;
232     max_value.battery_soc_ = DEFAULT_BATTERY_SOC;
233     max_value.battery_temp_ = DEFAULT_BATTERY_TEMP;
234     std::smatch pattern_match;
235     int odpm_index = 0, dvfs_index = 0;
236     std::string reason = android::base::GetProperty(brownoutReasonProp.c_str(), "");
237     if (reason.empty()) {
238         // Brownout not found
239         return;
240     }
241 
242     auto key = kBrownoutReason.find(reason);
243     if (key == kBrownoutReason.end()) {
244         return;
245     }
246     max_value.brownout_reason_ = key->second;
247     bool isAlreadyUpdated = false;
248     while (std::getline(content, line)) {
249         if (std::regex_match(line, pattern_match, kAlreadyUpdatedPattern)) {
250             isAlreadyUpdated = true;
251             break;
252         }
253         if (std::regex_match(line, pattern_match, kIrqPattern)) {
254             if (pattern_match.size() < (KEY_IDX + 1)) {
255                 return;
256             }
257             std::ssub_match irq = pattern_match[KEY_IDX];
258             if (irq.str().find("batoilo") != std::string::npos) {
259                 max_value.triggered_irq_ = BrownoutDetected::BATOILO;
260                 continue;
261             }
262             if (irq.str().find("vdroop1") != std::string::npos) {
263                 max_value.triggered_irq_ = BrownoutDetected::UVLO1;
264                 continue;
265             }
266             if (irq.str().find("vdroop2") != std::string::npos) {
267                 max_value.triggered_irq_ = BrownoutDetected::UVLO2;
268                 continue;
269             }
270             if (irq.str().find("smpl_gm") != std::string::npos) {
271                 max_value.triggered_irq_ = BrownoutDetected::SMPL_WARN;
272                 continue;
273             }
274             continue;
275         }
276         if (std::regex_match(line, pattern_match, kTimestampPattern)) {
277             max_value.triggered_timestamp_ = parseTimestamp(line.c_str());
278             continue;
279         }
280         if (updateIfFound(line, kBatterySocPattern, &max_value.battery_soc_, kUpdateMax)) {
281             continue;
282         }
283         if (updateIfFound(line, kBatteryTempPattern, &max_value.battery_temp_, kUpdateMin)) {
284             continue;
285         }
286         if (updateIfFound(line, kBatteryCyclePattern, &max_value.battery_cycle_, kUpdateMax)) {
287             continue;
288         }
289         if (updateIfFound(line, kFgPattern, &max_value.voltage_now_, kUpdateMin)) {
290             continue;
291         }
292         if (updateIfFound(line, kDvfsPattern, &max_value.dvfs_value_[dvfs_index], kUpdateMax)) {
293             dvfs_index++;
294             // Discarding previous value and update with new DVFS value
295             if (dvfs_index == DVFS_MAX_IDX) {
296                 dvfs_index = 0;
297             }
298             continue;
299         }
300         if (updateIfFound(line, kOdpmPattern, &max_value.odpm_value_[odpm_index], kUpdateMax)) {
301             odpm_index++;
302             // Discarding previous value and update with new ODPM value
303             if (odpm_index == ODPM_MAX_IDX) {
304                 odpm_index = 0;
305             }
306             continue;
307         }
308     }
309     if (!isAlreadyUpdated && max_value.battery_temp_ != DEFAULT_BATTERY_TEMP) {
310         std::string file_content = "LASTMEAL_UPDATED\n" + logFile;
311         android::base::WriteStringToFile(file_content, logFilePath);
312         uploadData(stats_client, max_value);
313     }
314 }
315 
316 }  // namespace pixel
317 }  // namespace google
318 }  // namespace hardware
319 }  // namespace android
320