第五板块:Android 系统服务与电源管理 | 第十七篇:Power Manager Service 与 WakeLock 机制

27 阅读7分钟

第五板块:Android 系统服务与电源管理 | 第十七篇:Power Manager Service 与 WakeLock 机制

所属板块:第五板块 — Android 系统服务与电源管理

前置知识:第十六篇中的输入系统、System Server 架构、Binder IPC、Linux 内核基础、Wakelock 基础概念

本篇定位:这是 Android 设备续航能力的守护神。我们将以手术刀级的精度,深入 Linux 内核电源管理Power Manager Service (PMS) 的锁仲裁算法WakeLock 的引用计数与 Binder 死亡回收Doze 模式的时空置换算法。本篇将包含内核 sysfs 接口native 层调用System Server 的源码级逻辑,揭示 Android 如何在 纳秒级 的精度上平衡 性能功耗


1. 核心结论先行(Thesis Statement)

Android 的电源管理是一个基于时间的强制休眠系统

  • Power Manager Service (PMS) 的本质中央仲裁器。它运行在 System Server 中,维护着 SuspendBlocker(内核级锁)和 WakeLock(应用级锁)的双层计数模型。只有当所有计数归零,且屏幕熄灭后,才会触发内核休眠。
  • WakeLock 的本质带作用域的引用计数。它不是一把“锁”,而是一个计数器。同一个 Binder 代理可以申请多次,释放时需对应次数。如果持有者进程死亡,Binder 死亡通知会强制清零。
  • 休眠(Suspend)的本质冻结用户空间进程,并让 CPU 进入 C-State(空闲状态)。此时 CPU 几乎不耗电,只有 RAM 保持自刷新。
  • 唤醒(Wakeup)的本质中断(IRQ)。硬件中断(如电源键、RTC Alarm)会打破 CPU 的空闲状态,PMS 重新计算锁状态。

2. Linux 内核电源管理框架(Kernel Layer)

2.1 内核的休眠状态机

Linux 内核定义了多种休眠状态,Android 在此基础上进行了定制。

状态学术定义硬件表现
Running正常运行CPU 全速,设备供电。
Freeze浅度休眠冻结用户进程,CPU 可能仍运行。
Standby待机CPU 暂停,RAM 自刷新,大部分设备断电。
Mem (Suspend-to-RAM)Android 主要休眠模式CPU 断电,RAM 自刷新,上下文保存在 RAM。
Disk (Hibernate)休眠到磁盘上下文写入磁盘,RAM 断电(Android 极少使用)。

2.2 Wakeup Source 机制

内核使用 Wakeup Source 来阻止系统休眠。

学术定义

  • Wakeup Source: 一个内核对象,表示一个可以唤醒系统的源。可以是 IRQ、Timer、或者用户空间持有的锁。
  • wakeup_count: 内核维护的唤醒事件计数。用户空间写入一个值,内核只有在唤醒事件不超过该值时才允许休眠。

内核接口

# 查看当前 Wakeup Source
cat /sys/kernel/debug/wakeup_sources

# 查看休眠状态
cat /sys/power/state
# 输出: freeze standby mem disk

# 触发休眠
echo mem > /sys/power/state

2.3 suspend 流程(内核侧)

当 PMS 写入 mem/sys/power/state 时,内核执行:

  1. 冻结进程(Freezer):向所有用户空间进程发送 SIGSTOP,冻结它们。
  2. 设备挂起(Device Suspend):调用所有设备驱动的 suspend() 回调,关闭屏幕、背光、传感器、网络。
  3. 平台挂起(Platform Suspend):SoC 特定的挂起代码,保存 CPU 上下文,关闭 CPU 时钟。
  4. 进入 C-State:CPU 进入低功耗状态,等待中断唤醒。

3. Power Manager Service 的锁仲裁模型

3.1 双层锁架构

PMS 使用两层锁来与内核交互:

锁类型层级作用对应内核机制
WakeLock应用层应用申请,防止系统休眠映射到 SuspendBlocker
SuspendBlocker系统层PMS 内部使用,直接控制内核wakeup source / sysfs

3.2 WakeLock 的引用计数与 Binder 死亡回收

PMS 内部维护一个 HashMap<IBinder, WakeLock>,其中 IBinder 是申请者的 Binder 代理。

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
final class WakeLock {
    final IBinder mBinder;         // 申请者的 Binder 标识
    final int mFlags;              // WakeLock 类型 (PARTIAL, SCREEN_BRIGHT 等)
    int mCount;                    // 引用计数 (可重入)
    boolean mEnabled;              // 是否激活
    String mTag;                   // 调试标签
    WorkSource mWorkSource;        // 工作源 (用于统计)
}

关键逻辑

  1. 申请(acquire):如果 mBinder 不存在,创建新 WakeLock,mCount = 1;如果存在,mCount++
  2. 释放(release)mCount--。当 mCount == 0 时,从 mWakeLocks 中移除。
  3. 死亡回收(Binder Death Recipient):PMS 为每个 mBinder 注册死亡通知。如果应用进程崩溃,Binder 死亡,PMS 强制将该 mBinder 对应的所有 WakeLock 清零。

3.3 SuspendBlocker 的持有

PMS 根据 WakeLock 的状态,决定是否持有 SuspendBlocker

// PowerManagerService.java
void updatePowerStateLocked() {
    // 计算是否需要保持唤醒
    boolean needWakeLock = (mWakeLockSummary & WAKE_LOCK_CPU) != 0;
    boolean screenOn = (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0;

    if (needWakeLock || screenOn) {
        // 持有 SuspendBlocker,阻止内核休眠
        mSuspendBlocker.acquire();
    } else {
        // 释放 SuspendBlocker,允许内核休眠
        mSuspendBlocker.release();
    }
}

4. 屏幕与 CPU 的协同

4.1 屏幕状态机

屏幕状态独立于 CPU 状态。

stateDiagram-v2
    [*] --> ScreenOff: 电源键 / 超时
    ScreenOff --> ScreenDim: 用户交互 / 动画
    ScreenDim --> ScreenOn: 用户交互
    ScreenOn --> ScreenOff: 电源键 / 超时

4.2 屏幕与 WakeLock 的交互

场景WakeLock 状态屏幕状态CPU 状态
玩游戏PARTIAL_WAKE_LOCKON全速
听音乐PARTIAL_WAKE_LOCKOFF全速
看视频SCREEN_BRIGHT_WAKE_LOCKON全速
待机无 WakeLockOFF休眠

学术定义

  • PARTIAL_WAKE_LOCK:仅保持 CPU 运行,屏幕可关。这是后台任务唯一合法的 WakeLock。
  • SCREEN_BRIGHT_WAKE_LOCK:保持屏幕亮。Android 已弃用,改用 Window Flag FLAG_KEEP_SCREEN_ON,因为 Window 销毁时系统能自动释放。

5. Doze 模式的时空置换算法

5.1 DeviceIdleController 的状态机

Doze 是 Android 6.0 引入的革命性省电机制。

stateDiagram-v2
    [*] --> ACTIVE: 屏幕亮 / 充电
    ACTIVE --> INACTIVE: 屏幕关 / 拔充电线
    INACTIVE --> IDLE_PENDING: 静止一段时间
    IDLE_PENDING --> IDLE: 进入深度休眠
    IDLE --> IDLE_MAINTENANCE: 维护窗口
    IDLE_MAINTENANCE --> IDLE: 维护结束
    IDLE --> ACTIVE: 屏幕亮 / 充电

5.2 维护窗口(Maintenance Window)算法

Doze 不是一直睡,而是周期性地醒过来处理任务。

学术定义

  • 维护窗口间隔:随着时间推移,间隔越来越长(指数退避)。
  • 网络访问限制:在 IDLE 状态下,应用无法访问网络。
  • Alarm 延迟AlarmManager 的非唤醒 Alarm 被延迟到维护窗口执行。

5.3 应用待机桶(App Standby Buckets)

Android 9.0 引入,根据应用使用频率限制后台任务。

桶 (Bucket)限制程度定义
ACTIVE无限制用户正在交互。
WORKING_SET轻微限制经常使用。
FREQUENT较强限制偶尔使用。
RARE强限制很少使用。
NEVER完全限制从未使用或禁止后台运行。

算法:PMS 根据应用的最近使用时间、启动次数、与前台应用的交互,动态计算应用所属的桶,并限制其 JobScheduler 和 Alarm 的频率。


6. AlarmManager 的唤醒机制

6.1 Alarm 的类型与内核接口

AlarmManager 是唤醒系统的主要手段,它使用内核的 alarmtimer 驱动。

Alarm 类型是否唤醒 CPU时间基准适用场景
RTC_WAKEUP绝对时间 (1970-01-01)闹钟、日历提醒。
RTC绝对时间定时记录日志。
ELAPSED_REALTIME_WAKEUP相对时间 (开机至今)后台下载重试。
ELAPSED_REALTIME相对时间定时刷新 UI。

6.2 Alarm 的批处理(Batch Processing)

为了减少唤醒次数,PMS 将多个 Alarm 合并。

学术定义

  • Batch Window:PMS 维护一个时间窗口(如 5 分钟)。在这个窗口内的 Alarm 会被合并,在同一时刻触发。
  • 对齐(Alignment):将非紧急的 Alarm 对齐到最近的 Batch Window,减少 CPU 唤醒次数。

7. 关键源码深度解析

7.1 PowerManagerService 的休眠决策(Native 层)

PMS 通过 JNI 调用 native 方法,最终写入 sysfs。

// frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
static void nativeSetInteractive(JNIEnv* env, jclass clazz, jboolean enable) {
    // 调用内核接口
    setInteractive(enable);
}

static void nativeSetAutoSuspend(JNIEnv* env, jclass clazz, jboolean enable) {
    // 写入 /sys/power/state
    if (enable) {
        writeSysfs("/sys/power/state", "mem");
    } else {
        writeSysfs("/sys/power/state", "on");
    }
}

7.2 WakeLock 的 Binder 死亡回收

// PowerManagerService.java
private final class WakeLock implements IBinder.DeathRecipient {
    @Override
    public void binderDied() {
        // Binder 死亡,强制释放锁
        synchronized (mLock) {
            removeWakeLockLocked(mBinder, true);
        }
    }
}

7.3 DeviceIdleController 的维护窗口计算

// DeviceIdleController.java
void stepIdleStateLocked(String reason) {
    long now = SystemClock.elapsedRealtime();
    long nextAlarmTime = now + mConstants.IDLE_TIMEOUT;

    // 指数退避
    if (mState == STATE_IDLE) {
        nextAlarmTime = now + mConstants.IDLE_TIMEOUT_FACTOR * mConstants.IDLE_TIMEOUT;
    }

    // 设置 Alarm
    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextAlarmTime, mMaintenanceAlarmListener);
}

8. 电源管理的常见误区与坑(学术级)

8.1 WakeLock 泄漏的灾难性后果

错误做法学术后果
在 Service 的 onCreate 中申请,在 onDestroy 中释放Service 被系统杀死时,onDestroy 不一定调用,导致锁泄漏。
在 BroadcastReceiver 的 onReceive 中申请,异步释放onReceive 执行完即结束,异步任务未完成,锁泄漏。
使用 PARTIAL_WAKE_LOCK 播放音乐,但忘记释放即使按电源键,CPU 仍运行,电池几小时内耗尽。

8.2 过度唤醒的代价

错误做法学术后果
每秒钟一个 RTC_WAKEUP Alarm屏幕无法熄灭,CPU 每秒唤醒一次,电池疯狂消耗。
在 Doze 模式下申请 PARTIAL_WAKE_LOCK系统频繁唤醒,Doze 失去意义,手机发热。

9. 本篇总结(Knowledge Closure)

关键点纯学术定义
PMS 的本质中央仲裁器,基于双层锁(WakeLock/SuspendBlocker)计数决定系统休眠。
WakeLock 的本质带作用域的引用计数,由 Binder 死亡通知保障释放。
内核休眠冻结进程 -> 挂起设备 -> CPU 进入 C-State。
Doze 算法时空置换,通过维护窗口周期性唤醒,平衡省电与可用性。
Alarm 批处理将多个 Alarm 合并,减少 CPU 唤醒次数,提升续航。

下一篇预告第五板块:Android 系统服务与电源管理 | 第十八篇:Battery Service 与 电量统计(Fuel Gauge)算法