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/HealthHelper.h>
20 #include <pixelhealth/StatsHelper.h>
21
22 using aidl::android::hardware::health::BatteryStatus;
23 using aidl::android::hardware::health::HealthInfo;
24
25 namespace hardware {
26 namespace google {
27 namespace pixel {
28 namespace health {
29
toBatterySnapshotType(int type)30 VendorBatteryHealthSnapshot::BatterySnapshotType toBatterySnapshotType(int type) {
31 return static_cast<VendorBatteryHealthSnapshot::BatterySnapshotType>(type);
32 }
33
BatteryMetricsLogger(const char * const batt_res,const char * const batt_ocv,const char * const batt_avg_res,int sample_period,int upload_period)34 BatteryMetricsLogger::BatteryMetricsLogger(const char *const batt_res, const char *const batt_ocv,
35 const char *const batt_avg_res, int sample_period,
36 int upload_period)
37 : kBatteryResistance(batt_res),
38 kBatteryOCV(batt_ocv),
39 kBatteryAvgResistance(batt_avg_res),
40 kSamplePeriod(sample_period),
41 kUploadPeriod(upload_period),
42 kMaxSamples(upload_period / sample_period) {
43 last_sample_ = 0;
44 last_upload_ = 0;
45 num_samples_ = 0;
46 num_res_samples_ = 0;
47 memset(min_, 0, sizeof(min_));
48 memset(max_, 0, sizeof(max_));
49 }
50
getTime(void)51 int64_t BatteryMetricsLogger::getTime(void) {
52 return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
53 }
54
uploadOutlierMetric(const std::shared_ptr<IStats> & stats_client,sampleType type)55 bool BatteryMetricsLogger::uploadOutlierMetric(const std::shared_ptr<IStats> &stats_client,
56 sampleType type) {
57 if (kStatsSnapshotType[type] < 0)
58 return false;
59
60 VendorBatteryHealthSnapshot min_stats_ss;
61 min_stats_ss.set_type(toBatterySnapshotType(kStatsSnapshotType[type]));
62 min_stats_ss.set_temperature_deci_celsius(min_[type][TEMP]);
63 min_stats_ss.set_voltage_micro_volt(min_[type][VOLT]);
64 min_stats_ss.set_current_micro_amps(min_[type][CURR]);
65 min_stats_ss.set_open_circuit_micro_volt(min_[type][OCV]);
66 min_stats_ss.set_resistance_micro_ohm(min_[type][RES]);
67 min_stats_ss.set_level_percent(min_[type][SOC]);
68
69 VendorBatteryHealthSnapshot max_stats_ss;
70 max_stats_ss.set_type(toBatterySnapshotType(kStatsSnapshotType[type] + 1));
71 max_stats_ss.set_temperature_deci_celsius(max_[type][TEMP]);
72 max_stats_ss.set_voltage_micro_volt(max_[type][VOLT]);
73 max_stats_ss.set_current_micro_amps(max_[type][CURR]);
74 max_stats_ss.set_open_circuit_micro_volt(max_[type][OCV]);
75 max_stats_ss.set_resistance_micro_ohm(max_[type][RES]);
76 max_stats_ss.set_level_percent(max_[type][SOC]);
77
78 reportBatteryHealthSnapshot(stats_client, min_stats_ss);
79 reportBatteryHealthSnapshot(stats_client, max_stats_ss);
80
81 return true;
82 }
83
uploadAverageBatteryResistance(const std::shared_ptr<IStats> & stats_client)84 bool BatteryMetricsLogger::uploadAverageBatteryResistance(
85 const std::shared_ptr<IStats> &stats_client) {
86 if (strlen(kBatteryAvgResistance) == 0) {
87 LOG(INFO) << "Sysfs path for average battery resistance not specified";
88 return true;
89 }
90
91 std::string file_content;
92 int32_t batt_avg_res;
93
94 if (!android::base::ReadFileToString(kBatteryAvgResistance, &file_content)) {
95 LOG(ERROR) << "Can't read " << kBatteryAvgResistance;
96 return false;
97 }
98 std::stringstream ss(file_content);
99 if (!(ss >> batt_avg_res)) {
100 LOG(ERROR) << "Can't parse average battery resistance " << file_content;
101 return false;
102 }
103 // Upload average metric
104 VendorBatteryHealthSnapshot avg_res_ss_stats;
105 avg_res_ss_stats.set_type(VendorBatteryHealthSnapshot::BATTERY_SNAPSHOT_TYPE_AVG_RESISTANCE);
106 avg_res_ss_stats.set_temperature_deci_celsius(0);
107 avg_res_ss_stats.set_voltage_micro_volt(0);
108 avg_res_ss_stats.set_current_micro_amps(0);
109 avg_res_ss_stats.set_open_circuit_micro_volt(0);
110 avg_res_ss_stats.set_resistance_micro_ohm(batt_avg_res);
111 avg_res_ss_stats.set_level_percent(0);
112
113 reportBatteryHealthSnapshot(stats_client, avg_res_ss_stats);
114 return true;
115 }
116
uploadMetrics(void)117 bool BatteryMetricsLogger::uploadMetrics(void) {
118 int64_t time = getTime();
119
120 if (last_sample_ == 0)
121 return false;
122
123 LOG(INFO) << "Uploading metrics at time " << std::to_string(time) << " w/ "
124 << std::to_string(num_samples_) << " samples";
125
126 std::shared_ptr<IStats> stats_client = getStatsService();
127 if (!stats_client) {
128 LOG(ERROR) << "Unable to connect to Stats service";
129 return false;
130 }
131
132 // Only log and upload the min and max for metric types we want to upload
133 for (int metric = 0; metric < NUM_FIELDS; metric++) {
134 if ((metric == RES && num_res_samples_ == 0) || kStatsSnapshotType[metric] < 0)
135 continue;
136 std::string log_min = "min-" + std::to_string(metric) + " ";
137 std::string log_max = "max-" + std::to_string(metric) + " ";
138 for (int j = 0; j < NUM_FIELDS; j++) {
139 log_min += std::to_string(min_[metric][j]) + " ";
140 log_max += std::to_string(max_[metric][j]) + " ";
141 }
142 LOG(INFO) << log_min;
143 LOG(INFO) << log_max;
144 // Upload min/max metrics
145 uploadOutlierMetric(stats_client, static_cast<sampleType>(metric));
146 }
147
148 uploadAverageBatteryResistance(stats_client);
149
150 // Clear existing data
151 memset(min_, 0, sizeof(min_));
152 memset(max_, 0, sizeof(max_));
153 num_samples_ = 0;
154 num_res_samples_ = 0;
155 last_upload_ = time;
156 LOG(INFO) << "Finished uploading to tron";
157 return true;
158 }
159
recordSample(const HealthInfo & health_info)160 bool BatteryMetricsLogger::recordSample(const HealthInfo &health_info) {
161 std::string resistance_str, ocv_str;
162 int32_t resistance, ocv;
163 int32_t time = getTime();
164
165 LOG(INFO) << "Recording a sample at time " << std::to_string(time);
166
167 if (!android::base::ReadFileToString(kBatteryResistance, &resistance_str)) {
168 LOG(ERROR) << "Can't read the battery resistance from " << kBatteryResistance;
169 resistance = -INT_MAX;
170 } else if (!(std::stringstream(resistance_str) >> resistance)) {
171 LOG(ERROR) << "Can't parse battery resistance value " << resistance_str;
172 resistance = -INT_MAX;
173 }
174
175 if (!android::base::ReadFileToString(kBatteryOCV, &ocv_str)) {
176 LOG(ERROR) << "Can't read open-circuit voltage (ocv) value from " << kBatteryOCV;
177 ocv = -INT_MAX;
178 } else if (!(std::stringstream(ocv_str) >> ocv)) {
179 LOG(ERROR) << "Can't parse open-circuit voltage (ocv) value " << ocv_str;
180 ocv = -INT_MAX;
181 }
182
183 int32_t sample[NUM_FIELDS] = {[TIME] = time,
184 [RES] = resistance,
185 [CURR] = health_info.batteryCurrentMicroamps,
186 [VOLT] = health_info.batteryVoltageMillivolts,
187 [TEMP] = health_info.batteryTemperatureTenthsCelsius,
188 [SOC] = health_info.batteryLevel,
189 [OCV] = ocv};
190 if (health_info.batteryStatus != BatteryStatus::CHARGING) {
191 num_res_samples_++;
192 }
193
194 // Only calculate the min and max for metric types we want to upload
195 for (int metric = 0; metric < NUM_FIELDS; metric++) {
196 // Discard resistance min/max when charging
197 if ((metric == RES && health_info.batteryStatus == BatteryStatus::CHARGING) ||
198 kStatsSnapshotType[metric] < 0)
199 continue;
200 if (num_samples_ == 0 || (metric == RES && num_res_samples_ == 0) ||
201 sample[metric] < min_[metric][metric]) {
202 for (int i = 0; i < NUM_FIELDS; i++) { // update new min with current sample
203 min_[metric][i] = sample[i];
204 }
205 }
206 if (num_samples_ == 0 || (metric == RES && num_res_samples_ == 0) ||
207 sample[metric] > max_[metric][metric]) {
208 for (int i = 0; i < NUM_FIELDS; i++) { // update new max with current sample
209 max_[metric][i] = sample[i];
210 }
211 }
212 }
213
214 num_samples_++;
215 last_sample_ = time;
216 return true;
217 }
218
logBatteryProperties(const HealthInfo & health_info)219 void BatteryMetricsLogger::logBatteryProperties(const HealthInfo &health_info) {
220 int32_t time = getTime();
221 if (last_sample_ == 0 || time - last_sample_ >= kSamplePeriod)
222 recordSample(health_info);
223 if (last_sample_ - last_upload_ > kUploadPeriod || num_samples_ >= kMaxSamples)
224 uploadMetrics();
225
226 return;
227 }
logBatteryProperties(struct android::BatteryProperties * props)228 void BatteryMetricsLogger::logBatteryProperties(struct android::BatteryProperties *props) {
229 logBatteryProperties(ToHealthInfo(props));
230 }
231
232 } // namespace health
233 } // namespace pixel
234 } // namespace google
235 } // namespace hardware
236