Android 电池健康度检测全流程(驱动 → 内核 → HAL → Framework)

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/base master 分支,对应 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=battery
    • POWER_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_supply uevent 用于无效充电器检测;低电量自动关机、高温保护、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. 处理原则

  1. 先分清层级:UI 显示的 level 异常 → 查 BatteryService 与 HAL;HAL 数据异常 → 查 healthd 与 sysfs;sysfs 本身异常 → 查驱动和硬件。
  2. 善用 dumpsys battery set/unplug 做边界测试 (低电量关机、低电量模式、充电状态切换),不必等真实电量;测完记得 reset。
  3. 驱动层改动后验证 :cat /sys/class/power_supply/* 逐项核对,确认 power_supply_changed() 触发时机,避免 uevent 风暴(频繁 notify 会拖累 healthd 轮询循环)。
  4. 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 原始数据三层对照就能定位。

相关推荐
IT技术分享社区1 小时前
在 Windows 上跑未改动的 Linux 程序:微软 LiteBox v0.1 把这件事变简单了
linux·运维·windows·microsoft·开源
恋猫de小郭1 小时前
Genkit Dart 1.0 发布,Flutter 原生的 AI Agent 终于完整了
android·前端·flutter
迪康妍妍1 小时前
终端安全实战:用迪康终端安全管理系统实现U盘四分档管控与全量审计
android·运维·网络·安全·电脑
JackLam1 小时前
mobile-use 使用与接入第三方 LLM 网关完整指南OC
android·ai·mobile use
进击的荆棘2 小时前
Linux系统——库的原理与制作之制作和使用动静态库
linux·动静态库
论文复现现场2 小时前
8卡 A100 服务器哪里租?NVLink、RDMA 与 NCCL 验收指南
linux·服务器·php·分布式训练·rdma·nvlink·nccl
snotJam2 小时前
关于Uniapp的Android自定义基座使用
android·uni-app
TST-魚爷2 小时前
易视讯会议摄像头电视安装教程
android·电脑·钉钉·腾讯会议·zoom会议
小小的木头人2 小时前
CentOS 7.9 离线安装 NVIDIA Container Toolkit:1.14.0 与 1.20.1 两种版本方案
linux·运维·centos