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