SIM 卡监听(电话监听)

一、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)`
    `}`
`

demo地址:https://gitee.com/ganyilong/SimCard.git

相关推荐
小宋加油啊2 小时前
claude学习
学习
DeepModel2 小时前
机器学习数据预处理:特征构造
人工智能·学习·算法·机器学习
帅次2 小时前
Android 高级工程师面试参考答案:Framework、生命周期、View 与 Binder
android·面试·binder
qq_348231852 小时前
企业级避坑指南
人工智能·学习
我的征途是星辰大海。2 小时前
设计模式(学习笔记)(第一章)
笔记·学习·设计模式
程序员陆业聪2 小时前
AI提效Android开发全景图:从需求到上线的AI工具链
android
李白的天不白2 小时前
滚动条样式大全
android
qeen872 小时前
【算法笔记】差分与经典例题解析
c语言·c++·笔记·学习·算法·差分
ACCELERATOR_LLC3 小时前
【DataWhale组队学习】DIY-LLM Task3 语言模型架构和训练的技术细节
人工智能·学习·语言模型·transformer