2、系统架构设计:Android RIL架构解析、Telephony框架介绍、自定义飞行模式的模块划分

好,咱们正式开始动手之前,得先把地基打牢。这一章我带你看看Android系统里,电话和网络相关的底层架构长什么样。说白了,你要是不懂RIL和Telephony框架,那自定义飞行模式就是空中楼阁。

我个人习惯,在接手任何一个系统级开发任务时,第一件事就是画架构图。你想想看,代码是跑在硬件上的,硬件是跑在协议栈上的,协议栈又是跑在操作系统上的。每一层都有它的职责,搞混了就会出大问题。

2.1 Android RIL架构解析

RIL,全称Radio Interface Layer,翻译过来就是无线接口层。它是Android系统和基带处理器(Modem)之间的桥梁。嗯,这里要注意,基带处理器是独立的芯片,专门处理打电话、发短信、上网这些事。应用处理器(AP)跑Android系统,它俩通过RIL通信。

RIL架构分三层,我画了个图,你一看就明白:

Android RIL 架构分层图 应用层(App Layer) 电话应用、设置应用、飞行模式开关 Telephony框架(Framework层) TelephonyManager、Phone、ServiceStateTracker RIL层(Radio Interface Layer) RILJ(Java端)、RILD(Native守护进程) Modem层(基带处理器) AT指令、QMI、MBIM协议 Java空间 Java空间 Native空间 硬件空间

从上往下看,应用层就是咱们平时用的电话拨号盘、设置里的飞行模式开关。它们调用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就是开射频。嗯,就这么简单。但实际流程绕了好几圈,我带你捋一遍。

当你在设置里点一下飞行模式开关:

  1. Settings应用调用TelephonyManager.setAirplaneMode(true)
  2. TelephonyManager通过Binder调用TelephonyRegistry
  3. TelephonyRegistry通知Phone对象
  4. Phone对象调用ServiceStateTracker
  5. ServiceStateTracker调用CommandsInterface.setRadioPower(false)
  6. RILJ封装成RIL请求,发给RILD守护进程
  7. RILD通过串口/共享内存发给Modem
  8. Modem关闭射频电路

你看,一个简单的开关动作,走了8步。我以前做优化时,发现第4步到第5步之间有个状态同步的延迟,导致UI上显示飞行模式已开启,但实际Modem还在发射信号。这个坑我踩过,后面章节会讲怎么解决。

小技巧:调试Telephony框架时,可以用adb shell dumpsys telephony.registry查看当前电话状态。这个命令能告诉你服务状态、信号强度、飞行模式开关状态,非常实用。

2.3 自定义飞行模式的模块划分

好,前面铺垫了那么多,现在咱们来设计自定义飞行模式的模块。我个人习惯,做系统级功能时,先画模块划分图,再写代码。这样思路清晰,不会写着写着就乱了。

自定义飞行模式,我把它拆成四个模块:

自定义飞行模式 - 模块划分 模块一:策略控制模块 核心决策层 判断何时开启/关闭飞行模式 支持定时、条件触发、手动三种模式 模块二:RIL指令封装模块 通信执行层 封装setRadioPower指令 处理RIL响应与异常回调 模块三:状态监听模块 监控反馈层 监听ServiceState变化 检测飞行模式实际生效状态 模块四:UI交互模块 用户界面层 自定义设置界面 状态指示器、定时器设置 模块间通过Handler + Message通信

四个模块各司其职:

  • 策略控制模块:这是大脑。它决定什么时候该开飞行模式,什么时候该关。比如你设置每天晚上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