This project enables real-time video calling using a self-managed ConnectionService. The app handles outgoing and incoming calls, integrates with system Telecom APIs, and manages in-call notifications and audio routing.
- Outgoing and incoming VoIP calls with fullscreen call UIs
- Integration with Android Telecom API for call and notification management
- Real-time video sessions using OpenTok
- Audio device selection and call hold capabilities
- Self-managed calls for a streamlined calling experience
The app customizes Android’s ConnectionService and Connection classes to:
- Place outgoing calls through a tailored PhoneAccount.
- Report incoming calls.
- Manage call state changes, notifications, foreground execution handling and audio routing.
- Hardcoded local OpenTok credentials.
-
API Credentials:
Update yourOpenTokConfigwith yourAPI_KEY,SESSION_ID, andTOKENvariables. You can obtain these values from your TokBox account by creating a new session in the Video API Playground site. In a production setup, these values should be provided from a secure server. -
PhoneAccount:
ThePhoneAccountManagerregisters the PhoneAccount necessary to interface with the Telecom API. -
Manifest Setup:
Verify thatAndroidManifest.xmlincludes all necessary permissions such asCAMERA,RECORD_AUDIO,FOREGROUND_SERVICE,MANAGE_OWN_CALLS, andBIND_TELECOM_CONNECTION_SERVICE.
Register the service in AndroidManifest.xml file.
<service
android:name=".connectionservice.VonageConnectionService"
android:exported="true"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
android:foregroundServiceType="microphone|camera">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>- Audio Focus Management:
When using ConnectionService, you need to configure the SDK to delegate audio focus control to your app:
private AudioDeviceManager audioDeviceManager;
private BaseAudioDevice.AudioFocusManager audioFocusManager;
public void setupAudioFocusManager(Context context) {
audioDeviceManager = new AudioDeviceManager(context);
audioFocusManager = audioDeviceManager.getAudioFocusManager();
audioFocusManager.setRequestAudioFocus(false);
}
public void notifyAudioFocusIsActive() {
audioFocusManager.audioFocusActivated();
}
public void notifyAudioFocusIsInactive() {
audioFocusManager.audioFocusDeactivated();
}When delegating audio focus to the app, the SDK will stop its automatic audio routing. Instead, this routing logic will be handled by ConnectionService, which notifies the app about available audio devices through the Connection's CallEndpoint and CallAudioState APIs. Your app must implement support for audio device enumeration and selection logic.
This delegation ensures proper audio routing and coordination with the Android Telecom system.
- Android API Level 26 or higher is recommended.
- Ensure proper runtime permissions are granted for camera, audio recording, and foreground service usage.
A ConnectionService allows apps to manage VoIP or phone calls, whether they need to integrate with
the system dialer (system managed) or operate independently (self managed). By implementing this
service, a VoIP app can leverage Android’s Telecom APIs to provide features such as call switching,
unified audio route management, Bluetooth device support, and integration with companion devices
like smartwatches. This ensures calls are accessible and controllable across different devices, with
consistent user experiences for actions like answering, rejecting, or ending calls.
To initiate phone or VoIP calls, TelecomManager relies on a registered PhoneAccount. Register
your app’s PhoneAccount using TelecomManager.registerPhoneAccount(), and assign it the
CAPABILITY_SELF_MANAGED capability. This signals that your app will handle the connection logic
and call management independently.
To handle outgoing and incoming calls, your app should use TelecomManager.placeCall(Uri, Bundle) for outgoing calls and TelecomManager.addNewIncomingCall() to notify the system of new incoming calls. When these APIs are called, the Telecom framework binds to your app’s ConnectionService.
Your implementation should override the following ConnectionService methods:
- onCreateOutgoingConnection(): Invoked by Telecom to create a new Connection for an outgoing call initiated by your app.
- onCreateOutgoingConnectionFailed(): Called if an outgoing call cannot be processed. Your app should not attempt to place the call.
- onCreateIncomingConnection(): Invoked to create a new Connection for an incoming call reported by your app.
- onCreateIncomingConnectionFailed(): Called if an incoming call cannot be handled. Your app should not show a notification and should silently reject the call.
Implementing Connection
To represent calls in your app, extend the Connection class. When creating a new Connection instance to return from your ConnectionService, make sure to configure these properties:
- Use
Connection#setAddress(Uri, int)to specify the other party’s identifier. For phone calls, this should be aPhoneAccount#SCHEME_TELURI. - Set the display name with
Connection#setCallerDisplayName(String, int), which will appear on Bluetooth and wearable devices—especially important if no phone number is used. - Apply
Connection#PROPERTY_SELF_MANAGEDviaConnection#setConnectionProperties(int)to indicate your app manages the call. - If your app supports call hold, set
Connection#CAPABILITY_SUPPORT_HOLDandConnection#CAPABILITY_HOLDusingConnection#setConnectionCapabilities(int)to enable concurrent call scenarios. - Call
Connection#setAudioModeIsVoip(true)to inform the platform that the call is VoIP. - Do not change the call state (e.g., with
Connection#setActive()orConnection#setOnHold()) until theConnectionhas been added to Telecom by returning it fromonCreateOutgoingConnectionoronCreateIncomingConnection.
When receiving and answering an external call while the app is already in a call, ConnectionService will notify your app that the call changed to HOLDING state.
When an external call ended by the remote end the user has to manually unhold the call by pressing the unhold call button. If the external call is ended by the user, the app will automatically unhold the call and resume the audio playback.
To adapt the app so that calls are managed by the system (system managed), follow these steps:
-
Change the PhoneAccount capability:
- Replace
PhoneAccount.CAPABILITY_SELF_MANAGEDwithPhoneAccount.CAPABILITY_CALL_PROVIDERwhen registering yourPhoneAccount.
- Replace
-
Use standard URI schemes:
- When placing calls with
TelecomManager.placeCall(), usetel:orsip:schemes in the destination URI.
- When placing calls with
-
Update ConnectionService address parsing:
- Ensure your
ConnectionServiceimplementation correctly parses and handlestel:orsip:addresses when creating connections.
- Ensure your
-
Activate the PhoneAccount in system settings:
- The user must manually enable the call account in the system Settings app. You can launch the relevant screen with the following intent:
startActivity(new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS));
-
Call history integration:
- With system-managed mode, calls made and received through your app will appear in the device’s default phone app call history, just like native calls. This allows users to view, return, or manage these calls directly from the standard phone app, providing a more integrated experience.
With these changes, your app’s calls will be fully integrated and managed by the Android system, providing a native calling experience.
- Ensure connected bluetooth speakers have the call audio profile enabled. If that does not work, try disabling and re-enabling the bluetooth speaker.