当前位置: 首页 > 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.rkmt.cn/news/1485151.html

相关文章:

  • 玩转SSD1306的8种扫描模式:用Arduino实现OLED动画和特殊显示效果
  • 你的AR/机器人‘眼睛’准吗?手把手教你用手机和A4纸完成相机标定与精度验证
  • 别再复制粘贴了!手把手教你理解CMSIS-DAP离线下载器里那串神秘代码(附ARM反汇编实战)
  • 广州母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • Qt调用WPS导出Word报告踩坑记:管理员权限竟是罪魁祸首?
  • 鸿蒙Next实战开发(四):个人中心与系统设置页面开发
  • AIGC】story_agent_loop架构初步探讨5
  • 51单片机+ADC0809测电压不准?可能是这些细节没做好(附校准方法与代码优化)
  • 2026 安徽亳州市彩钢瓦修缮 TOP4 权威推荐 + 避坑指南(全区域服务) - 本地便民网
  • 阜阳母婴除甲醛CMA甲醛检测治理公司深度测评:绿呼吸环保稳居榜首 - 一修哥咨询
  • 光学萌新看过来:用Light Tools做第一个简单照明仿真(附B站教程高效学习法)
  • 别只盯着环路!用MPS那个EMI视频里的思路,重新审视你的DCDC开关节点Layout
  • 2026年企业在线培训系统选型避坑:从需求分析到供应商评估的全流程拆解
  • 告别Hello World:用ESP32-IDF 4.3和Blink示例,5分钟点亮你的第一盏灯
  • S5.1注意力捕获——如何在信息过载中抓住用户眼球
  • 高级java每日一道面试题-2026年01月26日-实战篇[Docker]-如何实现容器的外部访问?端口映射的原理是什么?
  • 深入TI C2000内核:TMS320F280049的GPIO输入限定,如何为ePWM故障保护与通信外设保驾护航?
  • 人脸验证训练工具包:含T2T-ViT、BotNet、MobileFaceNet和ResNet四套可切换主干实现
  • 从Wireshark GUI到命令行:在无图形界面的CentOS 7服务器上,用tshark抓取并分析HTTP请求的完整流程
  • 别再死记硬背了!用PyTorch动手画一遍,彻底搞懂CNN和MLP到底啥关系
  • XUnity.AutoTranslator字体管理实战指南:如何解决Unity游戏多语言显示难题
  • 别再只用System.out.printf了!Java保留小数点的3种方法实战对比(含DecimalFormat避坑)
  • Qt 高级开发 028:以代码为笔,以界面为卷
  • 别再只会升级GCC了!遇到‘unrecognized command line option‘的三种排查思路与降级方案
  • NTC温度采集全套开发资源:单片机驱动+查表工具+上位机显示+硬件设计文件
  • 从需求到代码:手把手教你用PlantUML插件,在IDEA里自动生成时序图和类图
  • PSCAD仿真效率提升技巧:从元件布局、参数复用到底层波形导出全流程优化
  • 告别裸机:在STM32CubeIDE中为STM32H7集成SOEM 1.4.0的完整配置流程
  • HC-05蓝牙模块玩转无线PID调参:一个SerialPlot,让你的STM32小车/机械臂调试效率翻倍
  • 2026年6月7日当周国内AI编程新发展:从工具革新到生态重构