---
title: Android SDK Integration
slug: voice-apis/android-sdk-integration
docTags: 
createdAt: 2025-10-03T04:01:06.520Z
---



## Getting Started

### Software Package

Th&#x65;*&#x20;ExotelVoice* SDK software package includes 

- Android AAR file
- Integration Guide
- Sample application for reference
- JavaDoc for API reference
- Subscriber Management API Documentation



### Add Exotel Voice SDK Library to Project

**Method I:**

- Open your Android application code in Android Studio
- Choose File -> New -> New Module -> Import exotel-voice-release.aar Package
- Provide the path to the downloaded AAR file
- Add the SDK as a dependency for the app

1. Go to File -> Project structure -> Dependencies > “+” that says “All Dependencies > Add path of AAR
2. Add Exotel Voice SDK as dependency to your application

**Method II**:

- Copy the AAR file to the libs folder and edit the build.gradle file to include

|     <br />    implementation project(path: '\:exotel-voice-sdk') |
| ---------------------------------------------------------------- |



- Below contents part of settings.gradle file

|            include '\:app', '\:exotel-voice-sdk'<br />    implementation fileTree(include: \['\*.jar'], dir: 'libs') |
| -------------------------------------------------------------------------------------------------------------------- |



- Add the following libraries to the build.gradle file

|       implementation 'com.google.code.gson\:gson:\[2.8.5]'<br />      implementation 'dnsjava\:dnsjava:\[2.1.9,)'<br />      implementation 'com.squareup.okhttp3\:okhttp:\[3.14.2,)'<br />      implementation 'com.squareup.okhttp3\:logging-interceptor:\[3.14.2,)' |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



### Target Platform

- Supported Android Version: 10.0+
- Supported Android ABIs: armeabi-v7a, arm64-v8a, x86\_64 and x86 

### Permissions

| \<uses-permission android\:name="android.permission.INTERNET" /><br />\<uses-permission android\:name="android.permission.ACCESS\_NETWORK\_STATE" /><br />\<uses-permission android\:name="android.permission.BROADCAST\_STICKY" /><br />\<uses-permission android\:name="android.permission.RECORD\_AUDIO" /><br />\<uses-permission android\:name="android.permission.MODIFY\_AUDIO\_SETTINGS" /><br />\<uses-permission android\:name="android.permission.READ\_PHONE\_STATE" /><br />\<uses-permission android\:name="android.permission.WAKE\_LOCK" /><br />\<uses-permission android\:name="android.permission.DISABLE\_KEYGUARD" /><br />\<uses-permission android\:name="android.permission.VIBRATE" /><br />\<uses-permission android\:name="android.permission.FOREGROUND\_SERVICE" /><br />\<uses-permission android\:name="android.permission.BLUETOOTH"/><br />\<uses-permission android\:name="android.permission.MANAGE\_OWN\_CALLS"/><br />\<uses-permission android\:name="android.permission.POST\_NOTIFICATIONS"/><br />\<uses-permission android\:name="android.permission.BLUETOOTH\_ADMIN"/><br />\<uses-permission android\:name="android.permission.BLUETOOTH\_CONNECT"/><br /><br /><br />//Required for Android 14 onwards<br />\<uses-permission android\:name="android.permission.FOREGROUND\_SERVICE\_PHONE\_CALL" /><br />\<uses-permission android\:name="android.permission.FOREGROUND\_SERVICE\_MICROPHONE" /><br /><br /> |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



### Proguard Rules

The proguard rules for *ExotelVoice* are included in the AAR file. These rules will be auto included when the proguard is enabled for your application build.

### Android Service

The application needs to integrate *ExotelVoice* in a service. This should be run as a foreground service when a call is in progress so that the application is not killed when it moves to the background. When the call is over, service should be moved to the background. Refer to *VoiceAppService* in the sample application.

**Use Foreground Service Types**

When setting up the Foreground Service, define the  foregroundServiceType attributes as phoneCall and microphone in the manifest. This will indicate that the service is responsible for handling calls and microphone access, preventing unnecessary security issues related to background access. For instance:



| \<service android\:name=".VoiceAppService"        android\:foregroundServiceType="phoneCall\|microphone"<br />android\:permission="android.permission.FOREGROUND\_SERVICE"<br />android\:exported="false" />           <br /> |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

This setup ensures that the service continues running during an active call and has the necessary permissions to access the microphone. Additionally, after the call ends, the service can be moved to the background to release unnecessary system resources.

**Managing Microphone Permission According to Android 14 Guidelines**

Android 14 enforces strict restrictions on background services, particularly for microphone access. To comply with these guidelines, apps must manage foreground services effectively, ensuring they are correctly configured for both incoming and outgoing calls. This ensures smooth call functionality while maintaining security and privacy.

When the foreground service is started, it should begin with FOREGROUND\_SERVICE\_TYPE\_PHONE\_CALL. This allows the app to operate in the background while displaying the incoming call notification.

|     <br />ServiceCompat.startForeground(this, NOTIFICATION\_ID, notification,ServiceInfo.FOREGROUND\_SERVICE\_TYPE\_PHONE\_CALL);<br /> |
| --------------------------------------------------------------------------------------------------------------------------------------- |



### Incoming Calls

When the app is closed and an incoming call is detected, it should first start as a normal service to handle initializing the SDK 

Once the user got the  onIncomingCall event from the SDK and trying to answer the call, so before invoking the answer method of the SDK, the application must be in foreground and  the service type must be updated to enable microphone access during the call. This will ensure that app will work seamlessly when the app is running in the background or is closed. 

, 

|  public void answer() throws Exception \{<br />        VoiceAppLogger.debug(TAG, "Answering call");<br />        if (null == mCall) \{<br />            String message = "Call object is NULL";<br />            throw new Exception(message);<br />        }<br />        try \{<br />            updateForegroundServiceType(mCall, CallState.ANSWERING);<br />            tonePlayback.stopTone();<br />            mCall.answer();<br />        } catch (Exception e) \{<br />            throw new Exception(e.getMessage());<br />        }<br />        VoiceAppLogger.debug(TAG, "After Answering call");<br />    } |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



### Outgoing Calls 

After dialing the call from the app the user will get the onCallInitiated()  callback.

Once the callback is received, the service should be updated to enable microphone access for the call. 

Note that , while updating the foreground service with microphone , application must be in foreground. It will ensure that , if during call, app goes into background then voice will flow properly.

|    @Override<br />    public void onCallInitiated(Call call) \{<br />        VoiceAppLogger.debug(TAG, "on Call initiated");<br />        for (CallEvents callEvents : callEventListenerList) \{<br />            callEvents.onCallInitiated(call);<br />        }<br />        mCall = call;<br />        updateForegroundServiceType(call,CallState.OUTGOING\_INITIATED);<br />        VoiceAppLogger.debug(TAG, "End: onCallInitiated");<br />    }<br /><br />    |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



### Update Foreground Service Type Function

 The updateForegroundServiceType method is responsible for updating the	 foreground service type based on the current call state. It ensures compliance with Android 14's guidelines for background services and microphone access.

|    private void updateForegroundServiceType(Call call, CallState outgoingInitiated) \{<br />        handler.post(new Runnable() \{<br />            @Override<br />            public void run() \{<br />                Notification notification = utils.createNotification(outgoingInitiated, call.getCallDetails().getRemoteId(), call.getCallDetails().getCallId(), call.getCallDetails().getCallDirection());<br />                if (Build.VERSION.SDK\_INT \< Build.VERSION\_CODES.TIRAMISU) \{<br />                    startForeground(NOTIFICATION\_ID, notification);<br />                } else \{<br />                    startForeground(NOTIFICATION\_ID, notification, ServiceInfo.FOREGROUND\_SERVICE\_TYPE\_MANIFEST);<br />                }<br />            }<br />        });<br /><br />} |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



## Initialize Library

ExotelVoice Interface Class provides an entry point to initialize the voice library and set it up for handling calls. 

| // Get Exotel Voice Client<br />ExotelVoiceClient exotelVoiceClient = ExotelVoiceClientSDK.getExotelVoiceClient();<br /><br />//set the event listener for sdk logs.<br />exotelVoiceClient.setEventListener(this);<br /><br />// Set listener to handle events from voice client <br />exotelVoiceClient.setEventListener(new ExotelVoiceClientListener()  \{<br /><br />// Initialization success handler<br />void onInitializationSuccess() \{ ... }<br /><br />// Initialization failure handler<br />void onInitializationFailure(ExotelVoipError exotelVoipError) \{ ... }<br /><br />// Logs reported by the voice library<br />void onLog(LogLevel logLevel, String Tag, String Message) \{ ... }<br /><br />// Log upload success handler<br />void onUploadLogSuccess() \{ ... }<br /><br />// Log upload failure handler<br />void onUploadLogFailure(ExotelVoipError exotelVoipError) \{ ... }<br /><br />// Authentication failure handler<br />void onAuthenticationFailure(ExotelVoipError exotelVoipError) \{ ... }<br />}); |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



SDK initialization requires a *subscriberToken* generated by the Exotel platform. Workflow for this token generation and management is described in section **Authentication and Authorization**. 

| // Request token from application backend (example)<br />String subscriberToken = getAccessToken(); |
| --------------------------------------------------------------------------------------------------- |



The *subscriberToken* is provided to *ExotelVoice* during initialization. 

| // Initialize client library<br />android.content.Context context = this.getApplicationContext();<br />exotelVoiceClient.initialize(<br />context,		// Android application context<br />hostname,           <br />subscriberName,     <br />displayName,        <br />accountSid,         // Exotel Account SID <br />subscriberToken,	<br />); |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



Client library notifies *onInitializationSuccess()* on success and *onInitializationFailure()* on failure. On expiry of *subscriberToken*, the library will post onAuthenticationFailure()  at which application should request new *subscriberToken* from application backend and program in the *initialize()* API.*&#xA0;*

| **Parameter**   | **Description**                                                                                                                                                                                           |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Hostname        | *https\://miles.apac-sg.exotel.in/v2*                                                                                                                                                                     |
| SubscriberName  | Param “subscriber\_name” returned as part of the *Subscriber management API* to create a subscriber.                                                                                                      |
| DisplayName     | Subscriber name.                                                                                                                                                                                          |
| AccountSId      | *Account SID param from API settings page* in the Exotel Dashboard.                                                                                                                                       |
| SubscriberToken | can be gotten from the API in the \`Get Subscriber Token\` section in the *Subscriber Management API document.* In the *subscriberToken*, both the *refresh token* and *access token* are base64 encoded. |



&#x9;Subscriber token format example 

| "subscriber\_token":<br />\{ <br />"refresh\_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJleG90ZWwiLCJzdWIiOiJBcmNoaXQiLCJpYXQiOjE1NzY2NDc5OTksImV4cCI6MTU3Njc0Nzk5OSwiY2xpZW50X2lkIjoiNUUwNTg2NUUifQ.Hc3umVfFlKIPiJ8R9kcP9o9hE9he51le08rO22u7eqs",<br />"access\_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJleG90ZWwiLCJzdWIiOiJBcmNoaXQiLCJpYXQiOjE1NzY2NDc5OTksImV4cCI6MTU3Njc0Nzk5OSwic2NvcGUiOiJ2b2ljZSIsImNsaWVudF9pZCI6IjVFMDU4NjVFIn0.uQd\_aHGBIT4XqTPfl-567dmjQrKlmLtPag0KpGB2QMA"<br />} |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



Below are the sample methods to get the subsriberToken, Customers can implement this in their own method.

Use the below HTTP APIs to get the subscriber token and convert subscriber token to jobject and pass it to exotelVoiceClient.initialize();

Note: 

- Copying manually generated subscriber token has issues. Best way is to make API calls in source code and pass them by converting to jobject to the exotelVoiceClient.initialize();
- Pass the **proper device ID** to the login api to avoid invalid refresh token error.
- build.gradle should be proper. Complete working build.gradle file is attached in the link *https\://github.com/exotel/exotel-voip-sdk-android/blob/main/build.gradle*
- Device\_id is the actual device id in which the app is running using below sample code.
  String androidId = Settings.Secure.*getString*(context.getContentResolver(), Settings.Secure.*ANDROID\_ID*);

**Generate access token:**

Refer “Subscriber Management API” documentation section 3.1 for creating subscriber token.

### Managing Calls

After successful initialization, the library is ready to handle calls. Dialing-out calls or receiving incoming calls is managed through *CallController* Interface.

| // Get Call Controller interface<br />CallController callController = exotelVoiceClient.getCallController(); |
| ------------------------------------------------------------------------------------------------------------ |



Call progress events are notified to the application through the *CallListener* Interface attached to the *CallController* object.  

| // Attach call listener<br />callController.setCallListener(new CallListener() \{<br /><br />// Handler for incoming call alert<br />void onIncomingCall(Call call) \{ ... }<br /><br />// Handler for outgoing call initiated event<br />void onCallInitiated(Call call) \{ ... }<br /><br />// Handler for call ringing event for outgoing call<br />void onCallRinging(Call call) \{ ... }<br /><br />// Handler for call connected event<br />void onCallEstablished(Call call) \{ ... }<br /><br />// Handler for call termination event<br />void onCallEnded(Call call) \{ ... }<br /><br />// Handler for missed call alert <br />void onMissedCall(String remoteId, Date time) \{ ... }<br />}); |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



Each call object provides a Call Interface to manage the call lifecycle and perform operations like answer incoming calls, hangup calls, mute, unmute, and get call statistics.

| // Mute<br />call.mute();<br /><br />// Terminate call<br />call.hangup(); |
| -------------------------------------------------------------------------- |



### Make Outgoing Call

To make outgoing calls, the *CallController* interface must be created and a listener interface is attached as mentioned in section **Managing Calls**. Provide sip exophone number in *dial()* API of *CallController* Interface to make outgoing calls.



| // Get Call Controller interface<br />CallController callController = exotelVoiceClient.getCallController();<br /><br />// Attache call progress listener<br />callController.setCallListener(new CallListener() \{ ... }<br /><br />// Dial out call to remoteId<br />Call call = callController.dial(\<sip exophone>, \<custom\_field>); |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |



Params for dial method - 

| **Param**     | **Mandatory** | **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| ------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| sip\_exophone | Yes           | ExotelVoice library always routes the call via SIP Exophone configured for your account. SIP Exophones are different from the PSTN / Mobile Exophones. They can be identified using sip: prefix. Contact *exotel support* to enable SIP Exophone in your account.<br /><br /><br />Similar to the number masking workflow, the application backend must maintain the call context between caller and callee. On receiving the call at SIP Exophone, the exotel platform will request the callee details from the application backend to bridge the call. Refer section *Dial Whom Endpoint* for details.  |
| custom\_field | No            | The string that will be passed here will be sent to the DialWhom Endpoint under the “CustomField” param. All alphanumeric characters are allowed along with comma \`,\` colon \`:\` and all kinds of brackets - \{}()\[]                                                                                                                                                                                                                                                                                                                                                                                  |



Once initiated, call progress events can be monitored through *CallListener* Interface and call control operations can be performed using *Call* Interface. 

### Receive Incoming Call

The Mobile application receives incoming call data in Push Notification sent by your application backend. Refer section **Push Notification Endpoint** for details. 

To handle the incoming call, the *ExotelVoice* library should be in an initialized state. Refer to section **Initialize Library**. After initialization, the CallController Interface should be created and the listener be attached as mentioned in section **Managing Calls**.

![](https://api.archbee.com/api/optimize/SK2urIgCC2ml99YyqWOSl/OVpMyaXgGCfZY579BMXPe_upload.png)



| // Get Call Controller interface<br />CallController callController = exotelVoiceClient.getCallController();<br /><br />// Attache call progress listener<br />callController.setCallListener(new CallListener() \{ ... } |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



The sample application uses Firebase Cloud Messaging to push call data to the device. Once received at the mobile app, it is sent to *ExotleVoiceClient* through *relaySessionData* API.

| public class VoiceAppFirebaseService extends FirebaseMessagingService\{<br />    public void onMessageReceived(RemoteMessage remoteMessage) \{<br />    <br />        Map\<String,String> callData = remoteMessage.getData();<br />        Map\<String,String> pushNotificationData = new HashMap\<>();<br /><br />        pushNotificationData.put("payload", callData.get(“payload”));<br />        pushNotificationData.put(<br />"payloadVersion", callData.get(“payloadVersion”));<br />        <br />        exotelVoiceClient.relaySessionData(pushNotificationData);<br />    }<br />} |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



The voice library will process and validate the data and send an *onIncomingCall()* event to the application with the call object.

Application has to update their foreground service with microphone permission.

And it must be required to app in foreground mode to update the foreground service with microphone permission. Hence for this we can move application in foreground with UI element. And then update the foreground service.

 The application can call the *answer()* API to accept the incoming call.

| // Accept call<br />call.answer(); |
| ---------------------------------- |



### Hangup Call

  	The application can call hangup() API on the call object to decline an incoming call.

| // Decline / Terminate call<br />call.hangup(); |
| ----------------------------------------------- |



### Audio Management

Before the call begins, Exotel’s SDK checks the current audio output mode and sets the audio route accordingly. By default, it is in the earpiece mode. For example, If a wired headset is plugged in, audio will get routed via wired headset.

Speaker phone mode can be enabled and disabled using *Call* object APIs. 

| // change audio route to speaker<br />call.enableSpeaker();<br /><br />// change audio route to phone earpiece<br />call.disableSpeaker(); |
| ------------------------------------------------------------------------------------------------------------------------------------------ |



Bluetooth mode can be enabled and disabled using *Call* object APIs. This will only work when any bluetooth device is connected to the phone. 

| // change audio route to bluetooth. Only works if a bluetooth device is connected to the phone. <br />call.enableBluetooth(); <br /><br />// change audio route to phone earpiece<br />call.disableBluetooth();  |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



Current Audio route can be fetched using Call object API getAudioRoute. It will return

CallAudioRoute Enum i.e EARPIECE, SPEAKER, BLUETOOTH, HEADPHONES

| // get current audio route<br />call.getAudioRoute();  |
| ------------------------------------------------------ |



Local mic can be muted and unmuted during in-call using *Call* object APIs.

| // mute the call<br />call.mute();<br /><br />// unmute the call<br />call.unmute(); |
| ------------------------------------------------------------------------------------ |



### Call State 

The Call State can be accessed by calling *getLatestCallDetails().getCallState()*.

This state can be displayed on the calling screen to let the user know about the state of the call. Refer to the *Call State Flow diagram*.



| **Call State**      | **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | **Call Back**           | **Recommended message to display on the calling screen** |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | -------------------------------------------------------- |
| NONE                | This state occurs when a call object is created but not yet initiated.                                                                                                                                                                                                                                                                                                                                                                                                                     | -                       | -                                                        |
| OUTGOING\_INITIATED | This state is set when an outgoing call is initiated. It’s set after dialing and before the call starts ringing.                                                                                                                                                                                                                                                                                                                                                                           | -                       | Connecting                                               |
| EARLY               | Indicates early media reception before the call is answered.                                                                                                                                                                                                                                                                                                                                                                                                                               | -                       | -                                                        |
| RINGING             | The receiver’s phone is ringing but hasn’t been answered yet.                                                                                                                                                                                                                                                                                                                                                                                                                              | onCallRinging()         | Ringing                                                  |
| CONNECTING          | The call starts connecting.                                                                                                                                                                                                                                                                                                                                                                                                                                                                | -                       | Connecting                                               |
| INCOMING            | This state is set when there’s an incoming call, and the system isn’t idle.                                                                                                                                                                                                                                                                                                                                                                                                                | onIncomingCall()        | Caller ID, custom information related to the callee      |
| ANSWERING           | Occurs when the user accepts the incoming call; it’s a transitional phase before the call gets established.                                                                                                                                                                                                                                                                                                                                                                                | -                       | Answering                                                |
| ESTABLISHED         | Indicates that both parties have accepted the call, and communication is established.                                                                                                                                                                                                                                                                                                                                                                                                      | onCallEstablished()     | Connected                                                |
| MEDIA\_DISRUPTED    | Occurs if there’s a disruption in media transmission during an ongoing call due to issues like RTP loss. <br />To handle such disruptions effectively ensuring continuity or termination based on severity it initiates reconnection procedures and moves to RECONNECTING if RTP loss persists. <br />To ensure calls remain active during temporary disruptions it monitors RTP resumption or port renewal activities and returns to ESTABLISHED upon successful media restoration.<br /> | onMediaDisrupted()      | Reconnecting                                             |
| RENEWING\_MEDIA     | This state indicates that media (RTP) is resuming or renewing after being disrupted.                                                                                                                                                                                                                                                                                                                                                                                                       | onRenewingMedia()       | Reconnecting                                             |
| ENDING              | The process of ending or dropping an ongoing or established call starts here                                                                                                                                                                                                                                                                                                                                                                                                               | -                       | -                                                        |
| ENDED               | Indicates that a call has ended completely, returning system status to idle.                                                                                                                                                                                                                                                                                                                                                                                                               | onCallEnded:<br />ENDED | -                                                        |
| -                   | This indicates that the media session has been cleaned up.                                                                                                                                                                                                                                                                                                                                                                                                                                 | onDestroyMediaSession() | -                                                        |



### Call Statistics

Application can query the call quality statistics periodically during the call by using *getStatistics()* API of the *Call* object. It provides info about codec and the call QoS parameters like packet loss, jitter and round trip delay.

### Call Details

Application can query the call details record of the call using *getCallDetails()* API of the *Call&#x20;*&#x69;nterface. The CDR provides info on callId, call state, call duration, call end reason etc. This API can be queried only for the latest call since SDK maintains only a single call context.

### Handling of PSTN calls

*ExotelVoice* has following behavior during PSTN calls:

- When a PSTN call is in progress, no outgoing calls are allowed by the voice client.
- When a PSTN call is in progress, any incoming VoIP call is automatically declined by the voice library and reported as a missed call to the application. 
- When a VoIP call is in progress and a PSTN call lands on the phone, then the VoIP call continues if the user declines or does not respond to the PSTN call. If the user accepts the PSTN call, then the VoIP call is automatically terminated by the voice client.

### Reporting Problems

The voice client library logs are stored in the application internal storage. Any issue along with the logs can be reported by app to Exotel using *uploadLogs()* API of *ExotelVoice&#x20;*&#x49;nterface. 



| // Upload logs with description from startDate and endDate<br />exotelVoiceClient.uploadLogs(startDate, endDate, description); |
| ------------------------------------------------------------------------------------------------------------------------------ |



The API triggers *onUploadLogSuccess()* or *onUpLoadLogFailure()* callback based on the success or failure of the operation.

### Reporting Call Quality Feedback

Call quality can be queried from the user and reported to the exotel platform at the end of the call.

| // Upload logs with description from startDate and endDate<br />int rating = 3<br />CallIssue issue = BACKGROUND\_NOISE<br />call.postFeedback(rating,issue); |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------- |



The rating is a quality score that can take values 1 to 5.



| 5 - Excellent quality. No issues.<br />4 - Good quality. Negligible issues.<br />3 - Average quality. Minor audio noise. <br />2 - Bad quality. Frequent choppy audio or high audio delay.<br />1 - Terrible. Unable to communicate. Call drop, No-audio or one-way audio.  |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |



The call issue is a descriptive explanation of the issue.



| NO\_ISSUE         | No issues observed                            |
| ----------------- | --------------------------------------------- |
| BACKGROUND\_NOISE | Low audio clarity due to noisy audio          |
| CHOPPY\_AUDIO     | Frequent breaks in audio or garbling in audio |
| HIGH\_LATENCY     | Significant delay in audio                    |
| NO\_AUDIO         | No audio received from far user               |
| ECHO              | Echo during the call                          |

