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