287 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			287 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * 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 <android-base/file.h>
 | |
| #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
 | |
| #include <log/log.h>
 | |
| #include <pixelstats/ChargeStatsReporter.h>
 | |
| #include <pixelstats/StatsHelper.h>
 | |
| #include <time.h>
 | |
| #include <utils/Timers.h>
 | |
| 
 | |
| #include <cmath>
 | |
| 
 | |
| 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<IStats> &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<VendorAtomValue> 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<VendorAtomValue::intValue>(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<IStats> &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<VendorAtomValue> 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<int>(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<VendorAtomValue::intValue>(tmp[0]);
 | |
|     values[voltage_tier_stats_fields[0] - kVendorAtomOffset] = val;
 | |
|     val.set<VendorAtomValue::floatValue>(ssoc_tmp);
 | |
|     values[voltage_tier_stats_fields[1] - kVendorAtomOffset] = val;
 | |
|     for (i = 2; i < fields_size; i++) {
 | |
|         val.set<VendorAtomValue::intValue>(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<IStats> &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
 |