1 
2 /*
3  * Copyright (C) 2018 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include <pixelhealth/BatteryMetricsLogger.h>
19 #include <pixelhealth/StatsHelper.h>
20 
21 namespace hardware {
22 namespace google {
23 namespace pixel {
24 namespace health {
25 
toBatterySnapshotType(int type)26 VendorBatteryHealthSnapshot::BatterySnapshotType toBatterySnapshotType(int type) {
27     return static_cast<VendorBatteryHealthSnapshot::BatterySnapshotType>(type);
28 }
29 
BatteryMetricsLogger(const char * const batt_res,const char * const batt_ocv,const char * const batt_avg_res,int sample_period,int upload_period)30 BatteryMetricsLogger::BatteryMetricsLogger(const char *const batt_res, const char *const batt_ocv,
31                                            const char *const batt_avg_res, int sample_period,
32                                            int upload_period)
33     : kBatteryResistance(batt_res),
34       kBatteryOCV(batt_ocv),
35       kBatteryAvgResistance(batt_avg_res),
36       kSamplePeriod(sample_period),
37       kUploadPeriod(upload_period),
38       kMaxSamples(upload_period / sample_period) {
39     last_sample_ = 0;
40     last_upload_ = 0;
41     num_samples_ = 0;
42     num_res_samples_ = 0;
43     memset(min_, 0, sizeof(min_));
44     memset(max_, 0, sizeof(max_));
45 }
46 
getTime(void)47 int64_t BatteryMetricsLogger::getTime(void) {
48     return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
49 }
50 
uploadOutlierMetric(const std::shared_ptr<IStats> & stats_client,sampleType type)51 bool BatteryMetricsLogger::uploadOutlierMetric(const std::shared_ptr<IStats> &stats_client,
52                                                sampleType type) {
53     if (kStatsSnapshotType[type] < 0)
54         return false;
55 
56     VendorBatteryHealthSnapshot min_stats_ss;
57     min_stats_ss.set_type(toBatterySnapshotType(kStatsSnapshotType[type]));
58     min_stats_ss.set_temperature_deci_celsius(min_[type][TEMP]);
59     min_stats_ss.set_voltage_micro_volt(min_[type][VOLT]);
60     min_stats_ss.set_current_micro_amps(min_[type][CURR]);
61     min_stats_ss.set_open_circuit_micro_volt(min_[type][OCV]);
62     min_stats_ss.set_resistance_micro_ohm(min_[type][RES]);
63     min_stats_ss.set_level_percent(min_[type][SOC]);
64 
65     VendorBatteryHealthSnapshot max_stats_ss;
66     max_stats_ss.set_type(toBatterySnapshotType(kStatsSnapshotType[type] + 1));
67     max_stats_ss.set_temperature_deci_celsius(max_[type][TEMP]);
68     max_stats_ss.set_voltage_micro_volt(max_[type][VOLT]);
69     max_stats_ss.set_current_micro_amps(max_[type][CURR]);
70     max_stats_ss.set_open_circuit_micro_volt(max_[type][OCV]);
71     max_stats_ss.set_resistance_micro_ohm(max_[type][RES]);
72     max_stats_ss.set_level_percent(max_[type][SOC]);
73 
74     reportBatteryHealthSnapshot(stats_client, min_stats_ss);
75     reportBatteryHealthSnapshot(stats_client, max_stats_ss);
76 
77     return true;
78 }
79 
uploadAverageBatteryResistance(const std::shared_ptr<IStats> & stats_client)80 bool BatteryMetricsLogger::uploadAverageBatteryResistance(
81         const std::shared_ptr<IStats> &stats_client) {
82     if (strlen(kBatteryAvgResistance) == 0) {
83         LOG(INFO) << "Sysfs path for average battery resistance not specified";
84         return true;
85     }
86 
87     std::string file_content;
88     int32_t batt_avg_res;
89 
90     if (!android::base::ReadFileToString(kBatteryAvgResistance, &file_content)) {
91         LOG(ERROR) << "Can't read " << kBatteryAvgResistance;
92         return false;
93     }
94     std::stringstream ss(file_content);
95     if (!(ss >> batt_avg_res)) {
96         LOG(ERROR) << "Can't parse average battery resistance " << file_content;
97         return false;
98     }
99     // Upload average metric
100     VendorBatteryHealthSnapshot avg_res_ss_stats;
101     avg_res_ss_stats.set_type(VendorBatteryHealthSnapshot::BATTERY_SNAPSHOT_TYPE_AVG_RESISTANCE);
102     avg_res_ss_stats.set_temperature_deci_celsius(0);
103     avg_res_ss_stats.set_voltage_micro_volt(0);
104     avg_res_ss_stats.set_current_micro_amps(0);
105     avg_res_ss_stats.set_open_circuit_micro_volt(0);
106     avg_res_ss_stats.set_resistance_micro_ohm(batt_avg_res);
107     avg_res_ss_stats.set_level_percent(0);
108 
109     reportBatteryHealthSnapshot(stats_client, avg_res_ss_stats);
110     return true;
111 }
112 
uploadMetrics(void)113 bool BatteryMetricsLogger::uploadMetrics(void) {
114     int64_t time = getTime();
115 
116     if (last_sample_ == 0)
117         return false;
118 
119     LOG(INFO) << "Uploading metrics at time " << std::to_string(time) << " w/ "
120               << std::to_string(num_samples_) << " samples";
121 
122     std::shared_ptr<IStats> stats_client = getStatsService();
123     if (!stats_client) {
124         LOG(ERROR) << "Unable to connect to Stats service";
125         return false;
126     }
127 
128     // Only log and upload the min and max for metric types we want to upload
129     for (int metric = 0; metric < NUM_FIELDS; metric++) {
130         if ((metric == RES && num_res_samples_ == 0) || kStatsSnapshotType[metric] < 0)
131             continue;
132         std::string log_min = "min-" + std::to_string(metric) + " ";
133         std::string log_max = "max-" + std::to_string(metric) + " ";
134         for (int j = 0; j < NUM_FIELDS; j++) {
135             log_min += std::to_string(min_[metric][j]) + " ";
136             log_max += std::to_string(max_[metric][j]) + " ";
137         }
138         LOG(INFO) << log_min;
139         LOG(INFO) << log_max;
140         // Upload min/max metrics
141         uploadOutlierMetric(stats_client, static_cast<sampleType>(metric));
142     }
143 
144     uploadAverageBatteryResistance(stats_client);
145 
146     // Clear existing data
147     memset(min_, 0, sizeof(min_));
148     memset(max_, 0, sizeof(max_));
149     num_samples_ = 0;
150     num_res_samples_ = 0;
151     last_upload_ = time;
152     LOG(INFO) << "Finished uploading to tron";
153     return true;
154 }
155 
recordSample(struct android::BatteryProperties * props)156 bool BatteryMetricsLogger::recordSample(struct android::BatteryProperties *props) {
157     std::string resistance_str, ocv_str;
158     int32_t resistance, ocv;
159     int32_t time = getTime();
160 
161     LOG(INFO) << "Recording a sample at time " << std::to_string(time);
162 
163     if (!android::base::ReadFileToString(kBatteryResistance, &resistance_str)) {
164         LOG(ERROR) << "Can't read the battery resistance from " << kBatteryResistance;
165         resistance = -INT_MAX;
166     } else if (!(std::stringstream(resistance_str) >> resistance)) {
167         LOG(ERROR) << "Can't parse battery resistance value " << resistance_str;
168         resistance = -INT_MAX;
169     }
170 
171     if (!android::base::ReadFileToString(kBatteryOCV, &ocv_str)) {
172         LOG(ERROR) << "Can't read open-circuit voltage (ocv) value from " << kBatteryOCV;
173         ocv = -INT_MAX;
174     } else if (!(std::stringstream(ocv_str) >> ocv)) {
175         LOG(ERROR) << "Can't parse open-circuit voltage (ocv) value " << ocv_str;
176         ocv = -INT_MAX;
177     }
178 
179     int32_t sample[NUM_FIELDS] = {[TIME] = time,
180                                   [RES] = resistance,
181                                   [CURR] = props->batteryCurrent,
182                                   [VOLT] = props->batteryVoltage,
183                                   [TEMP] = props->batteryTemperature,
184                                   [SOC] = props->batteryLevel,
185                                   [OCV] = ocv};
186     if (props->batteryStatus != android::BATTERY_STATUS_CHARGING) {
187         num_res_samples_++;
188     }
189 
190     // Only calculate the min and max for metric types we want to upload
191     for (int metric = 0; metric < NUM_FIELDS; metric++) {
192         // Discard resistance min/max when charging
193         if ((metric == RES && props->batteryStatus == android::BATTERY_STATUS_CHARGING) ||
194             kStatsSnapshotType[metric] < 0)
195             continue;
196         if (num_samples_ == 0 || (metric == RES && num_res_samples_ == 0) ||
197             sample[metric] < min_[metric][metric]) {
198             for (int i = 0; i < NUM_FIELDS; i++) {  // update new min with current sample
199                 min_[metric][i] = sample[i];
200             }
201         }
202         if (num_samples_ == 0 || (metric == RES && num_res_samples_ == 0) ||
203             sample[metric] > max_[metric][metric]) {
204             for (int i = 0; i < NUM_FIELDS; i++) {  // update new max with current sample
205                 max_[metric][i] = sample[i];
206             }
207         }
208     }
209 
210     num_samples_++;
211     last_sample_ = time;
212     return true;
213 }
214 
logBatteryProperties(struct android::BatteryProperties * props)215 void BatteryMetricsLogger::logBatteryProperties(struct android::BatteryProperties *props) {
216     int32_t time = getTime();
217     if (last_sample_ == 0 || time - last_sample_ >= kSamplePeriod)
218         recordSample(props);
219     if (last_sample_ - last_upload_ > kUploadPeriod || num_samples_ >= kMaxSamples)
220         uploadMetrics();
221 
222     return;
223 }
224 
225 }  // namespace health
226 }  // namespace pixel
227 }  // namespace google
228 }  // namespace hardware
229