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

