/* * Copyright (C) 2021 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. */ #define LOG_TAG "pixelstats-uevent: ChargeStatsReporter" #include #include #include #include #include #include #include #include namespace android { namespace hardware { namespace google { namespace pixel { using aidl::android::frameworks::stats::IStats; using aidl::android::frameworks::stats::VendorAtom; using aidl::android::frameworks::stats::VendorAtomValue; using android::base::ReadFileToString; using android::base::WriteStringToFile; using android::hardware::google::pixel::PixelAtoms::ChargeStats; using android::hardware::google::pixel::PixelAtoms::VoltageTierStats; ChargeStatsReporter::ChargeStatsReporter() {} void ChargeStatsReporter::ReportChargeStats(const std::shared_ptr &stats_client, const std::string line, const std::string wline_at, const std::string wline_ac, const std::string pca_line) { int charge_stats_fields[] = { ChargeStats::kAdapterTypeFieldNumber, ChargeStats::kAdapterVoltageFieldNumber, ChargeStats::kAdapterAmperageFieldNumber, ChargeStats::kSsocInFieldNumber, ChargeStats::kVoltageInFieldNumber, ChargeStats::kSsocOutFieldNumber, ChargeStats::kVoltageOutFieldNumber, ChargeStats::kChargeCapacityFieldNumber, ChargeStats::kAdapterCapabilities0FieldNumber, ChargeStats::kAdapterCapabilities1FieldNumber, ChargeStats::kAdapterCapabilities2FieldNumber, ChargeStats::kAdapterCapabilities3FieldNumber, ChargeStats::kAdapterCapabilities4FieldNumber, ChargeStats::kReceiverState0FieldNumber, ChargeStats::kReceiverState1FieldNumber, }; const int32_t chg_fields_size = std::size(charge_stats_fields); static_assert(chg_fields_size == 15, "Unexpected charge stats fields size"); const int32_t wlc_fields_size = 7; std::vector values(chg_fields_size); VendorAtomValue val; int32_t i = 0, tmp[chg_fields_size] = {0}, fields_size = (chg_fields_size - wlc_fields_size); int32_t pca_ac[2] = {0}, pca_rs[5] = {0}; ALOGD("processing %s", line.c_str()); if (sscanf(line.c_str(), "%d,%d,%d, %d,%d,%d,%d %d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5], &tmp[6], &tmp[7]) == 8) { /* Age Adjusted Charge Rate (AACR) logs an additional battery capacity in order to determine * the charge curve needed to minimize battery cycle life degradation, while also minimizing * impact to the user. */ } else if (sscanf(line.c_str(), "%d,%d,%d, %d,%d,%d,%d", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5], &tmp[6]) != 7) { ALOGE("Couldn't process %s", line.c_str()); return; } if (!wline_at.empty()) { int32_t ssoc_tmp = 0; ALOGD("wlc: processing %s", wline_at.c_str()); if (sscanf(wline_at.c_str(), "A:%d", &ssoc_tmp) != 1) { ALOGE("Couldn't process %s", wline_at.c_str()); } else { tmp[0] = wireless_charge_stats_.TranslateSysModeToAtomValue(ssoc_tmp); ALOGD("wlc: processing %s", wline_ac.c_str()); if (sscanf(wline_ac.c_str(), "D:%x,%x,%x,%x,%x, %x,%x", &tmp[8], &tmp[9], &tmp[10], &tmp[11], &tmp[12], &tmp[13], &tmp[14]) != 7) ALOGE("Couldn't process %s", wline_ac.c_str()); else fields_size = chg_fields_size; /* include wlc stats */ } } if (!pca_line.empty()) { ALOGD("pca: processing %s", pca_line.c_str()); if (sscanf(pca_line.c_str(), "D:%x,%x %x,%x,%x,%x,%x", &pca_ac[0], &pca_ac[1], &pca_rs[0], &pca_rs[1], &pca_rs[2], &pca_rs[3], &pca_rs[4]) != 7) { ALOGE("Couldn't process %s", pca_line.c_str()); } else { fields_size = chg_fields_size; /* include pca stats */ tmp[10] = pca_rs[2]; tmp[11] = pca_rs[3]; tmp[12] = pca_rs[4]; tmp[14] = pca_rs[1]; if (wline_at.empty()) { tmp[8] = pca_ac[0]; tmp[9] = pca_ac[1]; tmp[13] = pca_rs[0]; } } } for (i = 0; i < fields_size; i++) { val.set(tmp[i]); values[charge_stats_fields[i] - kVendorAtomOffset] = val; } VendorAtom event = {.reverseDomainName = "", .atomId = PixelAtoms::Atom::kChargeStats, .values = std::move(values)}; const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event); if (!ret.isOk()) ALOGE("Unable to report ChargeStats to Stats service"); } void ChargeStatsReporter::ReportVoltageTierStats(const std::shared_ptr &stats_client, const char *line, const bool has_wireless = false, const std::string &wfile_contents = "") { int voltage_tier_stats_fields[] = { VoltageTierStats::kVoltageTierFieldNumber, VoltageTierStats::kSocInFieldNumber, /* retrieved via ssoc_tmp */ VoltageTierStats::kCcInFieldNumber, VoltageTierStats::kTempInFieldNumber, VoltageTierStats::kTimeFastSecsFieldNumber, VoltageTierStats::kTimeTaperSecsFieldNumber, VoltageTierStats::kTimeOtherSecsFieldNumber, VoltageTierStats::kTempMinFieldNumber, VoltageTierStats::kTempAvgFieldNumber, VoltageTierStats::kTempMaxFieldNumber, VoltageTierStats::kIbattMinFieldNumber, VoltageTierStats::kIbattAvgFieldNumber, VoltageTierStats::kIbattMaxFieldNumber, VoltageTierStats::kIclMinFieldNumber, VoltageTierStats::kIclAvgFieldNumber, VoltageTierStats::kIclMaxFieldNumber, VoltageTierStats::kMinAdapterPowerOutFieldNumber, VoltageTierStats::kTimeAvgAdapterPowerOutFieldNumber, VoltageTierStats::kMaxAdapterPowerOutFieldNumber, VoltageTierStats::kChargingOperatingPointFieldNumber}; const int32_t vtier_fields_size = std::size(voltage_tier_stats_fields); static_assert(vtier_fields_size == 20, "Unexpected voltage tier stats fields size"); const int32_t wlc_fields_size = 4; std::vector values(vtier_fields_size); VendorAtomValue val; float ssoc_tmp; int32_t i = 0, tmp[vtier_fields_size - 1] = {0}, /* ssoc_tmp is not saved in this array */ fields_size = (vtier_fields_size - wlc_fields_size); if (sscanf(line, "%d, %f,%d,%d, %d,%d,%d, %d,%d,%d, %d,%d,%d, %d,%d,%d", &tmp[0], &ssoc_tmp, &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5], &tmp[6], &tmp[7], &tmp[8], &tmp[9], &tmp[10], &tmp[11], &tmp[12], &tmp[13], &tmp[14]) != 16) { /* If format isn't as expected, then ignore line on purpose */ return; } if (has_wireless) { wireless_charge_stats_.CalculateWirelessChargeStats(static_cast(ssoc_tmp), wfile_contents); tmp[15] = wireless_charge_stats_.pout_min_; tmp[16] = wireless_charge_stats_.pout_avg_; tmp[17] = wireless_charge_stats_.pout_max_; tmp[18] = wireless_charge_stats_.of_freq_; fields_size = vtier_fields_size; /* include wlc stats */ } ALOGD("VoltageTierStats: processed %s", line); val.set(tmp[0]); values[voltage_tier_stats_fields[0] - kVendorAtomOffset] = val; val.set(ssoc_tmp); values[voltage_tier_stats_fields[1] - kVendorAtomOffset] = val; for (i = 2; i < fields_size; i++) { val.set(tmp[i - 1]); values[voltage_tier_stats_fields[i] - kVendorAtomOffset] = val; } VendorAtom event = {.reverseDomainName = "", .atomId = PixelAtoms::Atom::kVoltageTierStats, .values = std::move(values)}; const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event); if (!ret.isOk()) ALOGE("Unable to report VoltageTierStats to Stats service"); } void ChargeStatsReporter::checkAndReport(const std::shared_ptr &stats_client, const std::string &path) { std::string file_contents, line, wfile_contents, wline_at, wline_ac, pca_file_contents, pca_line, thermal_file_contents, gcharger_file_contents; std::istringstream ss; bool has_wireless = wireless_charge_stats_.CheckWirelessContentsAndAck(&wfile_contents); bool has_pca = pca_charge_stats_.CheckPcaContentsAndAck(&pca_file_contents); bool has_thermal = checkContentsAndAck(&thermal_file_contents, kThermalChargeMetricsPath); bool has_gcharger = checkContentsAndAck(&gcharger_file_contents, kGChargerMetricsPath); if (!ReadFileToString(path.c_str(), &file_contents)) { ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno)); return; } ss.str(file_contents); if (!std::getline(ss, line)) { ALOGE("Unable to read first line"); return; } if (!WriteStringToFile("0", path.c_str())) { ALOGE("Couldn't clear %s - %s", path.c_str(), strerror(errno)); } if (has_pca) { std::istringstream pca_ss; pca_ss.str(pca_file_contents); std::getline(pca_ss, pca_line); } if (has_wireless) { std::istringstream wss; /* there are two lines in the head, A: ...(Adapter Type) and D: ...(Adapter Capabilities) */ wss.str(wfile_contents); std::getline(wss, wline_at); std::getline(wss, wline_ac); /* reset initial tier soc */ wireless_charge_stats_.tier_soc_ = 0; } ReportChargeStats(stats_client, line, wline_at, wline_ac, pca_line); while (std::getline(ss, line)) { ReportVoltageTierStats(stats_client, line.c_str(), has_wireless, wfile_contents); } if (has_thermal) { std::istringstream wss; wss.str(thermal_file_contents); while (std::getline(wss, line)) { ReportVoltageTierStats(stats_client, line.c_str()); } } if (has_gcharger) { std::istringstream wss; wss.str(gcharger_file_contents); while (std::getline(wss, line)) { ReportVoltageTierStats(stats_client, line.c_str()); } } } bool ChargeStatsReporter::checkContentsAndAck(std::string *file_contents, const std::string &path) { if (!ReadFileToString(path.c_str(), file_contents)) { return false; } if (!WriteStringToFile("0", path.c_str())) { ALOGE("Couldn't clear %s - %s", path.c_str(), strerror(errno)); return false; } return true; } } // namespace pixel } // namespace google } // namespace hardware } // namespace android