第五板块:Android 系统服务与电源管理 | 第十八篇:Battery Service 与 电量统计(Fuel Gauge)算法
所属板块:第五板块 — Android 系统服务与电源管理
前置知识:第十七篇中的 Power Manager Service、WakeLock 机制、Linux 内核驱动、Sysfs 接口
本篇定位:这是 Android 设备能量视角的终极审判。如果说 PMS 是控制能量消耗的阀门,那么 Battery Service 就是计量能量流逝的仪表盘。本篇将彻底拆解 Linux 电源供应子系统(Power Supply Class)、Healthd 守护进程的轮询机制、电池容量(Capacity)的百分比算法、电流积分法(Coulomb Counting)、电池曲线校准(Gas Gauging)。我们将深入 Kernel Driver、Native Daemon 与 Framework Service,揭示 Android 如何从毫安时(mAh)的微观层面计算剩余电量。
1. 核心结论先行(Thesis Statement)
Android 的电量统计是一个基于物理测量的估算系统。
- Battery Service 的本质:一个数据聚合器。它运行在 System Server 中,通过 Binder 从 Healthd 守护进程获取电池数据,并广播给所有应用。
- Healthd 的本质:内核接口的翻译官。它是一个 Native 守护进程,通过轮询
/sys/class/power_supply/下的 sysfs 节点,读取电压、电流、温度等原始物理数据。 - 电量百分比的本质:开路电压(OCV)与库仑计数的混合估算。系统通过查找电池曲线(Battery Profile),将当前的电压和电流映射到剩余容量百分比。
- 电量统计(Battery Stats)的本质:差分累加器。系统记录每个 UID(应用)在单位时间内持有的 WakeLock 时间和 CPU 使用率,以此计算耗电占比。
2. 电池硬件与内核层(Kernel Layer)
2.1 电源供应子系统(Power Supply Class)
Linux 内核提供了一套标准的电源管理接口。
学术定义:
- Power Supply Class: 内核中的一个子系统,用于管理各种电源设备(电池、USB、AC 适配器)。
- Sysfs 接口: 内核通过
/sys/class/power_supply/<supply_name>/目录下的文件,向用户空间暴露电源属性。
2.2 关键 Sysfs 节点
| 节点路径 | 含义 | 单位 | 学术定义 |
|---|---|---|---|
/sys/class/power_supply/battery/capacity | 剩余容量百分比 | % | 系统估算的剩余电量。 |
/sys/class/power_supply/battery/voltage_now | 当前电压 | uV (微伏) | 电池两端的电势差。 |
/sys/class/power_supply/battery/current_now | 当前电流 | uA (微安) | 正值为充电,负值为放电。 |
/sys/class/power_supply/battery/temp | 电池温度 | 0.1 °C | 电池的热力学温度。 |
/sys/class/power_supply/battery/charge_counter | 累计电荷量 | uAh (微安时) | 库仑计数的原始值。 |
/sys/class/power_supply/battery/status | 充电状态 | 文本 | Charging, Discharging, Full, Not charging。 |
2.3 Fuel Gauge(电量计)IC
硬件上,电池通常通过 I2C 或 SPI 连接一个专门的 Fuel Gauge IC(如 TI 的 BQ27541)。
学术定义:
- OCV (Open Circuit Voltage): 电池开路电压。电池在不充不放时的电压,与剩余电量有固定的化学对应关系。
- Coulomb Counter (库仑计): 一个积分器。测量流过电池的电流,对时间积分,得到流过的电荷量(Q = I × t)。
3. Healthd 守护进程(Native Layer)
3.1 Healthd 的架构
Healthd 是 Android 系统中负责监控电池状态的 Native 守护进程。
graph TB
subgraph Kernel ["Linux 内核"]
Driver["电池驱动"]
Sysfs["/sys/class/power_supply/battery/"]
end
subgraph Healthd ["healthd 进程"]
Poll["轮询线程 (ueventd)"]
Parse["解析器"]
Calc["电量计算算法"]
Binder["Binder 服务 (IBatteryPropertiesRegistrar)"]
end
subgraph Framework ["System Server"]
BS["BatteryService"]
BSStats["BatteryStatsService"]
end
Driver -->|"更新数据"| Sysfs
Poll -->|"读取"| Sysfs
Parse -->|"解析"| Poll
Calc -->|"计算百分比"| Parse
Binder -->|"推送更新"| BS
BS -->|"广播"| BSStats
3.2 Healthd 的轮询机制
Healthd 通过 uevent 机制监听内核事件,并定期轮询 sysfs。
源码解析:
// system/core/healthd/healthd.cpp
void healthd_mainloop(void) {
while (1) {
// 1. 轮询 uevent 和 sysfs
poll(fds, nfds, -1);
// 2. 处理电池事件
healthd_battery_update();
}
}
void healthd_battery_update(void) {
// 读取 sysfs
battery_capacity = read_sysfs("/sys/class/power_supply/battery/capacity");
battery_voltage = read_sysfs("/sys/class/power_supply/battery/voltage_now");
// 通过 Binder 通知 Framework
battery_properties.prop = battery_capacity;
battery_properties.prop |= BATTERY_PROP_CAPACITY;
gBatteryPropertiesRegistrar->notifyListeners(battery_properties);
}
4. Battery Service 的估算算法
4.1 电量百分比的计算
系统不直接相信硬件上报的百分比,而是结合电压和电流进行修正。
学术定义:
- 电池曲线(Battery Profile): 厂商预置的一个查找表(Look-up Table)。横轴是电压,纵轴是电量百分比。不同温度下有不同的曲线。
- 算法逻辑:
- 读取
voltage_now(V) 和current_now(I)。 - 根据当前温度,选择对应的电池曲线。
- 查找电压 V 对应的百分比 P1。
- 结合电流 I(放电越快,电压降越大),对 P1 进行补偿,得到最终百分比 P。
- 读取
4.2 库仑计数法(Coulomb Counting)
为了更精确,系统使用库仑计数法。
公式:
学术定义:
- : 剩余电荷量。
- : 随时间变化的电流。
- 校准: 每次充满电()时,重置库仑计数器的基准值。
4.3 电池老化与补偿
电池会随着循环次数增加而老化(容量衰减)。
学术定义:
- Design Capacity: 设计容量(如 4000 mAh)。
- Full Charge Capacity: 当前充满电的实际容量(如 3800 mAh)。
- Cycle Count: 充放电循环次数。
- 算法: 当
Full Charge Capacity低于Design Capacity的 80% 时,系统认为电池已老化,并基于新的满容量进行百分比计算。
5. BatteryStatsService 的耗电统计
5.1 基于 UID 的耗电追踪
BatteryStatsService 负责统计每个应用的耗电量。
学术定义:
- UID (User ID): 每个应用分配的唯一 Linux UID。
- Power Profile: 设备的功率配置文件。定义在
framework/base/core/res/res/xml/power_profile.xml中,包含 CPU、WiFi、GPS 等硬件的功耗参数。
5.2 耗电计算公式
应用的总耗电量 = CPU 耗电 + 硬件耗电 + WakeLock 耗电。
| 组件 | 统计方式 | 算法 |
|---|---|---|
| CPU | 时间片统计 | |
| WiFi | 状态统计 | |
| GPS | 时间统计 | |
| WakeLock | 持有时间 |
源码解析:
// BatteryStatsImpl.java
void updateTimeBasesLocked(boolean onBattery, long uptime, long realtime) {
// 更新 CPU 使用时间
mCpuStats.update(onBattery, uptime, realtime);
// 更新 WakeLock 持有时间
mWakeLockStats.update(onBattery, uptime, realtime);
}
6. 充电状态机
6.1 充电阶段的物理过程
stateDiagram-v2
direction LR %% 核心:声明横向排列(Left to Right)
[*] --> Unknown: 初始状态
Unknown --> Charging: 插入充电器
Charging --> PreCharging: 低压预充
PreCharging --> FastCharging: 恒流快充
FastCharging --> TrickleCharging: 恒压涓流
TrickleCharging --> Full: 充满
Full --> Discharging: 拔掉充电器
Discharging --> Low: 电量低 (15%)
Low --> Critical: 电量极低 (5%)
Critical --> Shutdown: 关机
6.2 热管理(Thermal Throttling)
充电过程中,电池温度是关键。
| 温度范围 | 状态 | 学术定义 |
|---|---|---|
| < 0°C | Cold | 禁止充电,防止锂析出。 |
| 0°C - 10°C | Cool | 限制充电电流。 |
| 10°C - 45°C | Normal | 正常快充。 |
| > 45°C | Hot | 停止充电,防止热失控。 |
7. 关键源码深度解析
7.1 BatteryService 的广播机制
// BatteryService.java
private void updateBatteryLevelLocked() {
// 1. 从 Healthd 获取原始数据
int level = mBatteryProperties.level;
int voltage = mBatteryProperties.voltage;
// 2. 计算百分比(如果需要)
level = calculateLevel(level, voltage);
// 3. 发送广播
Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
intent.putExtra(BatteryManager.EXTRA_LEVEL, level);
intent.putExtra(BatteryManager.EXTRA_VOLTAGE, voltage);
mContext.sendBroadcast(intent);
}
7.2 Power Profile 的定义
<!-- power_profile.xml -->
<device name="generic">
<item name="cpu.speeds">125000 250000 500000 1000000</item>
<item name="cpu.active">200</item> <!-- mA -->
<item name="wifi.active">150</item> <!-- mA -->
<item name="gps.on">50</item> <!-- mA -->
</device>
8. 电量统计的常见误区
8.1 百分比跳变
| 现象 | 学术原因 |
|---|---|
| 电量从 30% 突然跳到 1% | 电池内阻增大,在大电流放电时电压骤降,导致 OCV 估算错误。 |
| 充电到 100% 后很快掉到 90% | 电池曲线校准不准,或者电池老化。 |
8.2 应用耗电统计不准
| 现象 | 学术原因 |
|---|---|
| 某应用显示耗电 50%,但实际没用多久 | 该应用持有 WakeLock 但 CPU 并未全速运行,统计的是“持有时间”而非“实际功耗”。 |
| 系统进程耗电高 | 系统进程(如 System UI)代理了其他应用的操作,导致统计归属错误。 |
9. 本篇总结(Knowledge Closure)
| 关键点 | 纯学术定义 |
|---|---|
| Battery Service 的本质 | 数据聚合器,从 Healthd 获取原始物理数据。 |
| Healthd 的本质 | 内核接口翻译官,轮询 sysfs 节点。 |
| 电量百分比算法 | OCV 电压查表与库仑计数积分的混合估算。 |
| BatteryStats 的本质 | 基于 UID 的差分累加器,利用 Power Profile 计算硬件功耗。 |
| 充电管理 | 恒流、恒压、涓流的三段式物理过程,受温度严格限制。 |
10. 第五板块结语
至此,第五板块:Android 系统服务与电源管理 已全部完结。
我们从 Power Manager Service 的 WakeLock 仲裁 出发,深入 Linux 内核的休眠机制,探索 Doze 模式的时空置换,最终抵达 Battery Service 的库仑计数与耗电统计。
我们揭示了 Android 电源管理的终极哲学:用最严苛的手段限制能量消耗,用最精确的算法度量能量流逝。
下一篇预告:第六板块:Android 安全与权限体系 | 第十九篇:SELinux 强制访问控制与沙箱机制