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 <pixelstats/BatteryCapacityReporter.h>
27 #include <pixelstats/StatsHelper.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 aidl::android::frameworks::stats::IStats;
37 using aidl::android::frameworks::stats::VendorAtom;
38 using aidl::android::frameworks::stats::VendorAtomValue;
39 using android::base::ReadFileToString;
40 using android::hardware::google::pixel::PixelAtoms::BatteryCapacityFG;
41
42 #define ONE_HOUR_SECS (60 * 60)
43
BatteryCapacityReporter()44 BatteryCapacityReporter::BatteryCapacityReporter() {
45 // Remove the need for a translation function/table, while removing the dependency on the
46 // generated <pixelatoms.pb.h> in BatteryCapacityReporter.h.
47 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_UNKNOWN) ==
48 static_cast<int>(BatteryCapacityFG::LOG_REASON_UNKNOWN));
49 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_CONNECTED) ==
50 static_cast<int>(BatteryCapacityFG::LOG_REASON_CONNECTED));
51 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_DISCONNECTED) ==
52 static_cast<int>(BatteryCapacityFG::LOG_REASON_DISCONNECTED));
53 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_FULL_CHARGE) ==
54 static_cast<int>(BatteryCapacityFG::LOG_REASON_FULL_CHARGE));
55 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_PERCENT_SKIP) ==
56 static_cast<int>(BatteryCapacityFG::LOG_REASON_PERCENT_SKIP));
57 static_assert(static_cast<int>(BatteryCapacityReporter::LOG_REASON_DIVERGING_FG) ==
58 static_cast<int>(BatteryCapacityFG::LOG_REASON_DIVERGING_FG));
59 }
60
checkAndReport(const std::shared_ptr<IStats> & stats_client,const std::string & path)61 void BatteryCapacityReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client,
62 const std::string &path) {
63 if (parse(path)) {
64 if (checkLogEvent()) {
65 reportEvent(stats_client);
66 }
67 }
68 }
69
getTimeSecs(void)70 int64_t BatteryCapacityReporter::getTimeSecs(void) {
71 return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
72 }
73
parse(const std::string & path)74 bool BatteryCapacityReporter::parse(const std::string &path) {
75 std::string batterySSOCContents;
76 if (!ReadFileToString(path, &batterySSOCContents)) {
77 ALOGE("Unable to read ssoc_details path: %s - %s", path.c_str(), strerror(errno));
78 return false;
79 }
80
81 // Parse file. Example format:
82 // soc: l=97% gdf=97.72 uic=97.72 rl=97.72
83 // curve:[15.00 15.00][97.87 97.87][100.00 100.00]
84 // status: ct=1 rl=0 s=1
85 if (sscanf(batterySSOCContents.c_str(),
86 "soc: %*s gdf=%f %*s rl=%f\n"
87 "curve:[%*f %*f][%f %f][%*f %*f]\n"
88 "status: %*s %*s s=%d",
89 &gdf_, &ssoc_, &gdf_curve_, &ssoc_curve_, &status_) != 5) {
90 ALOGE("Unable to parse ssoc_details [%s] from file %s to int.", batterySSOCContents.c_str(),
91 path.c_str());
92 return false;
93 }
94
95 return true;
96 }
97
shouldReportEvent(void)98 bool BatteryCapacityReporter::shouldReportEvent(void) {
99 const int64_t current_time = getTimeSecs();
100 if (current_time == 0) {
101 ALOGE("Current boot time is zero!");
102 return false;
103 }
104
105 /* Perform cleanup of events that are older than 1 hour */
106 for (int i = 0; i < MAX_LOG_EVENTS_PER_HOUR; i++) {
107 if (log_event_time_secs_[i] != 0 && /* Non-empty */
108 log_event_time_secs_[i] + ONE_HOUR_SECS < current_time) {
109 log_event_time_secs_[i] = 0;
110 num_events_in_last_hour_--;
111 }
112 }
113
114 /* Log event if there hasn't been many events in the past hour */
115 if (num_events_in_last_hour_ < MAX_LOG_EVENTS_PER_HOUR) {
116 for (int i = 0; i < MAX_LOG_EVENTS_PER_HOUR; i++) {
117 if (log_event_time_secs_[i] == 0) { /* Empty */
118 log_event_time_secs_[i] = current_time;
119 num_events_in_last_hour_++;
120 return true;
121 }
122 }
123 } else {
124 ALOGD("Too many log events in past hour; event ignored.");
125 }
126
127 return false;
128 }
129
130 /**
131 * @return true if a log should be reported, else false
132 */
checkLogEvent(void)133 bool BatteryCapacityReporter::checkLogEvent(void) {
134 LogReason log_reason = LOG_REASON_UNKNOWN;
135 if (status_previous_ != status_) {
136 // Handle nominal events
137
138 if (status_ == SOC_STATUS_CONNECTED) {
139 log_reason = LOG_REASON_CONNECTED;
140
141 } else if (status_ == SOC_STATUS_DISCONNECTED) {
142 log_reason = LOG_REASON_DISCONNECTED;
143
144 } else if (status_ == SOC_STATUS_FULL) {
145 log_reason = LOG_REASON_FULL_CHARGE;
146 }
147
148 status_previous_ = status_;
149
150 } else {
151 // Handle abnormal events
152 const float diff = fabsf(ssoc_ - gdf_);
153
154 if (fabsf(ssoc_ - ssoc_previous_) >= 2.0f) {
155 log_reason = LOG_REASON_PERCENT_SKIP;
156
157 // Every +- 1% when above a 4% SOC difference (w/ timer)
158 } else if (static_cast<int>(round(ssoc_gdf_diff_previous_)) !=
159 static_cast<int>(round(diff)) &&
160 diff >= 4.0f) {
161 log_reason = LOG_REASON_DIVERGING_FG;
162
163 ssoc_gdf_diff_previous_ = diff;
164 }
165 }
166 ssoc_previous_ = ssoc_;
167 log_reason_ = log_reason;
168
169 if (log_reason != LOG_REASON_UNKNOWN) {
170 /* Found new log event! */
171 /* Check if we should actually report the event */
172 return shouldReportEvent();
173 }
174
175 return false;
176 }
177
reportEvent(const std::shared_ptr<IStats> & stats_client)178 void BatteryCapacityReporter::reportEvent(const std::shared_ptr<IStats> &stats_client) {
179 // Load values array
180 std::vector<VendorAtomValue> values(5);
181 VendorAtomValue tmp;
182 tmp.set<VendorAtomValue::intValue>(log_reason_);
183 values[BatteryCapacityFG::kCapacityLogReasonFieldNumber - kVendorAtomOffset] = tmp;
184 tmp.set<VendorAtomValue::floatValue>(gdf_);
185 values[BatteryCapacityFG::kCapacityGdfFieldNumber - kVendorAtomOffset] = tmp;
186 tmp.set<VendorAtomValue::floatValue>(ssoc_);
187 values[BatteryCapacityFG::kCapacitySsocFieldNumber - kVendorAtomOffset] = tmp;
188 tmp.set<VendorAtomValue::floatValue>(gdf_curve_);
189 values[BatteryCapacityFG::kCapacityGdfCurveFieldNumber - kVendorAtomOffset] = tmp;
190 tmp.set<VendorAtomValue::floatValue>(ssoc_curve_);
191 values[BatteryCapacityFG::kCapacitySsocCurveFieldNumber - kVendorAtomOffset] = tmp;
192
193 // Send vendor atom to IStats HAL
194 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
195 .atomId = PixelAtoms::Atom::kFgCapacity,
196 .values = std::move(values)};
197 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
198 if (!ret.isOk())
199 ALOGE("Unable to report to IStats service");
200 }
201
202 } // namespace pixel
203 } // namespace google
204 } // namespace hardware
205 } // namespace android
206