summaryrefslogtreecommitdiffstats
path: root/pixelstats
diff options
context:
space:
mode:
authorStephane Lee <stayfan@google.com>2020-03-23 17:13:33 -0700
committerStephane Lee <stayfan@google.com>2020-04-15 16:00:37 -0700
commitf2ea194ef22b9b01bcb9d8902c8aa6365dbada5a (patch)
tree0ffab2413f99f5b41bc4bc9247b31cc7f7d2158c /pixelstats
parent8603b85a1d2494e610da3db6e22bb459cdee0e7e (diff)
downloadplatform_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.bp1
-rw-r--r--pixelstats/BatteryCapacityReporter.cpp182
-rw-r--r--pixelstats/UeventListener.cpp42
-rw-r--r--pixelstats/include/pixelstats/BatteryCapacityReporter.h82
-rw-r--r--pixelstats/include/pixelstats/UeventListener.h8
-rw-r--r--pixelstats/pixelatoms.proto26
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;
+}
+