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