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