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: BatteryEEPROM"
18 
19 #include <log/log.h>
20 #include <time.h>
21 #include <utils/Timers.h>
22 #include <cinttypes>
23 #include <cmath>
24 
25 #include <android-base/file.h>
26 #include <pixelstats/BatteryEEPROMReporter.h>
27 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
28 
29 namespace android {
30 namespace hardware {
31 namespace google {
32 namespace pixel {
33 
34 using aidl::android::frameworks::stats::VendorAtom;
35 using aidl::android::frameworks::stats::VendorAtomValue;
36 using android::base::ReadFileToString;
37 using android::hardware::google::pixel::PixelAtoms::BatteryEEPROM;
38 
39 #define LINESIZE 71
40 
BatteryEEPROMReporter()41 BatteryEEPROMReporter::BatteryEEPROMReporter() {}
42 
checkAndReport(const std::shared_ptr<IStats> & stats_client,const std::string & path)43 void BatteryEEPROMReporter::checkAndReport(const std::shared_ptr<IStats> &stats_client,
44                                            const std::string &path) {
45     std::string file_contents;
46     std::string history_each;
47 
48     const int kSecondsPerMonth = 60 * 60 * 24 * 30;
49     int64_t now = getTimeSecs();
50 
51     if ((report_time_ != 0) && (now - report_time_ < kSecondsPerMonth)) {
52         ALOGD("Not upload time. now: %" PRId64 ", pre: %" PRId64, now, report_time_);
53         return;
54     }
55 
56     if (!ReadFileToString(path.c_str(), &file_contents)) {
57         ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno));
58         return;
59     }
60     ALOGD("checkAndReport: %s", file_contents.c_str());
61 
62     int16_t i, num;
63     struct BatteryHistory hist;
64     const int kHistTotalLen = strlen(file_contents.c_str());
65 
66     for (i = 0; i < (LINESIZE * BATT_HIST_NUM_MAX); i = i + LINESIZE) {
67         if (i + LINESIZE > kHistTotalLen)
68             break;
69         history_each = file_contents.substr(i, LINESIZE);
70         num = sscanf(history_each.c_str(),
71                    "%4" SCNx16 "%4" SCNx16 "%4" SCNx16 "%4" SCNx16
72                    "%2" SCNx8 "%2" SCNx8 " %2" SCNx8 "%2" SCNx8
73                    "%2" SCNx8 "%2" SCNx8 " %2" SCNx8 "%2" SCNx8
74                    "%2" SCNx8 "%2" SCNx8 " %4" SCNx16 "%4" SCNx16
75                    "%4" SCNx16 "%4" SCNx16 "%4" SCNx16,
76                    &hist.cycle_cnt, &hist.full_cap, &hist.esr,
77                    &hist.rslow, &hist.batt_temp, &hist.soh,
78                    &hist.cc_soc, &hist.cutoff_soc, &hist.msoc,
79                    &hist.sys_soc, &hist.reserve, &hist.batt_soc,
80                    &hist.min_temp, &hist.max_temp,  &hist.max_vbatt,
81                    &hist.min_vbatt, &hist.max_ibatt, &hist.min_ibatt,
82                    &hist.checksum);
83 
84         if (num != kNumBatteryHistoryFields) {
85             ALOGE("Couldn't process %s", history_each.c_str());
86             continue;
87         }
88 
89         if (checkLogEvent(hist)) {
90             reportEvent(stats_client, hist);
91             report_time_ = getTimeSecs();
92         }
93     }
94 }
95 
getTimeSecs(void)96 int64_t BatteryEEPROMReporter::getTimeSecs(void) {
97     return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
98 }
99 
100 /**
101  * @return true if a log should be reported, else false.
102  * Here we use checksum to confirm the data is usable or not.
103  * The checksum mismatch when storage data overflow or corrupt.
104  * We don't need data in such cases.
105  */
checkLogEvent(struct BatteryHistory hist)106 bool BatteryEEPROMReporter::checkLogEvent(struct BatteryHistory hist) {
107     int checksum = 0;
108 
109     checksum = hist.cycle_cnt + hist.full_cap + hist.esr + hist.rslow
110                 + hist.soh + hist.batt_temp + hist.cutoff_soc + hist.cc_soc
111                 + hist.sys_soc + hist.msoc + hist.batt_soc + hist.reserve
112                 + hist.max_temp + hist.min_temp + hist.max_vbatt
113                 + hist.min_vbatt + hist.max_ibatt + hist.min_ibatt;
114     /* Compare with checksum data */
115     if (checksum == hist.checksum) {
116         return true;
117     } else {
118         return false;
119     }
120 }
121 
reportEvent(const std::shared_ptr<IStats> & stats_client,const struct BatteryHistory & hist)122 void BatteryEEPROMReporter::reportEvent(const std::shared_ptr<IStats> &stats_client,
123                                         const struct BatteryHistory &hist) {
124     // upload atom
125     const std::vector<int> eeprom_history_fields = {
126             BatteryEEPROM::kCycleCntFieldNumber,  BatteryEEPROM::kFullCapFieldNumber,
127             BatteryEEPROM::kEsrFieldNumber,       BatteryEEPROM::kRslowFieldNumber,
128             BatteryEEPROM::kSohFieldNumber,       BatteryEEPROM::kBattTempFieldNumber,
129             BatteryEEPROM::kCutoffSocFieldNumber, BatteryEEPROM::kCcSocFieldNumber,
130             BatteryEEPROM::kSysSocFieldNumber,    BatteryEEPROM::kMsocFieldNumber,
131             BatteryEEPROM::kBattSocFieldNumber,   BatteryEEPROM::kReserveFieldNumber,
132             BatteryEEPROM::kMaxTempFieldNumber,   BatteryEEPROM::kMinTempFieldNumber,
133             BatteryEEPROM::kMaxVbattFieldNumber,  BatteryEEPROM::kMinVbattFieldNumber,
134             BatteryEEPROM::kMaxIbattFieldNumber,  BatteryEEPROM::kMinIbattFieldNumber,
135             BatteryEEPROM::kChecksumFieldNumber};
136 
137     ALOGD("reportEvent: cycle_cnt:%d, full_cap:%d, esr:%d, rslow:%d, soh:%d, "
138           "batt_temp:%d, cutoff_soc:%d, cc_soc:%d, sys_soc:%d, msoc:%d, "
139           "batt_soc:%d, reserve:%d, max_temp:%d, min_temp:%d, max_vbatt:%d, "
140           "min_vbatt:%d, max_ibatt:%d, min_ibatt:%d, checksum:%d",
141           hist.cycle_cnt, hist.full_cap, hist.esr, hist.rslow, hist.soh, hist.batt_temp,
142           hist.cutoff_soc, hist.cc_soc, hist.sys_soc, hist.msoc, hist.batt_soc, hist.reserve,
143           hist.max_temp, hist.min_temp, hist.max_vbatt, hist.min_vbatt, hist.max_ibatt,
144           hist.min_ibatt, hist.checksum);
145 
146     std::vector<VendorAtomValue> values(eeprom_history_fields.size());
147     VendorAtomValue val;
148 
149     val.set<VendorAtomValue::intValue>(hist.cycle_cnt);
150     values[BatteryEEPROM::kCycleCntFieldNumber - kVendorAtomOffset] = val;
151     val.set<VendorAtomValue::intValue>(hist.full_cap);
152     values[BatteryEEPROM::kFullCapFieldNumber - kVendorAtomOffset] = val;
153     val.set<VendorAtomValue::intValue>(hist.esr);
154     values[BatteryEEPROM::kEsrFieldNumber - kVendorAtomOffset] = val;
155     val.set<VendorAtomValue::intValue>(hist.rslow);
156     values[BatteryEEPROM::kRslowFieldNumber - kVendorAtomOffset] = val;
157     val.set<VendorAtomValue::intValue>(hist.soh);
158     values[BatteryEEPROM::kSohFieldNumber - kVendorAtomOffset] = val;
159     val.set<VendorAtomValue::intValue>(hist.batt_temp);
160     values[BatteryEEPROM::kBattTempFieldNumber - kVendorAtomOffset] = val;
161     val.set<VendorAtomValue::intValue>(hist.cutoff_soc);
162     values[BatteryEEPROM::kCutoffSocFieldNumber - kVendorAtomOffset] = val;
163     val.set<VendorAtomValue::intValue>(hist.cc_soc);
164     values[BatteryEEPROM::kCcSocFieldNumber - kVendorAtomOffset] = val;
165     val.set<VendorAtomValue::intValue>(hist.sys_soc);
166     values[BatteryEEPROM::kSysSocFieldNumber - kVendorAtomOffset] = val;
167     val.set<VendorAtomValue::intValue>(hist.msoc);
168     values[BatteryEEPROM::kMsocFieldNumber - kVendorAtomOffset] = val;
169     val.set<VendorAtomValue::intValue>(hist.batt_soc);
170     values[BatteryEEPROM::kBattSocFieldNumber - kVendorAtomOffset] = val;
171     val.set<VendorAtomValue::intValue>(hist.reserve);
172     values[BatteryEEPROM::kReserveFieldNumber - kVendorAtomOffset] = val;
173     val.set<VendorAtomValue::intValue>(hist.max_temp);
174     values[BatteryEEPROM::kMaxTempFieldNumber - kVendorAtomOffset] = val;
175     val.set<VendorAtomValue::intValue>(hist.min_temp);
176     values[BatteryEEPROM::kMinTempFieldNumber - kVendorAtomOffset] = val;
177     val.set<VendorAtomValue::intValue>(hist.max_vbatt);
178     values[BatteryEEPROM::kMaxVbattFieldNumber - kVendorAtomOffset] = val;
179     val.set<VendorAtomValue::intValue>(hist.min_vbatt);
180     values[BatteryEEPROM::kMinVbattFieldNumber - kVendorAtomOffset] = val;
181     val.set<VendorAtomValue::intValue>(hist.max_ibatt);
182     values[BatteryEEPROM::kMaxIbattFieldNumber - kVendorAtomOffset] = val;
183     val.set<VendorAtomValue::intValue>(hist.min_ibatt);
184     values[BatteryEEPROM::kMinIbattFieldNumber - kVendorAtomOffset] = val;
185     val.set<VendorAtomValue::intValue>(hist.checksum);
186     values[BatteryEEPROM::kChecksumFieldNumber - kVendorAtomOffset] = val;
187 
188     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
189                         .atomId = PixelAtoms::Atom::kBatteryEeprom,
190                         .values = std::move(values)};
191     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
192     if (!ret.isOk())
193         ALOGE("Unable to report BatteryEEPROM to Stats service");
194 }
195 
196 }  // namespace pixel
197 }  // namespace google
198 }  // namespace hardware
199 }  // namespace android
200