1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "pixelstats-uevent: BatteryCapacityFG"
18
19 #include <log/log.h>
20 #include <time.h>
21 #include <utils/Timers.h>
22 #include <cmath>
23
24 #include <android-base/file.h>
25
26 #include <android/frameworks/stats/1.0/IStats.h>
27 #include <pixelstats/BatteryCapacityReporter.h>
28
29 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
30
31 namespace android {
32 namespace hardware {
33 namespace google {
34 namespace pixel {
35
36 using android::base::ReadFileToString;
37 using android::frameworks::stats::V1_0::IStats;
38 using android::frameworks::stats::V1_0::VendorAtom;
39 using android::hardware::google::pixel::PixelAtoms::BatteryCapacityFG;
40
BatteryCapacityReporter()41 BatteryCapacityReporter::BatteryCapacityReporter() {
42 // Remove the need for a translation function/table, while removing the dependency on the
43 // generated <pixelatoms.pb.h> in BatteryCapacityReporter.h.
44 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_UNKNOWN) ==
45 static_cast<int>(BatteryCapacityFG::LOG_REASON_UNKNOWN));
46 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_CONNECTED) ==
47 static_cast<int>(BatteryCapacityFG::LOG_REASON_CONNECTED));
48 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_DISCONNECTED) ==
49 static_cast<int>(BatteryCapacityFG::LOG_REASON_DISCONNECTED));
50 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_FULL_CHARGE) ==
51 static_cast<int>(BatteryCapacityFG::LOG_REASON_FULL_CHARGE));
52 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_PERCENT_SKIP) ==
53 static_cast<int>(BatteryCapacityFG::LOG_REASON_PERCENT_SKIP));
54 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_DIVERGING_FG) ==
55 static_cast<int>(BatteryCapacityFG::LOG_REASON_DIVERGING_FG));
56 }
57
checkAndReport(const std::string & path)58 void BatteryCapacityReporter::checkAndReport(const std::string &path) {
59 if (parse(path)) {
60 if (check()) {
61 report();
62 }
63 }
64 }
65
getTimeSecs(void)66 int64_t BatteryCapacityReporter::getTimeSecs(void) {
67 return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
68 }
69
parse(const std::string & path)70 bool BatteryCapacityReporter::parse(const std::string &path) {
71 std::string batterySSOCContents;
72 if (!ReadFileToString(path, &batterySSOCContents)) {
73 ALOGE("Unable to read ssoc_details path: %s - %s", path.c_str(), strerror(errno));
74 return false;
75 }
76
77 // Parse file. Example format:
78 // soc: l=97% gdf=97.72 uic=97.72 rl=97.72
79 // curve:[15.00 15.00][97.87 97.87][100.00 100.00]
80 // status: ct=1 rl=0 s=1
81 if (sscanf(batterySSOCContents.c_str(),
82 "soc: %*s gdf=%f %*s rl=%f\n"
83 "curve:[%*f %*f][%f %f][%*f %*f]\n"
84 "status: %*s %*s s=%d",
85 &gdf_, &ssoc_, &gdf_curve_, &ssoc_curve_, &status_) != 5) {
86 ALOGE("Unable to parse ssoc_details [%s] from file %s to int.", batterySSOCContents.c_str(),
87 path.c_str());
88 return false;
89 }
90
91 return true;
92 }
93
check(void)94 bool BatteryCapacityReporter::check(void) {
95 if (unexpected_event_timer_active_) {
96 // A 30 minute timer with a boolean gate helps prevent uninitialized timers and potential
97 // overflows.
98 // - Active when the timer is less than 30 minutes, thus continues checking the elapsed
99 // time.
100 // - Once expired (> 30 min), active becomes false and the timer no longer needs to check
101 // the elapsed time.
102 unexpected_event_timer_active_ =
103 (getTimeSecs() - unexpected_event_timer_secs_) <= (30 * 60);
104 }
105
106 LogReason log_reason = LOG_REASON_UNKNOWN;
107 if (status_previous_ != status_) {
108 // Handle nominal events
109
110 if (status_ == SOC_STATUS_CONNECTED) {
111 log_reason = LOG_REASON_CONNECTED;
112
113 } else if (status_ == SOC_STATUS_DISCONNECTED) {
114 log_reason = LOG_REASON_DISCONNECTED;
115
116 } else if (status_ == SOC_STATUS_FULL) {
117 log_reason = LOG_REASON_FULL_CHARGE;
118 }
119
120 status_previous_ = status_;
121
122 } else if (unexpected_event_timer_active_ == false) {
123 // Handle abnormal events at a minimum period
124
125 const float diff = fabsf(ssoc_ - gdf_);
126
127 if (fabsf(ssoc_ - ssoc_previous_) >= 2.0f) {
128 unexpected_event_timer_secs_ = getTimeSecs();
129 unexpected_event_timer_active_ = true;
130 log_reason = LOG_REASON_PERCENT_SKIP;
131
132 // Every +- 1% when above a 4% SOC difference (w/ timer)
133 } else if (static_cast<int>(round(ssoc_gdf_diff_previous_)) !=
134 static_cast<int>(round(diff)) &&
135 diff >= 4.0f) {
136 unexpected_event_timer_secs_ = getTimeSecs();
137 unexpected_event_timer_active_ = true;
138 log_reason = LOG_REASON_DIVERGING_FG;
139
140 ssoc_gdf_diff_previous_ = diff;
141 }
142 }
143 ssoc_previous_ = ssoc_;
144
145 log_reason_ = log_reason;
146 return (log_reason != LOG_REASON_UNKNOWN);
147 }
148
report(void)149 void BatteryCapacityReporter::report(void) {
150 sp<IStats> stats_client = IStats::tryGetService();
151 if (!stats_client) {
152 ALOGD("Couldn't connect to IStats service");
153 return;
154 }
155
156 // Load values array
157 std::vector<VendorAtom::Value> values(5);
158 VendorAtom::Value tmp;
159 tmp.intValue(log_reason_);
160 values[BatteryCapacityFG::kCapacityLogReasonFieldNumber - kVendorAtomOffset] = tmp;
161 tmp.floatValue(gdf_);
162 values[BatteryCapacityFG::kCapacityGdfFieldNumber - kVendorAtomOffset] = tmp;
163 tmp.floatValue(ssoc_);
164 values[BatteryCapacityFG::kCapacitySsocFieldNumber - kVendorAtomOffset] = tmp;
165 tmp.floatValue(gdf_curve_);
166 values[BatteryCapacityFG::kCapacityGdfCurveFieldNumber - kVendorAtomOffset] = tmp;
167 tmp.floatValue(ssoc_curve_);
168 values[BatteryCapacityFG::kCapacitySsocCurveFieldNumber - kVendorAtomOffset] = tmp;
169
170 // Send vendor atom to IStats HAL
171 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
172 .atomId = PixelAtoms::Ids::FG_CAPACITY,
173 .values = values};
174 Return<void> ret = stats_client->reportVendorAtom(event);
175 if (!ret.isOk())
176 ALOGE("Unable to report to IStats service");
177 }
178
179 } // namespace pixel
180 } // namespace google
181 } // namespace hardware
182 } // namespace android
183