iOS VoIP应用通过CallKit拒接PSTN来电报UnknownCallUUID错误排查
问题背景
我开发的VoIP应用通话功能正常,但当存在活跃VoIP通话时,用户接到普通PSTN来电后,活跃VoIP通话会出现双方无音频的问题。为解决该问题,我尝试通过代码自动挂断新来电,已成功检测到PSTN来电,但执行挂断操作时出现错误:
EndCallAction transaction request failed: The operation couldn’t be completed. (com.apple.CallKit.error.requesttransaction error 4.)
错误码4对应CXErrorCodeRequestTransactionErrorUnknownCallUUID。
相关代码
初始化代码:
// called during initialization self.callObserver = [[CXCallObserver alloc] init]; [self.callObserver setDelegate:self queue:dispatch_get_main_queue()];
回调处理代码:
- (void)callObserver:(nonnull CXCallObserver *)callObserver callChanged:(nonnull CXCall *)call { // isCallInProgress static variable tracks if a VoIP call is active and I verified its value - works as expected if(isCallInProgress) { if(!call.isOutgoing && !call.hasConnected && !call.hasEnded) { NSLog(@"*** New incoming call detected while a call is in progress - so reject it"); dispatch_async(dispatch_get_main_queue(), ^{ CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:call.UUID]; CXTransaction *transaction = [[CXTransaction alloc] initWithAction:endCallAction]; CXCallController *controller = [[CXCallController alloc] init]; [controller requestTransaction:transaction completion:^(NSError *error) { if (error) { NSLog(@"*** EndCallAction transaction request failed: %@", [error localizedDescription]); } else { NSLog(@"*** EndCallAction transaction request successful"); } }]; }); } } }
排查与解决步骤
确认CallUUID的有效时机
CXCallObserver回调中的call.UUID可能在来电振铃初期还未被系统完全注册到CallKit框架,此时执行EndCallAction会因UUID未被识别报错。可以尝试添加0.5-1秒的短延迟后再执行挂断操作,或者等待call.hasStartedConnecting变为YES后再处理,确保UUID已被系统认可。复用CXCallController实例
当前代码每次回调都新建CXCallController,可能导致上下文或权限问题。建议将CXCallController作为全局单例或类成员变量初始化,保持会话上下文的一致性。检查CallKit权限与集成完整性
确认应用已正确配置CallKit相关权限:Info.plist中添加NSVoIPUsageDescription,确保VoIP通话通过CXProvider完成注册。系统对PSTN来电的操作需要应用具备完整的CallKit集成权限,缺失可能导致UUID无法被识别。从根源解决音频抢占问题
与其强行挂断PSTN来电,不如优先处理音频焦点问题:在VoIP通话活跃时,配置AVAudioSession的类别为AVAudioSessionCategoryPlayAndRecord,并设置AVAudioSessionCategoryOptionMixWithOthers为NO,同时通过CXProviderConfiguration禁用通话等待(supportsCallHolding = NO),避免PSTN来电抢占音频资源,从根源解决VoIP通话无音频的问题。验证UUID的系统注册状态
在回调中打印call.UUID的字符串值,同时通过CXCallController的requestTransactionsWithCompletion方法获取系统当前所有通话事务,检查目标UUID是否存在。若不存在,说明系统尚未注册该来电UUID,需等待状态更新后再执行操作。过滤无效回调触发时机
CXCallObserver会在通话多个状态节点触发回调,当前条件!call.isOutgoing && !call.hasConnected && !call.hasEnded可能匹配到未就绪的状态。可添加call.hasStartedConnecting判断,确保来电已进入系统可操作的阶段。
内容的提问来源于stack exchange,提问作者Mahendra Liya

