2、系统架构设计:Android RIL架构解析、Telephony框架介绍、自定义飞行模式的模块划分
好,咱们正式开始动手之前,得先把地基打牢。这一章我带你看看Android系统里,电话和网络相关的底层架构长什么样。说白了,你要是不懂RIL和Telephony框架,那自定义飞行模式就是空中楼阁。
我个人习惯,在接手任何一个系统级开发任务时,第一件事就是画架构图。你想想看,代码是跑在硬件上的,硬件是跑在协议栈上的,协议栈又是跑在操作系统上的。每一层都有它的职责,搞混了就会出大问题。
2.1 Android RIL架构解析
RIL,全称Radio Interface Layer,翻译过来就是无线接口层。它是Android系统和基带处理器(Modem)之间的桥梁。嗯,这里要注意,基带处理器是独立的芯片,专门处理打电话、发短信、上网这些事。应用处理器(AP)跑Android系统,它俩通过RIL通信。
RIL架构分三层,我画了个图,你一看就明白:
从上往下看,应用层就是咱们平时用的电话拨号盘、设置里的飞行模式开关。它们调用Telephony框架提供的API。Telephony框架再往下,就是RIL层。RIL层又分两部分:RILJ(Java端)和RILD(Native守护进程)。RILD通过串口或者共享内存跟Modem通信。
我在项目中遇到过一个问题:某款手机飞行模式关不掉。查了半天,发现是RILD进程挂了,导致上层发下去的指令没人转给Modem。你想想看,RILD要是死了,整个电话功能就瘫痪了。所以后来我写代码时,都会加一个RILD的看门狗机制。
核心要点:RIL是Android系统与基带通信的唯一通道。自定义飞行模式本质上就是控制RIL层是否允许Modem开启射频。
2.2 Telephony框架介绍
Telephony框架是Android系统里管理电话功能的Java层代码。它位于frameworks/base/telephony目录下。我建议你打开AOSP源码,直接看这个目录,比看任何文档都直观。
Telephony框架里几个关键类,我列个表:
| 类名 | 职责 | 与飞行模式的关系 |
|---|---|---|
TelephonyManager |
对外提供的电话管理API,应用层通过它获取网络状态、设备ID等 | 提供isAirplaneModeOn()方法 |
Phone |
电话核心逻辑,管理SIM卡、网络注册、呼叫状态 | 接收飞行模式开关指令,控制射频开关 |
ServiceStateTracker |
跟踪网络服务状态,监听信号强度、漫游状态 | 飞行模式开启时,强制设置服务状态为STATE_OUT_OF_SERVICE |
CommandsInterface |
RIL指令的Java接口定义,RILJ实现它 | 发送setRadioPower()指令给RILD |
这里有个关键点:飞行模式开关最终调用的就是CommandsInterface.setRadioPower(boolean power)。传false就是关射频,传true就是开射频。嗯,就这么简单。但实际流程绕了好几圈,我带你捋一遍。
当你在设置里点一下飞行模式开关:
- Settings应用调用
TelephonyManager.setAirplaneMode(true) - TelephonyManager通过Binder调用TelephonyRegistry
- TelephonyRegistry通知Phone对象
- Phone对象调用ServiceStateTracker
- ServiceStateTracker调用CommandsInterface.setRadioPower(false)
- RILJ封装成RIL请求,发给RILD守护进程
- RILD通过串口/共享内存发给Modem
- Modem关闭射频电路
你看,一个简单的开关动作,走了8步。我以前做优化时,发现第4步到第5步之间有个状态同步的延迟,导致UI上显示飞行模式已开启,但实际Modem还在发射信号。这个坑我踩过,后面章节会讲怎么解决。
小技巧:调试Telephony框架时,可以用adb shell dumpsys telephony.registry查看当前电话状态。这个命令能告诉你服务状态、信号强度、飞行模式开关状态,非常实用。
2.3 自定义飞行模式的模块划分
好,前面铺垫了那么多,现在咱们来设计自定义飞行模式的模块。我个人习惯,做系统级功能时,先画模块划分图,再写代码。这样思路清晰,不会写着写着就乱了。
自定义飞行模式,我把它拆成四个模块:
四个模块各司其职:
- 策略控制模块:这是大脑。它决定什么时候该开飞行模式,什么时候该关。比如你设置每天晚上11点自动开启,早上7点自动关闭,就是它来算时间。我建议把策略做成可配置的,方便以后扩展。
- RIL指令封装模块:这是手。它负责把策略模块的决策,翻译成RIL能理解的指令。说白了就是调用
setRadioPower()。但要注意,RIL指令是异步的,发出去后要等回调。我曾经因为没处理好回调超时,导致飞行模式开关卡死。 - 状态监听模块:这是眼睛。它时刻盯着ServiceState的变化,确认飞行模式到底生效了没有。有时候你发了关射频指令,但Modem因为某些原因没关,这个模块就能发现异常。
- UI交互模块:这是脸。用户通过它来设置规则、查看状态。我建议UI层尽量薄,只做展示和事件传递,不要掺和业务逻辑。
避坑指南:我曾经在项目里把策略逻辑写在了UI层,结果UI一重构,策略全乱了。记住:UI只负责展示和收集输入,决策交给策略模块,执行交给RIL模块,监控交给状态模块。各层解耦,后期维护才轻松。
这四个模块的通信,我推荐用Handler + Message机制。为什么?因为Telephony框架本身就是基于Handler的,你强行用别的通信方式,反而会增加复杂度。Android系统里,跨线程通信用Handler是最稳的,没有之一。
好了,架构设计就讲到这里。你先把这张图刻在脑子里,下一章咱们开始动手写代码,从策略控制模块开始。
本章小结:
- RIL是Android与Modem的桥梁,分RILJ和RILD两层
- Telephony框架的核心类是TelephonyManager、Phone、ServiceStateTracker
- 飞行模式本质是调用setRadioPower()控制Modem射频
- 自定义飞行模式拆成四个模块:策略控制、RIL指令封装、状态监听、UI交互
- 模块间用Handler通信,保持低耦合
公众号:蓝海资料掘金营,微信deep3321