diff options
| author | Stephane Lee <stayfan@google.com> | 2020-03-23 17:13:33 -0700 |
|---|---|---|
| committer | Stephane Lee <stayfan@google.com> | 2020-04-15 16:00:37 -0700 |
| commit | f2ea194ef22b9b01bcb9d8902c8aa6365dbada5a (patch) | |
| tree | 0ffab2413f99f5b41bc4bc9247b31cc7f7d2158c /pixelstats | |
| parent | 8603b85a1d2494e610da3db6e22bb459cdee0e7e (diff) | |
| download | platform_hardware_google_pixel-f2ea194ef22b9b01bcb9d8902c8aa6365dbada5a.tar.gz platform_hardware_google_pixel-f2ea194ef22b9b01bcb9d8902c8aa6365dbada5a.tar.bz2 platform_hardware_google_pixel-f2ea194ef22b9b01bcb9d8902c8aa6365dbada5a.zip | |
Report FG capacity atom in pixelstats
Read ssoc_details, parse the battery FG SOC and the UI SOC.
Also parse the UI curve midpoint values and the log reason.
This will only nominally log when a device has been disconnected,
connected, or reached 100% capacity. In abnormal conditions, this
will log when the ssoc jumps by greater than 2%, or if abs(ssoc-gdf)
is greater than 4% (at a maximum of 30 min intervals).
Bug: 147992263
Bug: 122978471
Test: 'adb shell cmd stats print-logs', then check atom ID 105010 in logcat
Change-Id: I74b79158818d3ff9fad5ecd3c047c0b4184de23e
Diffstat (limited to 'pixelstats')
| -rw-r--r-- | pixelstats/Android.bp | 1 | ||||
| -rw-r--r-- | pixelstats/BatteryCapacityReporter.cpp | 182 | ||||
| -rw-r--r-- | pixelstats/UeventListener.cpp | 42 | ||||
| -rw-r--r-- | pixelstats/include/pixelstats/BatteryCapacityReporter.h | 82 | ||||
| -rw-r--r-- | pixelstats/include/pixelstats/UeventListener.h | 8 | ||||
| -rw-r--r-- | pixelstats/pixelatoms.proto | 26 |
6 files changed, 336 insertions, 5 deletions
diff --git a/pixelstats/Android.bp b/pixelstats/Android.bp index a4ec53b..7f2ee61 100644 --- a/pixelstats/Android.bp +++ b/pixelstats/Android.bp @@ -48,6 +48,7 @@ cc_library { "SysfsCollector.cpp", "UeventListener.cpp", "WlcReporter.cpp", + "BatteryCapacityReporter.cpp", ], cflags: [ "-Wall", diff --git a/pixelstats/BatteryCapacityReporter.cpp b/pixelstats/BatteryCapacityReporter.cpp new file mode 100644 index 0000000..9b2995f --- /dev/null +++ b/pixelstats/BatteryCapacityReporter.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2020 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: BatteryCapacityFG" + +#include <log/log.h> +#include <time.h> +#include <utils/Timers.h> +#include <cmath> + +#include <android-base/file.h> + +#include <android/frameworks/stats/1.0/IStats.h> +#include <pixelstats/BatteryCapacityReporter.h> + +#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h> + +namespace android { +namespace hardware { +namespace google { +namespace pixel { + +using android::base::ReadFileToString; +using android::frameworks::stats::V1_0::IStats; +using android::frameworks::stats::V1_0::VendorAtom; +using android::hardware::google::pixel::PixelAtoms::BatteryCapacityFG; + +BatteryCapacityReporter::BatteryCapacityReporter() { + // Remove the need for a translation function/table, while removing the dependency on the + // generated <pixelatoms.pb.h> in BatteryCapacityReporter.h. + static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_UNKNOWN) == + static_cast<int>(BatteryCapacityFG::LOG_REASON_UNKNOWN)); + static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_CONNECTED) == + static_cast<int>(BatteryCapacityFG::LOG_REASON_CONNECTED)); + static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_DISCONNECTED) == + static_cast<int>(BatteryCapacityFG::LOG_REASON_DISCONNECTED)); + static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_FULL_CHARGE) == + static_cast<int>(BatteryCapacityFG::LOG_REASON_FULL_CHARGE)); + static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_PERCENT_SKIP) == + static_cast<int>(BatteryCapacityFG::LOG_REASON_PERCENT_SKIP)); + static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_DIVERGING_FG) == + static_cast<int>(BatteryCapacityFG::LOG_REASON_DIVERGING_FG)); +} + +void BatteryCapacityReporter::checkAndReport(const std::string &path) { + if (parse(path)) { + if (check()) { + report(); + } + } +} + +int64_t BatteryCapacityReporter::getTimeSecs(void) { + return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME)); +} + +bool BatteryCapacityReporter::parse(const std::string &path) { + std::string batterySSOCContents; + if (!ReadFileToString(path, &batterySSOCContents)) { + ALOGE("Unable to read ssoc_details path: %s - %s", path.c_str(), strerror(errno)); + return false; + } + + // Parse file. Example format: + // soc: l=97% gdf=97.72 uic=97.72 rl=97.72 + // curve:[15.00 15.00][97.87 97.87][100.00 100.00] + // status: ct=1 rl=0 s=1 + if (sscanf(batterySSOCContents.c_str(), + "soc: %*s gdf=%f %*s rl=%f\n" + "curve:[%*f %*f][%f %f][%*f %*f]\n" + "status: %*s %*s s=%d", + &gdf_, &ssoc_, &gdf_curve_, &ssoc_curve_, &status_) != 5) { + ALOGE("Unable to parse ssoc_details [%s] from file %s to int.", batterySSOCContents.c_str(), + path.c_str()); + return false; + } + + return true; +} + +bool BatteryCapacityReporter::check(void) { + if (unexpected_event_timer_active_) { + // A 30 minute timer with a boolean gate helps prevent uninitialized timers and potential + // overflows. + // - Active when the timer is less than 30 minutes, thus continues checking the elapsed + // time. + // - Once expired (> 30 min), active becomes false and the timer no longer needs to check + // the elapsed time. + unexpected_event_timer_active_ = + (getTimeSecs() - unexpected_event_timer_secs_) <= (30 * 60); + } + + LogReason log_reason = LOG_REASON_UNKNOWN; + if (status_previous_ != status_) { + // Handle nominal events + + if (status_ == SOC_STATUS_CONNECTED) { + log_reason = LOG_REASON_CONNECTED; + + } else if (status_ == SOC_STATUS_DISCONNECTED) { + log_reason = LOG_REASON_DISCONNECTED; + + } else if (status_ == SOC_STATUS_FULL) { + log_reason = LOG_REASON_FULL_CHARGE; + } + + status_previous_ = status_; + + } else if (unexpected_event_timer_active_ == false) { + // Handle abnormal events at a minimum period + + const float diff = fabsf(ssoc_ - gdf_); + + if (fabsf(ssoc_ - ssoc_previous_) >= 2.0f) { + unexpected_event_timer_secs_ = getTimeSecs(); + unexpected_event_timer_active_ = true; + log_reason = LOG_REASON_PERCENT_SKIP; + + // Every +- 1% when above a 4% SOC difference (w/ timer) + } else if (static_cast<int>(round(ssoc_gdf_diff_previous_)) != + static_cast<int>(round(diff)) && + diff >= 4.0f) { + unexpected_event_timer_secs_ = getTimeSecs(); + unexpected_event_timer_active_ = true; + log_reason = LOG_REASON_DIVERGING_FG; + + ssoc_gdf_diff_previous_ = diff; + } + } + ssoc_previous_ = ssoc_; + + log_reason_ = log_reason; + return (log_reason != LOG_REASON_UNKNOWN); +} + +void BatteryCapacityReporter::report(void) { + sp<IStats> stats_client = IStats::tryGetService(); + if (!stats_client) { + ALOGD("Couldn't connect to IStats service"); + return; + } + + // Load values array + std::vector<VendorAtom::Value> values(5); + VendorAtom::Value tmp; + tmp.intValue(log_reason_); + values[BatteryCapacityFG::kCapacityLogReasonFieldNumber - kVendorAtomOffset] = tmp; + tmp.floatValue(gdf_); + values[BatteryCapacityFG::kCapacityGdfFieldNumber - kVendorAtomOffset] = tmp; + tmp.floatValue(ssoc_); + values[BatteryCapacityFG::kCapacitySsocFieldNumber - kVendorAtomOffset] = tmp; + tmp.floatValue(gdf_curve_); + values[BatteryCapacityFG::kCapacityGdfCurveFieldNumber - kVendorAtomOffset] = tmp; + tmp.floatValue(ssoc_curve_); + values[BatteryCapacityFG::kCapacitySsocCurveFieldNumber - kVendorAtomOffset] = tmp; + + // Send vendor atom to IStats HAL + VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(), + .atomId = PixelAtoms::Ids::FG_CAPACITY, + .values = values}; + Return<void> ret = stats_client->reportVendorAtom(event); + if (!ret.isOk()) + ALOGE("Unable to report to IStats service"); +} + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android diff --git a/pixelstats/UeventListener.cpp b/pixelstats/UeventListener.cpp index 1a740b6..f7b3c54 100644 --- a/pixelstats/UeventListener.cpp +++ b/pixelstats/UeventListener.cpp @@ -22,7 +22,6 @@ #include <android-base/strings.h> #include <android/frameworks/stats/1.0/IStats.h> #include <cutils/uevent.h> -#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h> #include <log/log.h> #include <pixelstats/UeventListener.h> #include <pixelstats/WlcReporter.h> @@ -268,10 +267,40 @@ void UeventListener::ReportWlc(const char *driver) { } } +/** + * Report raw battery capacity, system battery capacity and associated + * battery capacity curves. This data is collected to verify the filter + * applied on the battery capacity. This will allow debugging of issues + * ranging from incorrect fuel gauge hardware calculations to issues + * with the software reported battery capacity. + * + * The data is retrieved by parsing the battery power supply's ssoc_details. + * + * This atom logs data in 5 potential events: + * 1. When a device is connected + * 2. When a device is disconnected + * 3. When a device has reached a full charge (from the UI's perspective) + * 4. When there is a >= 2 percent skip in the UI reported SOC + * 5. When there is a difference of >= 4 percent between the raw hardware + * battery capacity and the system reported battery capacity. + */ +void UeventListener::ReportBatteryCapacityFGEvent(const char *subsystem) { + if (!subsystem || strcmp(subsystem, "SUBSYSTEM=power_supply")) { + return; + } + + // Indicates an implicit disable of the battery capacity reporting + if (kBatterySSOCPath.empty()) { + return; + } + + battery_capacity_reporter_.checkAndReport(kBatterySSOCPath); +} + bool UeventListener::ProcessUevent() { char msg[UEVENT_MSG_LEN + 2]; char *cp; - const char *driver, *product; + const char *driver, *product, *subsystem; const char *mic_break_status, *mic_degrade_status; const char *devpath; const char *powpresent; @@ -293,7 +322,7 @@ bool UeventListener::ProcessUevent() { msg[n] = '\0'; msg[n + 1] = '\0'; - driver = product = NULL; + driver = product = subsystem = NULL; mic_break_status = mic_degrade_status = devpath = powpresent = NULL; /** @@ -316,6 +345,8 @@ bool UeventListener::ProcessUevent() { } else if (!strncmp(cp, "POWER_SUPPLY_PRESENT=", strlen("POWER_SUPPLY_PRESENT="))) { powpresent = cp; + } else if (!strncmp(cp, "SUBSYSTEM=", strlen("SUBSYSTEM="))) { + subsystem = cp; } /* advance to after the next \0 */ @@ -329,13 +360,16 @@ bool UeventListener::ProcessUevent() { ReportUsbPortOverheatEvent(driver); ReportChargeMetricsEvent(driver); ReportWlc(powpresent); + ReportBatteryCapacityFGEvent(subsystem); return true; } -UeventListener::UeventListener(const std::string audio_uevent, const std::string overheat_path, +UeventListener::UeventListener(const std::string audio_uevent, const std::string ssoc_details_path, + const std::string overheat_path, const std::string charge_metrics_path) : kAudioUevent(audio_uevent), + kBatterySSOCPath(ssoc_details_path), kUsbPortOverheatPath(overheat_path), kChargeMetricsPath(charge_metrics_path), uevent_fd_(-1), diff --git a/pixelstats/include/pixelstats/BatteryCapacityReporter.h b/pixelstats/include/pixelstats/BatteryCapacityReporter.h new file mode 100644 index 0000000..eb0effe --- /dev/null +++ b/pixelstats/include/pixelstats/BatteryCapacityReporter.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 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. + */ + +#ifndef HARDWARE_GOOGLE_PIXEL_PIXELSTATS_BATTERYCAPACITYREPORTER_H +#define HARDWARE_GOOGLE_PIXEL_PIXELSTATS_BATTERYCAPACITYREPORTER_H + +namespace android { +namespace hardware { +namespace google { +namespace pixel { + +/** + * A class to upload battery capacity metrics + */ +class BatteryCapacityReporter { + public: + BatteryCapacityReporter(); + void checkAndReport(const std::string &path); + + private: + int64_t getTimeSecs(); + + bool parse(const std::string &path); + bool check(void); + void report(void); + + /** + * SOC status translation from sysfs node + */ + enum SOCStatus { + SOC_STATUS_UNKNOWN = 0, + SOC_STATUS_CONNECTED = 1, + SOC_STATUS_DISCONNECTED = 2, + SOC_STATUS_FULL = 3, + }; + + enum LogReason { + LOG_REASON_UNKNOWN = 0, + LOG_REASON_CONNECTED = 1, + LOG_REASON_DISCONNECTED = 2, + LOG_REASON_FULL_CHARGE = 3, + LOG_REASON_PERCENT_SKIP = 4, + LOG_REASON_DIVERGING_FG = 5, + }; + + SOCStatus status_ = SOC_STATUS_UNKNOWN; + SOCStatus status_previous_ = SOC_STATUS_UNKNOWN; + float gdf_ = 0.0; + float ssoc_ = 0.0f; + float gdf_curve_ = 0.0f; + float ssoc_curve_ = 0.0f; + float ssoc_previous_ = -1.0f; + float ssoc_gdf_diff_previous_ = 0.0f; + int64_t unexpected_event_timer_secs_ = 0; + bool unexpected_event_timer_active_ = false; + LogReason log_reason_ = LOG_REASON_UNKNOWN; + + // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so + // store everything in the values array at the index of the field number + // -2. + const int kVendorAtomOffset = 2; +}; + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android + +#endif // HARDWARE_GOOGLE_PIXEL_PIXELSTATS_BATTERYCAPACITYREPORTER_H diff --git a/pixelstats/include/pixelstats/UeventListener.h b/pixelstats/include/pixelstats/UeventListener.h index a7932a3..6ffcfb2 100644 --- a/pixelstats/include/pixelstats/UeventListener.h +++ b/pixelstats/include/pixelstats/UeventListener.h @@ -19,6 +19,7 @@ #include <android-base/chrono_utils.h> #include <android/frameworks/stats/1.0/IStats.h> +#include <pixelstats/BatteryCapacityReporter.h> using android::frameworks::stats::V1_0::IStats; using android::frameworks::stats::V1_0::UsbPortOverheatEvent; @@ -37,7 +38,7 @@ namespace pixel { class UeventListener { public: UeventListener( - const std::string audio_uevent, + const std::string audio_uevent, const std::string ssoc_details_path = "", const std::string overheat_path = "/sys/devices/platform/soc/soc:google,overheat_mitigation", const std::string charge_metrics_path = "/sys/class/power_supply/battery/charge_stats"); @@ -55,10 +56,15 @@ class UeventListener { void ReportVoltageTierStats(const sp<IStats> &stats_client, const char *line); void ReportChargeMetricsEvent(const char *driver); void ReportWlc(const char *driver); + void ReportBatteryCapacityFGEvent(const char *subsystem); const std::string kAudioUevent; + const std::string kBatterySSOCPath; const std::string kUsbPortOverheatPath; const std::string kChargeMetricsPath; + + BatteryCapacityReporter battery_capacity_reporter_; + // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so // store everything in the values array at the index of the field number // -2. diff --git a/pixelstats/pixelatoms.proto b/pixelstats/pixelatoms.proto index 2aba3ae..dc2c32a 100644 --- a/pixelstats/pixelatoms.proto +++ b/pixelstats/pixelatoms.proto @@ -44,6 +44,7 @@ enum Ids { BOOT_STATS = 105007; WIRELESS_CHARGING_STATS = 105008; DEVICE_ORIENTATION = 105009; + FG_CAPACITY = 105010; // AOSP atom ID range ends at 109999 } @@ -224,3 +225,28 @@ message DeviceOrientation { /* Device orientation. */ optional Orientation orientation = 2; } + +/* Raw battery capacity stats */ +message BatteryCapacityFG { + enum LogReason { + LOG_REASON_UNKNOWN = 0; + LOG_REASON_CONNECTED = 1; + LOG_REASON_DISCONNECTED = 2; + LOG_REASON_FULL_CHARGE = 3; + LOG_REASON_PERCENT_SKIP = 4; + LOG_REASON_DIVERGING_FG = 5; + } + + /* Uevent logging reason, enumerated above. */ + optional LogReason capacity_log_reason = 2; + + /* The battery capacity reported from the FG (fuel gauge) hardware */ + optional float capacity_gdf = 3; + /* The filtered system battery capacity reported to the UI */ + optional float capacity_ssoc = 4; + /* The fuel gauge capacity curve midpoint FG (fuel gauge) value */ + optional float capacity_gdf_curve = 5; + /* The fuel gauge capacity curve midpoint UI value */ + optional float capacity_ssoc_curve = 6; +} + |
