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