Android 电话基础知识

撰写于 2018 年 6 月 18 日,作者:javelinanddart

Fuchsia lines and waves interweave on a purple background as they spill out from the middle. Large red shapes, the Lineage logo, and the word Engineering sit on top.

Android 电话堆栈非常复杂,包含许多部分,每个部分或多或少可以归类为下图所示的类别。

telephony layers

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 将其自定义请求和主动上报添加到电话框架中的 requestToStringresponseToString 方法中。因此,可以通过在设备的 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_idcall_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 功能本身是标准化的,但它们的软件实现不幸地并非如此。