Symptom
Why These Frameworks Matter
Without CallsManager/ConnectionService or CallKit, an In-App Voice call behaves like any other app notification. It can be missed when the screen is locked, silenced by Do Not Disturb, or interrupted by a regular PSTN call. Integrating with these frameworks solves all of that:
- Native call UI - Users see the familiar system call screen (lock screen answer/decline) instead of a custom in-app notification.
- Background & killed-state support - Calls are delivered and ring even when the app is not running, via push notifications (FCM on Android, PushKit/APNs on iOS).
- Audio session management - The OS correctly routes audio, handles Bluetooth headsets, and manages interruptions (e.g., a PSTN call arriving mid-VoIP call).
- Call history integration - Calls appear in the device's native call log with duration and caller information.
- Speaker/audio output control - On iOS, audio output routing (earpiece, speaker, Bluetooth) is managed through CallKit; on Android, through CallsManager/ConnectionService.
Applies To
- Vonage Client SDK
- Voice API
- iOS
- Android
Resolution
iOS - Integrate CallKit
CallKit is an Apple framework that presents the native system call user interface (UI) for Voice over Internet Protocol (VoIP) calls. It allows the operating system to treat in-app calls the same way it treats regular PSTN calls, enabling proper call prioritisation and audio session management.
Apple mandates that every VoIP push notification delivered via PushKit must result in a call being reported to CallKit using reportNewIncomingCall. Failure to do so will cause the app to be terminated by the operating system. Additionally, without CallKit, an incoming PSTN call will take over the audio session and break the active in-app call.
- Import the CallKit framework into your iOS project.
- Create a CXProvider instance and configure it with a CXProviderConfiguration that matches your app's call UI such as, icon, and supported handle types.
- When the Vonage Client SDK invokes the didReceiveInviteForCall delegate callback after processing PushKit push notification payload, immediately report the incoming call to CallKit:
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .generic, value: callerDisplayName)
provider.reportNewIncomingCall(with: callUUID, update: update) { error in
// Handle error if CallKit rejects the call report
}
- When the user answers or ends the call through the CallKit UI, fulfil the corresponding CXAnswerCallAction or CXEndCallAction and call the matching Vonage SDK method (client.answer(callId) or client.hangup(callId)).
- When an active in-app call is in progress, use CXSetMutedCallAction to keep the mute state in sync between the CallKit UI and the SDK.
Note: CallKit is not available on iOS simulators. Test on a physical device.
Android - Integrate CallsManager or ConnectionService
Android provides two APIs for managing calls at the system level:
- CallsManager (via the androidx.core.telecom Jetpack library) - The recommended approach for new integrations. It is the modern replacement for ConnectionService and provides a simpler API with better compatibility across Android versions.
- ConnectionService (via the android.telecom framework) - A legacy approach that remains functional but is not recommended for new integrations.
Without a telecom framework integration, an incoming PSTN call triggers an audio focus change that interrupts the in-app call audio session. Registering your app with the Android telecom system allows the operating system to manage call concurrency and audio routing correctly.
Using CallsManager (Recommended)
- Add the Jetpack Core Telecom dependency to your build.gradle:
implementation "androidx.core:core-telecom:1.0.1"
- Declare the MANAGE_OWN_CALLS permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
- Register your app as a call-capable application using CallsManager:
val callsManager = CallsManager(context)
callsManager.registerAppWithTelecom(CallsManager.CAPABILITY_BASELINE)
- When the Vonage Client SDK fires a setCallInviteListener callback after processing FCM push notification message, add the call to the telecom system so the operating system is aware of the active in-app call:
val attributes = CallAttributesCompat(
displayName = callerName,
address = Uri.fromParts("sip", callerName, null),
direction = CallAttributesCompat.DIRECTION_INCOMING,// or DIRECTION_OUTGOING
)
callsManager.addCall(
attributes,
onAnswer = { _ -> client.answer(callId) },
onDisconnect = { _ -> client.hangup(callId) },
onSetActive = { /* call resumed */ },
onSetInactive = { /* call held */ },
)
Using ConnectionService (Legacy)
- Declare the MANAGE_OWN_CALLS permission and register a ConnectionService in your AndroidManifest.xml:
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<service android:name=".MyConnectionService"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>
- Register a PhoneAccount with TelecomManager, and prompt the user to enable it using the system enable-account activity.
- Create a class that extends ConnectionService and override onCreateIncomingConnection() and onCreateOutgoingConnection() to return a Connection object that maps to the Vonage SDK call information.
- When the Vonage Client SDK fires a setCallInviteListener callback after processing FCM push notification message, use TelecomManager.addNewIncomingCall() to notify the system of the incoming in-app call.
- Override Connection callbacks to bridge system UI actions to the Vonage SDK.
Note: On Android 14 (API 34)+, apps must declare foreground service types and the OS checks the matching permission (e.g. RECORD_AUDIO) when the service starts. CallsManager (androidx.core.telecom) handles this for you; with ConnectionService you have to manage it yourself.
Vonage Sample Apps
The following Vonage-provided sample apps demonstrate full integration of these frameworks with the In-App Voice Client SDK:
| Platform | Sample App | Description |
|---|---|---|
| iOS & Android | reference-client_sdk - Contact Center Use Case | Full reference app with CallKit (iOS) and CallsManager (Android) for both inbound and outbound calls |
| iOS & Android | nexmo-se/branded-call | Branded Call sample with CallKit (iOS) and TelecomManager/ConnectionService (Android) for both incoming and outgoing call flows |
| iOS (Blog + Code) |
Blog_outbound_calls_callkit Code_outbpund_calls_callkit |
SwiftUI app demonstrating outbound calls with CallKit, companion to the Vonage developer blog tutorial |
Additional Information
Related to:
Articles in this section
- Session failed due to a timeout expiration
- Enabling Native Calling Experience for Vonage Client SDK In-App Voice (iOS and Android)
- How to Use the Vonage Client SDK for In-App Voice Calling
- Client SDK App Size — Best Practices
- Does Deleting a User Remove Their Registered Device?
- Voice Client SDK — Best Practices for Long-Duration Calls
- Unable to initialize Client SDK on Android App API Level 31
- User:Error:Not-Found Username Doesn’t Exist When Logging in or Connecting Calls to an App User
- Why do I receive an answered event when I start an outbound call?
- Outbound In-App Voice Calls Fail to Complete