2. Android Automotive电源状态机(PSM)深度解析

电源状态机,简称PSM,是Android Automotive电源管理的核心骨架。说白了,它定义了系统在不同电源状态下的行为,以及如何在这些状态之间切换。

我个人习惯把PSM比作一个人的作息:清醒工作(On)、发呆放空(Standby)、打盹休息(Dozing)、深度睡眠(Sleep/Off)。每个状态都有明确的能耗目标和系统行为。

2.1 PSM的四个状态

Android Automotive定义了四个电源状态,我整理成了下面的表格,方便你对照理解:

状态 名称 典型功耗 用户感知
On 全功能运行 高(>10W) 屏幕亮,交互流畅
Standby 待机 中(3-5W) 屏幕灭,后台服务运行
Dozing 浅度休眠 低(1-2W) 屏幕灭,受限后台
Sleep/Off 深度休眠/关机 极低(<0.5W) 完全无响应

2.2 状态详解与系统行为

2.2.1 On状态

这是系统最活跃的状态。屏幕亮着,CPU全速运行,所有服务都可用。车载娱乐系统、导航、语音助手都在正常工作。

嗯,这里要注意:On状态并不等于"屏幕常亮"。在汽车场景下,即使屏幕熄灭(比如用户手动关闭),只要系统还在处理关键任务(比如导航播报、电话通话),它仍然处于On状态。

关键特征:

  • CPU调度策略:performance或schedutil高频率
  • 网络连接:Wi-Fi、蓝牙、蜂窝数据全开
  • 传感器:所有传感器正常工作
  • 显示:屏幕亮或准备随时亮起

2.2.2 Standby状态

用户离开车辆,但系统还在"待命"。屏幕灭了,但后台服务还在跑——比如等待远程解锁指令、检查OTA更新、保持蓝牙连接。

我在项目中遇到过一个问题:某款车在Standby状态下,蓝牙模块一直没休眠,导致一晚上掉了15%的电。后来发现是蓝牙协议栈里有个定时器没关。你想想看,这种问题在量产车上有多致命。

避坑指南:我曾经在调试Standby状态时发现,很多第三方应用在后台频繁唤醒系统。建议在Standby状态下,对非系统应用实施严格的JobScheduler限制,只允许白名单应用执行后台任务。

2.2.3 Dozing状态

Dozing是Android从6.0引入的机制,在Automotive上做了增强。系统进入"浅度休眠",大部分应用被冻结,只有高优先级的系统服务能运行。

为什么会这样?因为汽车需要保持对某些事件的快速响应——比如接收到CAN总线上的车门解锁信号,或者检测到有人靠近车辆。

// Dozing状态下的电源管理策略示例
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void updateDozingStateLocked() {
    if (mDozing && !mWakeLockSummary.isHeld()) {
        // 进入Dozing模式
        mDisplayManagerInternal.setDisplayState(Display.STATE_DOZE);
        mBatteryStats.noteScreenState(BatteryStats.SCREEN_STATE_DOZE);
        
        // 只允许高优先级唤醒源
        mWakefulness = WAKEFULNESS_DOZING;
        mNotifier.onWakefulnessChangeStarted(mWakefulness, 0);
    }
}

重要提醒:Dozing状态下,系统会进入"空闲维护窗口"。应用只能在窗口期内执行网络请求。如果应用设计不当,可能导致消息延迟——比如用户远程解锁后,车机要等几分钟才响应。这在汽车场景下是不可接受的。

2.2.4 Sleep/Off状态

这是最低功耗状态。系统几乎完全关闭,只保留必要的唤醒电路。在汽车上,通常对应"车辆休眠"或"深度睡眠"。

说白了,这个状态下只有几个模块还在工作:RTC(实时时钟)、CAN唤醒控制器、以及一些安全监控电路。其他所有模块都断电了。

2.3 状态转换触发条件

状态转换不是随意的,有明确的触发条件。我画了个简化的转换逻辑:

  1. On → Standby:用户熄火并离开车辆,或者屏幕超时且无用户交互
  2. Standby → Dozing:系统空闲超过阈值(通常30秒到2分钟),且无高优先级任务
  3. Dozing → Sleep/Off:系统空闲超过深度阈值(通常5-15分钟),且无待处理事件
  4. Sleep/Off → On:检测到唤醒事件(CAN总线唤醒、GPIO中断、RTC定时器)

实际项目中的经验:我曾经调试过一个案例,车辆在Dozing状态下频繁被唤醒。排查后发现是某个传感器在持续产生中断。解决方案是在进入Dozing前,先屏蔽非关键传感器的中断源,只保留CAN唤醒和RTC唤醒。

2.4 状态转换的代码实现

在PowerManagerService中,状态转换的核心逻辑在updatePowerStateLocked()方法里。我简化了关键部分:

// 状态转换的核心逻辑(简化版)
private void updatePowerStateLocked() {
    // 1. 检查是否有唤醒锁
    boolean hasWakeLock = mWakeLockSummary.isHeld();
    
    // 2. 检查用户活动
    boolean userActivity = mUserActivitySummary.isActive();
    
    // 3. 决定下一个状态
    if (hasWakeLock || userActivity) {
        // 保持在On状态
        setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
    } else if (mDozing && !hasWakeLock) {
        // 进入Dozing
        setWakefulnessLocked(WAKEFULNESS_DOZING, 0);
    } else if (mSleepTimeoutExpired) {
        // 进入Sleep
        setWakefulnessLocked(WAKEFULNESS_ASLEEP, 0);
    }
}

2.5 实际项目中的注意事项

最后,分享几个我在项目中踩过的坑:

  • 唤醒源管理:不是所有中断都能唤醒系统。必须明确配置哪些GPIO、哪些CAN消息可以唤醒。我曾经见过一个项目,把雨量传感器的中断也配成了唤醒源,结果下雨天车辆一直无法休眠。
  • 状态转换时间:从Sleep到On的唤醒时间,必须控制在200ms以内。否则用户会感觉到明显的延迟。这需要硬件和软件的紧密配合。
  • 调试技巧:使用dumpsys power命令可以查看当前电源状态和唤醒锁持有情况。我建议在开发阶段,把这个命令集成到自动化测试脚本中。

个人建议:在设计电源状态机时,一定要和硬件团队提前对齐。比如,Dozing状态下哪些电源轨可以关闭,哪些必须保持。软件上再完美的状态机,如果硬件不支持,也是白搭。

好了,这一章的内容就到这里。PSM是Android Automotive电源管理的基础,理解了这四个状态和它们的转换逻辑,后续的休眠唤醒机制就好理解了。