• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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