Behavior of CallKit with multiple incoming calls

Hi, Team.

We are currently creating a VoIP calling app using pjsip and want to be able to handle 4 calls at the same time. It is also necessary to be able to notice calls that are on hold or have not answered yet in hands-free (without looking the screen). However, It seems like CallKit has the intention to silence the ringtone when multiple calls come in.


Problem

What does actually happen?

  1. If a second call comes in while the first one have not answered yet, CallKit screen keeps showing about the first one.
  • If tapped "Accept" button, the second call ends.
  • If tapped "Decline" button, CallKit screen shows about the second call at last.
  1. If the first call has put on hold before the second one comes in, CallKit screen shows "Hold & Accept" button even though the first call is already on hold.
  2. When "Hold & Accept" button is displayed, ringtones and other sounds from app stop.
  3. When tapped "Hold & Accept" button for the second call and then unhold the first one in provider(_ provider: CXProvider, perform action: CXAnswerCallAction), ringtone of the first call doesn't ring.

What is expected to happen?

  1. If a second call comes in while the first one have not answered yet, CallKit screen shows about the second call.
  • If tapped "Accept" button, the second call starts and the first call is displayed on CallKit screen.
  • If tapped "Decline" button, the first call appears again.
  1. If the first call has put on hold before the second one comes in, CallKit screen shows only "Accept" and "Decline" button (not in full screen).
  2. If a second call comes in while the first one have not answered yet, ringtones continue to ring.
  3. When accepted the second call, ringtone of the first call rings again.

Information

Sample code

Using CallKit to simulate three incoming calls and two alarm notifications. Whether or not the first call is put on hold before the second one comes in is switchable from the bottom menu. https://github.com/ryu-akaike/CallKit-Multiple-Incoming-Test

Versions

macOS: Sequoia 15.1

Xcode: 16.2

iPhone: 11

iOS: 18.1.1


Thank you.

Ryu Akaike

Answered by DTS Engineer in 831743022

If a second call comes in while the first one have not answered yet, CallKit screen keeps showing about the first one.

Yes, this is how CallKit's call UI works and your app has no direct ability to manipulate/change that.

If a second call comes in while the first one have not answered yet, CallKit screen shows about the second call.

The call UI does not work this way.

The one suggestion I'd have here is that if you want to do something more complex (in particular, delaying the call report for the second call), then you can safely "hide" call reports by intentionally reporting new calls using the UUID of your existing call. So, for example, you could "chain" a series of incoming calls by doing this:

  • Call 1 is active and ringing.
  • Push for call 2 arrives.
  • Report call 2 with UUID of Call 1.
  • reportNewIncoming calls fails with "duplicate UUID" error. Ignore this error.
  • Call 1 is handled (answered/declined/ended).
  • Call reportNewIncoming for Call 2, initiating the call screen again.

This approach will allow you to safely "hide" new calls while ensuring the system doesn't terminate your app for failing to report a call.

When a second call comes in while the first one have answered and put on hold, pressing a button on the headset once (intended to answer the second call) will cause the following transactions:

Transaction A: CXEndCallAction for first one, CXAnswerCallAction for second one Transaction B: CXEndCallAction for second one

Transaction B is not expected to occur. Why is it happening?

Basically, because that's what the headset told us to do. HFP is a complicated and highly detailed specification and the EXACT behavior actually varies between headsets. Unfortunately, the system has very little ability to compensate for those issues because all it has to work with is the command that particular headset sent. All it can do is what the system told it do.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Additional problem is here.


  1. When a second call comes in while the first one have answered and put on hold, pressing a button on the headset once (intended to answer the second call) will cause the following transactions:
  • Transaction A: CXEndCallAction for first one, CXAnswerCallAction for second one
  • Transaction B: CXEndCallAction for second one

Transaction B is not expected to occur. Why is it happening?


Accepted Answer

If a second call comes in while the first one have not answered yet, CallKit screen keeps showing about the first one.

Yes, this is how CallKit's call UI works and your app has no direct ability to manipulate/change that.

If a second call comes in while the first one have not answered yet, CallKit screen shows about the second call.

The call UI does not work this way.

The one suggestion I'd have here is that if you want to do something more complex (in particular, delaying the call report for the second call), then you can safely "hide" call reports by intentionally reporting new calls using the UUID of your existing call. So, for example, you could "chain" a series of incoming calls by doing this:

  • Call 1 is active and ringing.
  • Push for call 2 arrives.
  • Report call 2 with UUID of Call 1.
  • reportNewIncoming calls fails with "duplicate UUID" error. Ignore this error.
  • Call 1 is handled (answered/declined/ended).
  • Call reportNewIncoming for Call 2, initiating the call screen again.

This approach will allow you to safely "hide" new calls while ensuring the system doesn't terminate your app for failing to report a call.

When a second call comes in while the first one have answered and put on hold, pressing a button on the headset once (intended to answer the second call) will cause the following transactions:

Transaction A: CXEndCallAction for first one, CXAnswerCallAction for second one Transaction B: CXEndCallAction for second one

Transaction B is not expected to occur. Why is it happening?

Basically, because that's what the headset told us to do. HFP is a complicated and highly detailed specification and the EXACT behavior actually varies between headsets. Unfortunately, the system has very little ability to compensate for those issues because all it has to work with is the command that particular headset sent. All it can do is what the system told it do.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Behavior of CallKit with multiple incoming calls
 
 
Q