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,int sample_period,int upload_period)30 BatteryMetricsLogger::BatteryMetricsLogger(const char *const batt_res, const char *const batt_ocv,
31 int sample_period, int upload_period)
32 : kBatteryResistance(batt_res),
33 kBatteryOCV(batt_ocv),
34 kSamplePeriod(sample_period),
35 kUploadPeriod(upload_period),
36 kMaxSamples(upload_period / sample_period) {
37 last_sample_ = 0;
38 last_upload_ = 0;
39 num_res_samples_ = 0;
40 num_samples_ = 0;
41 memset(min_, 0, sizeof(min_));
42 memset(max_, 0, sizeof(max_));
43 accum_resistance_ = 0;
44 }
45
getTime(void)46 int64_t BatteryMetricsLogger::getTime(void) {
47 return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
48 }
49
uploadOutlierMetric(sp<IStats> stats_client,sampleType type)50 bool BatteryMetricsLogger::uploadOutlierMetric(sp<IStats> stats_client, sampleType type) {
51 BatteryHealthSnapshotArgs min_stats_ss = {
52 .type = static_cast<BatteryHealthSnapshotArgs::BatterySnapshotType>(0),
53 .temperatureDeciC = min_[type][TEMP],
54 .voltageMicroV = min_[type][VOLT],
55 .currentMicroA = min_[type][CURR],
56 .openCircuitVoltageMicroV = min_[type][OCV],
57 .resistanceMicroOhm = min_[type][RES],
58 .levelPercent = min_[type][SOC]};
59 BatteryHealthSnapshotArgs max_stats_ss = {
60 .type = static_cast<BatteryHealthSnapshotArgs::BatterySnapshotType>(0),
61 .temperatureDeciC = max_[type][TEMP],
62 .voltageMicroV = max_[type][VOLT],
63 .currentMicroA = max_[type][CURR],
64 .openCircuitVoltageMicroV = max_[type][OCV],
65 .resistanceMicroOhm = max_[type][RES],
66 .levelPercent = max_[type][SOC]};
67 if (kStatsSnapshotType[type] < 0)
68 return false;
69
70 min_stats_ss.type = (BatteryHealthSnapshotArgs::BatterySnapshotType)kStatsSnapshotType[type];
71 max_stats_ss.type =
72 (BatteryHealthSnapshotArgs::BatterySnapshotType)(kStatsSnapshotType[type] + 1);
73
74 stats_client->reportBatteryHealthSnapshot(min_stats_ss);
75 stats_client->reportBatteryHealthSnapshot(max_stats_ss);
76
77 return true;
78 }
79
uploadMetrics(void)80 bool BatteryMetricsLogger::uploadMetrics(void) {
81 int64_t time = getTime();
82 int32_t avg_resistance = 0;
83
84 if (last_sample_ == 0)
85 return false;
86
87 LOG(INFO) << "Uploading metrics at time " << std::to_string(time) << " w/ "
88 << std::to_string(num_samples_) << " samples";
89
90 if (num_res_samples_)
91 avg_resistance = accum_resistance_ / num_res_samples_;
92
93 LOG(INFO) << "Logging metrics";
94
95 sp<IStats> stats_client = IStats::tryGetService();
96 if (!stats_client) {
97 LOG(ERROR) << "Unable to connect to Stats service";
98 return false;
99 }
100
101 // Only log and upload the min and max for metric types we want to upload
102 for (int metric = 0; metric < NUM_FIELDS; metric++) {
103 if ((metric == RES && num_res_samples_ == 0) || kStatsSnapshotType[metric] < 0)
104 continue;
105 std::string log_min = "min-" + std::to_string(metric) + " ";
106 std::string log_max = "max-" + std::to_string(metric) + " ";
107 for (int j = 0; j < NUM_FIELDS; j++) {
108 log_min += std::to_string(min_[metric][j]) + " ";
109 log_max += std::to_string(max_[metric][j]) + " ";
110 }
111 LOG(INFO) << log_min;
112 LOG(INFO) << log_max;
113 // Upload min/max metrics
114 uploadOutlierMetric(stats_client, static_cast<sampleType>(metric));
115 }
116
117 // Upload average metric
118 BatteryHealthSnapshotArgs avg_res_ss_stats = {
119 .type = BatteryHealthSnapshotArgs::BatterySnapshotType::AVG_RESISTANCE,
120 .temperatureDeciC = 0,
121 .voltageMicroV = 0,
122 .currentMicroA = 0,
123 .openCircuitVoltageMicroV = 0,
124 .resistanceMicroOhm = avg_resistance,
125 .levelPercent = 0};
126 if (num_res_samples_) {
127 stats_client->reportBatteryHealthSnapshot(avg_res_ss_stats);
128 }
129
130 // Clear existing data
131 memset(min_, 0, sizeof(min_));
132 memset(max_, 0, sizeof(max_));
133 num_res_samples_ = 0;
134 num_samples_ = 0;
135 last_upload_ = time;
136 accum_resistance_ = 0;
137 LOG(INFO) << "Finished uploading to tron";
138 return true;
139 }
140
recordSample(struct android::BatteryProperties * props)141 bool BatteryMetricsLogger::recordSample(struct android::BatteryProperties *props) {
142 std::string resistance_str, ocv_str;
143 int32_t resistance, ocv;
144 int32_t time = getTime();
145
146 LOG(INFO) << "Recording a sample at time " << std::to_string(time);
147
148 if (!android::base::ReadFileToString(kBatteryResistance, &resistance_str)) {
149 LOG(ERROR) << "Can't read the battery resistance";
150 resistance = 0;
151 } else if (!(resistance = stoi(resistance_str))) {
152 LOG(ERROR) << "Can't parse battery resistance value " << resistance_str;
153 }
154
155 if (!android::base::ReadFileToString(kBatteryOCV, &ocv_str)) {
156 LOG(ERROR) << "Can't read the open-circuit voltage (ocv) value";
157 ocv = 0;
158 } else if (!(ocv = stoi(ocv_str))) {
159 LOG(ERROR) << "Can't parse open-circuit voltage (ocv) value " << ocv_str;
160 }
161
162 int32_t sample[NUM_FIELDS] = {[TIME] = time,
163 [RES] = resistance,
164 [CURR] = props->batteryCurrent,
165 [VOLT] = props->batteryVoltage,
166 [TEMP] = props->batteryTemperature,
167 [SOC] = props->batteryLevel,
168 [OCV] = ocv};
169 if (props->batteryStatus != android::BATTERY_STATUS_CHARGING) {
170 accum_resistance_ += resistance;
171 num_res_samples_++;
172 }
173
174 // Only calculate the min and max for metric types we want to upload
175 for (int metric = 0; metric < NUM_FIELDS; metric++) {
176 // Discard resistance min/max when charging
177 if ((metric == RES && props->batteryStatus == android::BATTERY_STATUS_CHARGING) ||
178 kStatsSnapshotType[metric] < 0)
179 continue;
180 if (num_samples_ == 0 || (metric == RES && num_res_samples_ == 0) ||
181 sample[metric] < min_[metric][metric]) {
182 for (int i = 0; i < NUM_FIELDS; i++) { // update new min with current sample
183 min_[metric][i] = sample[i];
184 }
185 }
186 if (num_samples_ == 0 || (metric == RES && num_res_samples_ == 0) ||
187 sample[metric] > max_[metric][metric]) {
188 for (int i = 0; i < NUM_FIELDS; i++) { // update new max with current sample
189 max_[metric][i] = sample[i];
190 }
191 }
192 }
193
194 num_samples_++;
195 last_sample_ = time;
196 return true;
197 }
198
logBatteryProperties(struct android::BatteryProperties * props)199 void BatteryMetricsLogger::logBatteryProperties(struct android::BatteryProperties *props) {
200 int32_t time = getTime();
201 if (last_sample_ == 0 || time - last_sample_ >= kSamplePeriod)
202 recordSample(props);
203 if (last_sample_ - last_upload_ > kUploadPeriod || num_samples_ >= kMaxSamples)
204 uploadMetrics();
205
206 return;
207 }
208
209 } // namespace health
210 } // namespace pixel
211 } // namespace google
212 } // namespace hardware
213