You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在双卡设备上使用PhoneStateListener监听通话状态?

解决双SIM卡设备中SIM1通话状态异常触发的问题

嘿,这个问题在双SIM卡Android设备上真的挺常见的,我帮你分析下原因并给出可行的解决方案~

问题根源

你的PhoneStateListener是绑定到默认的TelephonyManager实例上的,而很多双SIM卡设备对主卡(通常是SIM1)的通话状态通知逻辑和副卡(SIM2)不一样:系统可能在SIM1通话建立时快速触发一次CALL_STATE_IDLE的状态波动,或者默认的TelephonyManager没有正确区分双卡的独立通话状态,导致你刚收到OFFHOOK就马上收到IDLE,直接终止了录音。

解决方案

要解决这个问题,核心是针对每个SIM卡独立注册通话状态监听,同时加入状态防抖逻辑避免误触发。

步骤1:适配双卡监听API(Android 5.1+)

从Android 5.1(API 22)开始,系统提供了SubscriptionManager来管理双卡的订阅信息,我们可以为每个SIM卡创建独立的PhoneStateListener,绑定到对应的SIM卡订阅ID上。

步骤2:修改代码实现

首先,获取所有活跃的SIM卡订阅ID,为每个ID注册专属的监听:

// 在初始化的地方获取双卡信息并注册监听
SubscriptionManager subscriptionManager = 
    (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
List<SubscriptionInfo> activeSubscriptions = subscriptionManager.getActiveSubscriptionInfoList();

if (activeSubscriptions != null) {
    for (SubscriptionInfo info : activeSubscriptions) {
        int subId = info.getSubscriptionId();
        TelephonyManager telephonyManager = 
            (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        // 为每个SIM卡注册独立的监听
        telephonyManager.listen(new SimSpecificPhoneStateListener(subId), 
            PhoneStateListener.LISTEN_CALL_STATE);
    }
}

然后,自定义PhoneStateListener来管理每个SIM卡的录音状态,同时加入延迟防抖:

// 用Map来分别记录每个SIM卡的录音状态和通话日志
private Map<Integer, AtomicBoolean> simRecordingStates = new HashMap<>();
private Map<Integer, CallLog> simCallLogs = new HashMap<>();
private Context context; // 确保你已经初始化了context

private class SimSpecificPhoneStateListener extends PhoneStateListener {
    private int subId;

    public SimSpecificPhoneStateListener(int subId) {
        this.subId = subId;
        // 初始化当前SIM卡的状态
        simRecordingStates.putIfAbsent(subId, new AtomicBoolean(false));
        simCallLogs.putIfAbsent(subId, null);
    }

    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        super.onCallStateChanged(state, incomingNumber);
        AtomicBoolean isRecording = simRecordingStates.get(subId);
        CallLog phoneCall = simCallLogs.get(subId);

        switch (state) {
            case TelephonyManager.CALL_STATE_IDLE:
                if (isRecording.get()) {
                    RecordCallService.stopRecording(context);
                    simCallLogs.put(subId, null);
                    isRecording.set(false);
                }
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
                // 加1秒延迟防抖,避免系统状态波动导致误触发
                new Handler(Looper.getMainLooper()).postDelayed(() -> {
                    // 再次确认当前SIM卡的通话状态是否真的是OFFHOOK
                    TelephonyManager tm = 
                        (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
                        if (tm.getCallStateForSubscription(subId) == TelephonyManager.CALL_STATE_OFFHOOK 
                            && !isRecording.get()) {
                            RecordCallService.startRecording(context, phoneCall);
                            isRecording.set(true);
                        }
                    }
                }, 1000); // 延迟时间可以根据你的设备调整,500ms-1500ms都可以
                break;
            case TelephonyManager.CALL_STATE_RINGING:
                if (phoneCall == null) {
                    phoneCall = new CallLog();
                    simCallLogs.put(subId, phoneCall);
                }
                break;
        }
    }
}

额外注意事项

  • 权限检查:确保你已经申请了READ_PHONE_STATERECORD_AUDIOREAD_CALL_LOG这些必要权限,Android 10及以上版本还需要适配通话录音的系统限制(比如使用MediaProjection或者针对特定厂商的API)。
  • 版本适配getCallStateForSubscription是API 22才有的方法,记得在代码中加入版本判断,避免低版本设备崩溃。
  • 状态清理:当应用退到后台或者销毁时,记得注销所有注册的PhoneStateListener,避免内存泄漏。

这样修改后,每个SIM卡的通话状态都会被独立监听,再加上延迟防抖的逻辑,就能解决SIM1上IDLE状态异常触发的问题啦~

内容的提问来源于stack exchange,提问作者priojeet priyom

火山引擎 最新活动