1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.telecom;
18 
19 import android.annotation.CallbackExecutor;
20 import android.annotation.FlaggedApi;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SdkConstant;
25 import android.annotation.SystemApi;
26 import android.annotation.TestApi;
27 import android.app.Service;
28 import android.content.ComponentName;
29 import android.content.Intent;
30 import android.location.Location;
31 import android.net.Uri;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.IBinder;
35 import android.os.Looper;
36 import android.os.Message;
37 import android.os.OutcomeReceiver;
38 import android.os.ParcelFileDescriptor;
39 import android.os.RemoteException;
40 import android.telecom.Logging.Session;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.os.SomeArgs;
44 import com.android.internal.telecom.IConnectionService;
45 import com.android.internal.telecom.IConnectionServiceAdapter;
46 import com.android.internal.telecom.RemoteServiceCallback;
47 import com.android.server.telecom.flags.Flags;
48 
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.Collections;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.UUID;
55 import java.util.concurrent.ConcurrentHashMap;
56 import java.util.concurrent.Executor;
57 
58 /**
59  * An abstract service that should be implemented by any apps which either:
60  * <ol>
61  *     <li>Can make phone calls (VoIP or otherwise) and want those calls to be integrated into the
62  *     built-in phone app.  Referred to as a <b>system managed</b> {@link ConnectionService}.</li>
63  *     <li>Are a standalone calling app and don't want their calls to be integrated into the
64  *     built-in phone app.  Referred to as a <b>self managed</b> {@link ConnectionService}.</li>
65  * </ol>
66  * Once implemented, the {@link ConnectionService} needs to take the following steps so that Telecom
67  * will bind to it:
68  * <p>
69  * 1. <i>Registration in AndroidManifest.xml</i>
70  * <br/>
71  * <pre>
72  * &lt;service android:name="com.example.package.MyConnectionService"
73  *    android:label="@string/some_label_for_my_connection_service"
74  *    android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"&gt;
75  *  &lt;intent-filter&gt;
76  *   &lt;action android:name="android.telecom.ConnectionService" /&gt;
77  *  &lt;/intent-filter&gt;
78  * &lt;/service&gt;
79  * </pre>
80  * <p>
81  * 2. <i> Registration of {@link PhoneAccount} with {@link TelecomManager}.</i>
82  * <br/>
83  * See {@link PhoneAccount} and {@link TelecomManager#registerPhoneAccount} for more information.
84  * <p>
85  * System managed {@link ConnectionService}s must be enabled by the user in the phone app settings
86  * before Telecom will bind to them.  Self-managed {@link ConnectionService}s must declare the
87  * {@link android.Manifest.permission#MANAGE_OWN_CALLS} permission in their manifest before Telecom
88  * will bind to them.
89  * <p>
90  * Once registered and enabled by the user in the phone app settings or granted permission, telecom
91  * will bind to a {@link ConnectionService} implementation when it wants that
92  * {@link ConnectionService} to place a call or the service has indicated that is has an incoming
93  * call through {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)}. The
94  * {@link ConnectionService} can then expect a call to
95  * {@link #onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)} or
96  * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}
97  * wherein it should provide a new instance of a {@link Connection} object.  It is through this
98  * {@link Connection} object that telecom receives state updates and the {@link ConnectionService}
99  * receives call-commands such as answer, reject, hold and disconnect.
100  * <p>
101  * When there are no more live calls, telecom will unbind from the {@link ConnectionService}.
102  * <p>
103  * <h1>Self-Managed Connection Services</h1>
104  * A VoIP app can implement a {@link ConnectionService} to ensure that its calls are integrated
105  * into the Android platform.  There are numerous benefits to using the Telecom APIs for a VoIP app:
106  * <ul>
107  *     <li>Call concurrency is handled - the user is able to swap between calls in different
108  *     apps and on the mobile network.</li>
109  *     <li>Simplified audio routing - the platform provides your app with a unified list of the
110  *     audio routes which are available
111  *     (e.g. {@link android.telecom.Connection#onAvailableCallEndpointsChanged(List)}) and a
112  *     standardized way to switch audio routes
113  *     (e.g. {@link android.telecom.Connection#requestCallEndpointChange(CallEndpoint, Executor,
114  *     OutcomeReceiver)} ).</li>
115  *     <li>Bluetooth integration - your calls will be visible on and controllable via
116  *     bluetooth devices (e.g. car head units and headsets).</li>
117  *     <li>Companion device integration - wearable devices such as watches which implement an
118  *     {@link InCallService} can optionally subscribe to see self-managed calls.  Similar to a
119  *     bluetooth headunit, wearables will typically render your call using a generic call UX and
120  *     provide the user with basic call controls such as hangup, answer, reject.</li>
121  *     <li>Automotive calling experiences - Android supports automotive optimized experiences which
122  *     provides a means for calls to be controlled and viewed in an automobile; these experiences
123  *     are capable of leveraging call metadata provided by your app.</li>
124  * </ul>
125  * <h2>Registering a Phone Account</h2>
126  * Before your app can handle incoming or outgoing calls through Telecom it needs to register a
127  * {@link PhoneAccount} with Telecom indicating to the platform that your app is capable of calling.
128  * <p>
129  * Your app should create a new instance of {@link PhoneAccount} which meets the following
130  * requirements:
131  * <ul>
132  *     <li>Has {@link PhoneAccount#CAPABILITY_SELF_MANAGED} (set using
133  *     {@link PhoneAccount.Builder#setCapabilities(int)}).  This indicates to Telecom that your
134  *     app will report calls but that it provides a primary UI for the calls by itself.</li>
135  *     <li>Provide a unique identifier for the {@link PhoneAccount} via the
136  *     {@link PhoneAccountHandle#getId()} attribute.  As per the {@link PhoneAccountHandle}
137  *     documentation, you should NOT use an identifier which contains PII or other sensitive
138  *     information.  A typical choice is a UUID.</li>
139  * </ul>
140  * Your app should register the new {@link PhoneAccount} with Telecom using
141  * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}.  {@link PhoneAccount}s persist across
142  * reboot.  You can use {@link TelecomManager#getOwnSelfManagedPhoneAccounts()} to confirm the
143  * {@link PhoneAccount} you registered.  Your app should generally only register a single
144  * {@link PhoneAccount}.
145  *
146  * <h2>Implementing ConnectionService</h2>
147  * Your app uses {@link TelecomManager#placeCall(Uri, Bundle)} to start new outgoing calls and
148  * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)} to report new incoming
149  * calls.  Calling these APIs causes the Telecom stack to bind to your app's
150  * {@link ConnectionService} implementation.  Telecom will either inform your app that it cannot
151  * handle a call request at the current time (i.e. there could be an ongoing emergency call, which
152  * means your app is not allowed to handle calls at the current time), or it will ask your app to
153  * create a new instance of {@link Connection} to represent a call in your app.
154  *
155  * Your app should implement the following {@link ConnectionService} methods:
156  * <ul>
157  *     <li>{@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle,
158  *     ConnectionRequest)} - called by Telecom to ask your app to make a new {@link Connection}
159  *     to represent an outgoing call your app requested via
160  *     {@link TelecomManager#placeCall(Uri, Bundle)}.</li>
161  *     <li><{@link ConnectionService#onCreateOutgoingConnectionFailed(PhoneAccountHandle,
162  *     ConnectionRequest)} - called by Telecom to inform your app that a call it reported via
163  *     {@link TelecomManager#placeCall(Uri, Bundle)} cannot be handled at this time.  Your app
164  *     should NOT place a call at the current time.</li>
165  *     <li>{@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle,
166  *     ConnectionRequest)} - called by Telecom to ask your app to make a new {@link Connection}
167  *     to represent an incoming call your app reported via
168  *     {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)}.</li>
169  *     <li>{@link ConnectionService#onCreateIncomingConnectionFailed(PhoneAccountHandle,
170  *     ConnectionRequest)} - called by Telecom to inform your app that an incoming call it reported
171  *     via {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)} cannot be handled
172  *     at this time.  Your app should NOT post a new incoming call notification and should silently
173  *     reject the call.</li>
174  * </ul>
175  *
176  * <h2>Implementing a Connection</h2>
177  * Your app should extend the {@link Connection} class to represent calls in your app.  When you
178  * create new instances of your {@link Connection}, you should ensure the following properties are
179  * set on the new {@link Connection} instance returned by your {@link ConnectionService}:
180  * <ul>
181  *     <li>{@link Connection#setAddress(Uri, int)} - the identifier for the other party.  For
182  *     apps that user phone numbers the {@link Uri} can be a {@link PhoneAccount#SCHEME_TEL} URI
183  *     representing the phone number.</li>
184  *     <li>{@link Connection#setCallerDisplayName(String, int)} - the display name of the other
185  *     party.  This is what will be shown on Bluetooth devices and other calling surfaces such
186  *     as wearable devices.  This is particularly important for calls that do not use a phone
187  *     number to identify the caller or called party.</li>
188  *     <li>{@link Connection#setConnectionProperties(int)} - ensure you set
189  *     {@link Connection#PROPERTY_SELF_MANAGED} to identify to the platform that the call is
190  *     handled by your app.</li>
191  *     <li>{@link Connection#setConnectionCapabilities(int)} - if your app supports making calls
192  *     inactive (i.e. holding calls) you should get {@link Connection#CAPABILITY_SUPPORT_HOLD} and
193  *     {@link Connection#CAPABILITY_HOLD} to indicate to the platform that you calls can potentially
194  *     be held for concurrent calling scenarios.</li>
195  *     <li>{@link Connection#setAudioModeIsVoip(boolean)} - set to {@code true} to ensure that the
196  *     platform knows your call is a VoIP call.</li>
197  *     <li>For newly created {@link Connection} instances, do NOT change the state of your call
198  *     using {@link Connection#setActive()}, {@link Connection#setOnHold()} until the call is added
199  *     to Telecom (ie you have returned it via
200  *     {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}
201  *     or
202  *     {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}).
203  *     </li>
204  * </ul>
205  *
206  * <h2>How to Place Outgoing Calls</h2>
207  * When your app wants to place an outgoing call it calls
208  * {@link TelecomManager#placeCall(Uri, Bundle)}.  You should specify a {@link Uri} to identify
209  * who the call is being placed to, and specify the {@link PhoneAccountHandle} associated with the
210  * {@link PhoneAccount} you registered for your app using
211  * {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE} in the {@link Bundle} parameter.
212  * <p>
213  * Telecom will bind to your app's {@link ConnectionService} implementation and call either:
214  * <ul>
215  *     <li>{@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle,
216  *     ConnectionRequest)} - the {@link ConnectionRequest#getAddress()} will match the address
217  *     you specified when placing the call.  You should return a new instance of your app's
218  *     {@link Connection} class to represent the outgoing call.</li>
219  *     <li>{@link ConnectionService#onCreateOutgoingConnectionFailed(PhoneAccountHandle,
220  *     ConnectionRequest)} - your app should not place the call at this time; the call should be
221  *     cancelled and the user informed that the call cannot be placed.</li>
222  * </ul>
223  * <p>
224  * New outgoing calls will start in a {@link Connection#STATE_DIALING} state.  This state indicates
225  * that your app is in the process of connecting the call to the other party.
226  * <p>
227  * Once the other party answers the call (or it is set up successfully), your app should call
228  * {@link Connection#setActive()} to inform Telecom that the call is now active.
229  *
230  * <h2>How to Add Incoming Calls</h2>
231  * When your app receives an incoming call, it should call
232  * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)}.  Set the
233  * {@link PhoneAccountHandle} parameter to the {@link PhoneAccountHandle} associated with your
234  * app's {@link PhoneAccount}.
235  * <p>
236  * Telecom will bind to your app's {@link ConnectionService} implementation and call either:
237  * <ul>
238  *     <li>{@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle,
239  *     ConnectionRequest)} - You should return a new instance of your app's
240  *     {@link Connection} class to represent the incoming call.</li>
241  *     <li>{@link ConnectionService#onCreateIncomingConnectionFailed(PhoneAccountHandle,
242  *     ConnectionRequest)} - your app should not receive the call at this time; the call should be
243  *     rejected silently; the user may be informed of a missed call.</li>
244  * </ul>
245  * <p>
246  * New incoming calls will start with a {@link Connection#STATE_RINGING} state.  This state
247  * indicates that your app has a new incoming call pending.  Telecom will NOT play a ringtone or
248  * post a notification for your app.  It is up to your app to post an incoming call notification
249  * with an associated ringtone.  Telecom will call {@link Connection#onShowIncomingCallUi()} on the
250  * {@link Connection} when your app can post its incoming call notification.  See
251  * {@link Connection#onShowIncomingCallUi() the docs} for more information on how to post the
252  * notification.
253  * <p>
254  * Your incoming call notification (or full screen UI) will typically have an "answer" and "decline"
255  * action which the user chooses.  When your app receives the "answer" or "decline"
256  * {@link android.app.PendingIntent}, you should must call either {@link Connection#setActive()} to
257  * inform Telecom that the call was answered, or
258  * {@link Connection#setDisconnected(DisconnectCause)} to inform Telecom that the call was rejected.
259  * If the call was rejected, supply an instance of {@link DisconnectCause} with
260  * {@link DisconnectCause#REJECTED}, and then call {@link Connection#destroy()}.
261  * <p>
262  * In addition to handling requests to answer or decline the call via notification actions, your
263  * app should also be implement the {@link Connection#onAnswer(int)} and
264  * {@link Connection#onAnswer()} methods on the {@link Connection}.  These will be raised if the
265  * user answers your call via a Bluetooth device or another device like a wearable or automotive
266  * calling UX.  In response, your app should call {@link Connection#setActive()} to inform Telecom
267  * that the call was answered.
268  * <p>
269  * Additionally, your app should implement {@link Connection#onReject()} to handle requests to
270  * reject the call which are raised via Bluetooth or other calling surfaces.  Your app should call
271  * {@link Connection#setDisconnected(DisconnectCause)} and supply an instance of
272  * {@link DisconnectCause} with {@link DisconnectCause#REJECTED} in this case.
273  *
274  * <h2>Ending Calls</h2>
275  * When an ongoing active call (incoming or outgoing) has ended, your app is responsible for
276  * informing Telecom that the call ended.
277  * <p>
278  * Your app calls:
279  * <ul>
280  *     <li>{@link Connection#setDisconnected(DisconnectCause)} - this informs Telecom that the
281  *     call has terminated.  You should provide a new instance of {@link DisconnectCause} with
282  *     either {@link DisconnectCause#LOCAL} or {@link DisconnectCause#REMOTE} to indicate where the
283  *     call disconnection took place.  {@link DisconnectCause#LOCAL} indicates that the call
284  *     terminated in your app on the current device (i.e. via user action), where
285  *     {@link DisconnectCause#REMOTE} indicates that the call terminates on the remote device.</li>
286  *     <li>{@link Connection#destroy()} - this informs Telecom that your call instance can be
287  *     cleaned up.  You should always call this when you are finished with a call.</li>
288  * </ul>
289  * <p>
290  * Similar to answering incoming calls, requests to disconnect your call may originate from outside
291  * your app.  You can handle these by implementing {@link Connection#onDisconnect()}.  Your app
292  * should call {@link Connection#setDisconnected(DisconnectCause)} with an instance of
293  * {@link DisconnectCause} and reason {@link DisconnectCause#LOCAL} to indicate to Telecom that your
294  * app has disconnected the call as requested based on the user's request.
295  *
296  * <h2>Holding and Unholding Calls</h2>
297  * When your app specifies {@link Connection#CAPABILITY_SUPPORT_HOLD} and
298  * {@link Connection#CAPABILITY_HOLD} on your {@link Connection} instance, it is telling Telecom
299  * that your calls can be placed into a suspended, or "held" state if required.  If your app
300  * supports holding its calls, it will be possible for the user to switch between calls in your app
301  * and holdable calls in another app or on the mobile network.  If your app does not support
302  * holding its calls, you may receive a request to disconnect the call from Telecom if the user
303  * opts to answer an incoming call in another app or on the mobile network; this ensures that the
304  * user can only be in one call at a time.
305  * <p>
306  * Your app is free to change a call between the held and active state using
307  * {@link Connection#setOnHold()} and {@link Connection#setActive()}.
308  * <p>
309  * Your app may receive a request from Telecom to hold or unhold a call via
310  * {@link Connection#onHold()} and {@link Connection#onUnhold()}.  Telecom can ask your app to
311  * hold or unhold its {@link Connection} either if the user requests this action through another
312  * calling surface such as Bluetooth, or if the user answers or switches to a call in a different
313  * app or on the mobile network.
314  * <p>
315  * When your app receives an {@link Connection#onHold()} it must call {@link Connection#setOnHold()}
316  * to inform Telecom that the call has been held successfully.
317  * <p>
318  * When your app receives an {@link Connection#onUnhold()} it must call
319  * {@link Connection#setActive()} to inform Telecom that the call has been resumed successfully.
320  */
321 public abstract class ConnectionService extends Service {
322     /**
323      * The {@link Intent} that must be declared as handled by the service.
324      */
325     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
326     public static final String SERVICE_INTERFACE = "android.telecom.ConnectionService";
327 
328     /**
329      * Boolean extra used by Telecom to inform a {@link ConnectionService} that the purpose of it
330      * being asked to create a new outgoing {@link Connection} is to perform a handover of an
331      * ongoing call on the device from another {@link PhoneAccount}/{@link ConnectionService}.  Will
332      * be specified in the {@link ConnectionRequest#getExtras()} passed by Telecom when
333      * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} is called.
334      * <p>
335      * When your {@link ConnectionService} receives this extra, it should communicate the fact that
336      * this is a handover to the other device's matching {@link ConnectionService}.  That
337      * {@link ConnectionService} will continue the handover using
338      * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)}, specifying
339      * {@link TelecomManager#EXTRA_IS_HANDOVER}.  Telecom will match the phone numbers of the
340      * handover call on the other device with ongoing calls for {@link ConnectionService}s which
341      * support {@link PhoneAccount#EXTRA_SUPPORTS_HANDOVER_FROM}.
342      * @hide
343      */
344     public static final String EXTRA_IS_HANDOVER = TelecomManager.EXTRA_IS_HANDOVER;
345 
346     // Flag controlling whether PII is emitted into the logs
347     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
348 
349     // Session Definitions
350     private static final String SESSION_HANDLER = "H.";
351     private static final String SESSION_ADD_CS_ADAPTER = "CS.aCSA";
352     private static final String SESSION_REMOVE_CS_ADAPTER = "CS.rCSA";
353     private static final String SESSION_CREATE_CONN = "CS.crCo";
354     private static final String SESSION_CREATE_CONN_COMPLETE = "CS.crCoC";
355     private static final String SESSION_CREATE_CONN_FAILED = "CS.crCoF";
356     private static final String SESSION_ABORT = "CS.ab";
357     private static final String SESSION_ANSWER = "CS.an";
358     private static final String SESSION_ANSWER_VIDEO = "CS.anV";
359     private static final String SESSION_DEFLECT = "CS.def";
360     private static final String SESSION_TRANSFER = "CS.trans";
361     private static final String SESSION_CONSULTATIVE_TRANSFER = "CS.cTrans";
362     private static final String SESSION_REJECT = "CS.r";
363     private static final String SESSION_REJECT_MESSAGE = "CS.rWM";
364     private static final String SESSION_SILENCE = "CS.s";
365     private static final String SESSION_DISCONNECT = "CS.d";
366     private static final String SESSION_HOLD = "CS.h";
367     private static final String SESSION_UNHOLD = "CS.u";
368     private static final String SESSION_CALL_AUDIO_SC = "CS.cASC";
369     private static final String SESSION_USING_ALTERNATIVE_UI = "CS.uAU";
370     private static final String SESSION_TRACKED_BY_NON_UI_SERVICE = "CS.tBNUS";
371     private static final String SESSION_PLAY_DTMF = "CS.pDT";
372     private static final String SESSION_STOP_DTMF = "CS.sDT";
373     private static final String SESSION_CONFERENCE = "CS.c";
374     private static final String SESSION_SPLIT_CONFERENCE = "CS.sFC";
375     private static final String SESSION_MERGE_CONFERENCE = "CS.mC";
376     private static final String SESSION_SWAP_CONFERENCE = "CS.sC";
377     private static final String SESSION_ADD_PARTICIPANT = "CS.aP";
378     private static final String SESSION_POST_DIAL_CONT = "CS.oPDC";
379     private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
380     private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
381     private static final String SESSION_CALL_FILTERING_COMPLETED = "CS.oCFC";
382     private static final String SESSION_HANDOVER_COMPLETE = "CS.hC";
383     private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
384     private static final String SESSION_START_RTT = "CS.+RTT";
385     private static final String SESSION_UPDATE_RTT_PIPES = "CS.uRTT";
386     private static final String SESSION_STOP_RTT = "CS.-RTT";
387     private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR";
388     private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL";
389     private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG";
390     private static final String SESSION_HANDOVER_FAILED = "CS.haF";
391     private static final String SESSION_CREATE_CONF = "CS.crConf";
392     private static final String SESSION_CREATE_CONF_COMPLETE = "CS.crConfC";
393     private static final String SESSION_CREATE_CONF_FAILED = "CS.crConfF";
394     private static final String SESSION_CALL_ENDPOINT_CHANGED = "CS.oCEC";
395     private static final String SESSION_AVAILABLE_CALL_ENDPOINTS_CHANGED = "CS.oACEC";
396     private static final String SESSION_MUTE_STATE_CHANGED = "CS.oMSC";
397 
398     private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
399     private static final int MSG_CREATE_CONNECTION = 2;
400     private static final int MSG_ABORT = 3;
401     private static final int MSG_ANSWER = 4;
402     private static final int MSG_REJECT = 5;
403     private static final int MSG_DISCONNECT = 6;
404     private static final int MSG_HOLD = 7;
405     private static final int MSG_UNHOLD = 8;
406     private static final int MSG_ON_CALL_AUDIO_STATE_CHANGED = 9;
407     private static final int MSG_PLAY_DTMF_TONE = 10;
408     private static final int MSG_STOP_DTMF_TONE = 11;
409     private static final int MSG_CONFERENCE = 12;
410     private static final int MSG_SPLIT_FROM_CONFERENCE = 13;
411     private static final int MSG_ON_POST_DIAL_CONTINUE = 14;
412     private static final int MSG_REMOVE_CONNECTION_SERVICE_ADAPTER = 16;
413     private static final int MSG_ANSWER_VIDEO = 17;
414     private static final int MSG_MERGE_CONFERENCE = 18;
415     private static final int MSG_SWAP_CONFERENCE = 19;
416     private static final int MSG_REJECT_WITH_MESSAGE = 20;
417     private static final int MSG_SILENCE = 21;
418     private static final int MSG_PULL_EXTERNAL_CALL = 22;
419     private static final int MSG_SEND_CALL_EVENT = 23;
420     private static final int MSG_ON_EXTRAS_CHANGED = 24;
421     private static final int MSG_CREATE_CONNECTION_FAILED = 25;
422     private static final int MSG_ON_START_RTT = 26;
423     private static final int MSG_ON_STOP_RTT = 27;
424     private static final int MSG_RTT_UPGRADE_RESPONSE = 28;
425     private static final int MSG_CREATE_CONNECTION_COMPLETE = 29;
426     private static final int MSG_CONNECTION_SERVICE_FOCUS_LOST = 30;
427     private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31;
428     private static final int MSG_HANDOVER_FAILED = 32;
429     private static final int MSG_HANDOVER_COMPLETE = 33;
430     private static final int MSG_DEFLECT = 34;
431     private static final int MSG_CREATE_CONFERENCE = 35;
432     private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36;
433     private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
434     private static final int MSG_REJECT_WITH_REASON = 38;
435     private static final int MSG_ADD_PARTICIPANT = 39;
436     private static final int MSG_EXPLICIT_CALL_TRANSFER = 40;
437     private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41;
438     private static final int MSG_ON_CALL_FILTERING_COMPLETED = 42;
439     private static final int MSG_ON_USING_ALTERNATIVE_UI = 43;
440     private static final int MSG_ON_TRACKED_BY_NON_UI_SERVICE = 44;
441     private static final int MSG_ON_CALL_ENDPOINT_CHANGED = 45;
442     private static final int MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED = 46;
443     private static final int MSG_ON_MUTE_STATE_CHANGED = 47;
444 
445     private static Connection sNullConnection;
446 
447     private final Map<String, Connection> mConnectionById = new ConcurrentHashMap<>();
448     private final Map<Connection, String> mIdByConnection = new ConcurrentHashMap<>();
449     private final Map<String, Conference> mConferenceById = new ConcurrentHashMap<>();
450     private final Map<Conference, String> mIdByConference = new ConcurrentHashMap<>();
451     private final RemoteConnectionManager mRemoteConnectionManager =
452             new RemoteConnectionManager(this);
453     private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>();
454     private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
455 
456     private boolean mAreAccountsInitialized = false;
457     private Conference sNullConference;
458     private Object mIdSyncRoot = new Object();
459     private int mId = 0;
460 
461     private final IBinder mBinder = new IConnectionService.Stub() {
462         @Override
463         public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter,
464                 Session.Info sessionInfo) {
465             Log.startSession(sessionInfo, SESSION_ADD_CS_ADAPTER);
466             try {
467                 SomeArgs args = SomeArgs.obtain();
468                 args.arg1 = adapter;
469                 args.arg2 = Log.createSubsession();
470                 mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, args).sendToTarget();
471             } finally {
472                 Log.endSession();
473             }
474         }
475 
476         public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter,
477                 Session.Info sessionInfo) {
478             Log.startSession(sessionInfo, SESSION_REMOVE_CS_ADAPTER);
479             try {
480                 SomeArgs args = SomeArgs.obtain();
481                 args.arg1 = adapter;
482                 args.arg2 = Log.createSubsession();
483                 mHandler.obtainMessage(MSG_REMOVE_CONNECTION_SERVICE_ADAPTER, args).sendToTarget();
484             } finally {
485                 Log.endSession();
486             }
487         }
488 
489         @Override
490         public void createConnection(
491                 PhoneAccountHandle connectionManagerPhoneAccount,
492                 String id,
493                 ConnectionRequest request,
494                 boolean isIncoming,
495                 boolean isUnknown,
496                 Session.Info sessionInfo) {
497             Log.startSession(sessionInfo, SESSION_CREATE_CONN);
498             try {
499                 SomeArgs args = SomeArgs.obtain();
500                 args.arg1 = connectionManagerPhoneAccount;
501                 args.arg2 = id;
502                 args.arg3 = request;
503                 args.arg4 = Log.createSubsession();
504                 args.argi1 = isIncoming ? 1 : 0;
505                 args.argi2 = isUnknown ? 1 : 0;
506                 mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
507             } finally {
508                 Log.endSession();
509             }
510         }
511 
512         @Override
513         public void createConnectionComplete(String id, Session.Info sessionInfo) {
514             Log.startSession(sessionInfo, SESSION_CREATE_CONN_COMPLETE);
515             try {
516                 SomeArgs args = SomeArgs.obtain();
517                 args.arg1 = id;
518                 args.arg2 = Log.createSubsession();
519                 mHandler.obtainMessage(MSG_CREATE_CONNECTION_COMPLETE, args).sendToTarget();
520             } finally {
521                 Log.endSession();
522             }
523         }
524 
525         @Override
526         public void createConnectionFailed(
527                 PhoneAccountHandle connectionManagerPhoneAccount,
528                 String callId,
529                 ConnectionRequest request,
530                 boolean isIncoming,
531                 Session.Info sessionInfo) {
532             Log.startSession(sessionInfo, SESSION_CREATE_CONN_FAILED);
533             try {
534                 SomeArgs args = SomeArgs.obtain();
535                 args.arg1 = callId;
536                 args.arg2 = request;
537                 args.arg3 = Log.createSubsession();
538                 args.arg4 = connectionManagerPhoneAccount;
539                 args.argi1 = isIncoming ? 1 : 0;
540                 mHandler.obtainMessage(MSG_CREATE_CONNECTION_FAILED, args).sendToTarget();
541             } finally {
542                 Log.endSession();
543             }
544         }
545 
546         @Override
547         public void createConference(
548                 PhoneAccountHandle connectionManagerPhoneAccount,
549                 String id,
550                 ConnectionRequest request,
551                 boolean isIncoming,
552                 boolean isUnknown,
553                 Session.Info sessionInfo) {
554             Log.startSession(sessionInfo, SESSION_CREATE_CONF);
555             try {
556                 SomeArgs args = SomeArgs.obtain();
557                 args.arg1 = connectionManagerPhoneAccount;
558                 args.arg2 = id;
559                 args.arg3 = request;
560                 args.arg4 = Log.createSubsession();
561                 args.argi1 = isIncoming ? 1 : 0;
562                 args.argi2 = isUnknown ? 1 : 0;
563                 mHandler.obtainMessage(MSG_CREATE_CONFERENCE, args).sendToTarget();
564             } finally {
565                 Log.endSession();
566             }
567         }
568 
569         @Override
570         public void createConferenceComplete(String id, Session.Info sessionInfo) {
571             Log.startSession(sessionInfo, SESSION_CREATE_CONF_COMPLETE);
572             try {
573                 SomeArgs args = SomeArgs.obtain();
574                 args.arg1 = id;
575                 args.arg2 = Log.createSubsession();
576                 mHandler.obtainMessage(MSG_CREATE_CONFERENCE_COMPLETE, args).sendToTarget();
577             } finally {
578                 Log.endSession();
579             }
580         }
581 
582         @Override
583         public void createConferenceFailed(
584                 PhoneAccountHandle connectionManagerPhoneAccount,
585                 String callId,
586                 ConnectionRequest request,
587                 boolean isIncoming,
588                 Session.Info sessionInfo) {
589             Log.startSession(sessionInfo, SESSION_CREATE_CONF_FAILED);
590             try {
591                 SomeArgs args = SomeArgs.obtain();
592                 args.arg1 = callId;
593                 args.arg2 = request;
594                 args.arg3 = Log.createSubsession();
595                 args.arg4 = connectionManagerPhoneAccount;
596                 args.argi1 = isIncoming ? 1 : 0;
597                 mHandler.obtainMessage(MSG_CREATE_CONFERENCE_FAILED, args).sendToTarget();
598             } finally {
599                 Log.endSession();
600             }
601         }
602 
603         @Override
604         public void handoverFailed(String callId, ConnectionRequest request, int reason,
605                                    Session.Info sessionInfo) {
606             Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED);
607             try {
608                 SomeArgs args = SomeArgs.obtain();
609                 args.arg1 = callId;
610                 args.arg2 = request;
611                 args.arg3 = Log.createSubsession();
612                 args.arg4 = reason;
613                 mHandler.obtainMessage(MSG_HANDOVER_FAILED, args).sendToTarget();
614             } finally {
615                 Log.endSession();
616             }
617         }
618 
619         @Override
620         public void handoverComplete(String callId, Session.Info sessionInfo) {
621             Log.startSession(sessionInfo, SESSION_HANDOVER_COMPLETE);
622             try {
623                 SomeArgs args = SomeArgs.obtain();
624                 args.arg1 = callId;
625                 args.arg2 = Log.createSubsession();
626                 mHandler.obtainMessage(MSG_HANDOVER_COMPLETE, args).sendToTarget();
627             } finally {
628                 Log.endSession();
629             }
630         }
631 
632         @Override
633         public void abort(String callId, Session.Info sessionInfo) {
634             Log.startSession(sessionInfo, SESSION_ABORT);
635             try {
636                 SomeArgs args = SomeArgs.obtain();
637                 args.arg1 = callId;
638                 args.arg2 = Log.createSubsession();
639                 mHandler.obtainMessage(MSG_ABORT, args).sendToTarget();
640             } finally {
641                 Log.endSession();
642             }
643         }
644 
645         @Override
646         public void answerVideo(String callId, int videoState, Session.Info sessionInfo) {
647             Log.startSession(sessionInfo, SESSION_ANSWER_VIDEO);
648             try {
649                 SomeArgs args = SomeArgs.obtain();
650                 args.arg1 = callId;
651                 args.arg2 = Log.createSubsession();
652                 args.argi1 = videoState;
653                 mHandler.obtainMessage(MSG_ANSWER_VIDEO, args).sendToTarget();
654             } finally {
655                 Log.endSession();
656             }
657         }
658 
659         @Override
660         public void answer(String callId, Session.Info sessionInfo) {
661             Log.startSession(sessionInfo, SESSION_ANSWER);
662             try {
663                 SomeArgs args = SomeArgs.obtain();
664                 args.arg1 = callId;
665                 args.arg2 = Log.createSubsession();
666                 mHandler.obtainMessage(MSG_ANSWER, args).sendToTarget();
667             } finally {
668                 Log.endSession();
669             }
670         }
671 
672         @Override
673         public void deflect(String callId, Uri address, Session.Info sessionInfo) {
674             Log.startSession(sessionInfo, SESSION_DEFLECT);
675             try {
676                 SomeArgs args = SomeArgs.obtain();
677                 args.arg1 = callId;
678                 args.arg2 = address;
679                 args.arg3 = Log.createSubsession();
680                 mHandler.obtainMessage(MSG_DEFLECT, args).sendToTarget();
681             } finally {
682                 Log.endSession();
683             }
684         }
685 
686         @Override
687         public void reject(String callId, Session.Info sessionInfo) {
688             Log.startSession(sessionInfo, SESSION_REJECT);
689             try {
690                 SomeArgs args = SomeArgs.obtain();
691                 args.arg1 = callId;
692                 args.arg2 = Log.createSubsession();
693                 mHandler.obtainMessage(MSG_REJECT, args).sendToTarget();
694             } finally {
695                 Log.endSession();
696             }
697         }
698 
699         @Override
700         public void rejectWithReason(String callId,
701                 @android.telecom.Call.RejectReason int rejectReason, Session.Info sessionInfo) {
702             Log.startSession(sessionInfo, SESSION_REJECT);
703             try {
704                 SomeArgs args = SomeArgs.obtain();
705                 args.arg1 = callId;
706                 args.argi1 = rejectReason;
707                 args.arg2 = Log.createSubsession();
708                 mHandler.obtainMessage(MSG_REJECT_WITH_REASON, args).sendToTarget();
709             } finally {
710                 Log.endSession();
711             }
712         }
713 
714         @Override
715         public void rejectWithMessage(String callId, String message, Session.Info sessionInfo) {
716             Log.startSession(sessionInfo, SESSION_REJECT_MESSAGE);
717             try {
718                 SomeArgs args = SomeArgs.obtain();
719                 args.arg1 = callId;
720                 args.arg2 = message;
721                 args.arg3 = Log.createSubsession();
722                 mHandler.obtainMessage(MSG_REJECT_WITH_MESSAGE, args).sendToTarget();
723             } finally {
724                 Log.endSession();
725             }
726         }
727 
728         @Override
729         public void transfer(@NonNull String callId, @NonNull Uri number,
730                 boolean isConfirmationRequired, Session.Info sessionInfo) {
731             Log.startSession(sessionInfo, SESSION_TRANSFER);
732             try {
733                 SomeArgs args = SomeArgs.obtain();
734                 args.arg1 = callId;
735                 args.arg2 = number;
736                 args.argi1 = isConfirmationRequired ? 1 : 0;
737                 args.arg3 = Log.createSubsession();
738                 mHandler.obtainMessage(MSG_EXPLICIT_CALL_TRANSFER, args).sendToTarget();
739             } finally {
740                 Log.endSession();
741             }
742         }
743 
744         @Override
745         public void consultativeTransfer(@NonNull String callId, @NonNull String otherCallId,
746                 Session.Info sessionInfo) {
747             Log.startSession(sessionInfo, SESSION_CONSULTATIVE_TRANSFER);
748             try {
749                 SomeArgs args = SomeArgs.obtain();
750                 args.arg1 = callId;
751                 args.arg2 = otherCallId;
752                 args.arg3 = Log.createSubsession();
753                 mHandler.obtainMessage(
754                         MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE, args).sendToTarget();
755             } finally {
756                 Log.endSession();
757             }
758         }
759 
760         @Override
761         public void silence(String callId, Session.Info sessionInfo) {
762             Log.startSession(sessionInfo, SESSION_SILENCE);
763             try {
764                 SomeArgs args = SomeArgs.obtain();
765                 args.arg1 = callId;
766                 args.arg2 = Log.createSubsession();
767                 mHandler.obtainMessage(MSG_SILENCE, args).sendToTarget();
768             } finally {
769                 Log.endSession();
770             }
771         }
772 
773         @Override
774         public void disconnect(String callId, Session.Info sessionInfo) {
775             Log.startSession(sessionInfo, SESSION_DISCONNECT);
776             try {
777                 SomeArgs args = SomeArgs.obtain();
778                 args.arg1 = callId;
779                 args.arg2 = Log.createSubsession();
780                 mHandler.obtainMessage(MSG_DISCONNECT, args).sendToTarget();
781             } finally {
782                 Log.endSession();
783             }
784         }
785 
786         @Override
787         public void hold(String callId, Session.Info sessionInfo) {
788             Log.startSession(sessionInfo, SESSION_HOLD);
789             try {
790                 SomeArgs args = SomeArgs.obtain();
791                 args.arg1 = callId;
792                 args.arg2 = Log.createSubsession();
793                 mHandler.obtainMessage(MSG_HOLD, args).sendToTarget();
794             } finally {
795                 Log.endSession();
796             }
797         }
798 
799         @Override
800         public void unhold(String callId, Session.Info sessionInfo) {
801             Log.startSession(sessionInfo, SESSION_UNHOLD);
802             try {
803                 SomeArgs args = SomeArgs.obtain();
804                 args.arg1 = callId;
805                 args.arg2 = Log.createSubsession();
806                 mHandler.obtainMessage(MSG_UNHOLD, args).sendToTarget();
807             } finally {
808                 Log.endSession();
809             }
810         }
811 
812         @Override
813         public void onCallAudioStateChanged(String callId, CallAudioState callAudioState,
814                 Session.Info sessionInfo) {
815             Log.startSession(sessionInfo, SESSION_CALL_AUDIO_SC);
816             try {
817                 SomeArgs args = SomeArgs.obtain();
818                 args.arg1 = callId;
819                 args.arg2 = callAudioState;
820                 args.arg3 = Log.createSubsession();
821                 mHandler.obtainMessage(MSG_ON_CALL_AUDIO_STATE_CHANGED, args).sendToTarget();
822             } finally {
823                 Log.endSession();
824             }
825         }
826 
827         @Override
828         public void onCallEndpointChanged(String callId, CallEndpoint callEndpoint,
829                 Session.Info sessionInfo) {
830             Log.startSession(sessionInfo, SESSION_CALL_ENDPOINT_CHANGED);
831             try {
832                 SomeArgs args = SomeArgs.obtain();
833                 args.arg1 = callId;
834                 args.arg2 = callEndpoint;
835                 args.arg3 = Log.createSubsession();
836                 mHandler.obtainMessage(MSG_ON_CALL_ENDPOINT_CHANGED, args).sendToTarget();
837             } finally {
838                 Log.endSession();
839             }
840         }
841 
842         @Override
843         public void onAvailableCallEndpointsChanged(String callId,
844                 List<CallEndpoint> availableCallEndpoints, Session.Info sessionInfo) {
845             Log.startSession(sessionInfo, SESSION_AVAILABLE_CALL_ENDPOINTS_CHANGED);
846             try {
847                 SomeArgs args = SomeArgs.obtain();
848                 args.arg1 = callId;
849                 args.arg2 = availableCallEndpoints;
850                 args.arg3 = Log.createSubsession();
851                 mHandler.obtainMessage(MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED, args)
852                        .sendToTarget();
853             } finally {
854                 Log.endSession();
855             }
856         }
857 
858         @Override
859         public void onMuteStateChanged(String callId, boolean isMuted, Session.Info sessionInfo) {
860             Log.startSession(sessionInfo, SESSION_MUTE_STATE_CHANGED);
861             try {
862                 SomeArgs args = SomeArgs.obtain();
863                 args.arg1 = callId;
864                 args.arg2 = isMuted;
865                 args.arg3 = Log.createSubsession();
866                 mHandler.obtainMessage(MSG_ON_MUTE_STATE_CHANGED, args).sendToTarget();
867             } finally {
868                 Log.endSession();
869             }
870         }
871 
872         @Override
873         public void onUsingAlternativeUi(String callId, boolean usingAlternativeUiShowing,
874                 Session.Info sessionInfo) {
875             Log.startSession(sessionInfo, SESSION_USING_ALTERNATIVE_UI);
876             try {
877                 SomeArgs args = SomeArgs.obtain();
878                 args.arg1 = callId;
879                 args.arg2 = usingAlternativeUiShowing;
880                 args.arg3 = Log.createSubsession();
881                 mHandler.obtainMessage(MSG_ON_USING_ALTERNATIVE_UI, args).sendToTarget();
882             } finally {
883                 Log.endSession();
884             }
885         }
886 
887         @Override
888         public void onTrackedByNonUiService(String callId, boolean isTracked,
889                 Session.Info sessionInfo) {
890             Log.startSession(sessionInfo, SESSION_TRACKED_BY_NON_UI_SERVICE);
891             try {
892                 SomeArgs args = SomeArgs.obtain();
893                 args.arg1 = callId;
894                 args.arg2 = isTracked;
895                 args.arg3 = Log.createSubsession();
896                 mHandler.obtainMessage(MSG_ON_TRACKED_BY_NON_UI_SERVICE, args).sendToTarget();
897             } finally {
898                 Log.endSession();
899             }
900         }
901 
902         @Override
903         public void playDtmfTone(String callId, char digit, Session.Info sessionInfo) {
904             Log.startSession(sessionInfo, SESSION_PLAY_DTMF);
905             try {
906                 SomeArgs args = SomeArgs.obtain();
907                 args.arg1 = digit;
908                 args.arg2 = callId;
909                 args.arg3 = Log.createSubsession();
910                 mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, args).sendToTarget();
911             } finally {
912                 Log.endSession();
913             }
914         }
915 
916         @Override
917         public void stopDtmfTone(String callId, Session.Info sessionInfo) {
918             Log.startSession(sessionInfo, SESSION_STOP_DTMF);
919             try {
920                 SomeArgs args = SomeArgs.obtain();
921                 args.arg1 = callId;
922                 args.arg2 = Log.createSubsession();
923                 mHandler.obtainMessage(MSG_STOP_DTMF_TONE, args).sendToTarget();
924             } finally {
925                 Log.endSession();
926             }
927         }
928 
929         @Override
930         public void conference(String callId1, String callId2, Session.Info sessionInfo) {
931             Log.startSession(sessionInfo, SESSION_CONFERENCE);
932             try {
933                 SomeArgs args = SomeArgs.obtain();
934                 args.arg1 = callId1;
935                 args.arg2 = callId2;
936                 args.arg3 = Log.createSubsession();
937                 mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
938             } finally {
939                 Log.endSession();
940             }
941         }
942 
943         @Override
944         public void splitFromConference(String callId, Session.Info sessionInfo) {
945             Log.startSession(sessionInfo, SESSION_SPLIT_CONFERENCE);
946             try {
947                 SomeArgs args = SomeArgs.obtain();
948                 args.arg1 = callId;
949                 args.arg2 = Log.createSubsession();
950                 mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget();
951             } finally {
952                 Log.endSession();
953             }
954         }
955 
956         @Override
957         public void mergeConference(String callId, Session.Info sessionInfo) {
958             Log.startSession(sessionInfo, SESSION_MERGE_CONFERENCE);
959             try {
960                 SomeArgs args = SomeArgs.obtain();
961                 args.arg1 = callId;
962                 args.arg2 = Log.createSubsession();
963                 mHandler.obtainMessage(MSG_MERGE_CONFERENCE, args).sendToTarget();
964             } finally {
965                 Log.endSession();
966             }
967         }
968 
969         @Override
970         public void swapConference(String callId, Session.Info sessionInfo) {
971             Log.startSession(sessionInfo, SESSION_SWAP_CONFERENCE);
972             try {
973                 SomeArgs args = SomeArgs.obtain();
974                 args.arg1 = callId;
975                 args.arg2 = Log.createSubsession();
976                 mHandler.obtainMessage(MSG_SWAP_CONFERENCE, args).sendToTarget();
977             } finally {
978                 Log.endSession();
979             }
980         }
981 
982         @Override
983         public void addConferenceParticipants(String callId, List<Uri> participants,
984                 Session.Info sessionInfo) {
985             Log.startSession(sessionInfo, SESSION_ADD_PARTICIPANT);
986             try {
987                 SomeArgs args = SomeArgs.obtain();
988                 args.arg1 = callId;
989                 args.arg2 = participants;
990                 args.arg3 = Log.createSubsession();
991                 mHandler.obtainMessage(MSG_ADD_PARTICIPANT, args).sendToTarget();
992             } finally {
993                 Log.endSession();
994             }
995         }
996 
997         @Override
998         public void onPostDialContinue(String callId, boolean proceed, Session.Info sessionInfo) {
999             Log.startSession(sessionInfo, SESSION_POST_DIAL_CONT);
1000             try {
1001                 SomeArgs args = SomeArgs.obtain();
1002                 args.arg1 = callId;
1003                 args.arg2 = Log.createSubsession();
1004                 args.argi1 = proceed ? 1 : 0;
1005                 mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
1006             } finally {
1007                 Log.endSession();
1008             }
1009         }
1010 
1011         @Override
1012         public void pullExternalCall(String callId, Session.Info sessionInfo) {
1013             Log.startSession(sessionInfo, SESSION_PULL_EXTERNAL_CALL);
1014             try {
1015                 SomeArgs args = SomeArgs.obtain();
1016                 args.arg1 = callId;
1017                 args.arg2 = Log.createSubsession();
1018                 mHandler.obtainMessage(MSG_PULL_EXTERNAL_CALL, args).sendToTarget();
1019             } finally {
1020                 Log.endSession();
1021             }
1022         }
1023 
1024         @Override
1025         public void sendCallEvent(String callId, String event, Bundle extras,
1026                 Session.Info sessionInfo) {
1027             Log.startSession(sessionInfo, SESSION_SEND_CALL_EVENT);
1028             try {
1029                 SomeArgs args = SomeArgs.obtain();
1030                 args.arg1 = callId;
1031                 args.arg2 = event;
1032                 args.arg3 = extras;
1033                 args.arg4 = Log.createSubsession();
1034                 mHandler.obtainMessage(MSG_SEND_CALL_EVENT, args).sendToTarget();
1035             } finally {
1036                 Log.endSession();
1037             }
1038         }
1039 
1040         @Override
1041         public void onCallFilteringCompleted(String callId,
1042                 Connection.CallFilteringCompletionInfo completionInfo,
1043                 Session.Info sessionInfo) {
1044             Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED);
1045             try {
1046                 SomeArgs args = SomeArgs.obtain();
1047                 args.arg1 = callId;
1048                 args.arg2 = completionInfo;
1049                 args.arg3 = Log.createSubsession();
1050                 mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget();
1051             } finally {
1052                 Log.endSession();
1053             }
1054         }
1055 
1056         @Override
1057         public void onExtrasChanged(String callId, Bundle extras, Session.Info sessionInfo) {
1058             Log.startSession(sessionInfo, SESSION_EXTRAS_CHANGED);
1059             try {
1060                 SomeArgs args = SomeArgs.obtain();
1061                 args.arg1 = callId;
1062                 args.arg2 = extras;
1063                 args.arg3 = Log.createSubsession();
1064                 mHandler.obtainMessage(MSG_ON_EXTRAS_CHANGED, args).sendToTarget();
1065             } finally {
1066                 Log.endSession();
1067             }
1068         }
1069 
1070         @Override
1071         public void startRtt(String callId, ParcelFileDescriptor fromInCall,
1072                 ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException {
1073             Log.startSession(sessionInfo, SESSION_START_RTT);
1074             try {
1075                 SomeArgs args = SomeArgs.obtain();
1076                 args.arg1 = callId;
1077                 args.arg2 = new Connection.RttTextStream(toInCall, fromInCall);
1078                 args.arg3 = Log.createSubsession();
1079                 mHandler.obtainMessage(MSG_ON_START_RTT, args).sendToTarget();
1080             } finally {
1081                 Log.endSession();
1082             }
1083         }
1084 
1085         @Override
1086         public void stopRtt(String callId, Session.Info sessionInfo) throws RemoteException {
1087             Log.startSession(sessionInfo, SESSION_STOP_RTT);
1088             try {
1089                 SomeArgs args = SomeArgs.obtain();
1090                 args.arg1 = callId;
1091                 args.arg2 = Log.createSubsession();
1092                 mHandler.obtainMessage(MSG_ON_STOP_RTT, args).sendToTarget();
1093             } finally {
1094                 Log.endSession();
1095             }
1096         }
1097 
1098         @Override
1099         public void respondToRttUpgradeRequest(String callId, ParcelFileDescriptor fromInCall,
1100                 ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException {
1101             Log.startSession(sessionInfo, SESSION_RTT_UPGRADE_RESPONSE);
1102             try {
1103                 SomeArgs args = SomeArgs.obtain();
1104                 args.arg1 = callId;
1105                 if (toInCall == null || fromInCall == null) {
1106                     args.arg2 = null;
1107                 } else {
1108                     args.arg2 = new Connection.RttTextStream(toInCall, fromInCall);
1109                 }
1110                 args.arg3 = Log.createSubsession();
1111                 mHandler.obtainMessage(MSG_RTT_UPGRADE_RESPONSE, args).sendToTarget();
1112             } finally {
1113                 Log.endSession();
1114             }
1115         }
1116 
1117         @Override
1118         public void connectionServiceFocusLost(Session.Info sessionInfo) throws RemoteException {
1119             Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_LOST);
1120             try {
1121                 mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_LOST).sendToTarget();
1122             } finally {
1123                 Log.endSession();
1124             }
1125         }
1126 
1127         @Override
1128         public void connectionServiceFocusGained(Session.Info sessionInfo) throws RemoteException {
1129             Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_GAINED);
1130             try {
1131                 mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_GAINED).sendToTarget();
1132             } finally {
1133                 Log.endSession();
1134             }
1135         }
1136     };
1137 
1138     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
1139         @Override
1140         public void handleMessage(Message msg) {
1141             switch (msg.what) {
1142                 case MSG_ADD_CONNECTION_SERVICE_ADAPTER: {
1143                     SomeArgs args = (SomeArgs) msg.obj;
1144                     try {
1145                         IConnectionServiceAdapter adapter = (IConnectionServiceAdapter) args.arg1;
1146                         Log.continueSession((Session) args.arg2,
1147                                 SESSION_HANDLER + SESSION_ADD_CS_ADAPTER);
1148                         mAdapter.addAdapter(adapter);
1149                         onAdapterAttached();
1150                     } finally {
1151                         args.recycle();
1152                         Log.endSession();
1153                     }
1154                     break;
1155                 }
1156                 case MSG_REMOVE_CONNECTION_SERVICE_ADAPTER: {
1157                     SomeArgs args = (SomeArgs) msg.obj;
1158                     try {
1159                         Log.continueSession((Session) args.arg2,
1160                                 SESSION_HANDLER + SESSION_REMOVE_CS_ADAPTER);
1161                         mAdapter.removeAdapter((IConnectionServiceAdapter) args.arg1);
1162                     } finally {
1163                         args.recycle();
1164                         Log.endSession();
1165                     }
1166                     break;
1167                 }
1168                 case MSG_CREATE_CONNECTION: {
1169                     SomeArgs args = (SomeArgs) msg.obj;
1170                     Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN);
1171                     try {
1172                         final PhoneAccountHandle connectionManagerPhoneAccount =
1173                                 (PhoneAccountHandle) args.arg1;
1174                         final String id = (String) args.arg2;
1175                         final ConnectionRequest request = (ConnectionRequest) args.arg3;
1176                         final boolean isIncoming = args.argi1 == 1;
1177                         final boolean isUnknown = args.argi2 == 1;
1178                         if (!mAreAccountsInitialized) {
1179                             Log.d(this, "Enqueueing pre-init request %s", id);
1180                             mPreInitializationConnectionRequests.add(
1181                                     new android.telecom.Logging.Runnable(
1182                                             SESSION_HANDLER + SESSION_CREATE_CONN + ".pICR",
1183                                             null /*lock*/) {
1184                                 @Override
1185                                 public void loggedRun() {
1186                                     createConnection(
1187                                             connectionManagerPhoneAccount,
1188                                             id,
1189                                             request,
1190                                             isIncoming,
1191                                             isUnknown);
1192                                 }
1193                             }.prepare());
1194                         } else {
1195                             createConnection(
1196                                     connectionManagerPhoneAccount,
1197                                     id,
1198                                     request,
1199                                     isIncoming,
1200                                     isUnknown);
1201                         }
1202                     } finally {
1203                         args.recycle();
1204                         Log.endSession();
1205                     }
1206                     break;
1207                 }
1208                 case MSG_CREATE_CONNECTION_COMPLETE: {
1209                     SomeArgs args = (SomeArgs) msg.obj;
1210                     Log.continueSession((Session) args.arg2,
1211                             SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE);
1212                     try {
1213                         final String id = (String) args.arg1;
1214                         if (!mAreAccountsInitialized) {
1215                             Log.d(this, "Enqueueing pre-init request %s", id);
1216                             mPreInitializationConnectionRequests.add(
1217                                     new android.telecom.Logging.Runnable(
1218                                             SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE
1219                                                     + ".pICR",
1220                                             null /*lock*/) {
1221                                         @Override
1222                                         public void loggedRun() {
1223                                             notifyCreateConnectionComplete(id);
1224                                         }
1225                                     }.prepare());
1226                         } else {
1227                             notifyCreateConnectionComplete(id);
1228                         }
1229                     } finally {
1230                         args.recycle();
1231                         Log.endSession();
1232                     }
1233                     break;
1234                 }
1235                 case MSG_CREATE_CONNECTION_FAILED: {
1236                     SomeArgs args = (SomeArgs) msg.obj;
1237                     Log.continueSession((Session) args.arg3, SESSION_HANDLER +
1238                             SESSION_CREATE_CONN_FAILED);
1239                     try {
1240                         final String id = (String) args.arg1;
1241                         final ConnectionRequest request = (ConnectionRequest) args.arg2;
1242                         final boolean isIncoming = args.argi1 == 1;
1243                         final PhoneAccountHandle connectionMgrPhoneAccount =
1244                                 (PhoneAccountHandle) args.arg4;
1245                         if (!mAreAccountsInitialized) {
1246                             Log.d(this, "Enqueueing pre-init request %s", id);
1247                             mPreInitializationConnectionRequests.add(
1248                                     new android.telecom.Logging.Runnable(
1249                                             SESSION_HANDLER + SESSION_CREATE_CONN_FAILED + ".pICR",
1250                                             null /*lock*/) {
1251                                         @Override
1252                                         public void loggedRun() {
1253                                             createConnectionFailed(connectionMgrPhoneAccount, id,
1254                                                     request, isIncoming);
1255                                         }
1256                                     }.prepare());
1257                         } else {
1258                             Log.i(this, "createConnectionFailed %s", id);
1259                             createConnectionFailed(connectionMgrPhoneAccount, id, request,
1260                                     isIncoming);
1261                         }
1262                     } finally {
1263                         args.recycle();
1264                         Log.endSession();
1265                     }
1266                     break;
1267                 }
1268                 case MSG_CREATE_CONFERENCE: {
1269                     SomeArgs args = (SomeArgs) msg.obj;
1270                     Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN);
1271                     try {
1272                         final PhoneAccountHandle connectionManagerPhoneAccount =
1273                                 (PhoneAccountHandle) args.arg1;
1274                         final String id = (String) args.arg2;
1275                         final ConnectionRequest request = (ConnectionRequest) args.arg3;
1276                         final boolean isIncoming = args.argi1 == 1;
1277                         final boolean isUnknown = args.argi2 == 1;
1278                         if (!mAreAccountsInitialized) {
1279                             Log.d(this, "Enqueueing pre-initconference request %s", id);
1280                             mPreInitializationConnectionRequests.add(
1281                                     new android.telecom.Logging.Runnable(
1282                                             SESSION_HANDLER + SESSION_CREATE_CONF + ".pIConfR",
1283                                             null /*lock*/) {
1284                                 @Override
1285                                 public void loggedRun() {
1286                                     createConference(connectionManagerPhoneAccount,
1287                                             id,
1288                                             request,
1289                                             isIncoming,
1290                                             isUnknown);
1291                                 }
1292                             }.prepare());
1293                         } else {
1294                             createConference(connectionManagerPhoneAccount,
1295                                     id,
1296                                     request,
1297                                     isIncoming,
1298                                     isUnknown);
1299                         }
1300                     } finally {
1301                         args.recycle();
1302                         Log.endSession();
1303                     }
1304                     break;
1305                 }
1306                 case MSG_CREATE_CONFERENCE_COMPLETE: {
1307                     SomeArgs args = (SomeArgs) msg.obj;
1308                     Log.continueSession((Session) args.arg2,
1309                             SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE);
1310                     try {
1311                         final String id = (String) args.arg1;
1312                         if (!mAreAccountsInitialized) {
1313                             Log.d(this, "Enqueueing pre-init conference request %s", id);
1314                             mPreInitializationConnectionRequests.add(
1315                                     new android.telecom.Logging.Runnable(
1316                                             SESSION_HANDLER + SESSION_CREATE_CONF_COMPLETE
1317                                                     + ".pIConfR",
1318                                             null /*lock*/) {
1319                                         @Override
1320                                         public void loggedRun() {
1321                                             notifyCreateConferenceComplete(id);
1322                                         }
1323                                     }.prepare());
1324                         } else {
1325                             notifyCreateConferenceComplete(id);
1326                         }
1327                     } finally {
1328                         args.recycle();
1329                         Log.endSession();
1330                     }
1331                     break;
1332                 }
1333                 case MSG_CREATE_CONFERENCE_FAILED: {
1334                     SomeArgs args = (SomeArgs) msg.obj;
1335                     Log.continueSession((Session) args.arg3, SESSION_HANDLER +
1336                             SESSION_CREATE_CONN_FAILED);
1337                     try {
1338                         final String id = (String) args.arg1;
1339                         final ConnectionRequest request = (ConnectionRequest) args.arg2;
1340                         final boolean isIncoming = args.argi1 == 1;
1341                         final PhoneAccountHandle connectionMgrPhoneAccount =
1342                                 (PhoneAccountHandle) args.arg4;
1343                         if (!mAreAccountsInitialized) {
1344                             Log.d(this, "Enqueueing pre-init conference request %s", id);
1345                             mPreInitializationConnectionRequests.add(
1346                                     new android.telecom.Logging.Runnable(
1347                                             SESSION_HANDLER + SESSION_CREATE_CONF_FAILED
1348                                                     + ".pIConfR",
1349                                             null /*lock*/) {
1350                                         @Override
1351                                         public void loggedRun() {
1352                                             createConferenceFailed(connectionMgrPhoneAccount, id,
1353                                                     request, isIncoming);
1354                                         }
1355                                     }.prepare());
1356                         } else {
1357                             Log.i(this, "createConferenceFailed %s", id);
1358                             createConferenceFailed(connectionMgrPhoneAccount, id, request,
1359                                     isIncoming);
1360                         }
1361                     } finally {
1362                         args.recycle();
1363                         Log.endSession();
1364                     }
1365                     break;
1366                 }
1367 
1368                 case MSG_HANDOVER_FAILED: {
1369                     SomeArgs args = (SomeArgs) msg.obj;
1370                     Log.continueSession((Session) args.arg3, SESSION_HANDLER +
1371                             SESSION_HANDOVER_FAILED);
1372                     try {
1373                         final String id = (String) args.arg1;
1374                         final ConnectionRequest request = (ConnectionRequest) args.arg2;
1375                         final int reason = (int) args.arg4;
1376                         if (!mAreAccountsInitialized) {
1377                             Log.d(this, "Enqueueing pre-init request %s", id);
1378                             mPreInitializationConnectionRequests.add(
1379                                     new android.telecom.Logging.Runnable(
1380                                             SESSION_HANDLER
1381                                                     + SESSION_HANDOVER_FAILED + ".pICR",
1382                                             null /*lock*/) {
1383                                         @Override
1384                                         public void loggedRun() {
1385                                             handoverFailed(id, request, reason);
1386                                         }
1387                                     }.prepare());
1388                         } else {
1389                             Log.i(this, "createConnectionFailed %s", id);
1390                             handoverFailed(id, request, reason);
1391                         }
1392                     } finally {
1393                         args.recycle();
1394                         Log.endSession();
1395                     }
1396                     break;
1397                 }
1398                 case MSG_ABORT: {
1399                     SomeArgs args = (SomeArgs) msg.obj;
1400                     Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ABORT);
1401                     try {
1402                         abort((String) args.arg1);
1403                     } finally {
1404                         args.recycle();
1405                         Log.endSession();
1406                     }
1407                     break;
1408                 }
1409                 case MSG_ANSWER: {
1410                     SomeArgs args = (SomeArgs) msg.obj;
1411                     Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ANSWER);
1412                     try {
1413                         answer((String) args.arg1);
1414                     } finally {
1415                         args.recycle();
1416                         Log.endSession();
1417                     }
1418                     break;
1419                 }
1420                 case MSG_ANSWER_VIDEO: {
1421                     SomeArgs args = (SomeArgs) msg.obj;
1422                     Log.continueSession((Session) args.arg2,
1423                             SESSION_HANDLER + SESSION_ANSWER_VIDEO);
1424                     try {
1425                         String callId = (String) args.arg1;
1426                         int videoState = args.argi1;
1427                         answerVideo(callId, videoState);
1428                     } finally {
1429                         args.recycle();
1430                         Log.endSession();
1431                     }
1432                     break;
1433                 }
1434                 case MSG_DEFLECT: {
1435                     SomeArgs args = (SomeArgs) msg.obj;
1436                     Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_DEFLECT);
1437                     try {
1438                         deflect((String) args.arg1, (Uri) args.arg2);
1439                     } finally {
1440                         args.recycle();
1441                         Log.endSession();
1442                     }
1443                     break;
1444                 }
1445                 case MSG_REJECT: {
1446                     SomeArgs args = (SomeArgs) msg.obj;
1447                     Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
1448                     try {
1449                         reject((String) args.arg1);
1450                     } finally {
1451                         args.recycle();
1452                         Log.endSession();
1453                     }
1454                     break;
1455                 }
1456                 case MSG_REJECT_WITH_REASON: {
1457                     SomeArgs args = (SomeArgs) msg.obj;
1458                     Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
1459                     try {
1460                         reject((String) args.arg1, args.argi1);
1461                     } finally {
1462                         args.recycle();
1463                         Log.endSession();
1464                     }
1465                     break;
1466                 }
1467                 case MSG_REJECT_WITH_MESSAGE: {
1468                     SomeArgs args = (SomeArgs) msg.obj;
1469                     Log.continueSession((Session) args.arg3,
1470                             SESSION_HANDLER + SESSION_REJECT_MESSAGE);
1471                     try {
1472                         reject((String) args.arg1, (String) args.arg2);
1473                     } finally {
1474                         args.recycle();
1475                         Log.endSession();
1476                     }
1477                     break;
1478                 }
1479                 case MSG_EXPLICIT_CALL_TRANSFER: {
1480                     SomeArgs args = (SomeArgs) msg.obj;
1481                     Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_TRANSFER);
1482                     try {
1483                         final boolean isConfirmationRequired = args.argi1 == 1;
1484                         transfer((String) args.arg1, (Uri) args.arg2, isConfirmationRequired);
1485                     } finally {
1486                         args.recycle();
1487                         Log.endSession();
1488                     }
1489                     break;
1490                 }
1491                 case MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE: {
1492                     SomeArgs args = (SomeArgs) msg.obj;
1493                     Log.continueSession(
1494                             (Session) args.arg3, SESSION_HANDLER + SESSION_CONSULTATIVE_TRANSFER);
1495                     try {
1496                         consultativeTransfer((String) args.arg1, (String) args.arg2);
1497                     } finally {
1498                         args.recycle();
1499                         Log.endSession();
1500                     }
1501                     break;
1502                 }
1503                 case MSG_DISCONNECT: {
1504                     SomeArgs args = (SomeArgs) msg.obj;
1505                     Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_DISCONNECT);
1506                     try {
1507                         disconnect((String) args.arg1);
1508                     } finally {
1509                         args.recycle();
1510                         Log.endSession();
1511                     }
1512                     break;
1513                 }
1514                 case MSG_SILENCE: {
1515                     SomeArgs args = (SomeArgs) msg.obj;
1516                     Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_SILENCE);
1517                     try {
1518                         silence((String) args.arg1);
1519                     } finally {
1520                         args.recycle();
1521                         Log.endSession();
1522                     }
1523                     break;
1524                 }
1525                 case MSG_HOLD: {
1526                     SomeArgs args = (SomeArgs) msg.obj;
1527                     Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
1528                     try {
1529                         hold((String) args.arg1);
1530                     } finally {
1531                         args.recycle();
1532                         Log.endSession();
1533                     }
1534                     break;
1535                 }
1536                 case MSG_UNHOLD: {
1537                     SomeArgs args = (SomeArgs) msg.obj;
1538                     Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_UNHOLD);
1539                     try {
1540                         unhold((String) args.arg1);
1541                     } finally {
1542                         args.recycle();
1543                         Log.endSession();
1544                     }
1545                     break;
1546                 }
1547                 case MSG_ON_CALL_AUDIO_STATE_CHANGED: {
1548                     SomeArgs args = (SomeArgs) msg.obj;
1549                     Log.continueSession((Session) args.arg3,
1550                             SESSION_HANDLER + SESSION_CALL_AUDIO_SC);
1551                     try {
1552                         String callId = (String) args.arg1;
1553                         CallAudioState audioState = (CallAudioState) args.arg2;
1554                         onCallAudioStateChanged(callId, new CallAudioState(audioState));
1555                     } finally {
1556                         args.recycle();
1557                         Log.endSession();
1558                     }
1559                     break;
1560                 }
1561                 case MSG_ON_USING_ALTERNATIVE_UI: {
1562                     SomeArgs args = (SomeArgs) msg.obj;
1563                     Log.continueSession((Session) args.arg3,
1564                             SESSION_HANDLER + SESSION_USING_ALTERNATIVE_UI);
1565                     try {
1566                         String callId = (String) args.arg1;
1567                         boolean isUsingAlternativeUi = (boolean) args.arg2;
1568                         onUsingAlternativeUi(callId, isUsingAlternativeUi);
1569                     } finally {
1570                         args.recycle();
1571                         Log.endSession();
1572                     }
1573                     break;
1574                 }
1575                 case MSG_ON_TRACKED_BY_NON_UI_SERVICE: {
1576                     SomeArgs args = (SomeArgs) msg.obj;
1577                     Log.continueSession((Session) args.arg3,
1578                             SESSION_HANDLER + SESSION_TRACKED_BY_NON_UI_SERVICE);
1579                     try {
1580                         String callId = (String) args.arg1;
1581                         boolean isTracked = (boolean) args.arg2;
1582                         onTrackedByNonUiService(callId, isTracked);
1583                     } finally {
1584                         args.recycle();
1585                         Log.endSession();
1586                     }
1587                     break;
1588                 }
1589                 case MSG_PLAY_DTMF_TONE: {
1590                     SomeArgs args = (SomeArgs) msg.obj;
1591                     try {
1592                         Log.continueSession((Session) args.arg3,
1593                                 SESSION_HANDLER + SESSION_PLAY_DTMF);
1594                         playDtmfTone((String) args.arg2, (char) args.arg1);
1595                     } finally {
1596                         args.recycle();
1597                         Log.endSession();
1598                     }
1599                     break;
1600                 }
1601                 case MSG_STOP_DTMF_TONE: {
1602                     SomeArgs args = (SomeArgs) msg.obj;
1603                     try {
1604                         Log.continueSession((Session) args.arg2,
1605                                 SESSION_HANDLER + SESSION_STOP_DTMF);
1606                         stopDtmfTone((String) args.arg1);
1607                     } finally {
1608                         args.recycle();
1609                         Log.endSession();
1610                     }
1611                     break;
1612                 }
1613                 case MSG_CONFERENCE: {
1614                     SomeArgs args = (SomeArgs) msg.obj;
1615                     try {
1616                         Log.continueSession((Session) args.arg3,
1617                                 SESSION_HANDLER + SESSION_CONFERENCE);
1618                         String callId1 = (String) args.arg1;
1619                         String callId2 = (String) args.arg2;
1620                         conference(callId1, callId2);
1621                     } finally {
1622                         args.recycle();
1623                         Log.endSession();
1624                     }
1625                     break;
1626                 }
1627                 case MSG_SPLIT_FROM_CONFERENCE: {
1628                     SomeArgs args = (SomeArgs) msg.obj;
1629                     try {
1630                         Log.continueSession((Session) args.arg2,
1631                                 SESSION_HANDLER + SESSION_SPLIT_CONFERENCE);
1632                         splitFromConference((String) args.arg1);
1633                     } finally {
1634                         args.recycle();
1635                         Log.endSession();
1636                     }
1637                     break;
1638                 }
1639                 case MSG_MERGE_CONFERENCE: {
1640                     SomeArgs args = (SomeArgs) msg.obj;
1641                     try {
1642                         Log.continueSession((Session) args.arg2,
1643                                 SESSION_HANDLER + SESSION_MERGE_CONFERENCE);
1644                         mergeConference((String) args.arg1);
1645                     } finally {
1646                         args.recycle();
1647                         Log.endSession();
1648                     }
1649                     break;
1650                 }
1651                 case MSG_SWAP_CONFERENCE: {
1652                     SomeArgs args = (SomeArgs) msg.obj;
1653                     try {
1654                         Log.continueSession((Session) args.arg2,
1655                                 SESSION_HANDLER + SESSION_SWAP_CONFERENCE);
1656                         swapConference((String) args.arg1);
1657                     } finally {
1658                         args.recycle();
1659                         Log.endSession();
1660                     }
1661                     break;
1662                 }
1663                 case MSG_ADD_PARTICIPANT: {
1664                     SomeArgs args = (SomeArgs) msg.obj;
1665                     try {
1666                         Log.continueSession((Session) args.arg3,
1667                                 SESSION_HANDLER + SESSION_ADD_PARTICIPANT);
1668                         addConferenceParticipants((String) args.arg1, (List<Uri>)args.arg2);
1669                     } finally {
1670                         args.recycle();
1671                         Log.endSession();
1672                     }
1673                     break;
1674                 }
1675 
1676                 case MSG_ON_POST_DIAL_CONTINUE: {
1677                     SomeArgs args = (SomeArgs) msg.obj;
1678                     try {
1679                         Log.continueSession((Session) args.arg2,
1680                                 SESSION_HANDLER + SESSION_POST_DIAL_CONT);
1681                         String callId = (String) args.arg1;
1682                         boolean proceed = (args.argi1 == 1);
1683                         onPostDialContinue(callId, proceed);
1684                     } finally {
1685                         args.recycle();
1686                         Log.endSession();
1687                     }
1688                     break;
1689                 }
1690                 case MSG_PULL_EXTERNAL_CALL: {
1691                     SomeArgs args = (SomeArgs) msg.obj;
1692                     try {
1693                         Log.continueSession((Session) args.arg2,
1694                                 SESSION_HANDLER + SESSION_PULL_EXTERNAL_CALL);
1695                         pullExternalCall((String) args.arg1);
1696                     } finally {
1697                         args.recycle();
1698                         Log.endSession();
1699                     }
1700                     break;
1701                 }
1702                 case MSG_SEND_CALL_EVENT: {
1703                     SomeArgs args = (SomeArgs) msg.obj;
1704                     try {
1705                         Log.continueSession((Session) args.arg4,
1706                                 SESSION_HANDLER + SESSION_SEND_CALL_EVENT);
1707                         String callId = (String) args.arg1;
1708                         String event = (String) args.arg2;
1709                         Bundle extras = (Bundle) args.arg3;
1710                         sendCallEvent(callId, event, extras);
1711                     } finally {
1712                         args.recycle();
1713                         Log.endSession();
1714                     }
1715                     break;
1716                 }
1717                 case MSG_ON_CALL_FILTERING_COMPLETED: {
1718                     SomeArgs args = (SomeArgs) msg.obj;
1719                     try {
1720                         Log.continueSession((Session) args.arg3,
1721                                 SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED);
1722                         String callId = (String) args.arg1;
1723                         Connection.CallFilteringCompletionInfo completionInfo =
1724                                 (Connection.CallFilteringCompletionInfo) args.arg2;
1725                         onCallFilteringCompleted(callId, completionInfo);
1726                     } finally {
1727                         args.recycle();
1728                         Log.endSession();
1729                     }
1730                     break;
1731                 }
1732                 case MSG_HANDOVER_COMPLETE: {
1733                     SomeArgs args = (SomeArgs) msg.obj;
1734                     try {
1735                         Log.continueSession((Session) args.arg2,
1736                                 SESSION_HANDLER + SESSION_HANDOVER_COMPLETE);
1737                         String callId = (String) args.arg1;
1738                         notifyHandoverComplete(callId);
1739                     } finally {
1740                         args.recycle();
1741                         Log.endSession();
1742                     }
1743                     break;
1744                 }
1745                 case MSG_ON_EXTRAS_CHANGED: {
1746                     SomeArgs args = (SomeArgs) msg.obj;
1747                     try {
1748                         Log.continueSession((Session) args.arg3,
1749                                 SESSION_HANDLER + SESSION_EXTRAS_CHANGED);
1750                         String callId = (String) args.arg1;
1751                         Bundle extras = (Bundle) args.arg2;
1752                         handleExtrasChanged(callId, extras);
1753                     } finally {
1754                         args.recycle();
1755                         Log.endSession();
1756                     }
1757                     break;
1758                 }
1759                 case MSG_ON_START_RTT: {
1760                     SomeArgs args = (SomeArgs) msg.obj;
1761                     try {
1762                         Log.continueSession((Session) args.arg3,
1763                                 SESSION_HANDLER + SESSION_START_RTT);
1764                         String callId = (String) args.arg1;
1765                         Connection.RttTextStream rttTextStream =
1766                                 (Connection.RttTextStream) args.arg2;
1767                         startRtt(callId, rttTextStream);
1768                     } finally {
1769                         args.recycle();
1770                         Log.endSession();
1771                     }
1772                     break;
1773                 }
1774                 case MSG_ON_STOP_RTT: {
1775                     SomeArgs args = (SomeArgs) msg.obj;
1776                     try {
1777                         Log.continueSession((Session) args.arg2,
1778                                 SESSION_HANDLER + SESSION_STOP_RTT);
1779                         String callId = (String) args.arg1;
1780                         stopRtt(callId);
1781                     } finally {
1782                         args.recycle();
1783                         Log.endSession();
1784                     }
1785                     break;
1786                 }
1787                 case MSG_RTT_UPGRADE_RESPONSE: {
1788                     SomeArgs args = (SomeArgs) msg.obj;
1789                     try {
1790                         Log.continueSession((Session) args.arg3,
1791                                 SESSION_HANDLER + SESSION_RTT_UPGRADE_RESPONSE);
1792                         String callId = (String) args.arg1;
1793                         Connection.RttTextStream rttTextStream =
1794                                 (Connection.RttTextStream) args.arg2;
1795                         handleRttUpgradeResponse(callId, rttTextStream);
1796                     } finally {
1797                         args.recycle();
1798                         Log.endSession();
1799                     }
1800                     break;
1801                 }
1802                 case MSG_CONNECTION_SERVICE_FOCUS_GAINED:
1803                     onConnectionServiceFocusGained();
1804                     break;
1805                 case MSG_CONNECTION_SERVICE_FOCUS_LOST:
1806                     onConnectionServiceFocusLost();
1807                     break;
1808                 case MSG_ON_CALL_ENDPOINT_CHANGED: {
1809                     SomeArgs args = (SomeArgs) msg.obj;
1810                     Log.continueSession((Session) args.arg3,
1811                             SESSION_HANDLER + SESSION_CALL_AUDIO_SC);
1812                     try {
1813                         String callId = (String) args.arg1;
1814                         CallEndpoint callEndpoint = (CallEndpoint) args.arg2;
1815                         onCallEndpointChanged(callId, callEndpoint);
1816                     } finally {
1817                         args.recycle();
1818                         Log.endSession();
1819                     }
1820                     break;
1821                 }
1822                 case MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED: {
1823                     SomeArgs args = (SomeArgs) msg.obj;
1824                     Log.continueSession((Session) args.arg3,
1825                             SESSION_HANDLER + SESSION_CALL_AUDIO_SC);
1826                     try {
1827                         String callId = (String) args.arg1;
1828                         List<CallEndpoint>  availableCallEndpoints = (List<CallEndpoint>) args.arg2;
1829                         onAvailableCallEndpointsChanged(callId, availableCallEndpoints);
1830                     } finally {
1831                         args.recycle();
1832                         Log.endSession();
1833                     }
1834                     break;
1835                 }
1836                 case MSG_ON_MUTE_STATE_CHANGED: {
1837                     SomeArgs args = (SomeArgs) msg.obj;
1838                     Log.continueSession((Session) args.arg3,
1839                             SESSION_HANDLER + SESSION_CALL_AUDIO_SC);
1840                     try {
1841                         String callId = (String) args.arg1;
1842                         boolean isMuted = (boolean) args.arg2;
1843                         onMuteStateChanged(callId, isMuted);
1844                     } finally {
1845                         args.recycle();
1846                         Log.endSession();
1847                     }
1848                     break;
1849                 }
1850                 default:
1851                     break;
1852             }
1853         }
1854     };
1855 
1856     private final Conference.Listener mConferenceListener = new Conference.Listener() {
1857         @Override
1858         public void onStateChanged(Conference conference, int oldState, int newState) {
1859             String id = mIdByConference.get(conference);
1860             switch (newState) {
1861                 case Connection.STATE_RINGING:
1862                     mAdapter.setRinging(id);
1863                     break;
1864                 case Connection.STATE_DIALING:
1865                     mAdapter.setDialing(id);
1866                     break;
1867                 case Connection.STATE_ACTIVE:
1868                     mAdapter.setActive(id);
1869                     break;
1870                 case Connection.STATE_HOLDING:
1871                     mAdapter.setOnHold(id);
1872                     break;
1873                 case Connection.STATE_DISCONNECTED:
1874                     // handled by onDisconnected
1875                     break;
1876             }
1877         }
1878 
1879         @Override
1880         public void onDisconnected(Conference conference, DisconnectCause disconnectCause) {
1881             String id = mIdByConference.get(conference);
1882             mAdapter.setDisconnected(id, disconnectCause);
1883         }
1884 
1885         @Override
1886         public void onConnectionAdded(Conference conference, Connection connection) {
1887         }
1888 
1889         @Override
1890         public void onConnectionRemoved(Conference conference, Connection connection) {
1891         }
1892 
1893         @Override
1894         public void onConferenceableConnectionsChanged(
1895                 Conference conference, List<Connection> conferenceableConnections) {
1896             mAdapter.setConferenceableConnections(
1897                     mIdByConference.get(conference),
1898                     createConnectionIdList(conferenceableConnections));
1899         }
1900 
1901         @Override
1902         public void onDestroyed(Conference conference) {
1903             removeConference(conference);
1904         }
1905 
1906         @Override
1907         public void onConnectionCapabilitiesChanged(
1908                 Conference conference,
1909                 int connectionCapabilities) {
1910             String id = mIdByConference.get(conference);
1911             Log.d(this, "call capabilities: conference: %s",
1912                     Connection.capabilitiesToString(connectionCapabilities));
1913             mAdapter.setConnectionCapabilities(id, connectionCapabilities);
1914         }
1915 
1916         @Override
1917         public void onConnectionPropertiesChanged(
1918                 Conference conference,
1919                 int connectionProperties) {
1920             String id = mIdByConference.get(conference);
1921             Log.d(this, "call capabilities: conference: %s",
1922                     Connection.propertiesToString(connectionProperties));
1923             mAdapter.setConnectionProperties(id, connectionProperties);
1924         }
1925 
1926         @Override
1927         public void onVideoStateChanged(Conference c, int videoState) {
1928             String id = mIdByConference.get(c);
1929             Log.d(this, "onVideoStateChanged set video state %d", videoState);
1930             mAdapter.setVideoState(id, videoState);
1931         }
1932 
1933         @Override
1934         public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {
1935             String id = mIdByConference.get(c);
1936             Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
1937                     videoProvider);
1938             mAdapter.setVideoProvider(id, videoProvider);
1939         }
1940 
1941         @Override
1942         public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {
1943             String id = mIdByConference.get(conference);
1944             if (id != null) {
1945                 mAdapter.setStatusHints(id, statusHints);
1946             }
1947         }
1948 
1949         @Override
1950         public void onExtrasChanged(Conference c, Bundle extras) {
1951             String id = mIdByConference.get(c);
1952             if (id != null) {
1953                 mAdapter.putExtras(id, extras);
1954             }
1955         }
1956 
1957         @Override
1958         public void onExtrasRemoved(Conference c, List<String> keys) {
1959             String id = mIdByConference.get(c);
1960             if (id != null) {
1961                 mAdapter.removeExtras(id, keys);
1962             }
1963         }
1964 
1965         @Override
1966         public void onConferenceStateChanged(Conference c, boolean isConference) {
1967             String id = mIdByConference.get(c);
1968             if (id != null) {
1969                 mAdapter.setConferenceState(id, isConference);
1970             }
1971         }
1972 
1973         @Override
1974         public void onCallDirectionChanged(Conference c, int direction) {
1975             String id = mIdByConference.get(c);
1976             if (id != null) {
1977                 mAdapter.setCallDirection(id, direction);
1978             }
1979         }
1980 
1981         @Override
1982         public void onAddressChanged(Conference c, Uri newAddress, int presentation) {
1983             String id = mIdByConference.get(c);
1984             if (id != null) {
1985                 mAdapter.setAddress(id, newAddress, presentation);
1986             }
1987         }
1988 
1989         @Override
1990         public void onCallerDisplayNameChanged(Conference c, String callerDisplayName,
1991                 int presentation) {
1992             String id = mIdByConference.get(c);
1993             if (id != null) {
1994                 mAdapter.setCallerDisplayName(id, callerDisplayName, presentation);
1995             }
1996         }
1997 
1998         @Override
1999         public void onConnectionEvent(Conference c, String event, Bundle extras) {
2000             String id = mIdByConference.get(c);
2001             if (id != null) {
2002                 mAdapter.onConnectionEvent(id, event, extras);
2003             }
2004         }
2005 
2006         @Override
2007         public void onRingbackRequested(Conference c, boolean ringback) {
2008             String id = mIdByConference.get(c);
2009             Log.d(this, "Adapter conference onRingback %b", ringback);
2010             mAdapter.setRingbackRequested(id, ringback);
2011         }
2012     };
2013 
2014     private final Connection.Listener mConnectionListener = new Connection.Listener() {
2015         @Override
2016         public void onStateChanged(Connection c, int state) {
2017             String id = mIdByConnection.get(c);
2018             Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state));
2019             switch (state) {
2020                 case Connection.STATE_ACTIVE:
2021                     mAdapter.setActive(id);
2022                     break;
2023                 case Connection.STATE_DIALING:
2024                     mAdapter.setDialing(id);
2025                     break;
2026                 case Connection.STATE_PULLING_CALL:
2027                     mAdapter.setPulling(id);
2028                     break;
2029                 case Connection.STATE_DISCONNECTED:
2030                     // Handled in onDisconnected()
2031                     break;
2032                 case Connection.STATE_HOLDING:
2033                     mAdapter.setOnHold(id);
2034                     break;
2035                 case Connection.STATE_NEW:
2036                     // Nothing to tell Telecom
2037                     break;
2038                 case Connection.STATE_RINGING:
2039                     mAdapter.setRinging(id);
2040                     break;
2041             }
2042         }
2043 
2044         @Override
2045         public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
2046             String id = mIdByConnection.get(c);
2047             Log.d(this, "Adapter set disconnected %s", disconnectCause);
2048             mAdapter.setDisconnected(id, disconnectCause);
2049         }
2050 
2051         @Override
2052         public void onVideoStateChanged(Connection c, int videoState) {
2053             String id = mIdByConnection.get(c);
2054             Log.d(this, "Adapter set video state %d", videoState);
2055             mAdapter.setVideoState(id, videoState);
2056         }
2057 
2058         @Override
2059         public void onAddressChanged(Connection c, Uri address, int presentation) {
2060             String id = mIdByConnection.get(c);
2061             mAdapter.setAddress(id, address, presentation);
2062         }
2063 
2064         @Override
2065         public void onCallerDisplayNameChanged(
2066                 Connection c, String callerDisplayName, int presentation) {
2067             String id = mIdByConnection.get(c);
2068             mAdapter.setCallerDisplayName(id, callerDisplayName, presentation);
2069         }
2070 
2071         @Override
2072         public void onDestroyed(Connection c) {
2073             removeConnection(c);
2074         }
2075 
2076         @Override
2077         public void onPostDialWait(Connection c, String remaining) {
2078             String id = mIdByConnection.get(c);
2079             Log.d(this, "Adapter onPostDialWait %s, %s", c, remaining);
2080             mAdapter.onPostDialWait(id, remaining);
2081         }
2082 
2083         @Override
2084         public void onPostDialChar(Connection c, char nextChar) {
2085             String id = mIdByConnection.get(c);
2086             Log.d(this, "Adapter onPostDialChar %s, %s", c, nextChar);
2087             mAdapter.onPostDialChar(id, nextChar);
2088         }
2089 
2090         @Override
2091         public void onRingbackRequested(Connection c, boolean ringback) {
2092             String id = mIdByConnection.get(c);
2093             Log.d(this, "Adapter onRingback %b", ringback);
2094             mAdapter.setRingbackRequested(id, ringback);
2095         }
2096 
2097         @Override
2098         public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {
2099             String id = mIdByConnection.get(c);
2100             Log.d(this, "capabilities: parcelableconnection: %s",
2101                     Connection.capabilitiesToString(capabilities));
2102             mAdapter.setConnectionCapabilities(id, capabilities);
2103         }
2104 
2105         @Override
2106         public void onConnectionPropertiesChanged(Connection c, int properties) {
2107             String id = mIdByConnection.get(c);
2108             Log.d(this, "properties: parcelableconnection: %s",
2109                     Connection.propertiesToString(properties));
2110             mAdapter.setConnectionProperties(id, properties);
2111         }
2112 
2113         @Override
2114         public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) {
2115             String id = mIdByConnection.get(c);
2116             Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
2117                     videoProvider);
2118             mAdapter.setVideoProvider(id, videoProvider);
2119         }
2120 
2121         @Override
2122         public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {
2123             String id = mIdByConnection.get(c);
2124             mAdapter.setIsVoipAudioMode(id, isVoip);
2125         }
2126 
2127         @Override
2128         public void onStatusHintsChanged(Connection c, StatusHints statusHints) {
2129             String id = mIdByConnection.get(c);
2130             mAdapter.setStatusHints(id, statusHints);
2131         }
2132 
2133         @Override
2134         public void onConferenceablesChanged(
2135                 Connection connection, List<Conferenceable> conferenceables) {
2136             mAdapter.setConferenceableConnections(
2137                     mIdByConnection.get(connection),
2138                     createIdList(conferenceables));
2139         }
2140 
2141         @Override
2142         public void onConferenceChanged(Connection connection, Conference conference) {
2143             String id = mIdByConnection.get(connection);
2144             if (id != null) {
2145                 String conferenceId = null;
2146                 if (conference != null) {
2147                     conferenceId = mIdByConference.get(conference);
2148                 }
2149                 mAdapter.setIsConferenced(id, conferenceId);
2150             }
2151         }
2152 
2153         @Override
2154         public void onConferenceMergeFailed(Connection connection) {
2155             String id = mIdByConnection.get(connection);
2156             if (id != null) {
2157                 mAdapter.onConferenceMergeFailed(id);
2158             }
2159         }
2160 
2161         @Override
2162         public void onExtrasChanged(Connection c, Bundle extras) {
2163             String id = mIdByConnection.get(c);
2164             if (id != null) {
2165                 mAdapter.putExtras(id, extras);
2166             }
2167         }
2168 
2169         @Override
2170         public void onExtrasRemoved(Connection c, List<String> keys) {
2171             String id = mIdByConnection.get(c);
2172             if (id != null) {
2173                 mAdapter.removeExtras(id, keys);
2174             }
2175         }
2176 
2177         @Override
2178         public void onConnectionEvent(Connection connection, String event, Bundle extras) {
2179             String id = mIdByConnection.get(connection);
2180             if (id != null) {
2181                 mAdapter.onConnectionEvent(id, event, extras);
2182             }
2183         }
2184 
2185         @Override
2186         public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) {
2187             String id = mIdByConnection.get(c);
2188             if (id != null) {
2189                 mAdapter.setAudioRoute(id, audioRoute, bluetoothAddress);
2190             }
2191         }
2192 
2193         @Override
2194         public void onRttInitiationSuccess(Connection c) {
2195             String id = mIdByConnection.get(c);
2196             if (id != null) {
2197                 mAdapter.onRttInitiationSuccess(id);
2198             }
2199         }
2200 
2201         @Override
2202         public void onRttInitiationFailure(Connection c, int reason) {
2203             String id = mIdByConnection.get(c);
2204             if (id != null) {
2205                 mAdapter.onRttInitiationFailure(id, reason);
2206             }
2207         }
2208 
2209         @Override
2210         public void onRttSessionRemotelyTerminated(Connection c) {
2211             String id = mIdByConnection.get(c);
2212             if (id != null) {
2213                 mAdapter.onRttSessionRemotelyTerminated(id);
2214             }
2215         }
2216 
2217         @Override
2218         public void onRemoteRttRequest(Connection c) {
2219             String id = mIdByConnection.get(c);
2220             if (id != null) {
2221                 mAdapter.onRemoteRttRequest(id);
2222             }
2223         }
2224 
2225         @Override
2226         public void onPhoneAccountChanged(Connection c, PhoneAccountHandle pHandle) {
2227             String id = mIdByConnection.get(c);
2228             if (id != null) {
2229                 mAdapter.onPhoneAccountChanged(id, pHandle);
2230             }
2231         }
2232 
2233         public void onConnectionTimeReset(Connection c) {
2234             String id = mIdByConnection.get(c);
2235             if (id != null) {
2236                 mAdapter.resetConnectionTime(id);
2237             }
2238         }
2239 
2240         @Override
2241         public void onEndpointChanged(Connection c, CallEndpoint endpoint, Executor executor,
2242                 OutcomeReceiver<Void, CallEndpointException> callback) {
2243             String id = mIdByConnection.get(c);
2244             if (id != null) {
2245                 mAdapter.requestCallEndpointChange(id, endpoint, executor, callback);
2246             }
2247         }
2248 
2249         @Override
2250         public void onQueryLocation(Connection c, long timeoutMillis, @NonNull String provider,
2251                 @NonNull @CallbackExecutor Executor executor,
2252                 @NonNull OutcomeReceiver<Location, QueryLocationException> callback) {
2253             String id = mIdByConnection.get(c);
2254             if (id != null) {
2255                 mAdapter.queryLocation(id, timeoutMillis, provider, executor, callback);
2256             }
2257         }
2258     };
2259 
2260     /** {@inheritDoc} */
2261     @Override
onBind(Intent intent)2262     public final IBinder onBind(Intent intent) {
2263         onBindClient(intent);
2264         return mBinder;
2265     }
2266 
2267     /** {@inheritDoc} */
2268     @Override
onUnbind(Intent intent)2269     public boolean onUnbind(Intent intent) {
2270         endAllConnections();
2271         return super.onUnbind(intent);
2272     }
2273 
2274     /**
2275      * Used for testing to let the test suite know when the connection service has been bound.
2276      * @hide
2277      */
2278     @TestApi
onBindClient(@ullable Intent intent)2279     public void onBindClient(@Nullable Intent intent) {
2280     }
2281 
2282     /**
2283      * This can be used by telecom to either create a new outgoing conference call or attach
2284      * to an existing incoming conference call. In either case, telecom will cycle through a
2285      * set of services and call createConference until a connection service cancels the process
2286      * or completes it successfully.
2287      */
createConference( final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming, boolean isUnknown)2288     private void createConference(
2289             final PhoneAccountHandle callManagerAccount,
2290             final String callId,
2291             final ConnectionRequest request,
2292             boolean isIncoming,
2293             boolean isUnknown) {
2294 
2295         Conference conference = null;
2296         conference = isIncoming ? onCreateIncomingConference(callManagerAccount, request)
2297                     : onCreateOutgoingConference(callManagerAccount, request);
2298 
2299         Log.d(this, "createConference, conference: %s", conference);
2300         if (conference == null) {
2301             Log.i(this, "createConference, implementation returned null conference.");
2302             conference = Conference.createFailedConference(
2303                     new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONFERENCE"),
2304                     request.getAccountHandle());
2305         }
2306 
2307         Bundle extras = request.getExtras();
2308         Bundle newExtras = new Bundle();
2309         newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
2310         if (extras != null) {
2311             // If the request originated from a remote connection service, we will add some
2312             // tracking information that Telecom can use to keep informed of which package
2313             // made the remote request, and which remote connection service was used.
2314             if (extras.containsKey(Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) {
2315                 newExtras.putString(
2316                         Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME,
2317                         extras.getString(
2318                                 Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME));
2319                 newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
2320                         request.getAccountHandle());
2321             }
2322         }
2323         conference.putExtras(newExtras);
2324 
2325         mConferenceById.put(callId, conference);
2326         mIdByConference.put(conference, callId);
2327 
2328         conference.addListener(mConferenceListener);
2329         ParcelableConference parcelableConference = new ParcelableConference.Builder(
2330                 request.getAccountHandle(), conference.getState())
2331                 .setConnectionCapabilities(conference.getConnectionCapabilities())
2332                 .setConnectionProperties(conference.getConnectionProperties())
2333                 .setVideoAttributes(conference.getVideoProvider() == null
2334                                 ? null : conference.getVideoProvider().getInterface(),
2335                         conference.getVideoState())
2336                 .setConnectTimeMillis(conference.getConnectTimeMillis(),
2337                         conference.getConnectionStartElapsedRealtimeMillis())
2338                 .setStatusHints(conference.getStatusHints())
2339                 .setExtras(conference.getExtras())
2340                 .setAddress(conference.getAddress(), conference.getAddressPresentation())
2341                 .setCallerDisplayName(conference.getCallerDisplayName(),
2342                         conference.getCallerDisplayNamePresentation())
2343                 .setDisconnectCause(conference.getDisconnectCause())
2344                 .setRingbackRequested(conference.isRingbackRequested())
2345                 .build();
2346         if (conference.getState() != Connection.STATE_DISCONNECTED) {
2347             conference.setTelecomCallId(callId);
2348             mAdapter.setVideoProvider(callId, conference.getVideoProvider());
2349             mAdapter.setVideoState(callId, conference.getVideoState());
2350             onConferenceAdded(conference);
2351         }
2352 
2353         Log.d(this, "createConference, calling handleCreateConferenceSuccessful %s", callId);
2354         mAdapter.handleCreateConferenceComplete(
2355                 callId,
2356                 request,
2357                 parcelableConference);
2358     }
2359 
2360     /**
2361      * This can be used by telecom to either create a new outgoing call or attach to an existing
2362      * incoming call. In either case, telecom will cycle through a set of services and call
2363      * createConnection util a connection service cancels the process or completes it successfully.
2364      */
createConnection( final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming, boolean isUnknown)2365     private void createConnection(
2366             final PhoneAccountHandle callManagerAccount,
2367             final String callId,
2368             final ConnectionRequest request,
2369             boolean isIncoming,
2370             boolean isUnknown) {
2371         boolean isLegacyHandover = request.getExtras() != null &&
2372                 request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);
2373         boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
2374                 TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
2375         boolean addSelfManaged = request.getExtras() != null && request.getExtras().getBoolean(
2376                 PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true);
2377         Log.i(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, "
2378                         + "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b, "
2379                         + " addSelfManaged: %b", callManagerAccount, callId, request, isIncoming,
2380                 isUnknown, isLegacyHandover, isHandover, addSelfManaged);
2381 
2382         Connection connection = null;
2383         if (isHandover) {
2384             PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null
2385                     ? (PhoneAccountHandle) request.getExtras().getParcelable(
2386                     TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, android.telecom.PhoneAccountHandle.class) : null;
2387             if (!isIncoming) {
2388                 connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request);
2389             } else {
2390                 connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request);
2391             }
2392         } else {
2393             connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
2394                     : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
2395                     : onCreateOutgoingConnection(callManagerAccount, request);
2396         }
2397         Log.d(this, "createConnection, connection: %s", connection);
2398         if (connection == null) {
2399             Log.i(this, "createConnection, implementation returned null connection.");
2400             connection = Connection.createFailedConnection(
2401                     new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"));
2402         } else {
2403             try {
2404                 Bundle extras = request.getExtras();
2405                 if (extras != null) {
2406                     // If the request originated from a remote connection service, we will add some
2407                     // tracking information that Telecom can use to keep informed of which package
2408                     // made the remote request, and which remote connection service was used.
2409                     if (extras.containsKey(
2410                             Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) {
2411                         Bundle newExtras = new Bundle();
2412                         newExtras.putString(
2413                                 Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME,
2414                                 extras.getString(
2415                                         Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME
2416                                 ));
2417                         newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
2418                                 request.getAccountHandle());
2419                         connection.putExtras(newExtras);
2420                     }
2421                 }
2422             } catch (UnsupportedOperationException ose) {
2423                 // Do nothing; if the ConnectionService reported a failure it will be an instance
2424                 // of an immutable Connection which we cannot edit, so we're out of luck.
2425             }
2426         }
2427 
2428         boolean isSelfManaged =
2429                 (connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED)
2430                         == Connection.PROPERTY_SELF_MANAGED;
2431         // Self-managed Connections should always use voip audio mode; we default here so that the
2432         // local state within the ConnectionService matches the default we assume in Telecom.
2433         if (isSelfManaged) {
2434             connection.setAudioModeIsVoip(true);
2435         }
2436         connection.setTelecomCallId(callId);
2437         PhoneAccountHandle phoneAccountHandle = connection.getPhoneAccountHandle() == null
2438                             ? request.getAccountHandle() : connection.getPhoneAccountHandle();
2439         if (connection.getState() != Connection.STATE_DISCONNECTED) {
2440             addConnection(phoneAccountHandle, callId, connection);
2441         }
2442 
2443         Uri address = connection.getAddress();
2444         String number = address == null ? "null" : address.getSchemeSpecificPart();
2445         Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s",
2446                 Connection.toLogSafePhoneNumber(number),
2447                 Connection.stateToString(connection.getState()),
2448                 Connection.capabilitiesToString(connection.getConnectionCapabilities()),
2449                 Connection.propertiesToString(connection.getConnectionProperties()));
2450 
2451         Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
2452         mAdapter.handleCreateConnectionComplete(
2453                 callId,
2454                 request,
2455                 new ParcelableConnection(
2456                         phoneAccountHandle,
2457                         connection.getState(),
2458                         connection.getConnectionCapabilities(),
2459                         connection.getConnectionProperties(),
2460                         connection.getSupportedAudioRoutes(),
2461                         connection.getAddress(),
2462                         connection.getAddressPresentation(),
2463                         connection.getCallerDisplayName(),
2464                         connection.getCallerDisplayNamePresentation(),
2465                         connection.getVideoProvider() == null ?
2466                                 null : connection.getVideoProvider().getInterface(),
2467                         connection.getVideoState(),
2468                         connection.isRingbackRequested(),
2469                         connection.getAudioModeIsVoip(),
2470                         connection.getConnectTimeMillis(),
2471                         connection.getConnectionStartElapsedRealtimeMillis(),
2472                         connection.getStatusHints(),
2473                         connection.getDisconnectCause(),
2474                         createIdList(connection.getConferenceables()),
2475                         connection.getExtras(),
2476                         connection.getCallerNumberVerificationStatus()));
2477 
2478         if (isIncoming && request.shouldShowIncomingCallUi() && isSelfManaged) {
2479             // Tell ConnectionService to show its incoming call UX.
2480             connection.onShowIncomingCallUi();
2481         }
2482         if (isUnknown) {
2483             triggerConferenceRecalculate();
2484         }
2485     }
2486 
createConnectionFailed(final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming)2487     private void createConnectionFailed(final PhoneAccountHandle callManagerAccount,
2488                                         final String callId, final ConnectionRequest request,
2489                                         boolean isIncoming) {
2490 
2491         Log.i(this, "createConnectionFailed %s", callId);
2492         if (isIncoming) {
2493             onCreateIncomingConnectionFailed(callManagerAccount, request);
2494         } else {
2495             onCreateOutgoingConnectionFailed(callManagerAccount, request);
2496         }
2497     }
2498 
createConferenceFailed(final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming)2499     private void createConferenceFailed(final PhoneAccountHandle callManagerAccount,
2500                                         final String callId, final ConnectionRequest request,
2501                                         boolean isIncoming) {
2502 
2503         Log.i(this, "createConferenceFailed %s", callId);
2504         if (isIncoming) {
2505             onCreateIncomingConferenceFailed(callManagerAccount, request);
2506         } else {
2507             onCreateOutgoingConferenceFailed(callManagerAccount, request);
2508         }
2509     }
2510 
handoverFailed(final String callId, final ConnectionRequest request, int reason)2511     private void handoverFailed(final String callId, final ConnectionRequest request,
2512                                         int reason) {
2513 
2514         Log.i(this, "handoverFailed %s", callId);
2515         onHandoverFailed(request, reason);
2516     }
2517 
2518     /**
2519      * Called by Telecom when the creation of a new Connection has completed and it is now added
2520      * to Telecom.
2521      * @param callId The ID of the connection.
2522      */
notifyCreateConnectionComplete(final String callId)2523     private void notifyCreateConnectionComplete(final String callId) {
2524         Log.i(this, "notifyCreateConnectionComplete %s", callId);
2525         if (callId == null) {
2526             // This could happen if the connection fails quickly and is removed from the
2527             // ConnectionService before Telecom sends the create connection complete callback.
2528             Log.w(this, "notifyCreateConnectionComplete: callId is null.");
2529             return;
2530         }
2531         onCreateConnectionComplete(findConnectionForAction(callId,
2532                 "notifyCreateConnectionComplete"));
2533     }
2534 
2535     /**
2536      * Called by Telecom when the creation of a new Conference has completed and it is now added
2537      * to Telecom.
2538      * @param callId The ID of the connection.
2539      */
notifyCreateConferenceComplete(final String callId)2540     private void notifyCreateConferenceComplete(final String callId) {
2541         Log.i(this, "notifyCreateConferenceComplete %s", callId);
2542         if (callId == null) {
2543             // This could happen if the conference fails quickly and is removed from the
2544             // ConnectionService before Telecom sends the create conference complete callback.
2545             Log.w(this, "notifyCreateConferenceComplete: callId is null.");
2546             return;
2547         }
2548         onCreateConferenceComplete(findConferenceForAction(callId,
2549                 "notifyCreateConferenceComplete"));
2550     }
2551 
2552 
abort(String callId)2553     private void abort(String callId) {
2554         Log.i(this, "abort %s", callId);
2555         findConnectionForAction(callId, "abort").onAbort();
2556     }
2557 
answerVideo(String callId, int videoState)2558     private void answerVideo(String callId, int videoState) {
2559         Log.i(this, "answerVideo %s", callId);
2560         if (mConnectionById.containsKey(callId)) {
2561             findConnectionForAction(callId, "answer").onAnswer(videoState);
2562         } else {
2563             findConferenceForAction(callId, "answer").onAnswer(videoState);
2564         }
2565     }
2566 
answer(String callId)2567     private void answer(String callId) {
2568         Log.i(this, "answer %s", callId);
2569         if (mConnectionById.containsKey(callId)) {
2570             findConnectionForAction(callId, "answer").onAnswer();
2571         } else {
2572             findConferenceForAction(callId, "answer").onAnswer();
2573         }
2574     }
2575 
deflect(String callId, Uri address)2576     private void deflect(String callId, Uri address) {
2577         Log.i(this, "deflect %s", callId);
2578         findConnectionForAction(callId, "deflect").onDeflect(address);
2579     }
2580 
reject(String callId)2581     private void reject(String callId) {
2582         Log.i(this, "reject %s", callId);
2583         if (mConnectionById.containsKey(callId)) {
2584             findConnectionForAction(callId, "reject").onReject();
2585         } else {
2586             findConferenceForAction(callId, "reject").onReject();
2587         }
2588     }
2589 
reject(String callId, String rejectWithMessage)2590     private void reject(String callId, String rejectWithMessage) {
2591         Log.i(this, "reject %s with message", callId);
2592         findConnectionForAction(callId, "reject").onReject(rejectWithMessage);
2593     }
2594 
reject(String callId, @android.telecom.Call.RejectReason int rejectReason)2595     private void reject(String callId, @android.telecom.Call.RejectReason int rejectReason) {
2596         Log.i(this, "reject %s with reason %d", callId, rejectReason);
2597         findConnectionForAction(callId, "reject").onReject(rejectReason);
2598     }
2599 
transfer(String callId, Uri number, boolean isConfirmationRequired)2600     private void transfer(String callId, Uri number, boolean isConfirmationRequired) {
2601         Log.i(this, "transfer %s", callId);
2602         findConnectionForAction(callId, "transfer").onTransfer(number, isConfirmationRequired);
2603     }
2604 
consultativeTransfer(String callId, String otherCallId)2605     private void consultativeTransfer(String callId, String otherCallId) {
2606         Log.i(this, "consultativeTransfer %s", callId);
2607         Connection connection1 = findConnectionForAction(callId, "consultativeTransfer");
2608         Connection connection2 = findConnectionForAction(otherCallId, " consultativeTransfer");
2609         connection1.onTransfer(connection2);
2610     }
2611 
silence(String callId)2612     private void silence(String callId) {
2613         Log.i(this, "silence %s", callId);
2614         findConnectionForAction(callId, "silence").onSilence();
2615     }
2616 
disconnect(String callId)2617     private void disconnect(String callId) {
2618         Log.i(this, "disconnect %s", callId);
2619         if (mConnectionById.containsKey(callId)) {
2620             findConnectionForAction(callId, "disconnect").onDisconnect();
2621         } else {
2622             findConferenceForAction(callId, "disconnect").onDisconnect();
2623         }
2624     }
2625 
hold(String callId)2626     private void hold(String callId) {
2627         Log.i(this, "hold %s", callId);
2628         if (mConnectionById.containsKey(callId)) {
2629             findConnectionForAction(callId, "hold").onHold();
2630         } else {
2631             findConferenceForAction(callId, "hold").onHold();
2632         }
2633     }
2634 
unhold(String callId)2635     private void unhold(String callId) {
2636         Log.i(this, "unhold %s", callId);
2637         if (mConnectionById.containsKey(callId)) {
2638             findConnectionForAction(callId, "unhold").onUnhold();
2639         } else {
2640             findConferenceForAction(callId, "unhold").onUnhold();
2641         }
2642     }
2643 
onCallAudioStateChanged(String callId, CallAudioState callAudioState)2644     private void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {
2645         Log.i(this, "onAudioStateChanged %s %s", callId, callAudioState);
2646         if (mConnectionById.containsKey(callId)) {
2647             findConnectionForAction(callId, "onCallAudioStateChanged").setCallAudioState(
2648                     callAudioState);
2649         } else {
2650             findConferenceForAction(callId, "onCallAudioStateChanged").setCallAudioState(
2651                     callAudioState);
2652         }
2653     }
2654 
onCallEndpointChanged(String callId, CallEndpoint callEndpoint)2655     private void onCallEndpointChanged(String callId, CallEndpoint callEndpoint) {
2656         Log.i(this, "onCallEndpointChanged %s %s", callId, callEndpoint);
2657         if (mConnectionById.containsKey(callId)) {
2658             findConnectionForAction(callId, "onCallEndpointChanged").setCallEndpoint(callEndpoint);
2659         } else {
2660             findConferenceForAction(callId, "onCallEndpointChanged").setCallEndpoint(callEndpoint);
2661         }
2662     }
2663 
onAvailableCallEndpointsChanged(String callId, List<CallEndpoint> availableCallEndpoints)2664     private void onAvailableCallEndpointsChanged(String callId,
2665             List<CallEndpoint> availableCallEndpoints) {
2666         Log.i(this, "onAvailableCallEndpointsChanged %s", callId);
2667         if (mConnectionById.containsKey(callId)) {
2668             findConnectionForAction(callId, "onAvailableCallEndpointsChanged")
2669                     .setAvailableCallEndpoints(availableCallEndpoints);
2670         } else {
2671             findConferenceForAction(callId, "onAvailableCallEndpointsChanged")
2672                     .setAvailableCallEndpoints(availableCallEndpoints);
2673         }
2674     }
2675 
onMuteStateChanged(String callId, boolean isMuted)2676     private void onMuteStateChanged(String callId, boolean isMuted) {
2677         Log.i(this, "onMuteStateChanged %s %s", callId, isMuted);
2678         if (mConnectionById.containsKey(callId)) {
2679             findConnectionForAction(callId, "onMuteStateChanged").setMuteState(isMuted);
2680         } else {
2681             findConferenceForAction(callId, "onMuteStateChanged").setMuteState(isMuted);
2682         }
2683     }
2684 
onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi)2685     private void onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi) {
2686         Log.i(this, "onUsingAlternativeUi %s %s", callId, isUsingAlternativeUi);
2687         if (mConnectionById.containsKey(callId)) {
2688             findConnectionForAction(callId, "onUsingAlternativeUi")
2689                     .onUsingAlternativeUi(isUsingAlternativeUi);
2690         }
2691     }
2692 
onTrackedByNonUiService(String callId, boolean isTracked)2693     private void onTrackedByNonUiService(String callId, boolean isTracked) {
2694         Log.i(this, "onTrackedByNonUiService %s %s", callId, isTracked);
2695         if (mConnectionById.containsKey(callId)) {
2696             findConnectionForAction(callId, "onTrackedByNonUiService")
2697                     .onTrackedByNonUiService(isTracked);
2698         }
2699     }
2700 
playDtmfTone(String callId, char digit)2701     private void playDtmfTone(String callId, char digit) {
2702         Log.i(this, "playDtmfTone %s %s", callId, Log.pii(digit));
2703         if (mConnectionById.containsKey(callId)) {
2704             findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
2705         } else {
2706             findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
2707         }
2708     }
2709 
stopDtmfTone(String callId)2710     private void stopDtmfTone(String callId) {
2711         Log.i(this, "stopDtmfTone %s", callId);
2712         if (mConnectionById.containsKey(callId)) {
2713             findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
2714         } else {
2715             findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone();
2716         }
2717     }
2718 
conference(String callId1, String callId2)2719     private void conference(String callId1, String callId2) {
2720         Log.i(this, "conference %s, %s", callId1, callId2);
2721 
2722         // Attempt to get second connection or conference.
2723         Connection connection2 = findConnectionForAction(callId2, "conference");
2724         Conference conference2 = getNullConference();
2725         if (connection2 == getNullConnection()) {
2726             conference2 = findConferenceForAction(callId2, "conference");
2727             if (conference2 == getNullConference()) {
2728                 Log.w(this, "Connection2 or Conference2 missing in conference request %s.",
2729                         callId2);
2730                 return;
2731             }
2732         }
2733 
2734         // Attempt to get first connection or conference and perform merge.
2735         Connection connection1 = findConnectionForAction(callId1, "conference");
2736         if (connection1 == getNullConnection()) {
2737             Conference conference1 = findConferenceForAction(callId1, "addConnection");
2738             if (conference1 == getNullConference()) {
2739                 Log.w(this,
2740                         "Connection1 or Conference1 missing in conference request %s.",
2741                         callId1);
2742             } else {
2743                 // Call 1 is a conference.
2744                 if (connection2 != getNullConnection()) {
2745                     // Call 2 is a connection so merge via call 1 (conference).
2746                     conference1.onMerge(connection2);
2747                 } else {
2748                     // Call 2 is ALSO a conference; this should never happen.
2749                     Log.wtf(this, "There can only be one conference and an attempt was made to " +
2750                             "merge two conferences.");
2751                     return;
2752                 }
2753             }
2754         } else {
2755             // Call 1 is a connection.
2756             if (conference2 != getNullConference()) {
2757                 // Call 2 is a conference, so merge via call 2.
2758                 conference2.onMerge(connection1);
2759             } else {
2760                 // Call 2 is a connection, so merge together.
2761                 onConference(connection1, connection2);
2762             }
2763         }
2764     }
2765 
splitFromConference(String callId)2766     private void splitFromConference(String callId) {
2767         Log.i(this, "splitFromConference(%s)", callId);
2768 
2769         Connection connection = findConnectionForAction(callId, "splitFromConference");
2770         if (connection == getNullConnection()) {
2771             Log.w(this, "Connection missing in conference request %s.", callId);
2772             return;
2773         }
2774 
2775         Conference conference = connection.getConference();
2776         if (conference != null) {
2777             conference.onSeparate(connection);
2778         }
2779     }
2780 
mergeConference(String callId)2781     private void mergeConference(String callId) {
2782         Log.i(this, "mergeConference(%s)", callId);
2783         Conference conference = findConferenceForAction(callId, "mergeConference");
2784         if (conference != null) {
2785             conference.onMerge();
2786         }
2787     }
2788 
swapConference(String callId)2789     private void swapConference(String callId) {
2790         Log.i(this, "swapConference(%s)", callId);
2791         Conference conference = findConferenceForAction(callId, "swapConference");
2792         if (conference != null) {
2793             conference.onSwap();
2794         }
2795     }
2796 
addConferenceParticipants(String callId, List<Uri> participants)2797     private void addConferenceParticipants(String callId, List<Uri> participants) {
2798         Log.i(this, "addConferenceParticipants(%s)", callId);
2799         if (mConnectionById.containsKey(callId)) {
2800             findConnectionForAction(callId, "addConferenceParticipants")
2801                     .onAddConferenceParticipants(participants);
2802         } else {
2803             findConferenceForAction(callId, "addConferenceParticipants")
2804                     .onAddConferenceParticipants(participants);
2805         }
2806     }
2807 
2808     /**
2809      * Notifies a {@link Connection} of a request to pull an external call.
2810      *
2811      * See {@link Call#pullExternalCall()}.
2812      *
2813      * @param callId The ID of the call to pull.
2814      */
pullExternalCall(String callId)2815     private void pullExternalCall(String callId) {
2816         Log.i(this, "pullExternalCall(%s)", callId);
2817         Connection connection = findConnectionForAction(callId, "pullExternalCall");
2818         if (connection != null) {
2819             connection.onPullExternalCall();
2820         }
2821     }
2822 
2823     /**
2824      * Notifies a {@link Connection} of a call event.
2825      *
2826      * See {@link Call#sendCallEvent(String, Bundle)}.
2827      *
2828      * @param callId The ID of the call receiving the event.
2829      * @param event The event.
2830      * @param extras Extras associated with the event.
2831      */
sendCallEvent(String callId, String event, Bundle extras)2832     private void sendCallEvent(String callId, String event, Bundle extras) {
2833         Log.i(this, "sendCallEvent(%s, %s)", callId, event);
2834         Connection connection = findConnectionForAction(callId, "sendCallEvent");
2835         if (connection != null) {
2836             connection.onCallEvent(event, extras);
2837         }
2838     }
2839 
onCallFilteringCompleted(String callId, Connection.CallFilteringCompletionInfo callFilteringCompletionInfo)2840     private void onCallFilteringCompleted(String callId, Connection.CallFilteringCompletionInfo
2841             callFilteringCompletionInfo) {
2842         Log.i(this, "onCallFilteringCompleted(%s, %s)", callId, callFilteringCompletionInfo);
2843         Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted");
2844         if (connection != null) {
2845             connection.onCallFilteringCompleted(callFilteringCompletionInfo);
2846         }
2847     }
2848 
2849     /**
2850      * Notifies a {@link Connection} that a handover has completed.
2851      *
2852      * @param callId The ID of the call which completed handover.
2853      */
notifyHandoverComplete(String callId)2854     private void notifyHandoverComplete(String callId) {
2855         Log.i(this, "notifyHandoverComplete(%s)", callId);
2856         Connection connection = findConnectionForAction(callId, "notifyHandoverComplete");
2857         if (connection != null) {
2858             connection.onHandoverComplete();
2859         }
2860     }
2861 
2862     /**
2863      * Notifies a {@link Connection} or {@link Conference} of a change to the extras from Telecom.
2864      * <p>
2865      * These extra changes can originate from Telecom itself, or from an {@link InCallService} via
2866      * the {@link android.telecom.Call#putExtra(String, boolean)},
2867      * {@link android.telecom.Call#putExtra(String, int)},
2868      * {@link android.telecom.Call#putExtra(String, String)},
2869      * {@link Call#removeExtras(List)}.
2870      *
2871      * @param callId The ID of the call receiving the event.
2872      * @param extras The new extras bundle.
2873      */
handleExtrasChanged(String callId, Bundle extras)2874     private void handleExtrasChanged(String callId, Bundle extras) {
2875         Log.i(this, "handleExtrasChanged(%s, %s)", callId, extras);
2876         if (mConnectionById.containsKey(callId)) {
2877             findConnectionForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras);
2878         } else if (mConferenceById.containsKey(callId)) {
2879             findConferenceForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras);
2880         }
2881     }
2882 
startRtt(String callId, Connection.RttTextStream rttTextStream)2883     private void startRtt(String callId, Connection.RttTextStream rttTextStream) {
2884         Log.i(this, "startRtt(%s)", callId);
2885         if (mConnectionById.containsKey(callId)) {
2886             findConnectionForAction(callId, "startRtt").onStartRtt(rttTextStream);
2887         } else if (mConferenceById.containsKey(callId)) {
2888             Log.w(this, "startRtt called on a conference.");
2889         }
2890     }
2891 
stopRtt(String callId)2892     private void stopRtt(String callId) {
2893         Log.i(this, "stopRtt(%s)", callId);
2894         if (mConnectionById.containsKey(callId)) {
2895             findConnectionForAction(callId, "stopRtt").onStopRtt();
2896         } else if (mConferenceById.containsKey(callId)) {
2897             Log.w(this, "stopRtt called on a conference.");
2898         }
2899     }
2900 
handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream)2901     private void handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream) {
2902         Log.i(this, "handleRttUpgradeResponse(%s, %s)", callId, rttTextStream == null);
2903         if (mConnectionById.containsKey(callId)) {
2904             findConnectionForAction(callId, "handleRttUpgradeResponse")
2905                     .handleRttUpgradeResponse(rttTextStream);
2906         } else if (mConferenceById.containsKey(callId)) {
2907             Log.w(this, "handleRttUpgradeResponse called on a conference.");
2908         }
2909     }
2910 
onPostDialContinue(String callId, boolean proceed)2911     private void onPostDialContinue(String callId, boolean proceed) {
2912         Log.i(this, "onPostDialContinue(%s)", callId);
2913         findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
2914     }
2915 
onAdapterAttached()2916     private void onAdapterAttached() {
2917         if (mAreAccountsInitialized) {
2918             // No need to query again if we already did it.
2919             return;
2920         }
2921 
2922         String callingPackage = getOpPackageName();
2923 
2924         mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
2925             @Override
2926             public void onResult(
2927                     final List<ComponentName> componentNames,
2928                     final List<IBinder> services) {
2929                 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oR", null /*lock*/) {
2930                     @Override
2931                     public void loggedRun() {
2932                         for (int i = 0; i < componentNames.size() && i < services.size(); i++) {
2933                             mRemoteConnectionManager.addConnectionService(
2934                                     componentNames.get(i),
2935                                     IConnectionService.Stub.asInterface(services.get(i)));
2936                         }
2937                         onAccountsInitialized();
2938                         Log.d(this, "remote connection services found: " + services);
2939                     }
2940                 }.prepare());
2941             }
2942 
2943             @Override
2944             public void onError() {
2945                 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oE", null /*lock*/) {
2946                     @Override
2947                     public void loggedRun() {
2948                         mAreAccountsInitialized = true;
2949                     }
2950                 }.prepare());
2951             }
2952         }, callingPackage);
2953     }
2954 
2955     /**
2956      * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
2957      * incoming request. This is used by {@code ConnectionService}s that are registered with
2958      * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to manage
2959      * SIM-based incoming calls.
2960      *
2961      * @param connectionManagerPhoneAccount See description at
2962      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
2963      * @param request Details about the incoming call.
2964      * @return The {@code Connection} object to satisfy this call, or {@code null} to
2965      *         not handle the call.
2966      */
createRemoteIncomingConnection( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)2967     public final @Nullable RemoteConnection createRemoteIncomingConnection(
2968             @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
2969             @NonNull ConnectionRequest request) {
2970         return mRemoteConnectionManager.createRemoteConnection(
2971                 connectionManagerPhoneAccount, request, true);
2972     }
2973 
2974     /**
2975      * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
2976      * outgoing request. This is used by {@code ConnectionService}s that are registered with
2977      * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to use the
2978      * SIM-based {@code ConnectionService} to place its outgoing calls.
2979      *
2980      * @param connectionManagerPhoneAccount See description at
2981      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
2982      * @param request Details about the outgoing call.
2983      * @return The {@code Connection} object to satisfy this call, or {@code null} to
2984      *         not handle the call.
2985      */
createRemoteOutgoingConnection( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)2986     public final @Nullable RemoteConnection createRemoteOutgoingConnection(
2987             @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
2988             @NonNull ConnectionRequest request) {
2989         return mRemoteConnectionManager.createRemoteConnection(
2990                 connectionManagerPhoneAccount, request, false);
2991     }
2992 
2993     /**
2994      * Ask some other {@code ConnectionService} to create a {@code RemoteConference} given an
2995      * incoming request. This is used by {@code ConnectionService}s that are registered with
2996      * {@link PhoneAccount#CAPABILITY_ADHOC_CONFERENCE_CALLING}.
2997      *
2998      * @param connectionManagerPhoneAccount See description at
2999      *          {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
3000      * @param request Details about the incoming conference call.
3001      * @return The {@code RemoteConference} object to satisfy this call, or {@code null} to not
3002      *         handle the call.
3003      */
createRemoteIncomingConference( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @Nullable ConnectionRequest request)3004     public final @Nullable RemoteConference createRemoteIncomingConference(
3005             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
3006             @Nullable ConnectionRequest request) {
3007         return mRemoteConnectionManager.createRemoteConference(connectionManagerPhoneAccount,
3008                 request, true);
3009     }
3010 
3011     /**
3012      * Ask some other {@code ConnectionService} to create a {@code RemoteConference} given an
3013      * outgoing request. This is used by {@code ConnectionService}s that are registered with
3014      * {@link PhoneAccount#CAPABILITY_ADHOC_CONFERENCE_CALLING}.
3015      *
3016      * @param connectionManagerPhoneAccount See description at
3017      *          {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
3018      * @param request Details about the outgoing conference call.
3019      * @return The {@code RemoteConference} object to satisfy this call, or {@code null} to not
3020      *         handle the call.
3021      */
createRemoteOutgoingConference( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @Nullable ConnectionRequest request)3022     public final @Nullable RemoteConference createRemoteOutgoingConference(
3023             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
3024             @Nullable ConnectionRequest request) {
3025         return mRemoteConnectionManager.createRemoteConference(connectionManagerPhoneAccount,
3026                 request, false);
3027     }
3028 
3029     /**
3030      * Indicates to the relevant {@code RemoteConnectionService} that the specified
3031      * {@link RemoteConnection}s should be merged into a conference call.
3032      * <p>
3033      * If the conference request is successful, the method {@link #onRemoteConferenceAdded} will
3034      * be invoked.
3035      *
3036      * @param remoteConnection1 The first of the remote connections to conference.
3037      * @param remoteConnection2 The second of the remote connections to conference.
3038      */
conferenceRemoteConnections( RemoteConnection remoteConnection1, RemoteConnection remoteConnection2)3039     public final void conferenceRemoteConnections(
3040             RemoteConnection remoteConnection1,
3041             RemoteConnection remoteConnection2) {
3042         mRemoteConnectionManager.conferenceRemoteConnections(remoteConnection1, remoteConnection2);
3043     }
3044 
3045     /**
3046      * Adds a new conference call. When a conference call is created either as a result of an
3047      * explicit request via {@link #onConference} or otherwise, the connection service should supply
3048      * an instance of {@link Conference} by invoking this method. A conference call provided by this
3049      * method will persist until {@link Conference#destroy} is invoked on the conference instance.
3050      *
3051      * @param conference The new conference object.
3052      */
addConference(Conference conference)3053     public final void addConference(Conference conference) {
3054         Log.d(this, "addConference: conference=%s", conference);
3055 
3056         String id = addConferenceInternal(conference);
3057         if (id != null) {
3058             List<String> connectionIds = new ArrayList<>(2);
3059             for (Connection connection : conference.getConnections()) {
3060                 if (mIdByConnection.containsKey(connection)) {
3061                     connectionIds.add(mIdByConnection.get(connection));
3062                 }
3063             }
3064             conference.setTelecomCallId(id);
3065             ParcelableConference parcelableConference = new ParcelableConference.Builder(
3066                     conference.getPhoneAccountHandle(), conference.getState())
3067                     .setConnectionCapabilities(conference.getConnectionCapabilities())
3068                     .setConnectionProperties(conference.getConnectionProperties())
3069                     .setConnectionIds(connectionIds)
3070                     .setVideoAttributes(conference.getVideoProvider() == null
3071                                     ? null : conference.getVideoProvider().getInterface(),
3072                             conference.getVideoState())
3073                     .setConnectTimeMillis(conference.getConnectTimeMillis(),
3074                             conference.getConnectionStartElapsedRealtimeMillis())
3075                     .setStatusHints(conference.getStatusHints())
3076                     .setExtras(conference.getExtras())
3077                     .setAddress(conference.getAddress(), conference.getAddressPresentation())
3078                     .setCallerDisplayName(conference.getCallerDisplayName(),
3079                             conference.getCallerDisplayNamePresentation())
3080                     .setDisconnectCause(conference.getDisconnectCause())
3081                     .setRingbackRequested(conference.isRingbackRequested())
3082                     .setCallDirection(conference.getCallDirection())
3083                     .build();
3084 
3085             mAdapter.addConferenceCall(id, parcelableConference);
3086             mAdapter.setVideoProvider(id, conference.getVideoProvider());
3087             mAdapter.setVideoState(id, conference.getVideoState());
3088             // In some instances a conference can start its life as a standalone call with just a
3089             // single participant; ensure we signal to Telecom in this case.
3090             if (!conference.isMultiparty()) {
3091                 mAdapter.setConferenceState(id, conference.isMultiparty());
3092             }
3093 
3094             // Go through any child calls and set the parent.
3095             for (Connection connection : conference.getConnections()) {
3096                 String connectionId = mIdByConnection.get(connection);
3097                 if (connectionId != null) {
3098                     mAdapter.setIsConferenced(connectionId, id);
3099                 }
3100             }
3101             onConferenceAdded(conference);
3102         }
3103     }
3104 
3105     /**
3106      * Adds a connection created by the {@link ConnectionService} and informs telecom of the new
3107      * connection.
3108      *
3109      * @param phoneAccountHandle The phone account handle for the connection.
3110      * @param connection The connection to add.
3111      */
addExistingConnection(PhoneAccountHandle phoneAccountHandle, Connection connection)3112     public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
3113             Connection connection) {
3114         addExistingConnection(phoneAccountHandle, connection, null /* conference */);
3115     }
3116 
3117     /**
3118      * Call to inform Telecom that your {@link ConnectionService} has released call resources (e.g
3119      * microphone, camera).
3120      *
3121      * <p>
3122      * The {@link ConnectionService} will be disconnected when it failed to call this method within
3123      * 5 seconds after {@link #onConnectionServiceFocusLost()} is called.
3124      *
3125      * @see ConnectionService#onConnectionServiceFocusLost()
3126      */
connectionServiceFocusReleased()3127     public final void connectionServiceFocusReleased() {
3128         mAdapter.onConnectionServiceFocusReleased();
3129     }
3130 
3131     /**
3132      * Adds a connection created by the {@link ConnectionService} and informs telecom of the new
3133      * connection, as well as adding that connection to the specified conference.
3134      * <p>
3135      * Note: This API is intended ONLY for use by the Telephony stack to provide an easy way to add
3136      * IMS conference participants to be added to a conference in a single step; this helps ensure
3137      * UI updates happen atomically, rather than adding the connection and then adding it to
3138      * the conference in another step.
3139      *
3140      * @param phoneAccountHandle The phone account handle for the connection.
3141      * @param connection The connection to add.
3142      * @param conference The parent conference of the new connection.
3143      * @hide
3144      */
3145     @SystemApi
addExistingConnection(@onNull PhoneAccountHandle phoneAccountHandle, @NonNull Connection connection, @NonNull Conference conference)3146     public final void addExistingConnection(@NonNull PhoneAccountHandle phoneAccountHandle,
3147             @NonNull Connection connection, @NonNull Conference conference) {
3148 
3149         String id = addExistingConnectionInternal(phoneAccountHandle, connection);
3150         if (id != null) {
3151             List<String> emptyList = new ArrayList<>(0);
3152             String conferenceId = null;
3153             if (conference != null) {
3154                 conferenceId = mIdByConference.get(conference);
3155             }
3156 
3157             ParcelableConnection parcelableConnection = new ParcelableConnection(
3158                     phoneAccountHandle,
3159                     connection.getState(),
3160                     connection.getConnectionCapabilities(),
3161                     connection.getConnectionProperties(),
3162                     connection.getSupportedAudioRoutes(),
3163                     connection.getAddress(),
3164                     connection.getAddressPresentation(),
3165                     connection.getCallerDisplayName(),
3166                     connection.getCallerDisplayNamePresentation(),
3167                     connection.getVideoProvider() == null ?
3168                             null : connection.getVideoProvider().getInterface(),
3169                     connection.getVideoState(),
3170                     connection.isRingbackRequested(),
3171                     connection.getAudioModeIsVoip(),
3172                     connection.getConnectTimeMillis(),
3173                     connection.getConnectionStartElapsedRealtimeMillis(),
3174                     connection.getStatusHints(),
3175                     connection.getDisconnectCause(),
3176                     emptyList,
3177                     connection.getExtras(),
3178                     conferenceId,
3179                     connection.getCallDirection(),
3180                     Connection.VERIFICATION_STATUS_NOT_VERIFIED);
3181             mAdapter.addExistingConnection(id, parcelableConnection);
3182         }
3183     }
3184 
3185     /**
3186      * Returns all the active {@code Connection}s for which this {@code ConnectionService}
3187      * has taken responsibility.
3188      *
3189      * @return A collection of {@code Connection}s created by this {@code ConnectionService}.
3190      */
getAllConnections()3191     public final Collection<Connection> getAllConnections() {
3192         return mConnectionById.values();
3193     }
3194 
3195     /**
3196      * Returns all the active {@code Conference}s for which this {@code ConnectionService}
3197      * has taken responsibility.
3198      *
3199      * @return A collection of {@code Conference}s created by this {@code ConnectionService}.
3200      */
getAllConferences()3201     public final Collection<Conference> getAllConferences() {
3202         return mConferenceById.values();
3203     }
3204 
3205     /**
3206      * Create a {@code Connection} given an incoming request. This is used to attach to existing
3207      * incoming calls.
3208      *
3209      * @param connectionManagerPhoneAccount See description at
3210      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
3211      * @param request Details about the incoming call.
3212      * @return The {@code Connection} object to satisfy this call, or {@code null} to
3213      *         not handle the call.
3214      */
onCreateIncomingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)3215     public Connection onCreateIncomingConnection(
3216             PhoneAccountHandle connectionManagerPhoneAccount,
3217             ConnectionRequest request) {
3218         return null;
3219     }
3220     /**
3221      * Create a {@code Conference} given an incoming request. This is used to attach to an incoming
3222      * conference call initiated via
3223      * {@link TelecomManager#addNewIncomingConference(PhoneAccountHandle, Bundle)}.
3224      *
3225      * @param connectionManagerPhoneAccount See description at
3226      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
3227      * @param request Details about the incoming conference call.
3228      * @return The {@code Conference} object to satisfy this call. If the conference attempt is
3229      *         failed, the return value will be a result of an invocation of
3230      *         {@link Connection#createFailedConnection(DisconnectCause)}.
3231      *         Return {@code null} if the {@link ConnectionService} cannot handle the call.
3232      */
onCreateIncomingConference( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)3233     public @Nullable Conference onCreateIncomingConference(
3234             @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
3235             @NonNull ConnectionRequest request) {
3236         return null;
3237     }
3238 
3239     /**
3240      * Called by Telecom after the {@link Connection} returned by
3241      * {@link #onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}
3242      * or {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} has been
3243      * added to the {@link ConnectionService} and sent to Telecom.
3244      *
3245      * @param connection the {@link Connection} which was added to Telecom.
3246      */
3247     @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
onCreateConnectionComplete(@onNull Connection connection)3248     public void onCreateConnectionComplete(@NonNull Connection connection) {
3249     }
3250 
3251     /**
3252      * Called by Telecom after the {@link Conference} returned by
3253      * {@link #onCreateIncomingConference(PhoneAccountHandle, ConnectionRequest)}
3254      * or {@link #onCreateOutgoingConference(PhoneAccountHandle, ConnectionRequest)} has been
3255      * added to the {@link ConnectionService} and sent to Telecom.
3256      *
3257      * @param conference the {@link Conference} which was added to Telecom.
3258      */
3259     @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
onCreateConferenceComplete(@onNull Conference conference)3260     public void onCreateConferenceComplete(@NonNull Conference conference) {
3261     }
3262 
3263 
3264     /**
3265      * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
3266      * incoming {@link Connection} was denied.
3267      * <p>
3268      * Used when a self-managed {@link ConnectionService} attempts to create a new incoming
3269      * {@link Connection}, but Telecom has determined that the call cannot be allowed at this time.
3270      * The {@link ConnectionService} is responsible for silently rejecting the new incoming
3271      * {@link Connection}.
3272      * <p>
3273      * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
3274      *
3275      * @param connectionManagerPhoneAccount See description at
3276      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
3277      * @param request The incoming connection request.
3278      */
onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)3279     public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
3280                                                  ConnectionRequest request) {
3281     }
3282 
3283     /**
3284      * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
3285      * outgoing {@link Connection} was denied.
3286      * <p>
3287      * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing
3288      * {@link Connection}, but Telecom has determined that the call cannot be placed at this time.
3289      * The {@link ConnectionService} is responisible for informing the user that the
3290      * {@link Connection} cannot be made at this time.
3291      * <p>
3292      * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
3293      *
3294      * @param connectionManagerPhoneAccount See description at
3295      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
3296      * @param request The outgoing connection request.
3297      */
onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)3298     public void onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
3299                                                  ConnectionRequest request) {
3300     }
3301 
3302     /**
3303      * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
3304      * incoming {@link Conference} was denied.
3305      * <p>
3306      * Used when a self-managed {@link ConnectionService} attempts to create a new incoming
3307      * {@link Conference}, but Telecom has determined that the call cannot be allowed at this time.
3308      * The {@link ConnectionService} is responsible for silently rejecting the new incoming
3309      * {@link Conference}.
3310      * <p>
3311      * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
3312      *
3313      * @param connectionManagerPhoneAccount See description at
3314      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
3315      * @param request The incoming connection request.
3316      */
onCreateIncomingConferenceFailed( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @Nullable ConnectionRequest request)3317     public void onCreateIncomingConferenceFailed(
3318             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
3319             @Nullable ConnectionRequest request) {
3320     }
3321 
3322     /**
3323      * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
3324      * outgoing {@link Conference} was denied.
3325      * <p>
3326      * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing
3327      * {@link Conference}, but Telecom has determined that the call cannot be placed at this time.
3328      * The {@link ConnectionService} is responisible for informing the user that the
3329      * {@link Conference} cannot be made at this time.
3330      * <p>
3331      * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
3332      *
3333      * @param connectionManagerPhoneAccount See description at
3334      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
3335      * @param request The outgoing connection request.
3336      */
onCreateOutgoingConferenceFailed( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)3337     public void onCreateOutgoingConferenceFailed(
3338             @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
3339             @NonNull ConnectionRequest request) {
3340     }
3341 
3342 
3343     /**
3344      * Trigger recalculate functinality for conference calls. This is used when a Telephony
3345      * Connection is part of a conference controller but is not yet added to Connection
3346      * Service and hence cannot be added to the conference call.
3347      *
3348      * @hide
3349      */
triggerConferenceRecalculate()3350     public void triggerConferenceRecalculate() {
3351     }
3352 
3353     /**
3354      * Create a {@code Connection} given an outgoing request. This is used to initiate new
3355      * outgoing calls.
3356      *
3357      * @param connectionManagerPhoneAccount The connection manager account to use for managing
3358      *         this call.
3359      *         <p>
3360      *         If this parameter is not {@code null}, it means that this {@code ConnectionService}
3361      *         has registered one or more {@code PhoneAccount}s having
3362      *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
3363      *         one of these {@code PhoneAccount}s, while the {@code request} will contain another
3364      *         (usually but not always distinct) {@code PhoneAccount} to be used for actually
3365      *         making the connection.
3366      *         <p>
3367      *         If this parameter is {@code null}, it means that this {@code ConnectionService} is
3368      *         being asked to make a direct connection. The
3369      *         {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
3370      *         a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
3371      *         making the connection.
3372      * @param request Details about the outgoing call.
3373      * @return The {@code Connection} object to satisfy this call, or the result of an invocation
3374      *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
3375      */
onCreateOutgoingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)3376     public Connection onCreateOutgoingConnection(
3377             PhoneAccountHandle connectionManagerPhoneAccount,
3378             ConnectionRequest request) {
3379         return null;
3380     }
3381 
3382     /**
3383      * Create a {@code Conference} given an outgoing request. This is used to initiate new
3384      * outgoing conference call requested via
3385      * {@link TelecomManager#startConference(List, Bundle)}.
3386      *
3387      * @param connectionManagerPhoneAccount The connection manager account to use for managing
3388      *         this call.
3389      *         <p>
3390      *         If this parameter is not {@code null}, it means that this {@code ConnectionService}
3391      *         has registered one or more {@code PhoneAccount}s having
3392      *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
3393      *         one of these {@code PhoneAccount}s, while the {@code request} will contain another
3394      *         (usually but not always distinct) {@code PhoneAccount} to be used for actually
3395      *         making the connection.
3396      *         <p>
3397      *         If this parameter is {@code null}, it means that this {@code ConnectionService} is
3398      *         being asked to make a direct connection. The
3399      *         {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
3400      *         a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
3401      *         making the connection.
3402      * @param request Details about the outgoing call.
3403      * @return The {@code Conference} object to satisfy this call. If the conference attempt is
3404      *         failed, the return value will be a result of an invocation of
3405      *         {@link Connection#createFailedConnection(DisconnectCause)}.
3406      *         Return {@code null} if the {@link ConnectionService} cannot handle the call.
3407      */
onCreateOutgoingConference( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)3408     public @Nullable Conference onCreateOutgoingConference(
3409             @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
3410             @NonNull ConnectionRequest request) {
3411         return null;
3412     }
3413 
3414 
3415     /**
3416      * Called by Telecom to request that a {@link ConnectionService} creates an instance of an
3417      * outgoing handover {@link Connection}.
3418      * <p>
3419      * A call handover is the process where an ongoing call is transferred from one app (i.e.
3420      * {@link ConnectionService} to another app.  The user could, for example, choose to continue a
3421      * mobile network call in a video calling app.  The mobile network call via the Telephony stack
3422      * is referred to as the source of the handover, and the video calling app is referred to as the
3423      * destination.
3424      * <p>
3425      * When considering a handover scenario the <em>initiating</em> device is where a user initiated
3426      * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo(
3427      * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em>
3428      * device.
3429      * <p>
3430      * This method is called on the destination {@link ConnectionService} on <em>initiating</em>
3431      * device when the user initiates a handover request from one app to another.  The user request
3432      * originates in the {@link InCallService} via
3433      * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
3434      * <p>
3435      * For a full discussion of the handover process and the APIs involved, see
3436      * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
3437      * <p>
3438      * Implementations of this method should return an instance of {@link Connection} which
3439      * represents the handover.  If your app does not wish to accept a handover to it at this time,
3440      * you can return {@code null}.  The code below shows an example of how this is done.
3441      * <pre>
3442      * {@code
3443      * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle
3444      *     fromPhoneAccountHandle, ConnectionRequest request) {
3445      *   if (!isHandoverAvailable()) {
3446      *       return null;
3447      *   }
3448      *   MyConnection connection = new MyConnection();
3449      *   connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
3450      *   connection.setVideoState(request.getVideoState());
3451      *   return connection;
3452      * }
3453      * }
3454      * </pre>
3455      *
3456      * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the
3457      *                               ConnectionService which needs to handover the call.
3458      * @param request Details about the call to handover.
3459      * @return {@link Connection} instance corresponding to the handover call.
3460      */
onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, ConnectionRequest request)3461     public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
3462                                                          ConnectionRequest request) {
3463         return null;
3464     }
3465 
3466     /**
3467      * Called by Telecom to request that a {@link ConnectionService} creates an instance of an
3468      * incoming handover {@link Connection}.
3469      * <p>
3470      * A call handover is the process where an ongoing call is transferred from one app (i.e.
3471      * {@link ConnectionService} to another app.  The user could, for example, choose to continue a
3472      * mobile network call in a video calling app.  The mobile network call via the Telephony stack
3473      * is referred to as the source of the handover, and the video calling app is referred to as the
3474      * destination.
3475      * <p>
3476      * When considering a handover scenario the <em>initiating</em> device is where a user initiated
3477      * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo(
3478      * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em>
3479      * device.
3480      * <p>
3481      * This method is called on the destination app on the <em>receiving</em> device when the
3482      * destination app calls {@link TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)} to
3483      * accept an incoming handover from the <em>initiating</em> device.
3484      * <p>
3485      * For a full discussion of the handover process and the APIs involved, see
3486      * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
3487      * <p>
3488      * Implementations of this method should return an instance of {@link Connection} which
3489      * represents the handover.  The code below shows an example of how this is done.
3490      * <pre>
3491      * {@code
3492      * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle
3493      *     fromPhoneAccountHandle, ConnectionRequest request) {
3494      *   // Given that your app requested to accept the handover, you should not return null here.
3495      *   MyConnection connection = new MyConnection();
3496      *   connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
3497      *   connection.setVideoState(request.getVideoState());
3498      *   return connection;
3499      * }
3500      * }
3501      * </pre>
3502      *
3503      * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the
3504      *                               ConnectionService which needs to handover the call.
3505      * @param request Details about the call which needs to be handover.
3506      * @return {@link Connection} instance corresponding to the handover call.
3507      */
onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, ConnectionRequest request)3508     public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
3509                                                          ConnectionRequest request) {
3510         return null;
3511     }
3512 
3513     /**
3514      * Called by Telecom in response to a {@code TelecomManager#acceptHandover()}
3515      * invocation which failed.
3516      * <p>
3517      * For a full discussion of the handover process and the APIs involved, see
3518      * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}
3519      *
3520      * @param request Details about the call which failed to handover.
3521      * @param error Reason for handover failure.  Will be one of the
3522      */
onHandoverFailed(ConnectionRequest request, @Call.Callback.HandoverFailureErrors int error)3523     public void onHandoverFailed(ConnectionRequest request,
3524             @Call.Callback.HandoverFailureErrors int error) {
3525         return;
3526     }
3527 
3528     /**
3529      * Calls of this type are created using
3530      * {@link TelecomManager#addNewUnknownCall(PhoneAccountHandle, Bundle)}.  Unknown calls
3531      * are used for representing calls which become known to the {@link ConnectionService}
3532      * midway through the call.
3533      *
3534      * For example, a call transferred from one device to answer would surface as an active
3535      * call in Telecom instead of going through a typical Ringing to Active transition, or
3536      * Dialing to Active transition.
3537      *
3538      * A {@link ConnectionService} can return {@code null} (the default behavior)
3539      * if it is not able to handle a request for the requested unknown connection.
3540      *
3541      * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}.
3542      *
3543      * @param connectionManagerPhoneAccount The connection manager account to use for managing
3544      *                                      this call
3545      * @param request Details about the outgoing call
3546      * @return The {@code Connection} object to satisfy this call, or the result of an invocation
3547      *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call
3548      * @hide
3549      */
3550     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
3551     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
onCreateUnknownConnection( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)3552     public @Nullable Connection onCreateUnknownConnection(
3553             @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
3554             @NonNull ConnectionRequest request) {
3555         return null;
3556     }
3557 
3558     /**
3559      * Conference two specified connections. Invoked when the user has made a request to merge the
3560      * specified connections into a conference call. In response, the connection service should
3561      * create an instance of {@link Conference} and pass it into {@link #addConference}.
3562      *
3563      * @param connection1 A connection to merge into a conference call.
3564      * @param connection2 A connection to merge into a conference call.
3565      */
onConference(Connection connection1, Connection connection2)3566     public void onConference(Connection connection1, Connection connection2) {}
3567 
3568     /**
3569      * Called when a connection is added.
3570      * @hide
3571      */
onConnectionAdded(Connection connection)3572     public void onConnectionAdded(Connection connection) {}
3573 
3574     /**
3575      * Called when a connection is removed.
3576      * @hide
3577      */
onConnectionRemoved(Connection connection)3578     public void onConnectionRemoved(Connection connection) {}
3579 
3580     /**
3581      * Called when a conference is added.
3582      * @hide
3583      */
onConferenceAdded(Conference conference)3584     public void onConferenceAdded(Conference conference) {}
3585 
3586     /**
3587      * Called when a conference is removed.
3588      * @hide
3589      */
onConferenceRemoved(Conference conference)3590     public void onConferenceRemoved(Conference conference) {}
3591 
3592     /**
3593      * Indicates that a remote conference has been created for existing {@link RemoteConnection}s.
3594      * When this method is invoked, this {@link ConnectionService} should create its own
3595      * representation of the conference call and send it to telecom using {@link #addConference}.
3596      * <p>
3597      * This is only relevant to {@link ConnectionService}s which are registered with
3598      * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
3599      *
3600      * @param conference The remote conference call.
3601      */
onRemoteConferenceAdded(RemoteConference conference)3602     public void onRemoteConferenceAdded(RemoteConference conference) {}
3603 
3604     /**
3605      * Called when an existing connection is added remotely.
3606      * @param connection The existing connection which was added.
3607      */
onRemoteExistingConnectionAdded(RemoteConnection connection)3608     public void onRemoteExistingConnectionAdded(RemoteConnection connection) {}
3609 
3610     /**
3611      * Called when the {@link ConnectionService} has lost the call focus.
3612      * The {@link ConnectionService} should release the call resources and invokes
3613      * {@link ConnectionService#connectionServiceFocusReleased()} to inform telecom that it has
3614      * released the call resources.
3615      */
onConnectionServiceFocusLost()3616     public void onConnectionServiceFocusLost() {}
3617 
3618     /**
3619      * Called when the {@link ConnectionService} has gained the call focus. The
3620      * {@link ConnectionService} can acquire the call resources at this time.
3621      */
onConnectionServiceFocusGained()3622     public void onConnectionServiceFocusGained() {}
3623 
3624     /**
3625      * @hide
3626      */
containsConference(Conference conference)3627     public boolean containsConference(Conference conference) {
3628         return mIdByConference.containsKey(conference);
3629     }
3630 
3631     /** {@hide} */
addRemoteConference(RemoteConference remoteConference)3632     void addRemoteConference(RemoteConference remoteConference) {
3633         onRemoteConferenceAdded(remoteConference);
3634     }
3635 
3636     /** {@hide} */
addRemoteExistingConnection(RemoteConnection remoteConnection)3637     void addRemoteExistingConnection(RemoteConnection remoteConnection) {
3638         onRemoteExistingConnectionAdded(remoteConnection);
3639     }
3640 
onAccountsInitialized()3641     private void onAccountsInitialized() {
3642         mAreAccountsInitialized = true;
3643         for (Runnable r : mPreInitializationConnectionRequests) {
3644             r.run();
3645         }
3646         mPreInitializationConnectionRequests.clear();
3647     }
3648 
3649     /**
3650      * Adds an existing connection to the list of connections, identified by a new call ID unique
3651      * to this connection service.
3652      *
3653      * @param connection The connection.
3654      * @return The ID of the connection (e.g. the call-id).
3655      */
addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection)3656     private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) {
3657         String id;
3658 
3659         if (connection.getExtras() != null && connection.getExtras()
3660                 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
3661             id = connection.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
3662             Log.d(this, "addExistingConnectionInternal - conn %s reusing original id %s",
3663                     connection.getTelecomCallId(), id);
3664         } else if (handle == null) {
3665             // If no phone account handle was provided, we cannot be sure the call ID is unique,
3666             // so just use a random UUID.
3667             id = UUID.randomUUID().toString();
3668         } else {
3669             // Phone account handle was provided, so use the ConnectionService class name as a
3670             // prefix for a unique incremental call ID.
3671             id = handle.getComponentName().getClassName() + "@" + getNextCallId();
3672         }
3673         addConnection(handle, id, connection);
3674         return id;
3675     }
3676 
addConnection(PhoneAccountHandle handle, String callId, Connection connection)3677     private void addConnection(PhoneAccountHandle handle, String callId, Connection connection) {
3678         connection.setTelecomCallId(callId);
3679         mConnectionById.put(callId, connection);
3680         mIdByConnection.put(connection, callId);
3681         connection.addConnectionListener(mConnectionListener);
3682         connection.setConnectionService(this);
3683         connection.setPhoneAccountHandle(handle);
3684         onConnectionAdded(connection);
3685     }
3686 
3687     /** {@hide} */
removeConnection(Connection connection)3688     protected void removeConnection(Connection connection) {
3689         connection.unsetConnectionService(this);
3690         connection.removeConnectionListener(mConnectionListener);
3691         String id = mIdByConnection.get(connection);
3692         if (id != null) {
3693             mConnectionById.remove(id);
3694             mIdByConnection.remove(connection);
3695             mAdapter.removeCall(id);
3696             onConnectionRemoved(connection);
3697         }
3698     }
3699 
addConferenceInternal(Conference conference)3700     private String addConferenceInternal(Conference conference) {
3701         String originalId = null;
3702         if (conference.getExtras() != null && conference.getExtras()
3703                 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
3704             originalId = conference.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
3705             Log.d(this, "addConferenceInternal: conf %s reusing original id %s",
3706                     conference.getTelecomCallId(),
3707                     originalId);
3708         }
3709         if (mIdByConference.containsKey(conference)) {
3710             Log.w(this, "Re-adding an existing conference: %s.", conference);
3711         } else if (conference != null) {
3712             // Conferences do not (yet) have a PhoneAccountHandle associated with them, so we
3713             // cannot determine a ConnectionService class name to associate with the ID, so use
3714             // a unique UUID (for now).
3715             String id = originalId == null ? UUID.randomUUID().toString() : originalId;
3716             mConferenceById.put(id, conference);
3717             mIdByConference.put(conference, id);
3718             conference.addListener(mConferenceListener);
3719             return id;
3720         }
3721 
3722         return null;
3723     }
3724 
removeConference(Conference conference)3725     private void removeConference(Conference conference) {
3726         if (mIdByConference.containsKey(conference)) {
3727             conference.removeListener(mConferenceListener);
3728 
3729             String id = mIdByConference.get(conference);
3730             mConferenceById.remove(id);
3731             mIdByConference.remove(conference);
3732             mAdapter.removeCall(id);
3733 
3734             onConferenceRemoved(conference);
3735         }
3736     }
3737 
findConnectionForAction(String callId, String action)3738     private Connection findConnectionForAction(String callId, String action) {
3739         if (callId != null && mConnectionById.containsKey(callId)) {
3740             return mConnectionById.get(callId);
3741         }
3742         Log.w(this, "%s - Cannot find Connection %s", action, callId);
3743         return getNullConnection();
3744     }
3745 
getNullConnection()3746     static synchronized Connection getNullConnection() {
3747         if (sNullConnection == null) {
3748             sNullConnection = new Connection() {};
3749         }
3750         return sNullConnection;
3751     }
3752 
findConferenceForAction(String conferenceId, String action)3753     private Conference findConferenceForAction(String conferenceId, String action) {
3754         if (mConferenceById.containsKey(conferenceId)) {
3755             return mConferenceById.get(conferenceId);
3756         }
3757         Log.w(this, "%s - Cannot find conference %s", action, conferenceId);
3758         return getNullConference();
3759     }
3760 
createConnectionIdList(List<Connection> connections)3761     private List<String> createConnectionIdList(List<Connection> connections) {
3762         List<String> ids = new ArrayList<>();
3763         for (Connection c : connections) {
3764             if (mIdByConnection.containsKey(c)) {
3765                 ids.add(mIdByConnection.get(c));
3766             }
3767         }
3768         Collections.sort(ids);
3769         return ids;
3770     }
3771 
3772     /**
3773      * Builds a list of {@link Connection} and {@link Conference} IDs based on the list of
3774      * {@link Conferenceable}s passed in.
3775      *
3776      * @param conferenceables The {@link Conferenceable} connections and conferences.
3777      * @return List of string conference and call Ids.
3778      */
createIdList(List<Conferenceable> conferenceables)3779     private List<String> createIdList(List<Conferenceable> conferenceables) {
3780         List<String> ids = new ArrayList<>();
3781         for (Conferenceable c : conferenceables) {
3782             // Only allow Connection and Conference conferenceables.
3783             if (c instanceof Connection) {
3784                 Connection connection = (Connection) c;
3785                 if (mIdByConnection.containsKey(connection)) {
3786                     ids.add(mIdByConnection.get(connection));
3787                 }
3788             } else if (c instanceof Conference) {
3789                 Conference conference = (Conference) c;
3790                 if (mIdByConference.containsKey(conference)) {
3791                     ids.add(mIdByConference.get(conference));
3792                 }
3793             }
3794         }
3795         Collections.sort(ids);
3796         return ids;
3797     }
3798 
getNullConference()3799     private Conference getNullConference() {
3800         if (sNullConference == null) {
3801             sNullConference = new Conference(null) {};
3802         }
3803         return sNullConference;
3804     }
3805 
endAllConnections()3806     private void endAllConnections() {
3807         // Unbound from telecomm.  We should end all connections and conferences.
3808         for (Connection connection : mIdByConnection.keySet()) {
3809             // only operate on top-level calls. Conference calls will be removed on their own.
3810             if (connection.getConference() == null) {
3811                 connection.onDisconnect();
3812             }
3813         }
3814         for (Conference conference : mIdByConference.keySet()) {
3815             conference.onDisconnect();
3816         }
3817     }
3818 
3819     /**
3820      * Retrieves the next call ID as maintainted by the connection service.
3821      *
3822      * @return The call ID.
3823      */
getNextCallId()3824     private int getNextCallId() {
3825         synchronized (mIdSyncRoot) {
3826             return ++mId;
3827         }
3828     }
3829 
3830     /**
3831      * Returns this handler, ONLY FOR TESTING.
3832      * @hide
3833      */
3834     @VisibleForTesting
getHandler()3835     public Handler getHandler() {
3836         return mHandler;
3837     }
3838 
3839     /**
3840      * Sets this {@link ConnectionService} ready for testing purposes.
3841      * @hide
3842      */
3843     @VisibleForTesting
setReadyForTest()3844     public void setReadyForTest() {
3845         mAreAccountsInitialized = true;
3846     }
3847 }
3848