Android 电话基础知识
撰写于 2018 年 6 月 18 日,作者:javelinanddart
Android 电话堆栈非常复杂,包含许多部分,每个部分或多或少可以归类为下图所示的类别。
HIDL Radio Interface 以下的所有内容通常都是专有的,并且可能无法在 LineageOS 上开箱即用。这篇文章将主要关注 HIDL Radio Wrapper(更常见的名称是 “libril”)如何与电话框架通信。HIDL wrapper 与 HIDL 接口的通信将在未来的文章中更深入地介绍。
术语表
- 命令 (Command): 框架向调制解调器发出的执行任务或返回用户空间所需数据的请求
- 请求 (Request): 参见“命令”
- 响应 (Response): 调制解调器响应框架发送的请求而返回的信息
- 指示 (Indication): 调制解调器由于事件发生而向框架发送的信息
- 请求响应 (Solicited Response): “响应”的旧名称
- 主动上报响应 (Unsolicited Response): “指示”的旧名称
- 主动上报 (Unsol): 主动上报响应的缩写
- DSDA: 双卡双通的缩写,一种多 SIM 卡设备类型,其中存在两个独立的调制解调器,并且可以同时激活两个订阅
- DSDS: 双卡双待的缩写,一种多 SIM 卡设备类型,其中一个调制解调器处理两个 SIM 卡插槽,并且在任何给定时间只能激活一个订阅
- IMS: IP 多媒体子系统的缩写,在 Android 中用作 VoLTE 和 WFC (VoWiFi) 等功能的统称
- VoLTE: LTE 语音的缩写,通过 LTE 网络传输电话的标准
- VoWiFi: WiFi 语音的缩写,WFC 的替代名称
- WFC: WiFi 呼叫的缩写
- RIL: Radio Interface Layer(无线电接口层)的缩写,通常用于指代调制解调器接口库或 libril,有时用于指代电话堆栈的任何部分或整体
什么是电话 (Telephony)?
电话是一个统称,用于描述几乎所有与蜂窝网络通信相关的事物。
基本概述
简单来说,从调制解调器到框架的通信只是数据序列化。数据在框架和调制解调器之间发送,由特殊的标识符代码标记,并以堆栈所有部分都理解的特殊格式结构化。具体来说,有一种方法可以将数据发送到调制解调器,以及两种方法从调制解调器接收数据。Android 使用所谓的“请求”将数据发送到调制解调器。这通常是为了执行任务(例如 拨打电话)、接收信息(例如 获取 SIM 卡状态)或发送信息(例如 发送已输入的 SIM PIN 码)。当从调制解调器接收数据时,有两种不同类型的响应:请求响应和主动上报响应。请求响应是 Android 发送的请求的“反应”。以拨号请求为例,会返回一个 拨号响应,其中包含 Android 用于确定下一步操作的信息。主动上报响应是对 Android 应该注意的事件的“反应”。一个这样的例子是 网络时间更新,这样,如果发生日期、时间、时区或夏令时更改,Android 就会知道,而无需显式轮询此类信息。AOSP 的请求和响应的名称和标识号的枚举可以在 RILConstants 类中找到,其中超链接了当前的修订版。
OEM 添加项
大多数 OEM 通过添加自己的命令和响应来扩展 AOSP 现有的命令和响应,以获取额外的信息或额外的功能。一般来说,这些对 LineageOS 没有影响,除非 OEM 使用了这些数字或重新编号了一个或多个 AOSP 的请求或主动上报。这很重要,因为 Android 在接收到不正确的信息后可能会变得混乱,可能永远不会接收到信息,或者调制解调器库在接收到不正确的信息后会感到困惑。一个这样的例子是在高通三星 Galaxy Note 3 上,其中 UNSOL_ON_SS 的主动上报代码在 AOSP 的实现和 三星的实现之间不一致,结果,Android 永远没有收到该信息。如今,大多数 OEM 将其所有自定义响应和命令代码与 AOSP 的代码偏移任意数字。例如,三星将其自定义请求编号从 10001 开始,自定义主动上报编号从 11000 开始,以避免与 AOSP 冲突,AOSP 的请求编号从 1 开始,主动上报编号从 1000 开始。然而,一些 OEM(例如 OnePlus)玩了一个危险的游戏,即不偏移他们的添加项。虽然这些在设备仍在 OEM 维护时无关紧要,但在超出 OEM 支持的 Android 版本上,这些以前未使用的请求和主动上报可能会突然被使用,从而导致崩溃、混乱和各种不愉快的情况。
反向工程 OEM 添加项
记录 OEM 请求和主动上报
通常,OEM 将其自定义请求和主动上报添加到电话框架中的 requestToString 和 responseToString 方法中。因此,可以通过在设备的 stock 镜像中反编译 deodexed telephony-common.jar (jadx 工具特别好用),然后将这些方法与从其获取 telephony-common jar 的 stock 镜像的 Android 版本对应的 AOSP 版本进行比较,来获得 OEM 添加项。例如,在前面提到的 Galaxy Note 3 上,比较 requestToString 方法得到了 几个 在 AOSP 中不存在的请求,比较 responseToString 方法得到了 许多 在 AOSP 中未包含的其他主动上报。正如之前指出的,三星的添加项在 AOSP 中没有的显著例外是添加了主动上报 11055,UNSOL_ON_SS,它在 AOSP 中为 1043。检查这些方法可以提供关于 OEM 如何处理请求和主动上报的大量信息。
反向工程 OEM libril 更改
按照上一节中概述的流程将 stock 电话框架与 AOSP 进行比较,有时可以类似地定位 OEM 对 libril 的更改。一般来说,AOSP 的 telephony-common 实现和 OEM 的实现之间数据结构的更改是 OEM 修改 libril 源代码的良好指标。RIL.java 是所有设备进行比较的一个很好的点,对于 OEM 发布 Oreo 或更高版本的设备,RadioIndication.java 和 RadioResponse.java 也是如此。对 libril 中的数据结构进行相应的更改是开始反向工程 OEM 的 libril 添加项的好方法。在前面提到的 Galaxy Note 3 上,将 三星 的 responseIccCardStatus 方法与 AOSP 的方法进行比较,成功地揭示了 三星对 AOSP 中的 RIL_AppStatus 结构的更改。不幸的是,情况并非总是如此。将 三星 的 responseCallList 方法与 AOSP 的方法进行比较,得到了额外的 call_id 和 call_details 字段;但是 结构 和 RIL 类 之间 call_details 字段的位置不同。在这些情况下,下一步是从与 OEM 版本相当的 AOSP 或 CAF 版本编译 libril,并比较二进制文件,这将在以后的文章中介绍。
其他注意事项
多 SIM 卡设备
在过去的几年里,多 SIM 卡设备变得越来越普遍。一般来说,这些设备中的大多数都有 2 个 SIM 卡插槽,可以分为两种不同的类别:DSDA 和 DSDS。DSDA 是两者中较不常见的,因为它需要额外的调制解调器硬件,这会增加 OEM 的成本,并带来更高的功耗成本。然而,DSDA 的优点是多个 SIM 卡可以同时处于活动状态。DSDS 更常见,因为它只需要额外的 SIM 卡插槽。当然,缺点是每次只能激活一个 SIM 卡。过去几年 Android 中都支持这两种技术,并且在设备树配置正确后,它们或多或少应该可以正常工作。
电话扩展
telephony-ext
高通提供了一个扩展的电话接口,LineageOS 使用它进行手动 SIM 卡配置(手动启用和禁用 SIM 卡)。对于最近的高通设备,这由 qti-telephony-common.jar 提供,它还扩展了 telephony-common.jar 中提供的各种其他电话组件。对于其他设备,存在 开源重新实现,尽管它不如高通的实现那样强大。
IMS
在撰写本文时,Android 中的 IMS 功能或多或少仅适用于最近的高通设备,这是由于每个 Android 版本的实现都有多个更改,并且电话框架和专有组件之间缺乏标准化接口。虽然 IMS 功能本身是标准化的,但它们的软件实现不幸地并非如此。