| |
| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <pixelhealth/BatteryMetricsLogger.h> |
| |
| namespace hardware { |
| namespace google { |
| namespace pixel { |
| namespace health { |
| |
| using ::hardware::google::pixelstats::V1_0::IPixelStats; |
| using BatteryHealthSnapshotArgs = |
| ::hardware::google::pixelstats::V1_0::IPixelStats::BatteryHealthSnapshotArgs; |
| using android::sp; |
| |
| BatteryMetricsLogger::BatteryMetricsLogger(const char *const batt_res, const char *const batt_ocv, |
| int sample_period, int upload_period) |
| : kBatteryResistance(batt_res), |
| kBatteryOCV(batt_ocv), |
| kSamplePeriod(sample_period), |
| kUploadPeriod(upload_period), |
| kMaxSamples(upload_period / sample_period) { |
| last_sample_ = 0; |
| last_upload_ = 0; |
| num_res_samples_ = 0; |
| num_samples_ = 0; |
| memset(min_, 0, sizeof(min_)); |
| memset(max_, 0, sizeof(max_)); |
| accum_resistance_ = 0; |
| } |
| |
| int64_t BatteryMetricsLogger::getTime(void) { |
| return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME)); |
| } |
| |
| bool BatteryMetricsLogger::uploadOutlierMetric(sp<IPixelStats> client, sampleType type) { |
| BatteryHealthSnapshotArgs min_ss = {static_cast<BatterySnapshotType>(0), |
| min_[type][TEMP], |
| min_[type][VOLT], |
| min_[type][CURR], |
| min_[type][OCV], |
| min_[type][RES], |
| min_[type][SOC]}; |
| BatteryHealthSnapshotArgs max_ss = {static_cast<BatterySnapshotType>(0), |
| max_[type][TEMP], |
| max_[type][VOLT], |
| max_[type][CURR], |
| max_[type][OCV], |
| max_[type][RES], |
| max_[type][SOC]}; |
| |
| if (kSnapshotType[type] < 0) |
| return false; |
| |
| min_ss.type = (BatterySnapshotType)kSnapshotType[type]; |
| max_ss.type = (BatterySnapshotType)(kSnapshotType[type] + 1); |
| |
| client->reportBatteryHealthSnapshot(min_ss); |
| client->reportBatteryHealthSnapshot(max_ss); |
| return true; |
| } |
| |
| bool BatteryMetricsLogger::uploadMetrics(void) { |
| int64_t time = getTime(); |
| int32_t avg_resistance = 0; |
| |
| if (last_sample_ == 0) |
| return false; |
| |
| LOG(INFO) << "Uploading metrics at time " << std::to_string(time) << " w/ " |
| << std::to_string(num_samples_) << " samples"; |
| |
| if (num_res_samples_) |
| avg_resistance = accum_resistance_ / num_res_samples_; |
| |
| LOG(INFO) << "Logging metrics to tron"; |
| |
| sp<IPixelStats> client = IPixelStats::tryGetService(); |
| if (!client) { |
| LOG(ERROR) << "Unable to connect to PixelStats service"; |
| return false; |
| } |
| |
| // Only log and upload the min and max for metric types we want to upload |
| for (int metric = 0; metric < NUM_FIELDS; metric++) { |
| if ((metric == RES && num_res_samples_ == 0) || kSnapshotType[metric] < 0) |
| continue; |
| std::string log_min = "min-" + std::to_string(metric) + " "; |
| std::string log_max = "max-" + std::to_string(metric) + " "; |
| for (int j = 0; j < NUM_FIELDS; j++) { |
| log_min += std::to_string(min_[metric][j]) + " "; |
| log_max += std::to_string(max_[metric][j]) + " "; |
| } |
| LOG(INFO) << log_min; |
| LOG(INFO) << log_max; |
| // Upload min/max metrics |
| uploadOutlierMetric(client, static_cast<sampleType>(metric)); |
| } |
| |
| // Upload average metric |
| BatteryHealthSnapshotArgs avg_res_ss = { |
| BatterySnapshotType::AVG_RESISTANCE, 0, 0, 0, 0, avg_resistance, 0}; |
| if (num_res_samples_) |
| client->reportBatteryHealthSnapshot(avg_res_ss); |
| |
| // Clear existing data |
| memset(min_, 0, sizeof(min_)); |
| memset(max_, 0, sizeof(max_)); |
| num_res_samples_ = 0; |
| num_samples_ = 0; |
| last_upload_ = time; |
| accum_resistance_ = 0; |
| LOG(INFO) << "Finished uploading to tron"; |
| return true; |
| } |
| |
| bool BatteryMetricsLogger::recordSample(struct android::BatteryProperties *props) { |
| std::string resistance_str, ocv_str; |
| int32_t resistance, ocv; |
| int32_t time = getTime(); |
| |
| LOG(INFO) << "Recording a sample at time " << std::to_string(time); |
| |
| if (!android::base::ReadFileToString(kBatteryResistance, &resistance_str)) { |
| LOG(ERROR) << "Can't read the battery resistance"; |
| resistance = 0; |
| } else if (!(resistance = stoi(resistance_str))) { |
| LOG(ERROR) << "Can't parse battery resistance value " << resistance_str; |
| } |
| |
| if (!android::base::ReadFileToString(kBatteryOCV, &ocv_str)) { |
| LOG(ERROR) << "Can't read the open-circuit voltage (ocv) value"; |
| ocv = 0; |
| } else if (!(ocv = stoi(ocv_str))) { |
| LOG(ERROR) << "Can't parse open-circuit voltage (ocv) value " << ocv_str; |
| } |
| |
| int32_t sample[NUM_FIELDS] = {time, |
| resistance, |
| props->batteryCurrent, |
| props->batteryVoltage, |
| props->batteryTemperature, |
| props->batteryLevel, |
| ocv}; |
| if (props->batteryStatus != android::BATTERY_STATUS_CHARGING) { |
| accum_resistance_ += resistance; |
| num_res_samples_++; |
| } |
| |
| // Only calculate the min and max for metric types we want to upload |
| for (int metric = 0; metric < NUM_FIELDS; metric++) { |
| // Discard resistance min/max when charging |
| if ((metric == RES && props->batteryStatus == android::BATTERY_STATUS_CHARGING) || |
| kSnapshotType[metric] < 0) |
| continue; |
| if (num_samples_ == 0 || (metric == RES && num_res_samples_ == 0) || |
| sample[metric] < min_[metric][metric]) { |
| for (int i = 0; i < NUM_FIELDS; i++) { // update new min with current sample |
| min_[metric][i] = sample[i]; |
| } |
| } |
| if (num_samples_ == 0 || (metric == RES && num_res_samples_ == 0) || |
| sample[metric] > max_[metric][metric]) { |
| for (int i = 0; i < NUM_FIELDS; i++) { // update new max with current sample |
| max_[metric][i] = sample[i]; |
| } |
| } |
| } |
| |
| num_samples_++; |
| last_sample_ = time; |
| return true; |
| } |
| |
| void BatteryMetricsLogger::logBatteryProperties(struct android::BatteryProperties *props) { |
| int32_t time = getTime(); |
| if (last_sample_ == 0 || time - last_sample_ >= kSamplePeriod) |
| recordSample(props); |
| if (last_sample_ - last_upload_ > kUploadPeriod || num_samples_ >= kMaxSamples) |
| uploadMetrics(); |
| |
| return; |
| } |
| |
| } // namespace health |
| } // namespace pixel |
| } // namespace google |
| } // namespace hardware |