当前位置: 首页 > news >正文

功耗管理与唤醒锁 (WakeLock) 架构文档


description: “Android 16 Telephony 功耗管理与唤醒锁架构文档,涵盖 RIL WakeLock 引用计数 + 序列号防重入机制、ClientWakelockTracker 调用方级电量追踪、WakeLockStateMachine 定向广播模式、Multi-Source Lock 多原因关机计数、Thermal 热缓解关机等完整功耗管理设计。”

功耗管理与唤醒锁 (WakeLock) 架构文档

1. 概述

Telephony 子系统是 Android 设备功耗的主要消耗源之一。Modem 与 AP 之间的通信存在本质的异步性:AP 发送命令后,Modem 可能需要几毫秒到几十秒才能响应。在此期间,如果 CPU 进入深度休眠,RIL 层将无法接收 Modem 的响应,导致命令超时、重试,甚至 Radio 挂死。

因此,Telephony 系统在所有关键路径上使用PowerManager.PARTIAL_WAKE_LOCK确保 CPU 不会在等待 Modem 响应期间休眠。Android 16 的 WakeLock 管理体现了引用计数 + 超时保护 + 精细追踪的三层设计。

1.1 核心组件

组件职责文件
RIL WakeLock每发送一个 RIL 请求就 acquire,每收一个响应就 releaseRIL.java
ClientWakelockTracker追踪每个调用方持有的 WakeLock,按 UID/PID 统计唤醒时长ClientWakelockTracker
WakeLockStateMachine小区广播/短信处理期间的 WakeLock 管理(状态机驱动)WakeLockStateMachine.java
ProxyController WakeLockRadio Capability 变更期间的 WakeLock 保护ProxyController.java
Multi-Reason Radio Power Off多原因关机集合,确保所有原因消除后才开机ServiceStateTracker.java

2. RIL WakeLock — 引用计数 + 序列号防重入

2.1 双 WakeLock 设计

RIL 有两个WakeLock,分别服务于不同用途:

// 主 WakeLock — 为每个 RIL 请求/响应周期持有// Tag: "*telephony-radio*"publicfinalWakeLockmWakeLock;// ACK WakeLock — 为 Modem 的 ACK 响应持有// Tag: "RILJ_ACK_WL"publicfinalWakeLockmAckWakeLock;// 超时配置privatestaticfinalintDEFAULT_WAKE_LOCK_TIMEOUT_MS=60000;// 主 WakeLock: 60 秒privatestaticfinalintDEFAULT_ACK_WAKE_LOCK_TIMEOUT_MS=200;// ACK WakeLock: 200 毫秒// 引用计数(不是简单的 acquire/release,而是计数管理)intmWakeLockCount;

Google设计技巧 #1 — 双 WakeLock 分级超时设计(Dual WakeLock with Tiered Timeout)
主请求的 WakeLock 超时 = 60 秒,足够大多数 Modem 操作完成。ACK 的 WakeLock 超时只有 200 毫秒,因为 ACK 只是 Modem 说"我收到了",应该在极短时间内完成。

这种分级设计避免了"一锅端"的超时:如果 ACK 超时,只释放 ACK WakeLock;主请求仍持有自己的 WakeLock 等待真实的响应数据。

2.2 WakeLock 序列号防重入(Sequence Number Anti-Reentrancy)

HAL 通信有两个并发通道:请求通道和 ACK 通道。两个通道各自使用独立的 WakeLock 和序列号,互不干扰:

// FOR_WAKELOCK = 0 — 请求/响应通道// FOR_ACK_WAKELOCK = 1 — ACK 通道publicstaticfinalintFOR_WAKELOCK=0;publicstaticfinalintFOR_ACK_WAKELOCK=1;volatileintmWlSequenceNum=0;// 请求通道序列号volatileintmAckWlSequenceNum=0;// ACK 通道序列号

序列号用于防止定时器误触发

// RilHandler.handleMessage()caseEVENT_WAKE_LOCK_TIMEOUT:synchronized(mRequestList){// 关键检查:只有当时钟到期且序列号匹配时才执行if(msg.arg1==mWlSequenceNum&&clearWakeLock(FOR_WAKELOCK)){if(mRadioBugDetector!=null){mRadioBugDetector.processWakelockTimeout();}// 记录当前所有未完成请求(用于诊断)intcount=mRequestList.size();riljLog("WAKE_LOCK_TIMEOUT mRequestList="+count);for(inti=0;i<count;i++){rr=mRequestList.valueAt(i);riljLog(i+": ["+rr.mSerial+"] "+RILUtils.requestToString(rr.mRequest));}}}break;

当 Modem 正常响应后,mWlSequenceNum++,之前的超时定时器因为msg.arg1 != mWlSequenceNum被自动忽略。

Google设计技巧 #2 — 序列号防重入(Sequence Number Guard)
在有大量并发请求的场景中,如果没有序列号机制,一个旧定时器可能在 WakeLock 已经被新的请求重新 acquire 后仍然触发,导致正常运行中的 WakeLock 被错误释放。

解决方法:每次 acquire 时递增mWlSequenceNum++,超时事件携带当时的序列号。只有当定时器的序列号与当前序列号匹配时,才执行超时清理。

2.3 acquireWakeLock — 引用计数 + WorkSource 追踪

privatevoidacquireWakeLock(RILRequestrr,intwakeLockType){synchronized(rr){if(rr.mWakeLockType!=INVALID_WAKELOCK){riljLog("Failed to acquire wakelock for "+rr.serialString());return;}switch(wakeLockType){caseFOR_WAKELOCK:synchronized(mWakeLock){mWakeLock.acquire();// 增加内核 WakeLock 引用计数mWakeLockCount++;mWlSequenceNum++;StringclientId=rr.getWorkSourceClientId();if(!mClientWakelockTracker.isClientActive(clientId)){// 新调用方 → 加入 WorkSourcemActiveWakelockWorkSource.add(rr.mWorkSource);mWakeLock.setWorkSource(mActiveWakelockWorkSource);}mClientWakelockTracker.startTracking(clientId,...);// 消息的超时时间携带当前序列号Messagemsg=mRilHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);msg.arg1=mWlSequenceNum;mRilHandler.sendMessageDelayed(msg,mWakeLockTimeout);}rr.mWakeLockType=FOR_WAKELOCK;break;// ...}}}

2.4 decrementWakeLock / clearWakeLock

// 正常递减:每个 RIL 请求完成时调用privatevoiddecrementWakeLock(RILRequestrr){synchronized(rr){switch(rr.mWakeLockType){caseFOR_WAKELOCK:synchronized(mWakeLock){// 从 ClientWakelockTracker 中移除该请求mClientWakelockTracker.stopTracking(rr.mClientId,...);if(mWakeLockCount>1){mWakeLockCount--;// 如果还有请求待处理,只递减计数,不真正 release}}break;}rr.mWakeLockType=INVALID_WAKELOCK;}}// 强制清理:WakeLock 超时时调用privatebooleanclearWakeLock(intwakeLockType){if(wakeLockType==FOR_WAKELOCK){synchronized(mWakeLock){if(mWakeLockCount==0&&!mWakeLock
http://www.jsqmd.com/news/973423/

相关文章:

  • 第36章:AI辅助合约性能压测——使用loadtest、forge snapshot
  • MuleSoft+LLM企业级AI编排:构建可治理、可审计、可落地的认知流水线
  • 高州母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • 别再复制粘贴了!手把手教你理解CMSIS-DAP离线下载器里那串神秘代码(附ARM反汇编实战)
  • 广州母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • 藁城母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • Qt调用WPS导出Word报告踩坑记:管理员权限竟是罪魁祸首?
  • 从故障录波到数据分析:COMTRADE文件在继电保护调试中的完整工作流
  • AIGC】story_agent_loop架构初步探讨5
  • 鸿蒙Next实战开发(四):个人中心与系统设置页面开发
  • Win10老显卡焕新记:GTX 1660 SUPER安装最新TensorFlow/PyTorch前的CUDA踩坑实录
  • 避开这些坑!TMS320F280049 SDFM模块调试常见问题与解决方案汇总
  • 2026 安徽阜阳市彩钢瓦修缮 TOP4 权威推荐 + 避坑指南(全区域服务) - 本地便民网
  • AD9831输出不过零?一个电容或变压器就能搞定(附Multisim仿真验证)
  • 2026 安徽亳州市彩钢瓦修缮 TOP4 权威推荐 + 避坑指南(全区域服务) - 本地便民网
  • 51单片机+ADC0809测电压不准?可能是这些细节没做好(附校准方法与代码优化)
  • C#反编译工具横评:dotPeek、ILSpy、dnSpy到底怎么选?附.NET 8实战对比
  • 阜阳母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • Mythos推理能力解析:多跳因果链与反事实推演的工程化实现
  • Advanced Matplotlib:数据可视化中的信息架构与认知效率
  • 光腿神器厂家直销 - 奔跑123
  • 深度挖掘显卡潜能:NVIDIA Profile Inspector终极配置指南
  • 数据科学落地五大硬核实战洞察:从问题定义到模型可观测性
  • 从‘哑巴’到‘对话’:用DBC文件手把手教你理解Autosar CAN网络里的‘信号语言’(保姆级解析)
  • 光学萌新看过来:用Light Tools做第一个简单照明仿真(附B站教程高效学习法)
  • 告别乱码!用PCtoLCD+ESP32在OLED上显示自定义汉字(保姆级图文教程)
  • 告别Hello World:用ESP32-IDF 4.3和Blink示例,5分钟点亮你的第一盏灯
  • 高要母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • 广汉母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • 鸿蒙Next实战开发(五):编译构建、调试运行与踩坑总结