| /* |
| * Copyright (C) 2024 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "pixelstats: BatteryFGReporter" |
| |
| #include <log/log.h> |
| #include <time.h> |
| #include <utils/Timers.h> |
| #include <cinttypes> |
| #include <cmath> |
| |
| #include <android-base/file.h> |
| #include <pixelstats/BatteryFGReporter.h> |
| #include <pixelstats/StatsHelper.h> |
| #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h> |
| |
| namespace android { |
| namespace hardware { |
| namespace google { |
| namespace pixel { |
| |
| using aidl::android::frameworks::stats::VendorAtom; |
| using aidl::android::frameworks::stats::VendorAtomValue; |
| using android::base::ReadFileToString; |
| using android::hardware::google::pixel::PixelAtoms::BatteryEEPROM; |
| using android::hardware::google::pixel::PixelAtoms::FuelGaugeAbnormalityReported; |
| |
| |
| BatteryFGReporter::BatteryFGReporter() {} |
| |
| int64_t BatteryFGReporter::getTimeSecs() { |
| return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME)); |
| } |
| |
| void BatteryFGReporter::setAtomFieldValue(std::vector<VendorAtomValue> *values, int offset, |
| int content) { |
| std::vector<VendorAtomValue> &val = *values; |
| if (offset - kVendorAtomOffset < val.size()) |
| val[offset - kVendorAtomOffset].set<VendorAtomValue::intValue>(content); |
| } |
| |
| void BatteryFGReporter::reportAbnormalEvent(const std::shared_ptr<IStats> &stats_client, |
| struct BatteryFGAbnormalData data) { |
| // Load values array |
| std::vector<VendorAtomValue> values(35); |
| uint32_t duration = 0; |
| |
| /* save time when trigger, calculate duration when clear */ |
| if (data.state == 1 && ab_trigger_time_[data.event] == 0) { |
| ab_trigger_time_[data.event] = getTimeSecs(); |
| } else { |
| duration = getTimeSecs() - ab_trigger_time_[data.event]; |
| ab_trigger_time_[data.event] = 0; |
| } |
| |
| ALOGD("reportEvent: event=%d,state=%d,cycles=%04X,vcel=%04X,avgv=%04X,curr=%04X,avgc=%04X," |
| "timerh=%04X,temp=%04X,repcap=%04X,mixcap=%04X,fcrep=%04X,fcnom=%04X,qresd=%04X," |
| "avcap=%04X,vfremcap=%04X,repsoc=%04X,vfsoc=%04X,msoc=%04X,vfocv=%04X,dpacc=%04X," |
| "dqacc=%04X,qh=%04X,qh0=%04X,vfsoc0=%04X,qrtable20=%04X,qrtable30=%04X,status=%04X," |
| "fstat=%04X,rcomp0=%04X,tempco=%04X,duration=%u", |
| data.event, data.state, data.cycles, data.vcel, data.avgv, data.curr, data.avgc, |
| data.timerh, data.temp, data.repcap, data.mixcap, data.fcrep, data.fcnom, data.qresd, |
| data.avcap, data.vfremcap, data.repsoc, data.vfsoc, data.msoc, data.vfocv, data.dpacc, |
| data.dqacc, data.qh, data.qh0, data.vfsoc0, data.qrtable20, data.qrtable30, data.status, |
| data.fstat, data.rcomp0, data.tempco, duration); |
| |
| /* |
| * state=0 -> untrigger, state=1 -> trigger |
| * Since atom enum reserves unknown value at 0, offset by 1 here |
| * state=1-> untrigger, state=2 -> trigger |
| */ |
| data.state += 1; |
| |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kEventFieldNumber, data.event); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kEventStateFieldNumber, data.state); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kDurationSecsFieldNumber, duration); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress1FieldNumber, data.cycles); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData1FieldNumber, data.vcel); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress2FieldNumber, data.avgv); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData2FieldNumber, data.curr); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress3FieldNumber, data.avgc); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData3FieldNumber, data.timerh); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress4FieldNumber, data.temp); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData4FieldNumber, data.repcap); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress5FieldNumber, data.mixcap); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData5FieldNumber, data.fcrep); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress6FieldNumber, data.fcnom); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData6FieldNumber, data.qresd); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress7FieldNumber, data.avcap); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData7FieldNumber, data.vfremcap); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress8FieldNumber, data.repsoc); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData8FieldNumber, data.vfsoc); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress9FieldNumber, data.msoc); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData9FieldNumber, data.vfocv); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress10FieldNumber, data.dpacc); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData10FieldNumber, data.dqacc); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress11FieldNumber, data.qh); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData11FieldNumber, data.qh0); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress12FieldNumber, data.vfsoc0); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData12FieldNumber, data.qrtable20); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress13FieldNumber, data.qrtable30); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData13FieldNumber, data.status); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress14FieldNumber, data.fstat); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData14FieldNumber, data.rcomp0); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress15FieldNumber, data.tempco); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData15FieldNumber, 0); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterAddress16FieldNumber, 0); |
| setAtomFieldValue(&values, FuelGaugeAbnormalityReported::kFgRegisterData16FieldNumber, 0); |
| |
| VendorAtom event = {.reverseDomainName = "", |
| .atomId = PixelAtoms::Atom::kFuelGaugeAbnormalityReported, |
| .values = std::move(values)}; |
| const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event); |
| if (!ret.isOk()) |
| ALOGE("Unable to report FuelGaugeAbnormalityReported to Stats service"); |
| } |
| |
| void BatteryFGReporter::reportEvent(const std::shared_ptr<IStats> &stats_client, |
| const struct BatteryFGLearningParam ¶ms) { |
| // upload atom |
| const std::vector<int> eeprom_history_fields = { |
| BatteryEEPROM::kCycleCntFieldNumber, BatteryEEPROM::kFullCapFieldNumber, |
| BatteryEEPROM::kEsrFieldNumber, BatteryEEPROM::kRslowFieldNumber, |
| BatteryEEPROM::kSohFieldNumber, BatteryEEPROM::kBattTempFieldNumber, |
| BatteryEEPROM::kCutoffSocFieldNumber, BatteryEEPROM::kCcSocFieldNumber, |
| BatteryEEPROM::kSysSocFieldNumber, BatteryEEPROM::kMsocFieldNumber, |
| BatteryEEPROM::kBattSocFieldNumber, BatteryEEPROM::kReserveFieldNumber, |
| BatteryEEPROM::kMaxTempFieldNumber, BatteryEEPROM::kMinTempFieldNumber, |
| BatteryEEPROM::kMaxVbattFieldNumber, BatteryEEPROM::kMinVbattFieldNumber, |
| BatteryEEPROM::kMaxIbattFieldNumber, BatteryEEPROM::kMinIbattFieldNumber, |
| BatteryEEPROM::kChecksumFieldNumber, BatteryEEPROM::kTempcoFieldNumber, |
| BatteryEEPROM::kRcomp0FieldNumber, BatteryEEPROM::kTimerHFieldNumber, |
| BatteryEEPROM::kFullRepFieldNumber}; |
| |
| switch(params.type) { |
| case EvtFWUpdate: |
| ALOGD("reportEvent: firmware update try: %u, success: %u, fail: %u", |
| params.fcnom, params.dpacc, params.dqacc); |
| break; |
| default: |
| ALOGD("unknown event type %04x", params.type); |
| break; |
| } |
| |
| std::vector<VendorAtomValue> values(eeprom_history_fields.size()); |
| VendorAtomValue val; |
| |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kCycleCntFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(params.fcnom); |
| values[BatteryEEPROM::kFullCapFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(params.dpacc); |
| values[BatteryEEPROM::kEsrFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(params.dqacc); |
| values[BatteryEEPROM::kRslowFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kSohFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kBattTempFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kCutoffSocFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kCcSocFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kSysSocFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kMsocFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kBattSocFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kReserveFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kMaxTempFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kMinTempFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(params.fcrep); |
| values[BatteryEEPROM::kMaxVbattFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(params.msoc); |
| values[BatteryEEPROM::kMinVbattFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(params.vfsoc); |
| values[BatteryEEPROM::kMaxIbattFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(params.fstat); |
| values[BatteryEEPROM::kMinIbattFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>((uint16_t)params.type); |
| values[BatteryEEPROM::kChecksumFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(params.tempco); |
| values[BatteryEEPROM::kTempcoFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(params.rcomp0); |
| values[BatteryEEPROM::kRcomp0FieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(0); |
| values[BatteryEEPROM::kTimerHFieldNumber - kVendorAtomOffset] = val; |
| val.set<VendorAtomValue::intValue>(params.repsoc); |
| values[BatteryEEPROM::kFullRepFieldNumber - kVendorAtomOffset] = val; |
| |
| VendorAtom event = {.reverseDomainName = "", |
| .atomId = PixelAtoms::Atom::kBatteryEeprom, |
| .values = std::move(values)}; |
| const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event); |
| if (!ret.isOk()) |
| ALOGE("Unable to report BatteryEEPROM to Stats service"); |
| } |
| |
| void BatteryFGReporter::checkAndReportFwUpdate(const std::shared_ptr<IStats> &stats_client, |
| const std::string &path) { |
| struct BatteryFGLearningParam params; |
| std::string file_contents; |
| int16_t num; |
| |
| if (path.empty()) |
| return; |
| |
| if (!ReadFileToString(path, &file_contents)) { |
| ALOGE("Unable to read FirmwareUpdate path: %s - %s", path.c_str(), strerror(errno)); |
| return; |
| } |
| |
| /* FU: Firmware Update */ |
| params.type = EvtFWUpdate; |
| num = sscanf(file_contents.c_str(), "%" SCNu16 " %" SCNu16 " %" SCNu16, |
| ¶ms.fcnom, ¶ms.dpacc, ¶ms.dqacc); |
| if (num != kNumFwUpdateFields) { |
| ALOGE("Couldn't process FirmwareUpdate history path. num=%d\n", num); |
| return; |
| } |
| |
| /* No event to report */ |
| if (params.fcnom == 0 ) |
| return; |
| |
| /* Reporting data only when can clear */ |
| if (::android::base::WriteStringToFile("0", path.c_str())) |
| reportEvent(stats_client, params); |
| else |
| ALOGE("Couldn't clear %s - %s", path.c_str(), strerror(errno)); |
| } |
| |
| void BatteryFGReporter::checkAndReportFGAbnormality(const std::shared_ptr<IStats> &stats_client, |
| const std::vector<std::string> &paths) { |
| std::string path; |
| struct timespec boot_time; |
| std::vector<std::vector<uint32_t>> events; |
| |
| if (paths.empty()) |
| return; |
| |
| for (int i = 0; i < paths.size(); i++) { |
| if (fileExists(paths[i])) { |
| path = paths[i]; |
| break; |
| } |
| } |
| |
| clock_gettime(CLOCK_MONOTONIC, &boot_time); |
| readLogbuffer(path, kNumAbnormalEventFields, EvtFGAbnormalEvent, FormatOnlyVal, last_ab_check_, events); |
| for (int seq = 0; seq < events.size(); seq++) { |
| if (events[seq].size() == kNumAbnormalEventFields) { |
| struct BatteryFGAbnormalData data; |
| uint16_t *pdata = (uint16_t *)&data; |
| for (int i = 0; i < kNumAbnormalEventFields; i++) |
| *pdata++ = events[seq][i]; |
| reportAbnormalEvent(stats_client, data); |
| } else { |
| ALOGE("Not support %zu fields for FG abnormal event", events[seq].size()); |
| } |
| } |
| |
| last_ab_check_ = (unsigned int)boot_time.tv_sec; |
| } |
| |
| } // namespace pixel |
| } // namespace google |
| } // namespace hardware |
| } // namespace android |