展讯平台安卓15电池健康实现分析
好像是说欧盟新规定,手机必须要有电池健康的查询,所以客户提了这个需求。
查询文档
参阅展讯文档 《Kernel 5.15充电模块配置指导手册V1.3》
以总电量值为基础,累计充电电量达到总电量值时记为一次完整的充电次数。此功能可以用来统计用户使用过程对电池进行充电、放电的循环次数。可以根据需求配置是否启用此功能,在
pmic fgu
节点中增加如下字段,即可开启此功能。
&pmic_fgu {
...
sprd,capacity-charge-cycle;
...
分析代码
在源代码中查询关键字:
./kernel5.15/kernel5.15/drivers/power/supply/sc27xx_fuel_gauge.c:4692: device_property_read_bool(&pdev->dev, "sprd,capacity-charge-cycle");
定位到代码:
static int sc27xx_fgu_probe(struct platform_device *pdev) {
...
data->support_charge_cycle =
device_property_read_bool(&pdev->dev, "sprd,capacity-charge-cycle");
if (!data->support_charge_cycle)
dev_info(&pdev->dev, "Do not support charge cycle function\n");
...
}
static void sc27xx_fgu_parse_cmdline(struct sc27xx_fgu_data *data)
{
...
/* parse charge cycle */
if (data->support_charge_cycle)
sc27xx_fgu_parse_charge_cycle(data);
...
}
static void sc27xx_fgu_parse_charge_cycle(struct sc27xx_fgu_data *data)
{
char result[32] = {};
int ret;
char *str;
str = "charge.charge_cycle=";
data->charge_cycle = -1;
ret = sc27xx_fgu_parse_cmdline_match(data, str, result, sizeof(result));
if (!ret) {
ret = kstrtoint(result, 10, &data->charge_cycle);
if (ret) {
data->charge_cycle = -1;
dev_err(data->dev, "Covert charge_cycle fail, ret = %d, result = %s\n",
ret, result);
}
}
}
static void sc27xx_fgu_calc_charge_cycle(struct sc27xx_fgu_data *data, int cap, int *fgu_cap)
{
int delta_cap;
if (!data->support_charge_cycle)
return;
if (*fgu_cap == SC27XX_FGU_MAGIC_NUMBER)
*fgu_cap = cap;
delta_cap = cap - *fgu_cap;
if (data->support_debug_log)
dev_info(data->dev, "%s: delta_cap = %d, fgu_cap = %d, cap = %d\n",
__func__, delta_cap, *fgu_cap, cap);
*fgu_cap = cap;
/*
* Formula:
* charge_cycle(0.001 cycle) = accumulate_cap * 1000 / SC27XX_FGU_FCC_PERCENT
*/
if (delta_cap > 0)
data->charge_cycle += delta_cap * 1000 / SC27XX_FGU_FCC_PERCENT;
}
继续查找数据存储位置:
// VND\bsp\bootloader\lk\platform\sprd_shared\driver\battery\sprd_battery.c
int fdt_fixup_charger_parameters(u8 *fdt)
{
...
int charge_cycle = -1;
...
/* Read charge cycle */
if (MISCDATA_CHARGE_CYCLE_OFFSET != 0 && MISCDATA_CHARGE_CYCLE_SIZE != 0) {
ret = sprdbat_read_miscdata_data((char *)(&charge_cycle),
MISCDATA_CHARGE_CYCLE_OFFSET,
MISCDATA_CHARGE_CYCLE_SIZE);
if (ret) {
errorf("sprd_chg: sprdbat Fail to read charge_cycle\n");
charge_cycle = -1;
}
}
}
发现是放在 miscdata
中,查看数据偏移
./VND/bsp/bootloader/lk/project/ums9230_6h10.mk
# miscdata
GLOBAL_DEFINES += \
MISCDATA_ADDRESS_BASE=10496 \
MISCDATA_MAGIC_OFFSET=0 \
MISCDATA_MAGIC_SIZE=4 \
MISCDATA_RTC_TIME_OFFSET=4 \
MISCDATA_RTC_TIME_SIZE=8 \
MISCDATA_CHARGE_CYCLE_OFFSET=12 \
MISCDATA_CHARGE_CYCLE_SIZE=4 \
MISCDATA_BASP_OFFSET=16 \
MISCDATA_BASP_SIZE=4 \
MISCDATA_CAPACITY_OFFSET=20 \
MISCDATA_CAPACITY_SIZE=4 \
MISCDATA_CAPACITY_CHECK_OFFSET=24 \
MISCDATA_CAPACITY_CHECK_SIZE=4 \
MISCDATA_SHUTDOWN_CHARGE_FLAG_OFFSET=28 \
MISCDATA_SHUTDOWN_CHARGE_FLAG_SIZE=4 \
MISCDATA_AUTO_POWERON_FLAG_OFFSET=32 \
MISCDATA_AUTO_POWERON_FLAG_SIZE=1
既然知道数据放哪里,后续写 Miscdata 就可以了
小插曲
最开始是起了一个 SystemServer
去实现这个需求
尝试1:读写 miscdata
以文件流读写,补权限会遇到 neverallow
尝试2:参考工模的 PhaseCheck
,使用 LocalSocket
连接到 phasecheckServer
,发现也有权限报错,SystemServer
不能连接其他的 Socket,也是 neverallow
,大无语。
尝试3:土法发广播给工模的 PhaseCheckReceiver
这下就好了。
其他
测试用 adb 手动写日期。
printf '\x2C\xFF\x34\x01\x90\xFF\x34\x01' | dd of=/dev/block/by-name/miscdata bs=1 seek=10536 conv=notrunc
设置里面界面默认没开,要打开
// packages\apps\Settings\src\com\android\settings\fuelgauge\BatterySettingsFeatureProviderImpl.java
/** Feature provider implementation for battery settings usage. */
public class BatterySettingsFeatureProviderImpl implements BatterySettingsFeatureProvider {
@Override
public boolean isManufactureDateAvailable(Context context, long manufactureDateMs) {
return false;
}
@Override
public boolean isFirstUseDateAvailable(Context context, long firstUseDateMs) {
return false;
}
@Override
public boolean isBatteryInfoEnabled(Context context) {
return false;
}