Added in API level 23

InCallService

abstract class InCallService : Service
kotlin.Any
   ↳ android.content.Context
   ↳ android.content.ContextWrapper
   ↳ android.app.Service
   ↳ android.telecom.InCallService

This service is implemented by an app that wishes to provide functionality for managing phone calls.

Becoming the Default Phone App

The default dialer/phone app is one which provides the in-call user interface while the device is in a call. It also provides the user with a means to initiate calls and see a history of calls on their device. A device is bundled with a system provided default dialer/phone app. The user may choose a single app to take over this role from the system app. An app which wishes to fulfill this role uses the android.app.role.RoleManager to request that they fill the android.app.role.RoleManager#ROLE_DIALER role.

The default phone app provides a user interface while the device is in a call, and the device is not in car mode (i.e. UiModeManager#getCurrentModeType() is not android.content.res.Configuration#UI_MODE_TYPE_CAR).

In order to fill the android.app.role.RoleManager#ROLE_DIALER role, an app must meet a number of requirements:

  • It must handle the Intent#ACTION_DIAL intent. This means the app must provide a dial pad UI for the user to initiate outgoing calls.
  • It must fully implement the InCallService API and provide both an incoming call UI, as well as an ongoing call UI.

Note: If the app filling the android.app.role.RoleManager#ROLE_DIALER returns a null InCallService during binding, the Telecom framework will automatically fall back to using the dialer app preloaded on the device. The system will display a notification to the user to let them know that their call was continued using the preloaded dialer app. Your app should never return a null binding; doing so means it does not fulfil the requirements of android.app.role.RoleManager#ROLE_DIALER.

Note: If your app fills android.app.role.RoleManager#ROLE_DIALER and makes changes at runtime which cause it to no longer fulfil the requirements of this role, android.app.role.RoleManager will automatically remove your app from the role and close your app. For example, if you use android.content.pm.PackageManager#setComponentEnabledSetting(ComponentName, int, int) to programmatically disable the InCallService your app declares in its manifest, your app will no longer fulfil the requirements expected of android.app.role.RoleManager#ROLE_DIALER.

The preloaded dialer will ALWAYS be used when the user places an emergency call, even if your app fills the android.app.role.RoleManager#ROLE_DIALER role. To ensure an optimal experience when placing an emergency call, the default dialer should ALWAYS use android.telecom.TelecomManager#placeCall(Uri, Bundle) to place calls (including emergency calls). This ensures that the platform is able to verify that the request came from the default dialer. If a non-preloaded dialer app uses Intent#ACTION_CALL to place an emergency call, it will be raised to the preloaded dialer app using Intent#ACTION_DIAL for confirmation; this is a suboptimal user experience.

Below is an example manifest registration for an InCallService. The meta-data TelecomManager#METADATA_IN_CALL_SERVICE_UI indicates that this particular InCallService implementation intends to replace the built-in in-call UI. The meta-data TelecomManager#METADATA_IN_CALL_SERVICE_RINGING indicates that this InCallService will play the ringtone for incoming calls. See below for more information on showing the incoming call UI and playing the ringtone in your app.

<code>&lt;service android:name="your.package.YourInCallServiceImplementation"
           android:permission="android.permission.BIND_INCALL_SERVICE"
           android:exported="true"&gt;
       &lt;meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" /&gt;
       &lt;meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING"
           android:value="true" /&gt;
       &lt;intent-filter&gt;
           &lt;action android:name="android.telecom.InCallService"/&gt;
       &lt;/intent-filter&gt;
  &lt;/service&gt;
  </code>
Note: You should NOT mark your InCallService with the attribute android:exported="false"; doing so can result in a failure to bind to your implementation during calls.

In addition to implementing the InCallService API, you must also declare an activity in your manifest which handles the Intent#ACTION_DIAL intent. The example below illustrates how this is done:

<code>&lt;activity android:name="your.package.YourDialerActivity"
            android:label="@string/yourDialerActivityLabel"&gt;
       &lt;intent-filter&gt;
            &lt;action android:name="android.intent.action.DIAL" /&gt;
            &lt;category android:name="android.intent.category.DEFAULT" /&gt;
       &lt;/intent-filter&gt;
       &lt;intent-filter&gt;
            &lt;action android:name="android.intent.action.DIAL" /&gt;
            &lt;category android:name="android.intent.category.DEFAULT" /&gt;
            &lt;data android:scheme="tel" /&gt;
       &lt;/intent-filter&gt;
  &lt;/activity&gt;
  </code>

When a user installs your application and runs it for the first time, you should use the android.app.role.RoleManager to prompt the user to see if they would like your app to be the new default phone app.

The code below shows how your app can request to become the default phone/dialer app:

<code>private static final int REQUEST_ID = 1;
 
  public void requestRole() {
      RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
      Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
      startActivityForResult(intent, REQUEST_ID);
  }
 
  &amp;#64;Override
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
      if (requestCode == REQUEST_ID) {
          if (resultCode == android.app.Activity.RESULT_OK) {
              // Your app is now the default dialer app
          } else {
              // Your app is not the default dialer app
          }
      }
  }
  </code>

Access to InCallService for Wearable Devices

    If your app is a third-party companion app and wants to access InCallService APIs, what your app could do are:

    1. Declare MANAGE_ONGOING_CALLS permission in your manifest
    2. Associate with a physical wearable device via the android.companion.CompanionDeviceManager API as a companion app. See: https://developer.android.com/guide/topics/connectivity/companion-device-pairing
    3. Implement this InCallService with BIND_INCALL_SERVICE permission

Showing the Incoming Call Notification

When your app receives a new incoming call via InCallService#onCallAdded(Call), it is responsible for displaying an incoming call UI for the incoming call. It should do this using android.app.NotificationManager APIs to post a new incoming call notification.

Where your app declares the meta-data TelecomManager#METADATA_IN_CALL_SERVICE_RINGING, it is responsible for playing the ringtone for incoming calls. Your app should create a android.app.NotificationChannel which specifies the desired ringtone. For example:

<code>
  NotificationChannel channel = new NotificationChannel(YOUR_CHANNEL_ID, "Incoming Calls",
           NotificationManager.IMPORTANCE_MAX);
  // other channel setup stuff goes here.
 
  // We'll use the default system ringtone for our incoming call notification channel.  You can
  // use your own audio resource here.
  Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
  channel.setSound(ringtoneUri, new AudioAttributes.Builder()
           // Setting the AudioAttributes is important as it identifies the purpose of your
           // notification sound.
           .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
           .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
       .build());
 
  NotificationManager mgr = getSystemService(NotificationManager.class);
  mgr.createNotificationChannel(channel);
  </code>

When your app receives a new incoming call, it creates a android.app.Notification for the incoming call and associates it with your incoming call notification channel. You can specify a android.app.PendingIntent on the notification which will launch your full screen incoming call UI. The notification manager framework will display your notification as a heads-up notification if the user is actively using the phone. When the user is not using the phone, your full-screen incoming call UI is used instead. For example:

<code><code>// Create an intent which triggers your fullscreen incoming call user interface. Intent intent = new Intent(Intent.ACTION_MAIN, null); intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClass(context, YourIncomingCallActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED); // Build the notification as an ongoing high priority item; this ensures it will show as // a heads up notification which slides down over top of the current content. final Notification.Builder builder = new Notification.Builder(context); builder.setOngoing(true); builder.setPriority(Notification.PRIORITY_HIGH); // Set notification content intent to take user to the fullscreen UI if user taps on the // notification body. builder.setContentIntent(pendingIntent); // Set full screen intent to trigger display of the fullscreen UI when the notification // manager deems it appropriate. builder.setFullScreenIntent(pendingIntent, true); // Setup notification content. builder.setSmallIcon( yourIconResourceId ); builder.setContentTitle("Your notification title"); builder.setContentText("Your notification content."); // Use builder.addAction(..) to add buttons to answer or reject the call. NotificationManager notificationManager = mContext.getSystemService( NotificationManager.class); notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, builder.build()); </code></code>

Summary

Nested classes
abstract

Used to issue commands to the Connection.VideoProvider associated with a Call.

Constants
static String

The Intent that must be declared as handled by the service.

Inherited constants
Public constructors

Public methods
Boolean

Returns if the device can support additional calls.

CallAudioState!

Obtains the current phone call audio state.

MutableList<Call!>!

Obtains the current list of Calls to be displayed by this in-call service.

CallEndpoint

Obtains the current CallEndpoint.

open Unit

Called when the available CallEndpoint changes.

open IBinder?
onBind(intent: Intent!)

open Unit

Called to bring the in-call screen to the foreground.

open Unit
onCallAdded(call: Call!)

Called when a Call has been added to this in-call session.

open Unit

Called when the audio state changes.

open Unit

Called when the current CallEndpoint changes.

open Unit

Called when a Call has been removed from this in-call session.

open Unit

Called when the ability to add more calls changes.

open Unit
onConnectionEvent(call: Call!, event: String!, extras: Bundle!)

Unused; to handle connection events issued by a ConnectionService, implement the android.telecom.Call.Callback#onConnectionEvent(Call, String, Bundle) callback.

open Unit

Called when the mute state changes.

open Unit

Called to silence the ringer if a ringing call exists.

open Boolean
onUnbind(intent: Intent!)

Unit

Request audio routing to a specific bluetooth device.

Unit

Request audio routing to a specific CallEndpoint.

Unit

Sets the audio route (speaker, bluetooth, etc...).

Unit
setMuted(state: Boolean)

Sets the microphone mute state.

Inherited functions

Constants

SERVICE_INTERFACE

Added in API level 23
static val SERVICE_INTERFACE: String

The Intent that must be declared as handled by the service.

Value: "android.telecom.InCallService"

Public constructors

InCallService

Added in API level 23
InCallService()

Public methods

canAddCall

Added in API level 23
fun canAddCall(): Boolean

Returns if the device can support additional calls.

Return
Boolean Whether the phone supports adding more calls.

getCallAudioState

Added in API level 23
Deprecated in API level 34
fun getCallAudioState(): CallAudioState!

Deprecated: Use getCurrentCallEndpoint(), onAvailableCallEndpointsChanged(java.util.List) and onMuteStateChanged(boolean) instead.

Obtains the current phone call audio state.

Return
CallAudioState! An object encapsulating the audio state. Returns null if the service is not fully initialized.

getCalls

Added in API level 23
fun getCalls(): MutableList<Call!>!

Obtains the current list of Calls to be displayed by this in-call service.

Return
MutableList<Call!>! A list of the relevant Calls.

getCurrentCallEndpoint

Added in API level 34
fun getCurrentCallEndpoint(): CallEndpoint

Obtains the current CallEndpoint.

Return
CallEndpoint An object encapsulating the CallEndpoint. This value cannot be null.

onAvailableCallEndpointsChanged

Added in API level 34
open fun onAvailableCallEndpointsChanged(availableEndpoints: MutableList<CallEndpoint!>): Unit

Called when the available CallEndpoint changes.

Parameters
availableEndpoints MutableList<CallEndpoint!>: The set of available CallEndpoint CallEndpoint. This value cannot be null.

onBind

Added in API level 23
open fun onBind(intent: Intent!): IBinder?
Parameters
intent Intent!: The Intent that was used to bind to this service, as given to android.content.Context#bindService. Note that any extras that were included with the Intent at that point will not be seen here.
Return
IBinder? Return an IBinder through which clients can call on to the service.

onBringToForeground

Added in API level 23
open fun onBringToForeground(showDialpad: Boolean): Unit

Called to bring the in-call screen to the foreground. The in-call experience should respond immediately by coming to the foreground to inform the user of the state of ongoing Calls.

Parameters
showDialpad Boolean: If true, put up the dialpad when the screen is shown.

onCallAdded

Added in API level 23
open fun onCallAdded(call: Call!): Unit

Called when a Call has been added to this in-call session. The in-call user experience should add necessary state listeners to the specified Call and immediately start to show the user information about the existence and nature of this Call. Subsequent invocations of getCalls() will include this Call.

Parameters
call Call!: A newly added Call.

onCallAudioStateChanged

Added in API level 23
Deprecated in API level 34
open fun onCallAudioStateChanged(audioState: CallAudioState!): Unit

Deprecated: Use onCallEndpointChanged(android.telecom.CallEndpoint), onAvailableCallEndpointsChanged(java.util.List) and onMuteStateChanged(boolean) instead.

Called when the audio state changes.

Parameters
audioState CallAudioState!: The new CallAudioState.

onCallEndpointChanged

Added in API level 34
open fun onCallEndpointChanged(callEndpoint: CallEndpoint): Unit

Called when the current CallEndpoint changes.

Parameters
callEndpoint CallEndpoint: The current CallEndpoint CallEndpoint. This value cannot be null.

onCallRemoved

Added in API level 23
open fun onCallRemoved(call: Call!): Unit

Called when a Call has been removed from this in-call session. The in-call user experience should remove any state listeners from the specified Call and immediately stop displaying any information about this Call. Subsequent invocations of getCalls() will no longer include this Call.

Parameters
call Call!: A newly removed Call.

onCanAddCallChanged

Added in API level 23
open fun onCanAddCallChanged(canAddCall: Boolean): Unit

Called when the ability to add more calls changes. If the phone cannot support more calls then canAddCall is set to false. If it can, then it is set to true. This can be used to control the visibility of UI to add more calls.

Parameters
canAddCall Boolean: Indicates whether an additional call can be added.

onConnectionEvent

Added in API level 25
open fun onConnectionEvent(
    call: Call!,
    event: String!,
    extras: Bundle!
): Unit

Unused; to handle connection events issued by a ConnectionService, implement the android.telecom.Call.Callback#onConnectionEvent(Call, String, Bundle) callback.

See Connection#sendConnectionEvent(String, Bundle).

Parameters
call Call!: The call the event is associated with.
event String!: The event.
extras Bundle!: Any associated extras.

onMuteStateChanged

Added in API level 34
open fun onMuteStateChanged(isMuted: Boolean): Unit

Called when the mute state changes.

Parameters
isMuted Boolean: The current mute state.

onSilenceRinger

Added in API level 24
open fun onSilenceRinger(): Unit

Called to silence the ringer if a ringing call exists.

onUnbind

Added in API level 23
open fun onUnbind(intent: Intent!): Boolean
Parameters
intent Intent!: The Intent that was used to bind to this service, as given to android.content.Context#bindService. Note that any extras that were included with the Intent at that point will not be seen here.
Return
Boolean Return true if you would like to have the service's onRebind method later called when new clients bind to it.

requestBluetoothAudio

Added in API level 28
Deprecated in API level 34
fun requestBluetoothAudio(bluetoothDevice: BluetoothDevice): Unit

Deprecated: Use requestCallEndpointChange(android.telecom.CallEndpoint,java.util.concurrent.Executor,android.os.OutcomeReceiver) instead.

Request audio routing to a specific bluetooth device. Calling this method may result in the device routing audio to a different bluetooth device than the one specified if the bluetooth stack is unable to route audio to the requested device. A list of available devices can be obtained via CallAudioState#getSupportedBluetoothDevices()

Parameters
bluetoothDevice BluetoothDevice: The bluetooth device to connect to. This value cannot be null.

requestCallEndpointChange

Added in API level 34
fun requestCallEndpointChange(
    endpoint: CallEndpoint,
    executor: Executor,
    callback: OutcomeReceiver<Void!, CallEndpointException!>
): Unit

Request audio routing to a specific CallEndpoint. Clients should not define their own CallEndpoint when requesting a change. Instead, the new endpoint should be one of the valid endpoints provided by onAvailableCallEndpointsChanged(java.util.List). When this request is honored, there will be change to the getCurrentCallEndpoint().

Parameters
endpoint CallEndpoint: The call endpoint to use. This value cannot be null.
executor Executor: The executor of where the callback will execute. This value cannot be null. Callback and listener events are dispatched through this Executor, providing an easy way to control which thread is used. To dispatch events through the main thread of your application, you can use Context.getMainExecutor(). Otherwise, provide an Executor that dispatches to an appropriate thread.
callback OutcomeReceiver<Void!, CallEndpointException!>: The callback to notify the result of the endpoint change. This value cannot be null.

setAudioRoute

Added in API level 23
Deprecated in API level 34
fun setAudioRoute(route: Int): Unit

Deprecated: Use requestCallEndpointChange(android.telecom.CallEndpoint,java.util.concurrent.Executor,android.os.OutcomeReceiver) instead.

Sets the audio route (speaker, bluetooth, etc...). When this request is honored, there will be change to the getCallAudioState().

Parameters
route Int: The audio route to use.

setMuted

Added in API level 23
fun setMuted(state: Boolean): Unit

Sets the microphone mute state. When this request is honored, there will be change to the getCallAudioState().

Parameters
state Boolean: true if the microphone should be muted; false otherwise.