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 <android/frameworks/stats/1.0/IStats.h>
19 #include <pixelhealth/BatteryMetricsLogger.h>
20
21 namespace hardware {
22 namespace google {
23 namespace pixel {
24 namespace health {
25
26 using android::sp;
27 using android::frameworks::stats::V1_0::BatteryHealthSnapshotArgs;
28 using android::frameworks::stats::V1_0::IStats;
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(sp<IStats> stats_client,sampleType type)51 bool BatteryMetricsLogger::uploadOutlierMetric(sp<IStats> stats_client, sampleType type) {
52 BatteryHealthSnapshotArgs min_stats_ss = {
53 .type = static_cast<BatteryHealthSnapshotArgs::BatterySnapshotType>(0),
54 .temperatureDeciC = min_[type][TEMP],
55 .voltageMicroV = min_[type][VOLT],
56 .currentMicroA = min_[type][CURR],
57 .openCircuitVoltageMicroV = min_[type][OCV],
58 .resistanceMicroOhm = min_[type][RES],
59 .levelPercent = min_[type][SOC]};
60 BatteryHealthSnapshotArgs max_stats_ss = {
61 .type = static_cast<BatteryHealthSnapshotArgs::BatterySnapshotType>(0),
62 .temperatureDeciC = max_[type][TEMP],
63 .voltageMicroV = max_[type][VOLT],
64 .currentMicroA = max_[type][CURR],
65 .openCircuitVoltageMicroV = max_[type][OCV],
66 .resistanceMicroOhm = max_[type][RES],
67 .levelPercent = max_[type][SOC]};
68 if (kStatsSnapshotType[type] < 0)
69 return false;
70
71 min_stats_ss.type = (BatteryHealthSnapshotArgs::BatterySnapshotType)kStatsSnapshotType[type];
72 max_stats_ss.type =
73 (BatteryHealthSnapshotArgs::BatterySnapshotType)(kStatsSnapshotType[type] + 1);
74
75 stats_client->reportBatteryHealthSnapshot(min_stats_ss);
76 stats_client->reportBatteryHealthSnapshot(max_stats_ss);
77
78 return true;
79 }
80
uploadAverageBatteryResistance(sp<IStats> stats_client)81 bool BatteryMetricsLogger::uploadAverageBatteryResistance(sp<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 BatteryHealthSnapshotArgs avg_res_ss_stats = {
101 .type = BatteryHealthSnapshotArgs::BatterySnapshotType::AVG_RESISTANCE,
102 .temperatureDeciC = 0,
103 .voltageMicroV = 0,
104 .currentMicroA = 0,
105 .openCircuitVoltageMicroV = 0,
106 .resistanceMicroOhm = batt_avg_res,
107 .levelPercent = 0};
108 stats_client->reportBatteryHealthSnapshot(avg_res_ss_stats);
109 return true;
110 }
111
uploadMetrics(void)112 bool BatteryMetricsLogger::uploadMetrics(void) {
113 int64_t time = getTime();
114
115 if (last_sample_ == 0)
116 return false;
117
118 LOG(INFO) << "Uploading metrics at time " << std::to_string(time) << " w/ "
119 << std::to_string(num_samples_) << " samples";
120
121 sp<IStats> stats_client = IStats::tryGetService();
122 if (!stats_client) {
123 LOG(ERROR) << "Unable to connect to Stats service";
124 return false;
125 }
126
127 // Only log and upload the min and max for metric types we want to upload
128 for (int metric = 0; metric < NUM_FIELDS; metric++) {
129 if ((metric == RES && num_res_samples_ == 0) || kStatsSnapshotType[metric] < 0)
130 continue;
131 std::string log_min = "min-" + std::to_string(metric) + " ";
132 std::string log_max = "max-" + std::to_string(metric) + " ";
133 for (int j = 0; j < NUM_FIELDS; j++) {
134 log_min += std::to_string(min_[metric][j]) + " ";
135 log_max += std::to_string(max_[metric][j]) + " ";
136 }
137 LOG(INFO) << log_min;
138 LOG(INFO) << log_max;
139 // Upload min/max metrics
140 uploadOutlierMetric(stats_client, static_cast<sampleType>(metric));
141 }
142
143 uploadAverageBatteryResistance(stats_client);
144
145 // Clear existing data
146 memset(min_, 0, sizeof(min_));
147 memset(max_, 0, sizeof(max_));
148 num_samples_ = 0;
149 num_res_samples_ = 0;
150 last_upload_ = time;
151 LOG(INFO) << "Finished uploading to tron";
152 return true;
153 }
154
recordSample(struct android::BatteryProperties * props)155 bool BatteryMetricsLogger::recordSample(struct android::BatteryProperties *props) {
156 std::string resistance_str, ocv_str;
157 int32_t resistance, ocv;
158 int32_t time = getTime();
159
160 LOG(INFO) << "Recording a sample at time " << std::to_string(time);
161
162 if (!android::base::ReadFileToString(kBatteryResistance, &resistance_str)) {
163 LOG(ERROR) << "Can't read the battery resistance from " << kBatteryResistance;
164 resistance = -INT_MAX;
165 } else if (!(std::stringstream(resistance_str) >> resistance)) {
166 LOG(ERROR) << "Can't parse battery resistance value " << resistance_str;
167 resistance = -INT_MAX;
168 }
169
170 if (!android::base::ReadFileToString(kBatteryOCV, &ocv_str)) {
171 LOG(ERROR) << "Can't read open-circuit voltage (ocv) value from " << kBatteryOCV;
172 ocv = -INT_MAX;
173 } else if (!(std::stringstream(ocv_str) >> ocv)) {
174 LOG(ERROR) << "Can't parse open-circuit voltage (ocv) value " << ocv_str;
175 ocv = -INT_MAX;
176 }
177
178 int32_t sample[NUM_FIELDS] = {[TIME] = time,
179 [RES] = resistance,
180 [CURR] = props->batteryCurrent,
181 [VOLT] = props->batteryVoltage,
182 [TEMP] = props->batteryTemperature,
183 [SOC] = props->batteryLevel,
184 [OCV] = ocv};
185 if (props->batteryStatus != android::BATTERY_STATUS_CHARGING) {
186 num_res_samples_++;
187 }
188
189 // Only calculate the min and max for metric types we want to upload
190 for (int metric = 0; metric < NUM_FIELDS; metric++) {
191 // Discard resistance min/max when charging
192 if ((metric == RES && props->batteryStatus == android::BATTERY_STATUS_CHARGING) ||
193 kStatsSnapshotType[metric] < 0)
194 continue;
195 if (num_samples_ == 0 || (metric == RES && num_res_samples_ == 0) ||
196 sample[metric] < min_[metric][metric]) {
197 for (int i = 0; i < NUM_FIELDS; i++) { // update new min with current sample
198 min_[metric][i] = sample[i];
199 }
200 }
201 if (num_samples_ == 0 || (metric == RES && num_res_samples_ == 0) ||
202 sample[metric] > max_[metric][metric]) {
203 for (int i = 0; i < NUM_FIELDS; i++) { // update new max with current sample
204 max_[metric][i] = sample[i];
205 }
206 }
207 }
208
209 num_samples_++;
210 last_sample_ = time;
211 return true;
212 }
213
logBatteryProperties(struct android::BatteryProperties * props)214 void BatteryMetricsLogger::logBatteryProperties(struct android::BatteryProperties *props) {
215 int32_t time = getTime();
216 if (last_sample_ == 0 || time - last_sample_ >= kSamplePeriod)
217 recordSample(props);
218 if (last_sample_ - last_upload_ > kUploadPeriod || num_samples_ >= kMaxSamples)
219 uploadMetrics();
220
221 return;
222 }
223
224 } // namespace health
225 } // namespace pixel
226 } // namespace google
227 } // namespace hardware
228