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