一、TelephonyCallback 概述
在 Android 12 前后,系统对 SIM 卡相关监听机制进行了调整,引入了 TelephonyCallback 回调机制。在此之前通常通过 PhoneStateListener 实现监听。
虽然 PhoneStateListener 同样支持监听:
- 通话状态
- 网络状态
- SIM 状态
- 数据状态
但由于设计限制,其在以下场景存在不足:
- 双卡支持能力较弱
- 5G 网络支持不完善
- 回调粒度较粗
相比之下,TelephonyCallback:
- 支持更细粒度监听接口拆分
- 权限按需申请
- 支持按订阅 ID 监听
- 更适配多 SIM 与 5G 网络环境
- 更利于未来系统扩展
PhoneStateListener 的问题
PhoneStateListener 常使用:
onCallStateChanged()`
`
监听通话状态,但该接口监听的是:
默认语音订阅(Default Voice Subscription)
而不是:
所有 SIM 卡订阅
因此:
双卡场景通常需要依赖返回数据进行判断,而不是监听阶段直接区分。
TelephonyCallback 的优势
TelephonyCallback 支持针对指定 SIM 卡监听:
createForSubscriptionId(subId)`
`
每张 SIM 卡独立监听回调
二、权限说明
TelephonyCallback 对权限进行了拆分设计:
只有实现对应监听接口时才需要对应权限。
|----------------------------------|--------------------------------|
| 接口 | 所需权限 |
| CallStateListener | READ_PHONE_STATE |
| ServiceStateListener | READ_PHONE_STATE |
| SignalStrengthsListener | READ_PHONE_STATE |
| CellInfoListener | ACCESS_FINE_LOCATION |
| DisplayInfoListener | READ_PHONE_STATE |
| DataConnectionStateListener | READ_PHONE_STATE |
| ActiveDataSubscriptionIdListener | READ_PHONE_STATE |
| PreciseCallStateListener | READ_PRECISE_PHONE_STATE(系统权限) |
| CallDisconnectCauseListener | READ_PRECISE_PHONE_STATE(系统权限) |
三、TelephonyCallback 使用示例
示例代码:
val telephonyManager =`
` context.getSystemService(Context.TELEPHONY_SERVICE)`
`as TelephonyManager`
`telephonyCallback =`
`object` `:` `TelephonyCallback(),`
` TelephonyCallback.ServiceStateListener,`
` TelephonyCallback.ActiveDataSubscriptionIdListener` `{`
`override` `fun` `onServiceStateChanged(`
` serviceState: ServiceState`
`)` `{`
`checkSimInsertOrRemove(context)`
`}`
`override` `fun` `onActiveDataSubscriptionIdChanged(`
` subId: Int`
`)` `{`
`checkSimChanged(context)`
`}`
`}`
`telephonyManager.registerTelephonyCallback(`
` context.mainExecutor,`
` telephonyCallback!!`
`)`
`
四、常用监听回调说明
以下为常见 TelephonyCallback 监听能力整理。
红色:常用
蓝色:需要运营商特权
1. onServiceStateChanged(服务状态变化)
触发时机:
- SIM 插入
- SIM 拔出
- 无网络
- 飞行模式
- 仅紧急呼叫状态
返回参数:
STATE_EMERGENCY_ONLY`
`STATE_IN_SERVICE`
`STATE_OUT_OF_SERVICE`
`STATE_POWER_OFF`
`
2. onSignalStrengthsChanged(信号强度变化)
触发时机:
- 移动位置变化
- 电梯进出
- 网络切换
- 信号波动
返回:
signalStrength.level`
`
信号等级范围:
0 ~ 4`
`
含义:
|--------|--------|
| 等级 | 含义 |
| 0 | 无信号 |
| 1 | 很弱 |
| 2 | 一般 |
| 3 | 良好 |
| 4 | 优秀 |
3. onCallStateChanged(通话状态变化)
触发时机:
- 来电响铃
- 接听电话
- 挂断电话
- 拨出电话
- VoLTE 通话
- 双卡切换通话
返回:
CALL_STATE_IDLE`
`CALL_STATE_RINGING`
`CALL_STATE_OFFHOOK`
`
4. onDataConnectionStateChanged(数据连接状态变化)
触发时机:
- 打开移动数据
- 关闭移动数据
- WiFi → 移动数据切换
- 4G → 5G 切换
- SIM 插入
- SIM 拔出
- 网络注册成功
- 进入无信号区域
返回参数:
state
DATA_UNKNOWN`
`DATA_DISCONNECTED`
`DATA_CONNECTING`
`DATA_CONNECTED`
`DATA_SUSPENDED`
`DATA_DISCONNECTING`
`DATA_HANDOVER_IN_PROGRESS`
`
networkType
可能返回:
NETWORK_TYPE_UNKNOWN`
`NETWORK_TYPE_GPRS`
`NETWORK_TYPE_EDGE`
`NETWORK_TYPE_UMTS`
`NETWORK_TYPE_CDMA`
`NETWORK_TYPE_EVDO_0`
`NETWORK_TYPE_EVDO_A`
`NETWORK_TYPE_1xRTT`
`NETWORK_TYPE_HSDPA`
`NETWORK_TYPE_HSUPA`
`NETWORK_TYPE_HSPA`
`NETWORK_TYPE_IDEN`
`NETWORK_TYPE_EVDO_B`
`NETWORK_TYPE_LTE`
`NETWORK_TYPE_EHRPD`
`NETWORK_TYPE_HSPAP`
`NETWORK_TYPE_GSM`
`NETWORK_TYPE_TD_SCDMA`
`NETWORK_TYPE_IWLAN`
`NETWORK_TYPE_NR`
`
其中:
NETWORK_TYPE_NR`
`
表示:
5G 网络
5. onDataActivity(实时流量方向变化)
触发时机:
- 下载数据
- 上传数据
- 同时上下行
- 网络空闲
- 后台心跳通讯
- 应用联网行为变化
返回:
DATA_ACTIVITY_NONE`
`DATA_ACTIVITY_IN`
`DATA_ACTIVITY_OUT`
`DATA_ACTIVITY_INOUT`
`DATA_ACTIVITY_DORMANT`
`
6. onCellInfoChanged(基站信息变化)
触发时机:
连接基站发生变化时触发。
返回:
MutableList<CellInfo>`
`
常用字段:
cellInfo.isRegistered`
`
说明:
true 当前连接基站`
`false 周边基站`
`
信号强度:
cellInfo.cellSignalStrength.level`
`
运营商信息:
cellInfo.cellIdentity.operatorAlphaLong`
`
7. onDisplayInfoChanged(UI 网络显示变化)
触发时机:
状态栏网络类型显示变化时触发。
返回:
info.networkType`
`info.overrideNetworkType`
`
8. onUserMobileDataStateChanged(用户移动数据开关变化)
触发时机:
- 开关移动数据
- 切换数据卡
- 开机恢复状态
说明:
虽然仅返回:
是否开启移动数据`
`
但:
切换数据卡时同样触发。
9. onMessageWaitingIndicatorChanged(语音留言变化)
触发时机:
- 收到语音留言
- 听完语音留言
- 开机同步状态
返回:
true 有语音留言`
`false 无语音留言`
`
10. onCallForwardingIndicatorChanged(呼叫转移变化)
触发时机:
- 开关呼叫转移
- 状态同步
返回:
true 开启`
`false 关闭`
`
11. onActiveDataSubscriptionIdChanged(数据订阅变化)
触发时机:
- 切换数据卡
- SIM 插拔
- 开机初始化
返回:
当前移动数据使用的订阅 ID`
`
12. onEmergencyNumberListChanged(紧急号码列表变化)
触发时机:
- SIM 初始化
- 国家地区变化
- 运营商配置更新
返回:
Map<Int, MutableList<EmergencyNumber>>`
`
字段说明:
emergencyNumber.number`
`emergencyNumber.emergencyServiceCategories`
`emergencyNumber.mnc`
`emergencyNumber.countryIso`
`
13. onPreciseDataConnectionStateChanged(详细数据连接状态变化)
触发时机:
与 onDataConnectionStateChanged 类似,但粒度更细,通常在以下情况触发:
- 打开/关闭移动数据
- WiFi ↔ 移动数据切换
- 4G ↔ 5G 切换
- SIM 插入 / 拔出
- 网络注册成功
- 网络异常或掉线恢复
- APN 切换
返回参数:
state.state // 当前连接状态`
`state.networkType // 网络类型`
`state.id // 唯一连接标识`
`state.apnSetting // APN 配置信息`
`state.linkProperties // 链路信息(IP / DNS / 路由等)`
`state.transportType // 传输类型(蜂窝/WiFi等)`
`
说明:
相比普通数据回调,该接口可以获取:
- IP 层信息
- DNS 配置
- 路由变化
- APN 级别变化
适用于:
网络诊断 / 企业网络管控 / MDM 网络审计
14. onPhysicalChannelConfigChanged(物理信道变化)
触发时机:
- 网络连接建立
- 网络切换(4G ↔ 5G)
- 基站切换
- 移动过程中信号重选
- 基带频段调整
返回参数:
configs: MutableList<PhysicalChannelConfig>`
`
说明:
包含完整基带物理层信息,例如:
- 载波频段(Band)
- EARFCN / NRARFCN
- 带宽
- 调度信息
适用于:
网络性能分析 / 射频调试 / 运营商级诊断
15. onBarringInfoChanged(业务限制状态变化)
触发时机:
- 运营商策略更新
- 网络拒绝某类业务
- 欠费 / 限制服务
- 进入特殊管控区域
- 漫游限制
返回参数:
val csServiceInfo =`
` barringInfo.getBarringServiceInfo(`
` BarringInfo.BARRING_SERVICE_TYPE_CS_SERVICE`
`)`
`
字段说明:
csServiceInfo.isBarred // 是否被屏蔽`
`csServiceInfo.barringType // 屏蔽类型`
`csServiceInfo.isConditionallyBarred // 是否条件屏蔽`
`csServiceInfo.conditionalBarringFactor // 条件屏蔽因子`
`
适用于:
运营商服务限制识别 / MDM 网络策略控制
16. onCarrierNetworkChange(运营商受限网络变化)
触发时机:
- 进入运营商限制网络
- 欠费降速
- 网络策略切换
- 运营商控制接入
- 网络恢复正常
返回参数:
true` `// 受限网络(Restricted)`
`false` `// 正常网络`
`
说明:
该状态通常用于判断:
- 是否被运营商限速
- 是否进入 captive / restricted network
- 是否影响数据能力
17. onCallDisconnectCauseChanged(通话断开原因变化)
触发时机:
通话结束时触发,包括:
- 主叫挂断
- 被叫挂断
- 网络掉线
- 信号中断
- VoLTE 通话结束
返回参数:
disconnectCause`
`
常见取值:
NOT_VALID // 未知`
`NOT_DISCONNECTED // 未结束`
`INCOMING_MISSED // 未接听`
`NORMAL // 正常挂断`
`LOCAL // 本端挂断`
`BUSY // 对方占线`
`CONGESTION // 网络拥塞`
`
说明:
用于判断:
- 用户挂断
- 网络掉话
- 运营商失败
18. onImsCallDisconnectCauseChanged(IMS 通话断开)
触发时机:
IMS 通话相关异常或结束:
- VoLTE 掉话
- VoWiFi 掉话
- IMS 注册失败导致通话失败
- 4G / WiFi 切换导致通话中断
返回参数:
imsReasonInfo.code // IMS 错误码`
`imsReasonInfo.extraMessage // 错误描述`
`imsReasonInfo.extraCode // 扩展错误码`
`
说明:
用于分析:
- VoLTE 质量问题
- WiFi Calling 稳定性
- IMS 注册异常
19. onRegistrationFailed(网络注册失败)
触发时机:
- 开机无法入网
- SIM 无法注册运营商
- 无信号区域
- 漫游失败
- 5G / 4G 注册失败
- 运营商拒绝接入
返回参数:
cellIdentity // 基站信息`
`chosenPlmn // 注册运营商`
`domain // 注册域`
`causeCode // 失败原因`
`additionalCauseCode // 附加原因`
`
说明:
用于判断:
- SIM 是否可用
- 是否入网失败
- 是否运营商拒绝
20. onDataActivationStateChanged(移动数据激活状态变化)
触发时机:
- 打开/关闭移动数据
- SIM 初始化完成
- 开机网络初始化
- 运营商禁用数据服务
- 数据能力恢复
返回参数:
DATA_DISCONNECTED // 未激活`
`DATA_CONNECTING // 激活中`
`DATA_CONNECTED // 已激活`
`DATA_SUSPENDED // 暂停`
`DATA_DISCONNECTING // 关闭中`
`
说明:
与 onDataConnectionStateChanged 区别:
|---------------------|------------|
| 回调 | 作用 |
| DataActivationState | 是否"具备数据能力" |
| DataConnectionState | 当前"数据连接状态" |
五、TelephonyCallback 与 Telephony 广播对比
测试过程中发现:
Telephony 广播存在如下问题:
SIM 插拔事件偶尔不触发广播`
`
尤其在:
多次连续插拔 SIM 卡`
`
场景下更明显。
相比之下:
TelephonyCallback
具有:
- 回调接口丰富
- 返回数据稳定
- 支持订阅级监听
- 支持双卡独立监听
但问题是:
没有单一明确的 SIM 插拔回调接口`
`
因此推荐方案:
TelephonyCallback + Telephony 广播结合使用`
`
实现:
` `private` `fun` `registerBroadcast(context: Context)` `{`
` receiver =` `object` `:` `BroadcastReceiver()` `{`
`override` `fun` `onReceive(`
` context: Context,`
` intent: Intent`
`)` `{`
`checkSimInsertOrRemove(context)`
`checkSimChanged(context)`
`}`
`}`
`val filter =` `IntentFilter().apply` `{`
`addAction(`
`"android.intent.action.SIM_STATE_CHANGED"`
`)`
`}`
` context.registerReceiver(receiver, filter)`
`}`
`