Android 电池健康度检测全流程(驱动 → 内核 → HAL → Framework)
校准说明(2026-10):本文基于本地 Linux 6.18.7 内核源码(
drivers/power/supply/power_supply_core.c、power_supply_sysfs.c)与 AOSP 线上最新源码(android.googlesource,hardware/interfaces/health/aidl、frameworks/basemaster 分支,对应 Android 17 / API 37,2026 年 6 月发布)逐项核对修订。
整体链路一句话概括:电量计/充电驱动(内核)→ power_supply sysfs + uevent → healthd / Health HAL(AIDL)→ BatteryService → ACTION_BATTERY_CHANGED 广播 / BatteryStats 。健康度的"原始数据"(循环次数、满充容量 vs 设计容量)内核一直都有;用户可见的"电池健康度"界面是 Android 16(Pixel 8a / Pixel 9 及更新机型)才落地的,Android 14 只是先在"关于手机 → 电池信息"里暴露了循环次数和日期。
一、内核层(Linux 6.18.7)
1. power_supply 电源子系统 ------ 内核的统一抽象
内核中所有电池、充电器、USB 供电设备都注册为 struct power_supply,由 drivers/power/supply/power_supply_core.c 管理:
-
每个设备注册时在
struct power_supply_desc中声明支持的enum power_supply_property列表,核心层据此在/sys/class/power_supply/<name>/下生成属性文件(power_supply_sysfs.c中POWER_SUPPLY_ATTR()宏批量生成)。 -
属性变化通知 :驱动调用
power_supply_changed(psy)(power_supply_core.c),核心层延迟合并后通过kobject_uevent(&psy->dev.kobj, KOBJ_CHANGE)发出 action 为change的 uevent ;环境变量由power_supply_uevent()(power_supply_sysfs.c)填充,形如:POWER_SUPPLY_NAME=batteryPOWER_SUPPLY_CAPACITY=87、POWER_SUPPLY_STATUS=Discharging...(每个属性一个POWER_SUPPLY_<属性大写名>=<值>)
⚠️ 注意:事件本身没有叫
POWER_SUPPLY_CHANGE的名字------"change" 是 uevent 的 action,POWER_SUPPLY_*是环境变量前缀。healthd 和 BatteryService 都是监听SUBSYSTEM=power_supply的 uevent。 -
与电池健康度相关的关键属性(属性枚举见
include/linux/power_supply.h,sysfs 文件名即枚举名小写):
| 属性 / sysfs 文件 | 含义 |
|---|---|
POWER_SUPPLY_PROP_CAPACITY (capacity) |
当前电量百分比(UI 的 level) |
POWER_SUPPLY_PROP_STATUS (status) |
Charging / Discharging / Full / Not charging |
POWER_SUPPLY_PROP_HEALTH (health) |
Good / Overheat / Dead / Over voltage / Cold / Unspecified failure 等 |
POWER_SUPPLY_PROP_CYCLE_COUNT (cycle_count) |
循环次数 |
POWER_SUPPLY_PROP_CHARGE_FULL (charge_full) |
当前满充容量(µAh,电量计"学会"的值) |
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN (charge_full_design) |
出厂设计容量(µAh) |
POWER_SUPPLY_PROP_CHARGE_COUNTER (charge_counter) |
库仑计累计电量(µAh) |
POWER_SUPPLY_PROP_CURRENT_NOW / VOLTAGE_NOW / TEMP |
瞬时电流(µA)/ 电压(µV)/ 温度(0.1°C 单位) |
健康度的核心计算:
电池健康度(SOH) ≈ charge_full / charge_full_design
| 量 | 来源 | 含义 |
|---|---|---|
charge_full(µAh) |
电量计芯片学习出来的 | 这块电池现在从零到满实际能充进去的电量 |
charge_full_design(µAh) |
出厂写入的固定值 | 这块电池全新时的设计容量(标称值) |
举例:一块设计容量 4,500,000 µAh(4500 mAh)的电池,用了两年后电量计学到的实际满充容量只剩 3,900,000 µAh:
SOH = 3,900,000 / 4,500,000 ≈ 86.7%
这就是设置里"电池健康度 86%"那个数字的本质------实际容量 ÷ 出厂容量。
电量计芯片(Fuel Gauge,如 MAX1704x/MAX1726x、TI BQ27z561、高通 FG、MTK gauge 等)内部长期学习"实际满充容量"(Qmax / FCC learning),电池老化后该值下降,驱动把结果上报为 CHARGE_FULL;循环次数由芯片内部或驱动累加维护。
2. 驱动代码要点(以 mainline 的 max17042 为例,示意)
c
// drivers/power/supply/max17042_battery.c(mainline 主线驱动,示意)
static enum power_supply_property max17042_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CAPACITY, // 当前百分比
POWER_SUPPLY_PROP_CYCLE_COUNT, // 循环次数
POWER_SUPPLY_PROP_CHARGE_FULL, // 学会的实际满充容量
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, // 设计容量
POWER_SUPPLY_PROP_TEMP,
...
};
static int max17042_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
switch (psp) {
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = max17042_get_capacity(chip); // 芯片寄存器换算
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
val->intval = chip->fullcap * 5000 / 2; // 寄存器 → µAh
break;
...
}
}
说明:本地参考源码树为裁剪版,不含具体 fuel gauge 驱动;上例为内核 mainline 中的真实驱动结构示意。厂商机型上通常是 SoC 厂商的 gauge/charger 驱动(高通、MTK、三星各有自己的一套)。
驱动更新属性后调用 power_supply_changed(psy) 触发上述 uevent,上层据此刷新。
二、Health HAL 层(android.hardware.health)
- Android 9 引入 Health HAL 2.0(HIDL),由 healthd 直接读 sysfs 演进为独立 HAL;Android 13 起迁移到 AIDL(
android.hardware.health.IHealth) ,HIDL 仅保留兼容。AOSP main(Android 17)上该 AIDL 接口已冻结到 V3,并新增电池部件防伪字段。 - AOSP 参考实现:
hardware/interfaces/health/aidl/default/,init 服务为vendor.health-default(rc 文件android.hardware.health-service.example.rc),注册到 ServiceManager 的名字是android.hardware.health.IHealth/default;厂商实现(如android.hardware.health-service.qti)可带不同后缀名覆盖。 - healthd (
system/core/healthd/)是核心守护进程:BatteryMonitor.cpp通过 uevent 监听 + epoll 周期轮询 sysfs (/sys/class/power_supply/...,路径可由healthd_config按板级覆盖)读取所有属性,填充到 AIDL 原生结构aidl::android::hardware::health::HealthInfo(新版已直接以 AIDL 结构为内部表示,仅在需要时 translate 回 HIDL 1.0/2.0/2.1 供 charger UI 等老客户端使用)。 - HealthInfo 关键字段名(校准后,注意与旧文档不同):
| AIDL 字段 | 对应 sysfs | 说明 |
|---|---|---|
batteryLevel |
capacity | 电量百分比 |
batteryStatus |
status | 充电状态 |
batteryHealth |
health | 健康状态枚举(GOOD/OVERHEAT/DEAD/OVER_VOLTAGE/COLD/UNSPECIFIED_FAILURE...) |
batteryFullChargeUah |
charge_full | 实际满充容量(µAh) |
batteryFullChargeDesignCapacityUah |
charge_full_design | 设计容量(µAh) |
batteryCycleCount |
cycle_count | 循环次数 |
batteryChargeCounterUah |
charge_counter | 库仑计(µAh) |
batteryVoltageMillivolts / batteryTemperatureTenthsCelsius |
voltage_now / temp | 电压 mV / 温度 0.1°C |
batteryCapacityLevel / batteryChargeTimeToFullNowSeconds |
capacity_level / time_to_full_now | 容量档位 / 预估充满时间 |
batteryHealthData(BatteryHealthData) |
厂商扩展 | 新增健康度载体 :batteryStateOfHealth(0--100,SOH%)、batteryManufacturingDateSeconds、batteryFirstUsageSeconds、(V3 新增)batterySerialNumber、batteryPartStatus(UNSUPPORTED/ORIGINAL/REPLACED,电池是否原装件) |
- 关机充电模式下 healthd 直接作为 charger UI 的数据源(
healthd_mode_charger)。 - 上层通过
IHealth::getHealthInfo()同步获取,或注册IHealthInfoCallback监听变化。 - 排障细节:healthd 每次更新会往 klog/dmesg 打一行形如
battery l=87 v=4123 t=25.0 h=2 st=3 c=-456 fc=3985000 cc=312 chg=au的日志(l=level, v=mV, t=0.1°C, h=health, st=status, c=µA, fc=满充µAh, cc=循环次数, chg=ac/usb/wireless/dock 在线标记),对驱动联调很有用。
三、Framework 层(AOSP master / Android 17,API 37)
1. BatteryService
frameworks/base/services/core/java/com/android/server/BatteryService.java:
- 启动时通过
com.android.server.health.HealthServiceWrapper.create(...)绑定 Health HAL------该封装是双模入口 :HealthServiceWrapperAidl(Android 13+ 优先)与HealthServiceWrapperHidl(兼容老 vendor HAL)。 - 每次 HAL 回调
update(HealthInfo)后,更新内部状态并发送Intent.ACTION_BATTERY_CHANGED粘性广播 。除经典的level/scale/status/health(对应BatteryManager.BATTERY_HEALTH_*)/present/plugged/voltage/temperature/technology/charge_counter外,新代码还会带上EXTRA_CYCLE_COUNT(batteryCycleCount)、EXTRA_MAXIMUM_CAPACITY(batteryFullChargeUah)、EXTRA_DESIGN_CAPACITY(batteryFullChargeDesignCapacityUah)------Settings 的电池信息页正是读这些 extra。 - 对 App 的属性查询走
BatteryPropertiesRegistrar(binder 服务batteryproperties),其中健康度相关属性有权限/flag 门控:BATTERY_PROPERTY_STATE_OF_HEALTH(SOH%,受stateOfHealthPublic()flag 控制)BATTERY_PROPERTY_MANUFACTURING_DATE/FIRST_USAGE_DATE/CHARGING_POLICY/SERIAL_NUMBER/PART_STATUS(需BATTERY_STATS权限)
- 另有
UEventObserver直接监听SUBSYSTEM=power_supplyuevent 用于无效充电器检测;低电量自动关机、高温保护、BatterySaver、LED、充电提示音等系统联动也在这里。
2. 电池健康度(SOH)展示(时间线校准)
- Android 14 (Pixel 8 系列起):
设置 → 关于手机 → 电池信息,首次官方展示循环次数、生产日期、首次使用日期。 - Android 16 (Pixel 8a / Pixel 9 及更新):
设置 → 电池 → 电池健康,首次官方展示 SOH 百分比 + Normal / Reduced 状态 ,并引入电池重校准流程;数据来自HealthInfo.batteryHealthData.batteryStateOfHealth(或batteryFullChargeUah / batteryFullChargeDesignCapacityUah推算)。Pixel 8 / 8 Pro 因硬件限制未开放数值。 - Android 17 :沿用同一条数据链路(HealthInfo V3 进一步补齐电池序列号与部件原装状态
batteryPartStatus,支持电池防伪);厂商侧 Samsung One UI 7+(电池信息页显示最大容量%、循环次数、生产日期)、Motorola 等也已接入同类数据。 - App 侧公开 API 仍是
BatteryManager:广播 +getIntProperty(BATTERY_PROPERTY_CAPACITY / CHARGE_COUNTER / CURRENT_NOW / CURRENT_AVERAGE / ENERGY_COUNTER / STATUS ...);SOH 等健康度属性目前仍受权限/flag 限制,普通 App 拿不到。
3. BatteryStats ------ 耗电统计
BatteryStatsService / BatteryStatsImpl 记录每个 UID 的 wakelock、alarm、job、网络、传感器、CPU 时间、前台时长等,结合 charge_counter(库仑计 µAh)估算每个 App 的耗电量。dumpsys batterystats 可导出全部统计,配合 Battery Historian 可视化。
四、在 Android 开发中定位/解决电池问题
1. 快速诊断命令
bash
# 模拟电池状态(仅测试,非常好用)
adb shell dumpsys battery set level 15
adb shell dumpsys battery set status 2 # charging
adb shell dumpsys battery unplug / reset # 恢复真实状态
# 查看内核上报的原始数据(健康度三要素)
adb shell cat /sys/class/power_supply/battery/capacity
adb shell cat /sys/class/power_supply/battery/charge_full
adb shell cat /sys/class/power_supply/battery/charge_full_design
adb shell cat /sys/class/power_supply/battery/cycle_count
adb shell cat /sys/class/power_supply/battery/temp
# SOH ≈ charge_full / charge_full_design(若厂商如实上报;注意有厂商在两个文件里填同一个值,显示永远 100%)
# 查看当前实际生效的电池状态(含 Max charging current/voltage 等)
adb shell dumpsys battery
# 耗电统计 & 电量消耗历史
adb shell dumpsys batterystats > bs.txt
adb shell dumpsys batterystats --charged com.xxx.app
# 导入 https://bathist.github.io/ (Battery Historian)看曲线
# 日志过滤(注意 healthd 的电池行也会进 dmesg/klog)
adb logcat -b all | grep -iE "BatteryService|healthd|health@|health-service|BatteryStatsService|batterystats"
adb shell dmesg | grep -iE "battery|fg|gauge|charger|power_supply"
2. 常见问题 → 定位思路
| 现象 | 定位方法 | 常见根因/解决 |
|---|---|---|
| 电量跳变、1% 掉电关机 | 对比 sysfs capacity 与 UI level;看 fuel gauge 日志 |
电量计未校准 → 做一次完整充放电循环让 Qmax 学习;检查电池曲线/老化参数 |
| 100% 充不满或长时间卡在某一百分比 | 看 status(Charging/Full)、charge_full 学习值、voltage_now |
恒压阶段截止电流阈值配置;JEITA 温度限流 |
| 温度过高/过低误报导致充电停止 | 读 sysfs temp、health |
NTC 分压/测温线路问题;驱动温度补偿表错误 |
| 耗电快 | dumpsys batterystats 排序、Battery Historian 看唤醒 |
唤醒锁未释放、频繁 alarm(对齐到 Doze 窗口)、定位/网络扫描;用 Android Studio Energy Profiler |
| 息屏后 App 无法后台工作 | dumpsys deviceidle、dumpsys jobscheduler |
Doze / App Standby 限制,改用 WorkManager 或申请白名单 |
| 健康 HAL 无数据 / 服务挂死 | adb shell dumpsys health、lshal / `adb shell service list |
grep health`;检查 health 服务 init rc 与 SELinux 策略(hal_health_default) |
| 设置里电池健康度数值异常(永远 100% 或为 0) | 对比 charge_full / charge_full_design 与 batteryStateOfHealth |
驱动未如实上报 FCC/design capacity;Settings 页自行按 maxCap/designCap 取整计算,与 HAL 的 SOH 可能有 1% 左右的舍入差 |
3. 处理原则
- 先分清层级:UI 显示的 level 异常 → 查 BatteryService 与 HAL;HAL 数据异常 → 查 healthd 与 sysfs;sysfs 本身异常 → 查驱动和硬件。
- 善用
dumpsys battery set/unplug做边界测试 (低电量关机、低电量模式、充电状态切换),不必等真实电量;测完记得reset。 - 驱动层改动后验证 :
cat /sys/class/power_supply/*逐项核对,确认power_supply_changed()触发时机,避免 uevent 风暴(频繁 notify 会拖累 healthd 轮询循环)。 - App 侧排查 :先确认不是自家问题(唤醒锁、定位、网络),再怀疑系统/驱动------用
adb shell am set-inactive <pkg> true、强制 Doze(adb shell dumpsys deviceidle force-idle)做对照实验。
总结:健康度的"检测"本质是电量计硬件持续学习满充容量并上报,内核 power_supply 子系统标准化暴露(sysfs + change uevent),healthd/Health HAL 搬运成 HealthInfo,BatteryService 广播给全系统;用户可见的 SOH 界面是 Android 16 起的产物。而 App 开发中的电池问题,90% 靠 dumpsys batterystats + Battery Historian + sysfs 原始数据三层对照就能定位。