blob: 9808b458600aff9d5165700e49cf56bd08e228a1 [file] [log] [blame]
/*
* 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 &params) {
// 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,
&params.fcnom, &params.dpacc, &params.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