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