|
| 1 | +# Basic Video Chat with `ConnectionService` |
| 2 | + |
| 3 | +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. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- Outgoing and incoming VoIP calls with fullscreen call UIs |
| 8 | +- Integration with Android Telecom API for call and notification management |
| 9 | +- Real-time video sessions using OpenTok |
| 10 | +- Audio device selection and call hold capabilities |
| 11 | +- Self-managed calls for a streamlined calling experience |
| 12 | + |
| 13 | +## How It Works |
| 14 | + |
| 15 | +The app customizes Android’s ConnectionService and Connection classes to: |
| 16 | +- Place outgoing calls through a tailored PhoneAccount. |
| 17 | +- Report incoming calls. |
| 18 | +- Manage call state changes, notifications, foreground execution handling and audio routing. |
| 19 | +- Hardcoded local OpenTok credentials. |
| 20 | + |
| 21 | +## Configuration |
| 22 | + |
| 23 | +1. **API Credentials:** |
| 24 | + Update your `OpenTokConfig` with your `API_KEY`, `SESSION_ID`, and `TOKEN` variables. You can obtain these values from your [TokBox account](https://tokbox.com/account/#/) by creating a new session in the [Video API Playground](https://tokbox.com/developer/tools/playground/) site. |
| 25 | + In a production setup, these values should be provided from a secure server. |
| 26 | + |
| 27 | +2. **PhoneAccount:** |
| 28 | + The `PhoneAccountManager` registers the PhoneAccount necessary to interface with the Telecom API. |
| 29 | + |
| 30 | +3. **Manifest Setup:** |
| 31 | + Verify that `AndroidManifest.xml` includes all necessary permissions such as `CAMERA`, `RECORD_AUDIO`, `FOREGROUND_SERVICE`, `MANAGE_OWN_CALLS`, and `BIND_TELECOM_CONNECTION_SERVICE`. |
| 32 | + |
| 33 | +Register the service in `AndroidManifest.xml` file. |
| 34 | + |
| 35 | +```xml |
| 36 | +<service |
| 37 | + android:name=".connectionservice.VonageConnectionService" |
| 38 | + android:exported="true" |
| 39 | + android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" |
| 40 | + android:foregroundServiceType="microphone|camera"> |
| 41 | + <intent-filter> |
| 42 | + <action android:name="android.telecom.ConnectionService" /> |
| 43 | + </intent-filter> |
| 44 | +</service> |
| 45 | +``` |
| 46 | + |
| 47 | +4. **Audio Focus Management:** |
| 48 | + When using ConnectionService, you need to configure the SDK to delegate audio focus control to your app: |
| 49 | + |
| 50 | +```java |
| 51 | + private AudioDeviceManager audioDeviceManager; |
| 52 | + private BaseAudioDevice.AudioFocusManager audioFocusManager; |
| 53 | + |
| 54 | + public void setupAudioFocusManager(Context context) { |
| 55 | + audioDeviceManager = new AudioDeviceManager(context); |
| 56 | + audioFocusManager = audioDeviceManager.getAudioFocusManager(); |
| 57 | + |
| 58 | + audioFocusManager.setRequestAudioFocus(false); |
| 59 | + } |
| 60 | + |
| 61 | + public void notifyAudioFocusIsActive() { |
| 62 | + audioFocusManager.audioFocusActivated(); |
| 63 | + } |
| 64 | + |
| 65 | + public void notifyAudioFocusIsInactive() { |
| 66 | + audioFocusManager.audioFocusDeactivated(); |
| 67 | + } |
| 68 | +``` |
| 69 | + |
| 70 | +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. |
| 71 | + |
| 72 | +This delegation ensures proper audio routing and coordination with the Android Telecom system. |
| 73 | + |
| 74 | + |
| 75 | +## Requirements |
| 76 | + |
| 77 | +- Android API Level 26 or higher is recommended. |
| 78 | +- Ensure proper runtime permissions are granted for camera, audio recording, and foreground service usage. |
| 79 | + |
| 80 | +## Overview |
| 81 | + |
| 82 | +A `ConnectionService` allows apps to manage VoIP or phone calls, whether they need to integrate with |
| 83 | +the system dialer (system managed) or operate independently (self managed). By implementing this |
| 84 | +service, a VoIP app can leverage Android’s Telecom APIs to provide features such as call switching, |
| 85 | +unified audio route management, Bluetooth device support, and integration with companion devices |
| 86 | +like smartwatches. This ensures calls are accessible and controllable across different devices, with |
| 87 | +consistent user experiences for actions like answering, rejecting, or ending calls. |
| 88 | + |
| 89 | +## TelecomManager and PhoneAccount |
| 90 | + |
| 91 | +To initiate phone or VoIP calls, `TelecomManager` relies on a registered `PhoneAccount`. Register |
| 92 | +your app’s `PhoneAccount` using `TelecomManager.registerPhoneAccount()`, and assign it the |
| 93 | +`CAPABILITY_SELF_MANAGED` capability. This signals that your app will handle the connection logic |
| 94 | +and call management independently. |
| 95 | + |
| 96 | +## Implementing ConnectionService |
| 97 | + |
| 98 | +To handle outgoing and incoming calls, your app should use [TelecomManager.placeCall(Uri, Bundle)](https://developer.android.com/reference/android/telecom/TelecomManager#placeCall(android.net.Uri,%20android.os.Bundle)) for outgoing calls and [TelecomManager.addNewIncomingCall()](https://developer.android.com/reference/android/telecom/TelecomManager#addNewIncomingCall(android.telecom.PhoneAccountHandle,%20android.os.Bundle)) to notify the system of new incoming calls. When these APIs are called, the Telecom framework binds to your app’s `ConnectionService`. |
| 99 | + |
| 100 | +Your implementation should override the following `ConnectionService` methods: |
| 101 | +- [onCreateOutgoingConnection()](https://developer.android.com/reference/android/telecom/ConnectionService#onCreateOutgoingConnection(android.telecom.PhoneAccountHandle,%20android.telecom.ConnectionRequest)): Invoked by Telecom to create a new [Connection](https://developer.android.com/reference/android/telecom/Connection) for an outgoing call initiated by your app. |
| 102 | +- [onCreateOutgoingConnectionFailed()](https://developer.android.com/reference/android/telecom/ConnectionService#onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle,%20android.telecom.ConnectionRequest)): Called if an outgoing call cannot be processed. Your app should not attempt to place the call. |
| 103 | +- [onCreateIncomingConnection()](https://developer.android.com/reference/android/telecom/ConnectionService#onCreateIncomingConnection(android.telecom.PhoneAccountHandle,%20android.telecom.ConnectionRequest)): Invoked to create a new [Connection](https://developer.android.com/reference/android/telecom/Connection) for an incoming call reported by your app. |
| 104 | +- [onCreateIncomingConnectionFailed()](https://developer.android.com/reference/android/telecom/ConnectionService#onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle,%20android.telecom.ConnectionRequest)): Called if an incoming call cannot be handled. Your app should not show a notification and should silently reject the call. |
| 105 | + |
| 106 | +## Implementing [Connection](https://developer.android.com/reference/android/telecom/Connection) |
| 107 | + |
| 108 | +To represent calls in your app, extend the [Connection](https://developer.android.com/reference/android/telecom/Connection) class. When creating a new `Connection` instance to return from your [`ConnectionService`](https://developer.android.com/reference/android/telecom/ConnectionService), make sure to configure these properties: |
| 109 | +- Use `Connection#setAddress(Uri, int)` to specify the other party’s identifier. For phone calls, this should be a `PhoneAccount#SCHEME_TEL` URI. |
| 110 | +- 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. |
| 111 | +- Apply `Connection#PROPERTY_SELF_MANAGED` via `Connection#setConnectionProperties(int)` to indicate your app manages the call. |
| 112 | +- If your app supports call hold, set `Connection#CAPABILITY_SUPPORT_HOLD` and `Connection#CAPABILITY_HOLD` using `Connection#setConnectionCapabilities(int)` to enable concurrent call scenarios. |
| 113 | +- Call `Connection#setAudioModeIsVoip(true)` to inform the platform that the call is VoIP. |
| 114 | +- Do not change the call state (e.g., with `Connection#setActive()` or `Connection#setOnHold()`) until the `Connection` has been added to Telecom by returning it from `onCreateOutgoingConnection` or `onCreateIncomingConnection`. |
| 115 | + |
| 116 | +## Hold/Un hold calls |
| 117 | + |
| 118 | +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. |
| 119 | + |
| 120 | +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. |
| 121 | + |
| 122 | +## Modifying the App for System Managed Calls |
| 123 | + |
| 124 | +To adapt the app so that calls are managed by the system (system managed), follow these steps: |
| 125 | + |
| 126 | +1. **Change the PhoneAccount capability:** |
| 127 | + - Replace `PhoneAccount.CAPABILITY_SELF_MANAGED` with `PhoneAccount.CAPABILITY_CALL_PROVIDER` when registering your `PhoneAccount`. |
| 128 | + |
| 129 | +2. **Use standard URI schemes:** |
| 130 | + - When placing calls with `TelecomManager.placeCall()`, use `tel:` or `sip:` schemes in the destination URI. |
| 131 | + |
| 132 | +3. **Update ConnectionService address parsing:** |
| 133 | + - Ensure your `ConnectionService` implementation correctly parses and handles `tel:` or `sip:` addresses when creating connections. |
| 134 | + |
| 135 | +4. **Activate the PhoneAccount in system settings:** |
| 136 | + - The user must manually enable the call account in the system Settings app. You can launch the relevant screen with the following intent: |
| 137 | + |
| 138 | + ```java |
| 139 | + startActivity(new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS)); |
| 140 | + ``` |
| 141 | + |
| 142 | +5. **Call history integration:** |
| 143 | + - 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. |
| 144 | + |
| 145 | +With these changes, your app’s calls will be fully integrated and managed by the Android system, providing a native calling experience. |
| 146 | + |
| 147 | +## Troubleshooting |
| 148 | + |
| 149 | +- Ensure connected bluetooth speakers have the call audio profile enabled. If that does not work, try disabling and re-enabling the bluetooth speaker. |
0 commit comments