You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android VoIP应用Connection Service无法创建新呼叫问题排查

问题描述

开发移动VoIP应用并实现Connection Service时,出现以下问题:使用应用一段时间后,调用placeCall或addNewIncomingCall无法创建新呼叫,但重启应用后可恢复正常。调用方法后Logcat输出如下日志:

2023-10-19 15:13:47.363 23963-23963 TelecomFramework I  : createConnectionFailed TC@270: (TSI.pC->CIP.sNOCI)->CS.crCoF->H.CS.crCoF(cast)@E-Kdk
2023-10-19 15:13:47.364 23963-23963 TelecomFramework I  TelecomConnectionService: createConnectionFailed TC@270: (TSI.pC->CIP.sNOCI)->CS.crCoF->H.CS.crCoF(cast)@E-Kdk

此时isIncomingCallPermitted和isOutgoingCallPermitted返回false。根据官方文档,返回false的场景包括:

  • PhoneAccount未设置PhoneAccount#CAPABILITY_SELF_MANAGED属性(非托管ConnectionService),且已达到活跃/保留呼叫限制
  • 存在正在进行的紧急呼叫

已在创建PhoneAccount时设置CAPABILITY_SELF_MANAGED标志,初始阶段功能正常,创建代码如下:

private static PhoneAccount createAccount(Context context) {
    ComponentName componentName = new ComponentName(context.getPackageName(),
            TelecomConnectionService.class.getName());
    PhoneAccountHandle accountHandle = new PhoneAccountHandle(componentName, context.getPackageName());
    String identity = "sip:app@example.com";
    Uri address = Uri.parse(identity);

    Bundle extras = new Bundle();
    extras.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO, true);
    extras.putBoolean(PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, true);
    extras.putBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE, true);

    PhoneAccount.Builder builder = PhoneAccount.builder(accountHandle, context.getPackageName())
            .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
            .setAddress(address)
            .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
            .setExtras(extras);

    return builder.build();
}

在连接状态变为Connection.STATE_DISCONNECTED时,通过onStateChanged调用destroyConnection终止连接,相关实现如下:

destroyConnection实现:

public void destroyConnection() {
    Log.d(TAG, "destroyConnection");
    TelecomConnectionService.destroyCurrentConnection();
    super.destroy();
}

destroyCurrentConnection实现:

public static void destroyCurrentConnection() {
    conn = null;
}

请问哪里可能出错了?

问题分析与解决方案

1. 连接销毁逻辑不完整

当前的destroyCurrentConnection仅将静态变量conn置为null,但未确保Telecom框架完全释放对该Connection实例的引用。对于自托管(SELF_MANAGED)的ConnectionService,正确的销毁流程需:

  • 确保Connection实例的状态正确设置为STATE_DISCONNECTED,并调用setDisconnectedCause指定断开原因
  • 不要仅靠静态变量置空管理连接,Telecom框架内部可能仍持有旧Connection的引用,导致误判存在活跃呼叫

修改建议:
在onStateChanged中处理断开时,先明确设置断开原因,再调用destroy():

@Override
public void onStateChanged(int state) {
    super.onStateChanged(state);
    if (state == Connection.STATE_DISCONNECTED) {
        setDisconnectedCause(DisconnectCause.LOCAL);
        destroy();
    }
}

移除自定义的destroyConnection和静态变量置空逻辑,直接使用super.destroy()即可,Telecom框架会负责清理内部引用。

2. 静态变量conn的内存泄漏问题

使用静态变量conn保存当前连接会导致内存泄漏:当Connection实例被销毁后,静态引用未被正确清理,Telecom框架可能检测到该实例仍存在,从而限制新呼叫的创建。

解决方法:

  • 移除静态变量conn,不要自行持有Connection的全局引用,Telecom框架会管理所有活跃连接
  • 若需跟踪当前连接,可通过ConnectionService的getActiveConnections()方法获取,而非自行维护静态变量

3. 自托管属性的有效性验证

虽然已设置CAPABILITY_SELF_MANAGED,但需确保注册PhoneAccount时该属性未被覆盖:

TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
telecomManager.registerPhoneAccount(createAccount(context));

注册后可通过telecomManager.getPhoneAccount(accountHandle)验证getCapabilities()是否包含CAPABILITY_SELF_MANAGED。

4. 呼叫状态同步问题

自托管ConnectionService需确保呼叫状态与Telecom框架完全同步,若状态更新不及时,框架会误判存在活跃呼叫。例如:

  • 呼叫断开时未及时通知Telecom框架
  • 未正确处理onDestroy()回调

确保在Connection的生命周期中,所有状态变更都通过setState()或相关方法通知框架,断开时必须调用setDisconnectedCause和destroy()。


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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 02:35:06