The self-managed ConnectionService
API is ideal for developers of standalone calling apps which wish
to use their own custom user interface for their calls rather than relying
on the default Phone app. Using a self-managed ConnectionService helps you ensure that your app will
interoperate not only with native telephony calling on the device, but also
other standalone calling apps implementing this API. It also manages audio
routing and focus for you.
Calling Constraints
To ensure a consistent and simple calling experience for the user, the
Telecom framework enforces some constraints for calling on the device. For
example purposes, suppose the user has installed two calling apps which
implement the self-managed ConnectionService API,
FooTalk and BarTalk.
- There can only be ongoing calls in any one calling app. This constraint means the user may only be in calls in either FooTalk or BarTalk at any one time. This does not mean the user cannot be in multiple FooTalk calls, but that the user cannot be in both a FooTalk and BarTalk call at the same time.
- If the user is engaged in regular managed calls (for example, via the built in Phone or Dialer app), the user may not be in calls originating from self-managed apps. This means if the user is in a regular call using their mobile carrier, they cannot also be in a FooTalk or BarTalk call concurrently.
- Your app's calls will be disconnected by Telecom if the user dials an emergency call.
- Your app may not receive or place calls while the user is in an emergency call.
- If there is an ongoing call in another calling app when your app receives an incoming call, answering the incoming call will end any ongoing calls in the other app. You app should not display its usual incoming call user interface; the Telecom framework will display the incoming call user interface and inform the user that answering the new call will end their ongoing call(s). This means if the user is in a FooTalk call and the BarTalk app receives an incoming call, the Telecom framework will inform the user that they have a new incoming BarTalk call and that answering the BarTalk call will end their FooTalk call.
How the Self-Managed ConnectionService API Fits
In
Making use of the self-managed ConnectionService API
in your call flow involves interacting with the Telecom APIs. The following
sections describe common scenarios you will encounter, and how your app
should interact with and respond to the Telecom APIs.
Incoming Calls When There are No Calls in Other Apps
This section describes how to handle an incoming call where none of the constraints mentioned in Calling Constraints apply. This means that there are no ongoing calls, or the only ongoing calls are in your app.
- Your app receives a new incoming call using its usual means.
- Use
addNewIncomingCall(PhoneAccountHandle, Bundle)to inform Telecom of the new incoming call. - Telecom binds to your app's
ConnectionServiceimplementation and requests a new instance ofConnectionrepresenting the new incoming call usingonCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest). - Telecom informs your app via
onShowIncomingCallUi()that it should show its incoming call user interface. - Your app shows its incoming UI using a
Notificationwith associated full-screenIntent. (seeonShowIncomingCallUi()for more information). - Call
setActive()if the user accepts the incoming call, orsetDisconnected(android.telecom.DisconnectCause)anddestroy()and specifyREJECTEDif the user rejects the incoming call.
Note: This is the first edition of the Self-Managed ConnectionService API. Future enhancements are planned to remove some of these restrictions and to add additional functionality such as the ability to write calls to the system call log.
Incoming Call When There are Calls in Other Apps
This section describes how to handle an incoming call where there are ongoing calls in another calling app. As mentioned in the calling constraints, Telecom will display the incoming call UI for your incoming call in this case.
- Your app receives a new incoming call using its usual means.
- Use
addNewIncomingCall(PhoneAccountHandle, Bundle)to inform Telecom of the new incoming call. - Telecom binds to your app's
ConnectionServiceimplementation and requests a new instance ofConnectionrepresenting the new incoming call usingonCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest). - Telecom displays the incoming call UI for your incoming call.
- If the user accepts the call, Telecom calls
onAnswer(int). You should callsetActive()to indicate to Telecom that the call is now connected. - If the user rejects the call, Telecom calls
onReject(). You should callsetDisconnected(android.telecom.DisconnectCause)anddestroy()and specifyREJECTEDas theDisconnectCause.
Outgoing Calls
This section describes how your app should handle placing outgoing calls. As mentioned in calling constraints, your app may or may not be permitted to place an outgoing call at the current time. The Telecom APIs will inform your app whether it can place an outgoing call.
- The user initiates an outgoing call within your app.
- Use
placeCall(android.net.Uri, android.os.Bundle)to inform Telecom of the new outgoing call. TheUriis the address the call is being placed to. For regular phone numbers, use the {@code tel:} URI scheme. In theBundle, specify thePhoneAccountHandleof your Self-ManagedConnectionServicein theEXTRA_PHONE_ACCOUNT_HANDLEextra. If the outgoing call is a video call, specifySTATE_BIDIRECTIONALin theEXTRA_START_CALL_WITH_VIDEO_STATEextra. Note: By default, Telecom routes video calls to the speakerphone. - Telecom binds to your app's
ConnectionServiceimplementation. - If your app is not able to place an outgoing call, Telecom calls
onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest)to inform yourConnectionServicethat the call cannot be placed at the current time. Your app should inform the user that the call cannot be placed. - If you app is able to place an outgoing call, Telecom calls
onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest). Your app should return an instance of yourConnectionclass to represent the new outgoing call. See below for more information on what properties you should set on theConnection. - When the outgoing call has been connected, call
setActive()to inform Telecom that the call is now active.
Ending a Call
When a call is ended, call setDisconnected(android.telecom.DisconnectCause)
and destroy(). If the call ended because
the user terminated the call, use LOCAL. If the call ended because the other
party terminated the call, use REMOTE.
Implementing the Self-Managed ConnectionService
API
Manifest Changes and Permissions
In your app's manifest, declare that your app uses permission MANAGE_OWN_CALLS.
See the <uses-permission> reference for more information about declaring application permissions.
In your app's manifest, declare a service entry for your self-managed ConnectionService. In order for Telecom to bind to your
service, it must require the BIND_TELECOM_CONNECTION_SERVICE permission. For
example:
<service android:name="com.example.MyConnectionService"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>
See the <service> reference for more information about declaring services.
ConnectionService Implementation
Your ConnectionService implementation should override
the following methods in the ConnectionService
API:
onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest)- Telecom calls this method in response to your app callingplaceCall(android.net.Uri, android.os.Bundle)to create a new outgoing call. Your app returns a new instance of yourConnectionimplementation (see below for more information) to represent the new outgoing call. Your app should callsetConnectionProperties(int)and setPROPERTY_SELF_MANAGED. If your app supports holding calls, callsetConnectionCapabilities(int)and setCAPABILITY_HOLDandCAPABILITY_SUPPORT_HOLD. To set the name of the caller, usesetCallerDisplayName(String, int). UsePRESENTATION_ALLOWEDfor the int parameter to indicate that the caller's name should be shown. You should callconnection.setVideoState(request.getVideoState());
to ensure that the outgoing call has the appropriate video state.onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest)- When your app callsplaceCall(android.net.Uri, android.os.Bundle)and the outgoing call cannot be placed, Telecom calls this method. In response to this, your app should inform the user (for example, using an alert box or toast) that the outgoing call cannot be placed.onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)- Telecom calls this when your app callsaddNewIncomingCall(PhoneAccountHandle, Bundle)to inform Telecom of a new incoming call in your app. Your app returns a new instance of yourConnectionimplementation (see below for more inforrmation) to represent the new incoming call. Your app should callsetConnectionProperties(int)and setPROPERTY_SELF_MANAGED. If your app supports holding calls, callsetConnectionCapabilities(int)and setCAPABILITY_HOLDandCAPABILITY_SUPPORT_HOLD. To set the name of the caller, usesetCallerDisplayName(String, int). UsePRESENTATION_ALLOWEDfor the int parameter to indicate that the caller's name should be shown. You should callconnection.setVideoState(request.getVideoState());
to ensure that the incoming call has the appropriate video state.onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)- When your app callsaddNewIncomingCall(PhoneAccountHandle, Bundle)to inform Telecom of a new incoming call, but the incoming call is not permitted (see calling constraints), Telecom calls this method. Your app should silently reject the incoming call, optionally posting a notification to inform the user of the missed call.
Connection
Implementation
Your app should create an extension of the Connection
class to represent the calls in your app. You should override the following
methods in your implementation:
onShowIncomingCallUi()- Telecom calls this method when you add a new incoming call and your app should show its incoming call UI.onCallAudioStateChanged(android.telecom.CallAudioState)- Telecom calls this method to inform your app that the current audio route or mode has changed. This is called in response to your app changing the audio mode usingsetAudioRoute(int). This may also be called if the system changes the audio route (when a Bluetooth headset disconnects itself, for example).onHold()- Telecom calls this method when it wants to hold a call. In response to this, your app should hold the call and then invokesetOnHold()to inform Telecom that the call is held. Telecom may call this method when aInCallServiceshowing your call (Android Auto, for example) wants to relay a user request to hold the call.onUnhold()- Telecom calls this method when it wants to unhold a call. Once your app has unheld the call, it should invokesetActive()to inform Telecom that the call is no longer held. Telecom may call this method when aInCallServiceshowing your call (Android Auto, for example) wants to relay a user request to unhold the call.onAnswer(int)andonAnswer()- Telecom calls this method to inform your app that an incoming call should be answered. Once your app has answered the call, it should invokesetActive()to inform Telecom that the call has been answered. Telecom may call this method when your app adds a new incoming call and there is already an ongoing call in another app. As discussed previously, Telecom will display the incoming call UI on behalf of your app in these instances.onReject()- Telecom calls this method when it wants to reject an incoming call. Once your app has rejected the call, it should callsetDisconnected(android.telecom.DisconnectCause)and specifyREJECTED. Your app should then calldestroy()to inform Telecom you are done with the call. Similar toonAnswer(), Telecom will call this method when the user has rejected an incoming call from your app.onDisconnect()- Telecom calls this method when it wants to disconnect a call. Once the call has ended, your app should callsetDisconnected(android.telecom.DisconnectCause)and specifyLOCALto indicate that a user request caused the call to be disconected. Your app should then calldestroy()to inform Telecom you are done with the call. Telecom may call this method when the user has disconnected a call through anotherInCallServicesuch as Android Auto.


