实现 IMS

Android 9 引入了一个名为 ImsService 的新 SystemApi 接口,可帮助您实现 IP 多媒体子系统 (IMS)。ImsService API 是在 Android 平台与供应商或运营商提供的 IMS 实现之间明确定义的接口。

ImsService 概览

图 1. ImsService 概览

通过使用 ImsService 接口,IMS 实现者可以向平台提供重要的信号信息(例如 IMS 注册信息、基于 IMS 集成的短信以及 MmTel 功能集成),以提供语音通话和视频通话功能。ImsService API 也是一种 Android System API,这意味着您可以直接针对 Android SDK(而不是来源)构建这种 API。预安装在设备上的 IMS 应用也可以配置为可通过 Play 商店更新。

示例和源代码

为了便于进行测试和开发,Android 在 AOSP 中提供了一个应用,它可以实现 ImsService API 的某些部分。您可以在 /testapps/ImsTestService 找到该应用。

您可以在 ImsService 和该 API 的其他类中找到关于 ImsService API 的文档。

实现

ImsService API 是一种高级 API,它可以让您通过多种方式实现 IMS(具体取决于可用的硬件)。例如,根据 IMS 实现是完全在应用处理器上进行,还是部分或完全分流到调制解调器,实现也会发生相应的变化。Android 不提供用于分流到基带处理器的公共 HAL,因此您必须使用调制解调器的 HAL 扩展来进行所有的分流。

与较早的 IMS 实现之间的兼容性

尽管 Android 9 包含 ImsService API,但使用较早的 IMS 实现的设备无法支持该 API。对于这类设备,较早的 AIDL 接口和封装容器类已移至 android.telephony.ims.compat 命名空间。升级到 Android 9 后,旧款设备必须执行以下操作才能继续支持旧版 API。

  • 更改 ImsService 实现的命名空间,以从 android.telephony.ims.compat 命名空间 API 进行扩展。
  • 修改 AndroidManifest.xml 中的 ImsService 服务定义,以使用 android.telephony.ims.compat.ImsService intent 过滤器操作,而不是 android.telephony.ims.ImsService 操作。

随后,框架将使用 Android 9 中提供的兼容性层绑定到 ImsService,以支持旧版 ImsService 实现。

向框架注册 ImsService

ImsService API 是作为服务实现的,Android 框架会绑定到该服务,以便与 IMS 实现进行通信。如需向框架注册实现了 ImsService 的应用,您必须执行以下 3 个步骤:首先,ImsService 实现必须使用相关应用的 AndroidManifest.xml 向平台注册自己;其次,它必须定义实现支持哪种 IMS 功能(MmTel 还是 RCS);最后,它必须在运营商配置或设备叠加层中验证为可信 IMS 实现。

服务定义

IMS 应用可使用以下格式将 service 条目添加到清单中,从而向框架注册 ImsService:

<service
    android:name="com.egcorp.ims.EgImsService"
    android:directBootAware="true"
    Android:persistent="true"
    ...
    android:permission="android.permission.BIND_IMS_SERVICE" >
    ...
    <intent-filter>
        <action android:name="android.telephony.ims.ImsService" />
    </intent-filter>
</service>

AndroidManifest.xml 中的 service 定义对以下属性进行了定义,没有这些属性,就无法正确运行:

  • directBootAware="true":在用户解锁设备之前,允许该服务被发现并由 telephony 运行。在用户解锁设备之前,该服务无法访问设备加密的存储空间。如需了解详情,请参阅支持直接启动模式文件级加密
  • persistent="true":允许此服务永久运行,不因系统回收内存而终止。只有在相应的应用作为系统应用进行构建时,此属性才有效。
  • permission="android.permission.BIND_IMS_SERVICE":确保只有被授予 BIND_IMS_SERVICE 权限的进程才能绑定到相应的应用。这样可以防止恶意应用绑定到该服务,因为框架只会向系统应用授予这项权限。

此外,该服务还必须指定具有 android.telephony.ims.ImsService 操作的 intent-filter 元素。这样一来,框架便可以找到 ImsService

IMS 功能规范

在 AndroidManifest.xml 中将 ImsService 定义为 Android 服务后,ImsService 必须定义它支持的 IMS 功能。Android 目前支持 MmTel 和 RCS 功能,不过只有 MmTel 集成到了框架中。虽然框架中没有集成 RCS API,但将其声明为 ImsService 的功能仍有诸多好处。

下面列出了 android.telephony.ims.ImsFeature 中定义的 ImsService 可以提供的有效功能,并举例说明了为什么 IMS 应用希望实现所有这些功能或其中某个功能。在对各个功能进行定义后,本页还概述了 ImsService 如何声明它为每个 SIM 卡插槽定义的功能集。

FEATURE_MMTEL

ImsService 可实现 IMS MMTEL 功能,该功能包含对所有 IMS 媒体(IR.92 和 IR.94 规范)的支持(紧急连接到 IMS PDN 以进行紧急呼叫的情况除外)。任何希望支持 MMTEL 功能的 ImsService 实现都应该扩展 android.telephony.ims.MmTelFeature 基类,并在 ImsService#createMmTelFeature 中返回自定义 MmTelFeature 实现。

FEATURE_EMERGENCY_MMTEL

声明这项功能只会向可紧急连接到 IMS PDN 寻求应急服务的平台发送信号。如果不为 ImsService 声明此功能,平台将始终默认使用电路域回退机制来寻求应急服务。如需定义此功能,您必须先定义 FEATURE_MMTEL 功能。

FEATURE_RCS

ImsService API 不实现任何 IMS RCS 功能,但 android.telephony.ims.RcsFeature 基类仍然非常有用。检测到软件包应提供 RCS 时,框架会自动绑定到 ImsService 并调用 ImsService#createRcsFeature。如果取出与 RCS 服务相关联的 SIM 卡,则框架会自动调用 RcsFeature#onFeatureRemoved,然后清除与 RCS 功能相关联的 ImsService。此功能可以移除 RCS 功能本来必须提供的某些自定义检测/绑定逻辑。

注册受支持的功能

电话框架会先绑定到 ImsService,以使用 ImsService#querySupportedImsFeatures API 查询 ImsService 支持的功能。在该框架计算出 ImsService 将支持的功能之后,它会针对 ImsService 将负责的各项功能调用 ImsService#create[...]Feature。如果 IMS 应用支持的功能发生更改,您可以使用 ImsService#onUpdateSupportedImsFeatures 向框架发出信号,以重新计算支持的功能。如需详细了解 ImsService 的初始化和绑定,请参见下图。

ImsService 初始化和绑定

图 2:ImsService 初始化和绑定

ImsService 实现的框架检测和验证

在 AndroidManifest.xml 中对 ImsService 进行正确定义后,必须将平台配置为适时(安全地)绑定到 ImsService。框架可绑定到以下两种 ImsService:

  1. 运营商“替换”ImsService:这类 ImsService 会预先加载到设备上,但与一个或多个手机运营商相关联,并且只有在插入匹配的 SIM 卡时才会绑定。它使用以下项进行配置:
  2. 设备“默认”ImsService:这是由原始设备制造商 (OEM) 加载到设备上的默认 ImsService。当运营商 ImsService 不可用时,它应该能够在任何情况下提供 IMS 服务。在设备没有插入 SIM 卡或插入的 SIM 卡没有安装运营商 ImsService 的情况下,这种 ImsService 非常有用。它使用以下配置在设备叠加层中定义:

Android 不支持采用第三方可下载 ImsService 实现的应用,因此在此处定义的任何 ImsService 实现必须是系统应用,并且必须位于 /system/priv-app/ 或 /product/中 priv-app/ 文件夹中,以授予相应的权限(即电话、麦克风、位置信息、相机和通讯录权限)。通过验证 IMS 实现的软件包名称是否与上文定义的 CarrierConfig 或设备叠加层值匹配,可以仅绑定受信任的预安装应用。

自定义

实现 ImsService 的应用只能绑定到以下设备:此设备上已将该应用配置为运营商“替换”ImsService 或设备“默认”ImsService 配置以实现 MMTEL 或 RCS 功能。ImsService 还允许通过更新(使用 ImsService#onUpdateSupportedImsFeatures 方法)以动态的形式启用或停用它支持的 IMS 功能(MMTEL 和 RCS)。这样会触发框架重新计算要绑定哪些 ImsService 以及它们支持哪些功能。如果 IMS 应用在没有任何受支持的功能的情况下更新框架,则在手机重新启动或插入与 IMS 应用匹配的新 SIM 卡之前,ImsService 将处于未绑定状态。

多个 ImsService 的绑定优先级

框架不支持绑定到预加载到设备上的所有可能的 ImsService,它将按以下顺序按功能绑定到 ImsService(每个 SIM 卡插槽最多可绑定两个 ImsService,每个功能一个 ImsService):

  1. 由 CarrierConfig 的 config_ims_[mmtel/rcs]_package_override_string 值定义的 ImsService 软件包名称(在插入了 SIM 卡的情况下)。
  2. 在设备叠加层的 config_ims_[mmtel/rcs]_package 值中定义的 ImsService 软件包名称(包括未插入 SIM 卡的情况)。此 ImsService 必须支持 Emergency MmTel 功能。

您必须在 CarrierConfig 中为每个将使用 ImsService 软件包的运营商定义您 ImsService 的软件包名称;如果您的 ImsService 是默认类型,应在设备叠加层中定义(如上所述)。

下面我们针对每种功能进行详细介绍。对于已加载单个 SIM 卡的设备(单 SIM 卡或多 SIM 卡),可以使用两种 CMS 功能:MMTel 和 RCS。 框架将尝试按照上文中所述的顺序为每种功能进行绑定;如果相应功能不适用于运营商配置替换中定义的 ImsService,则框架将回退到您的默认 ImsService。例如,下表说明了框架将使用哪种 IMS 功能(假设系统上安装了三个 IMS 应用,它们实现的 ImsService 具有以下功能):

  • 运营商 A ImsService 支持 RCS
  • 运营商 B ImsService 支持 RCS 和 MMTel
  • OEM ImsService 支持 RCS 和 MMTel
已插入 SIM 卡 RCS 功能 MMTel 功能
运营商 A 运营商 A OEM
运营商 B 运营商 B 运营商 B
没有 SIM 卡 OEM OEM

验证

由于 IMS 规范非常多且使用的是特殊验证设备,因此测试套件中不包含验证 IMS 实现本身的工具。这些测试只能验证电话框架是否正确响应 ImsService API。

开发 IMS 应用

在开发与 Android 电话堆栈交互的 IMS 应用时,我们建议指定该应用可以监听或修改为特定运营商订阅附加的 ImsService 实例的状态。

如需监听或修改 MMTEL 和 RCS 功能的 ImsService 状态,请使用 ImsManager 类获取 ImsMmTelManagerImsRcsManager 或 IMS 专属 ProvisioningManager 类的实例。然后,应用可以监听 IMS 专属服务和配置状态,例如:

  • 已启用且可用的 MMTEL 或 RCS 功能
  • 在 IMS 注册状态发生更改时进行更新
  • IMS 功能的配置状态
  • 用户已启用的 IMS 功能

使用 ImsStateCallback

尽管 ImsService 是一项持续性的绑定服务,但当新的 SIM 卡或嵌入式订阅变为活动状态或运营商配置发生更改时,其绑定属性可能会发生更改。由于 ImsService 不是电话进程的一部分,因此,如果 ImsService 因订阅或配置更改而出现无形崩溃或无法绑定的情况,那么应用在尝试访问 IMS API 时可能会发生异常。

在搭载 Android 13 或更高版本的设备上,如需监控关联订阅的 ImsService 实例是否可用,应用可以使用 ImsStateCallback 类。在获取 ImsMmTelManagerImsRcsManager 的实例时,建议应用先使用 ImsMmTelManager#registerImsStateCallbackImsRcsManager#registerImsStateCallback 注册 IMS 状态回调。如需在 ImsService 再次可用时继续接收特定订阅的回调更新,应用需要取消注册或舍弃通过 ImsMmTelManagerImsRcsManagerProvisioningManager 注册的现有回调,并注册新的回调。

如果存在不支持 IMS 的订阅,则框架会以 REASON_NO_IMS_SERVICE_CONFIGURED 为原因调用 ImsStateCallback#onUnavailable。这意味着 ImsService 以及 IMS 相关的 API 不适用于订阅。

在极少数情况下,电话进程会发生崩溃,应用会收到 ImsStateCallback#onError,并且不会再收到已注册的 ImsStateCallback 实例的更新。如需从此状况中恢复,请调用 ImsMmTelManager#registerImsStateCallbackImsRcsManager#registerImsStateCallback,重新注册关联订阅的 ImsStateCallback 实例。