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