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