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 
1863         Bundle extras = request.getExtras();
1864         Bundle newExtras = new Bundle();
1865         newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
1866         if (extras != null) {
1867             // If the request originated from a remote connection service, we will add some
1868             // tracking information that Telecom can use to keep informed of which package
1869             // made the remote request, and which remote connection service was used.
1870             if (extras.containsKey(Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) {
1871                 newExtras.putString(
1872                         Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME,
1873                         extras.getString(
1874                                 Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME));
1875                 newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
1876                         request.getAccountHandle());
1877             }
1878         }
1879         conference.putExtras(newExtras);
1880 
1881         mConferenceById.put(callId, conference);
1882         mIdByConference.put(conference, callId);
1883         conference.addListener(mConferenceListener);
1884         ParcelableConference parcelableConference = new ParcelableConference.Builder(
1885                 request.getAccountHandle(), conference.getState())
1886                 .setConnectionCapabilities(conference.getConnectionCapabilities())
1887                 .setConnectionProperties(conference.getConnectionProperties())
1888                 .setVideoAttributes(conference.getVideoProvider() == null
1889                                 ? null : conference.getVideoProvider().getInterface(),
1890                         conference.getVideoState())
1891                 .setConnectTimeMillis(conference.getConnectTimeMillis(),
1892                         conference.getConnectionStartElapsedRealtimeMillis())
1893                 .setStatusHints(conference.getStatusHints())
1894                 .setExtras(conference.getExtras())
1895                 .setAddress(conference.getAddress(), conference.getAddressPresentation())
1896                 .setCallerDisplayName(conference.getCallerDisplayName(),
1897                         conference.getCallerDisplayNamePresentation())
1898                 .setDisconnectCause(conference.getDisconnectCause())
1899                 .setRingbackRequested(conference.isRingbackRequested())
1900                 .build();
1901         if (conference.getState() != Connection.STATE_DISCONNECTED) {
1902             conference.setTelecomCallId(callId);
1903             mAdapter.setVideoProvider(callId, conference.getVideoProvider());
1904             mAdapter.setVideoState(callId, conference.getVideoState());
1905             onConferenceAdded(conference);
1906         }
1907 
1908         Log.d(this, "createConference, calling handleCreateConferenceSuccessful %s", callId);
1909         mAdapter.handleCreateConferenceComplete(
1910                 callId,
1911                 request,
1912                 parcelableConference);
1913     }
1914 
1915     /**
1916      * This can be used by telecom to either create a new outgoing call or attach to an existing
1917      * incoming call. In either case, telecom will cycle through a set of services and call
1918      * createConnection util a connection service cancels the process or completes it successfully.
1919      */
createConnection( final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming, boolean isUnknown)1920     private void createConnection(
1921             final PhoneAccountHandle callManagerAccount,
1922             final String callId,
1923             final ConnectionRequest request,
1924             boolean isIncoming,
1925             boolean isUnknown) {
1926         boolean isLegacyHandover = request.getExtras() != null &&
1927                 request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);
1928         boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
1929                 TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
1930         Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
1931                         "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b",
1932                 callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover,
1933                 isHandover);
1934 
1935         Connection connection = null;
1936         if (isHandover) {
1937             PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null
1938                     ? (PhoneAccountHandle) request.getExtras().getParcelable(
1939                     TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT) : null;
1940             if (!isIncoming) {
1941                 connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request);
1942             } else {
1943                 connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request);
1944             }
1945         } else {
1946             connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
1947                     : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
1948                     : onCreateOutgoingConnection(callManagerAccount, request);
1949         }
1950         Log.d(this, "createConnection, connection: %s", connection);
1951         if (connection == null) {
1952             Log.i(this, "createConnection, implementation returned null connection.");
1953             connection = Connection.createFailedConnection(
1954                     new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"));
1955         } else {
1956             try {
1957                 Bundle extras = request.getExtras();
1958                 if (extras != null) {
1959                     // If the request originated from a remote connection service, we will add some
1960                     // tracking information that Telecom can use to keep informed of which package
1961                     // made the remote request, and which remote connection service was used.
1962                     if (extras.containsKey(
1963                             Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) {
1964                         Bundle newExtras = new Bundle();
1965                         newExtras.putString(
1966                                 Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME,
1967                                 extras.getString(
1968                                         Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME
1969                                 ));
1970                         newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
1971                                 request.getAccountHandle());
1972                         connection.putExtras(newExtras);
1973                     }
1974                 }
1975             } catch (UnsupportedOperationException ose) {
1976                 // Do nothing; if the ConnectionService reported a failure it will be an instance
1977                 // of an immutable Connection which we cannot edit, so we're out of luck.
1978             }
1979         }
1980 
1981         boolean isSelfManaged =
1982                 (connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED)
1983                         == Connection.PROPERTY_SELF_MANAGED;
1984         // Self-managed Connections should always use voip audio mode; we default here so that the
1985         // local state within the ConnectionService matches the default we assume in Telecom.
1986         if (isSelfManaged) {
1987             connection.setAudioModeIsVoip(true);
1988         }
1989         connection.setTelecomCallId(callId);
1990         if (connection.getState() != Connection.STATE_DISCONNECTED) {
1991             addConnection(request.getAccountHandle(), callId, connection);
1992         }
1993 
1994         Uri address = connection.getAddress();
1995         String number = address == null ? "null" : address.getSchemeSpecificPart();
1996         Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s",
1997                 Connection.toLogSafePhoneNumber(number),
1998                 Connection.stateToString(connection.getState()),
1999                 Connection.capabilitiesToString(connection.getConnectionCapabilities()),
2000                 Connection.propertiesToString(connection.getConnectionProperties()));
2001 
2002         Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
2003         mAdapter.handleCreateConnectionComplete(
2004                 callId,
2005                 request,
2006                 new ParcelableConnection(
2007                         request.getAccountHandle(),
2008                         connection.getState(),
2009                         connection.getConnectionCapabilities(),
2010                         connection.getConnectionProperties(),
2011                         connection.getSupportedAudioRoutes(),
2012                         connection.getAddress(),
2013                         connection.getAddressPresentation(),
2014                         connection.getCallerDisplayName(),
2015                         connection.getCallerDisplayNamePresentation(),
2016                         connection.getVideoProvider() == null ?
2017                                 null : connection.getVideoProvider().getInterface(),
2018                         connection.getVideoState(),
2019                         connection.isRingbackRequested(),
2020                         connection.getAudioModeIsVoip(),
2021                         connection.getConnectTimeMillis(),
2022                         connection.getConnectionStartElapsedRealtimeMillis(),
2023                         connection.getStatusHints(),
2024                         connection.getDisconnectCause(),
2025                         createIdList(connection.getConferenceables()),
2026                         connection.getExtras(),
2027                         connection.getCallerNumberVerificationStatus()));
2028 
2029         if (isIncoming && request.shouldShowIncomingCallUi() && isSelfManaged) {
2030             // Tell ConnectionService to show its incoming call UX.
2031             connection.onShowIncomingCallUi();
2032         }
2033         if (isUnknown) {
2034             triggerConferenceRecalculate();
2035         }
2036     }
2037 
createConnectionFailed(final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming)2038     private void createConnectionFailed(final PhoneAccountHandle callManagerAccount,
2039                                         final String callId, final ConnectionRequest request,
2040                                         boolean isIncoming) {
2041 
2042         Log.i(this, "createConnectionFailed %s", callId);
2043         if (isIncoming) {
2044             onCreateIncomingConnectionFailed(callManagerAccount, request);
2045         } else {
2046             onCreateOutgoingConnectionFailed(callManagerAccount, request);
2047         }
2048     }
2049 
createConferenceFailed(final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming)2050     private void createConferenceFailed(final PhoneAccountHandle callManagerAccount,
2051                                         final String callId, final ConnectionRequest request,
2052                                         boolean isIncoming) {
2053 
2054         Log.i(this, "createConferenceFailed %s", callId);
2055         if (isIncoming) {
2056             onCreateIncomingConferenceFailed(callManagerAccount, request);
2057         } else {
2058             onCreateOutgoingConferenceFailed(callManagerAccount, request);
2059         }
2060     }
2061 
handoverFailed(final String callId, final ConnectionRequest request, int reason)2062     private void handoverFailed(final String callId, final ConnectionRequest request,
2063                                         int reason) {
2064 
2065         Log.i(this, "handoverFailed %s", callId);
2066         onHandoverFailed(request, reason);
2067     }
2068 
2069     /**
2070      * Called by Telecom when the creation of a new Connection has completed and it is now added
2071      * to Telecom.
2072      * @param callId The ID of the connection.
2073      */
notifyCreateConnectionComplete(final String callId)2074     private void notifyCreateConnectionComplete(final String callId) {
2075         Log.i(this, "notifyCreateConnectionComplete %s", callId);
2076         if (callId == null) {
2077             // This could happen if the connection fails quickly and is removed from the
2078             // ConnectionService before Telecom sends the create connection complete callback.
2079             Log.w(this, "notifyCreateConnectionComplete: callId is null.");
2080             return;
2081         }
2082         onCreateConnectionComplete(findConnectionForAction(callId,
2083                 "notifyCreateConnectionComplete"));
2084     }
2085 
2086     /**
2087      * Called by Telecom when the creation of a new Conference has completed and it is now added
2088      * to Telecom.
2089      * @param callId The ID of the connection.
2090      */
notifyCreateConferenceComplete(final String callId)2091     private void notifyCreateConferenceComplete(final String callId) {
2092         Log.i(this, "notifyCreateConferenceComplete %s", callId);
2093         if (callId == null) {
2094             // This could happen if the conference fails quickly and is removed from the
2095             // ConnectionService before Telecom sends the create conference complete callback.
2096             Log.w(this, "notifyCreateConferenceComplete: callId is null.");
2097             return;
2098         }
2099         onCreateConferenceComplete(findConferenceForAction(callId,
2100                 "notifyCreateConferenceComplete"));
2101     }
2102 
2103 
abort(String callId)2104     private void abort(String callId) {
2105         Log.i(this, "abort %s", callId);
2106         findConnectionForAction(callId, "abort").onAbort();
2107     }
2108 
answerVideo(String callId, int videoState)2109     private void answerVideo(String callId, int videoState) {
2110         Log.i(this, "answerVideo %s", callId);
2111         if (mConnectionById.containsKey(callId)) {
2112             findConnectionForAction(callId, "answer").onAnswer(videoState);
2113         } else {
2114             findConferenceForAction(callId, "answer").onAnswer(videoState);
2115         }
2116     }
2117 
answer(String callId)2118     private void answer(String callId) {
2119         Log.i(this, "answer %s", callId);
2120         if (mConnectionById.containsKey(callId)) {
2121             findConnectionForAction(callId, "answer").onAnswer();
2122         } else {
2123             findConferenceForAction(callId, "answer").onAnswer();
2124         }
2125     }
2126 
deflect(String callId, Uri address)2127     private void deflect(String callId, Uri address) {
2128         Log.i(this, "deflect %s", callId);
2129         findConnectionForAction(callId, "deflect").onDeflect(address);
2130     }
2131 
reject(String callId)2132     private void reject(String callId) {
2133         Log.i(this, "reject %s", callId);
2134         if (mConnectionById.containsKey(callId)) {
2135             findConnectionForAction(callId, "reject").onReject();
2136         } else {
2137             findConferenceForAction(callId, "reject").onReject();
2138         }
2139     }
2140 
reject(String callId, String rejectWithMessage)2141     private void reject(String callId, String rejectWithMessage) {
2142         Log.i(this, "reject %s with message", callId);
2143         findConnectionForAction(callId, "reject").onReject(rejectWithMessage);
2144     }
2145 
reject(String callId, @android.telecom.Call.RejectReason int rejectReason)2146     private void reject(String callId, @android.telecom.Call.RejectReason int rejectReason) {
2147         Log.i(this, "reject %s with reason %d", callId, rejectReason);
2148         findConnectionForAction(callId, "reject").onReject(rejectReason);
2149     }
2150 
transfer(String callId, Uri number, boolean isConfirmationRequired)2151     private void transfer(String callId, Uri number, boolean isConfirmationRequired) {
2152         Log.i(this, "transfer %s", callId);
2153         findConnectionForAction(callId, "transfer").onTransfer(number, isConfirmationRequired);
2154     }
2155 
consultativeTransfer(String callId, String otherCallId)2156     private void consultativeTransfer(String callId, String otherCallId) {
2157         Log.i(this, "consultativeTransfer %s", callId);
2158         Connection connection1 = findConnectionForAction(callId, "consultativeTransfer");
2159         Connection connection2 = findConnectionForAction(otherCallId, " consultativeTransfer");
2160         connection1.onTransfer(connection2);
2161     }
2162 
silence(String callId)2163     private void silence(String callId) {
2164         Log.i(this, "silence %s", callId);
2165         findConnectionForAction(callId, "silence").onSilence();
2166     }
2167 
disconnect(String callId)2168     private void disconnect(String callId) {
2169         Log.i(this, "disconnect %s", callId);
2170         if (mConnectionById.containsKey(callId)) {
2171             findConnectionForAction(callId, "disconnect").onDisconnect();
2172         } else {
2173             findConferenceForAction(callId, "disconnect").onDisconnect();
2174         }
2175     }
2176 
hold(String callId)2177     private void hold(String callId) {
2178         Log.i(this, "hold %s", callId);
2179         if (mConnectionById.containsKey(callId)) {
2180             findConnectionForAction(callId, "hold").onHold();
2181         } else {
2182             findConferenceForAction(callId, "hold").onHold();
2183         }
2184     }
2185 
unhold(String callId)2186     private void unhold(String callId) {
2187         Log.i(this, "unhold %s", callId);
2188         if (mConnectionById.containsKey(callId)) {
2189             findConnectionForAction(callId, "unhold").onUnhold();
2190         } else {
2191             findConferenceForAction(callId, "unhold").onUnhold();
2192         }
2193     }
2194 
onCallAudioStateChanged(String callId, CallAudioState callAudioState)2195     private void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {
2196         Log.i(this, "onAudioStateChanged %s %s", callId, callAudioState);
2197         if (mConnectionById.containsKey(callId)) {
2198             findConnectionForAction(callId, "onCallAudioStateChanged").setCallAudioState(
2199                     callAudioState);
2200         } else {
2201             findConferenceForAction(callId, "onCallAudioStateChanged").setCallAudioState(
2202                     callAudioState);
2203         }
2204     }
2205 
playDtmfTone(String callId, char digit)2206     private void playDtmfTone(String callId, char digit) {
2207         Log.i(this, "playDtmfTone %s %c", callId, digit);
2208         if (mConnectionById.containsKey(callId)) {
2209             findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
2210         } else {
2211             findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit);
2212         }
2213     }
2214 
stopDtmfTone(String callId)2215     private void stopDtmfTone(String callId) {
2216         Log.i(this, "stopDtmfTone %s", callId);
2217         if (mConnectionById.containsKey(callId)) {
2218             findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone();
2219         } else {
2220             findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone();
2221         }
2222     }
2223 
conference(String callId1, String callId2)2224     private void conference(String callId1, String callId2) {
2225         Log.i(this, "conference %s, %s", callId1, callId2);
2226 
2227         // Attempt to get second connection or conference.
2228         Connection connection2 = findConnectionForAction(callId2, "conference");
2229         Conference conference2 = getNullConference();
2230         if (connection2 == getNullConnection()) {
2231             conference2 = findConferenceForAction(callId2, "conference");
2232             if (conference2 == getNullConference()) {
2233                 Log.w(this, "Connection2 or Conference2 missing in conference request %s.",
2234                         callId2);
2235                 return;
2236             }
2237         }
2238 
2239         // Attempt to get first connection or conference and perform merge.
2240         Connection connection1 = findConnectionForAction(callId1, "conference");
2241         if (connection1 == getNullConnection()) {
2242             Conference conference1 = findConferenceForAction(callId1, "addConnection");
2243             if (conference1 == getNullConference()) {
2244                 Log.w(this,
2245                         "Connection1 or Conference1 missing in conference request %s.",
2246                         callId1);
2247             } else {
2248                 // Call 1 is a conference.
2249                 if (connection2 != getNullConnection()) {
2250                     // Call 2 is a connection so merge via call 1 (conference).
2251                     conference1.onMerge(connection2);
2252                 } else {
2253                     // Call 2 is ALSO a conference; this should never happen.
2254                     Log.wtf(this, "There can only be one conference and an attempt was made to " +
2255                             "merge two conferences.");
2256                     return;
2257                 }
2258             }
2259         } else {
2260             // Call 1 is a connection.
2261             if (conference2 != getNullConference()) {
2262                 // Call 2 is a conference, so merge via call 2.
2263                 conference2.onMerge(connection1);
2264             } else {
2265                 // Call 2 is a connection, so merge together.
2266                 onConference(connection1, connection2);
2267             }
2268         }
2269     }
2270 
splitFromConference(String callId)2271     private void splitFromConference(String callId) {
2272         Log.i(this, "splitFromConference(%s)", callId);
2273 
2274         Connection connection = findConnectionForAction(callId, "splitFromConference");
2275         if (connection == getNullConnection()) {
2276             Log.w(this, "Connection missing in conference request %s.", callId);
2277             return;
2278         }
2279 
2280         Conference conference = connection.getConference();
2281         if (conference != null) {
2282             conference.onSeparate(connection);
2283         }
2284     }
2285 
mergeConference(String callId)2286     private void mergeConference(String callId) {
2287         Log.i(this, "mergeConference(%s)", callId);
2288         Conference conference = findConferenceForAction(callId, "mergeConference");
2289         if (conference != null) {
2290             conference.onMerge();
2291         }
2292     }
2293 
swapConference(String callId)2294     private void swapConference(String callId) {
2295         Log.i(this, "swapConference(%s)", callId);
2296         Conference conference = findConferenceForAction(callId, "swapConference");
2297         if (conference != null) {
2298             conference.onSwap();
2299         }
2300     }
2301 
addConferenceParticipants(String callId, List<Uri> participants)2302     private void addConferenceParticipants(String callId, List<Uri> participants) {
2303         Log.i(this, "addConferenceParticipants(%s)", callId);
2304         if (mConnectionById.containsKey(callId)) {
2305             findConnectionForAction(callId, "addConferenceParticipants")
2306                     .onAddConferenceParticipants(participants);
2307         } else {
2308             findConferenceForAction(callId, "addConferenceParticipants")
2309                     .onAddConferenceParticipants(participants);
2310         }
2311     }
2312 
2313     /**
2314      * Notifies a {@link Connection} of a request to pull an external call.
2315      *
2316      * See {@link Call#pullExternalCall()}.
2317      *
2318      * @param callId The ID of the call to pull.
2319      */
pullExternalCall(String callId)2320     private void pullExternalCall(String callId) {
2321         Log.i(this, "pullExternalCall(%s)", callId);
2322         Connection connection = findConnectionForAction(callId, "pullExternalCall");
2323         if (connection != null) {
2324             connection.onPullExternalCall();
2325         }
2326     }
2327 
2328     /**
2329      * Notifies a {@link Connection} of a call event.
2330      *
2331      * See {@link Call#sendCallEvent(String, Bundle)}.
2332      *
2333      * @param callId The ID of the call receiving the event.
2334      * @param event The event.
2335      * @param extras Extras associated with the event.
2336      */
sendCallEvent(String callId, String event, Bundle extras)2337     private void sendCallEvent(String callId, String event, Bundle extras) {
2338         Log.i(this, "sendCallEvent(%s, %s)", callId, event);
2339         Connection connection = findConnectionForAction(callId, "sendCallEvent");
2340         if (connection != null) {
2341             connection.onCallEvent(event, extras);
2342         }
2343     }
2344 
2345     /**
2346      * Notifies a {@link Connection} that a handover has completed.
2347      *
2348      * @param callId The ID of the call which completed handover.
2349      */
notifyHandoverComplete(String callId)2350     private void notifyHandoverComplete(String callId) {
2351         Log.i(this, "notifyHandoverComplete(%s)", callId);
2352         Connection connection = findConnectionForAction(callId, "notifyHandoverComplete");
2353         if (connection != null) {
2354             connection.onHandoverComplete();
2355         }
2356     }
2357 
2358     /**
2359      * Notifies a {@link Connection} or {@link Conference} of a change to the extras from Telecom.
2360      * <p>
2361      * These extra changes can originate from Telecom itself, or from an {@link InCallService} via
2362      * the {@link android.telecom.Call#putExtra(String, boolean)},
2363      * {@link android.telecom.Call#putExtra(String, int)},
2364      * {@link android.telecom.Call#putExtra(String, String)},
2365      * {@link Call#removeExtras(List)}.
2366      *
2367      * @param callId The ID of the call receiving the event.
2368      * @param extras The new extras bundle.
2369      */
handleExtrasChanged(String callId, Bundle extras)2370     private void handleExtrasChanged(String callId, Bundle extras) {
2371         Log.i(this, "handleExtrasChanged(%s, %s)", callId, extras);
2372         if (mConnectionById.containsKey(callId)) {
2373             findConnectionForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras);
2374         } else if (mConferenceById.containsKey(callId)) {
2375             findConferenceForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras);
2376         }
2377     }
2378 
startRtt(String callId, Connection.RttTextStream rttTextStream)2379     private void startRtt(String callId, Connection.RttTextStream rttTextStream) {
2380         Log.i(this, "startRtt(%s)", callId);
2381         if (mConnectionById.containsKey(callId)) {
2382             findConnectionForAction(callId, "startRtt").onStartRtt(rttTextStream);
2383         } else if (mConferenceById.containsKey(callId)) {
2384             Log.w(this, "startRtt called on a conference.");
2385         }
2386     }
2387 
stopRtt(String callId)2388     private void stopRtt(String callId) {
2389         Log.i(this, "stopRtt(%s)", callId);
2390         if (mConnectionById.containsKey(callId)) {
2391             findConnectionForAction(callId, "stopRtt").onStopRtt();
2392         } else if (mConferenceById.containsKey(callId)) {
2393             Log.w(this, "stopRtt called on a conference.");
2394         }
2395     }
2396 
handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream)2397     private void handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream) {
2398         Log.i(this, "handleRttUpgradeResponse(%s, %s)", callId, rttTextStream == null);
2399         if (mConnectionById.containsKey(callId)) {
2400             findConnectionForAction(callId, "handleRttUpgradeResponse")
2401                     .handleRttUpgradeResponse(rttTextStream);
2402         } else if (mConferenceById.containsKey(callId)) {
2403             Log.w(this, "handleRttUpgradeResponse called on a conference.");
2404         }
2405     }
2406 
onPostDialContinue(String callId, boolean proceed)2407     private void onPostDialContinue(String callId, boolean proceed) {
2408         Log.i(this, "onPostDialContinue(%s)", callId);
2409         findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
2410     }
2411 
onAdapterAttached()2412     private void onAdapterAttached() {
2413         if (mAreAccountsInitialized) {
2414             // No need to query again if we already did it.
2415             return;
2416         }
2417 
2418         String callingPackage = getOpPackageName();
2419 
2420         mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
2421             @Override
2422             public void onResult(
2423                     final List<ComponentName> componentNames,
2424                     final List<IBinder> services) {
2425                 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oR", null /*lock*/) {
2426                     @Override
2427                     public void loggedRun() {
2428                         for (int i = 0; i < componentNames.size() && i < services.size(); i++) {
2429                             mRemoteConnectionManager.addConnectionService(
2430                                     componentNames.get(i),
2431                                     IConnectionService.Stub.asInterface(services.get(i)));
2432                         }
2433                         onAccountsInitialized();
2434                         Log.d(this, "remote connection services found: " + services);
2435                     }
2436                 }.prepare());
2437             }
2438 
2439             @Override
2440             public void onError() {
2441                 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oE", null /*lock*/) {
2442                     @Override
2443                     public void loggedRun() {
2444                         mAreAccountsInitialized = true;
2445                     }
2446                 }.prepare());
2447             }
2448         }, callingPackage);
2449     }
2450 
2451     /**
2452      * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
2453      * incoming request. This is used by {@code ConnectionService}s that are registered with
2454      * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to manage
2455      * SIM-based incoming calls.
2456      *
2457      * @param connectionManagerPhoneAccount See description at
2458      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
2459      * @param request Details about the incoming call.
2460      * @return The {@code Connection} object to satisfy this call, or {@code null} to
2461      *         not handle the call.
2462      */
createRemoteIncomingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2463     public final RemoteConnection createRemoteIncomingConnection(
2464             PhoneAccountHandle connectionManagerPhoneAccount,
2465             ConnectionRequest request) {
2466         return mRemoteConnectionManager.createRemoteConnection(
2467                 connectionManagerPhoneAccount, request, true);
2468     }
2469 
2470     /**
2471      * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an
2472      * outgoing request. This is used by {@code ConnectionService}s that are registered with
2473      * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to use the
2474      * SIM-based {@code ConnectionService} to place its outgoing calls.
2475      *
2476      * @param connectionManagerPhoneAccount See description at
2477      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
2478      * @param request Details about the outgoing call.
2479      * @return The {@code Connection} object to satisfy this call, or {@code null} to
2480      *         not handle the call.
2481      */
createRemoteOutgoingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2482     public final RemoteConnection createRemoteOutgoingConnection(
2483             PhoneAccountHandle connectionManagerPhoneAccount,
2484             ConnectionRequest request) {
2485         return mRemoteConnectionManager.createRemoteConnection(
2486                 connectionManagerPhoneAccount, request, false);
2487     }
2488 
2489     /**
2490      * Indicates to the relevant {@code RemoteConnectionService} that the specified
2491      * {@link RemoteConnection}s should be merged into a conference call.
2492      * <p>
2493      * If the conference request is successful, the method {@link #onRemoteConferenceAdded} will
2494      * be invoked.
2495      *
2496      * @param remoteConnection1 The first of the remote connections to conference.
2497      * @param remoteConnection2 The second of the remote connections to conference.
2498      */
conferenceRemoteConnections( RemoteConnection remoteConnection1, RemoteConnection remoteConnection2)2499     public final void conferenceRemoteConnections(
2500             RemoteConnection remoteConnection1,
2501             RemoteConnection remoteConnection2) {
2502         mRemoteConnectionManager.conferenceRemoteConnections(remoteConnection1, remoteConnection2);
2503     }
2504 
2505     /**
2506      * Adds a new conference call. When a conference call is created either as a result of an
2507      * explicit request via {@link #onConference} or otherwise, the connection service should supply
2508      * an instance of {@link Conference} by invoking this method. A conference call provided by this
2509      * method will persist until {@link Conference#destroy} is invoked on the conference instance.
2510      *
2511      * @param conference The new conference object.
2512      */
addConference(Conference conference)2513     public final void addConference(Conference conference) {
2514         Log.d(this, "addConference: conference=%s", conference);
2515 
2516         String id = addConferenceInternal(conference);
2517         if (id != null) {
2518             List<String> connectionIds = new ArrayList<>(2);
2519             for (Connection connection : conference.getConnections()) {
2520                 if (mIdByConnection.containsKey(connection)) {
2521                     connectionIds.add(mIdByConnection.get(connection));
2522                 }
2523             }
2524             conference.setTelecomCallId(id);
2525             ParcelableConference parcelableConference = new ParcelableConference.Builder(
2526                     conference.getPhoneAccountHandle(), conference.getState())
2527                     .setConnectionCapabilities(conference.getConnectionCapabilities())
2528                     .setConnectionProperties(conference.getConnectionProperties())
2529                     .setConnectionIds(connectionIds)
2530                     .setVideoAttributes(conference.getVideoProvider() == null
2531                                     ? null : conference.getVideoProvider().getInterface(),
2532                             conference.getVideoState())
2533                     .setConnectTimeMillis(conference.getConnectTimeMillis(),
2534                             conference.getConnectionStartElapsedRealtimeMillis())
2535                     .setStatusHints(conference.getStatusHints())
2536                     .setExtras(conference.getExtras())
2537                     .setAddress(conference.getAddress(), conference.getAddressPresentation())
2538                     .setCallerDisplayName(conference.getCallerDisplayName(),
2539                             conference.getCallerDisplayNamePresentation())
2540                     .setDisconnectCause(conference.getDisconnectCause())
2541                     .setRingbackRequested(conference.isRingbackRequested())
2542                     .setCallDirection(conference.getCallDirection())
2543                     .build();
2544 
2545             mAdapter.addConferenceCall(id, parcelableConference);
2546             mAdapter.setVideoProvider(id, conference.getVideoProvider());
2547             mAdapter.setVideoState(id, conference.getVideoState());
2548             // In some instances a conference can start its life as a standalone call with just a
2549             // single participant; ensure we signal to Telecom in this case.
2550             if (!conference.isMultiparty()) {
2551                 mAdapter.setConferenceState(id, conference.isMultiparty());
2552             }
2553 
2554             // Go through any child calls and set the parent.
2555             for (Connection connection : conference.getConnections()) {
2556                 String connectionId = mIdByConnection.get(connection);
2557                 if (connectionId != null) {
2558                     mAdapter.setIsConferenced(connectionId, id);
2559                 }
2560             }
2561             onConferenceAdded(conference);
2562         }
2563     }
2564 
2565     /**
2566      * Adds a connection created by the {@link ConnectionService} and informs telecom of the new
2567      * connection.
2568      *
2569      * @param phoneAccountHandle The phone account handle for the connection.
2570      * @param connection The connection to add.
2571      */
addExistingConnection(PhoneAccountHandle phoneAccountHandle, Connection connection)2572     public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
2573             Connection connection) {
2574         addExistingConnection(phoneAccountHandle, connection, null /* conference */);
2575     }
2576 
2577     /**
2578      * Call to inform Telecom that your {@link ConnectionService} has released call resources (e.g
2579      * microphone, camera).
2580      *
2581      * <p>
2582      * The {@link ConnectionService} will be disconnected when it failed to call this method within
2583      * 5 seconds after {@link #onConnectionServiceFocusLost()} is called.
2584      *
2585      * @see ConnectionService#onConnectionServiceFocusLost()
2586      */
connectionServiceFocusReleased()2587     public final void connectionServiceFocusReleased() {
2588         mAdapter.onConnectionServiceFocusReleased();
2589     }
2590 
2591     /**
2592      * Adds a connection created by the {@link ConnectionService} and informs telecom of the new
2593      * connection, as well as adding that connection to the specified conference.
2594      * <p>
2595      * Note: This API is intended ONLY for use by the Telephony stack to provide an easy way to add
2596      * IMS conference participants to be added to a conference in a single step; this helps ensure
2597      * UI updates happen atomically, rather than adding the connection and then adding it to
2598      * the conference in another step.
2599      *
2600      * @param phoneAccountHandle The phone account handle for the connection.
2601      * @param connection The connection to add.
2602      * @param conference The parent conference of the new connection.
2603      * @hide
2604      */
2605     @SystemApi
addExistingConnection(@onNull PhoneAccountHandle phoneAccountHandle, @NonNull Connection connection, @NonNull Conference conference)2606     public final void addExistingConnection(@NonNull PhoneAccountHandle phoneAccountHandle,
2607             @NonNull Connection connection, @NonNull Conference conference) {
2608 
2609         String id = addExistingConnectionInternal(phoneAccountHandle, connection);
2610         if (id != null) {
2611             List<String> emptyList = new ArrayList<>(0);
2612             String conferenceId = null;
2613             if (conference != null) {
2614                 conferenceId = mIdByConference.get(conference);
2615             }
2616 
2617             ParcelableConnection parcelableConnection = new ParcelableConnection(
2618                     phoneAccountHandle,
2619                     connection.getState(),
2620                     connection.getConnectionCapabilities(),
2621                     connection.getConnectionProperties(),
2622                     connection.getSupportedAudioRoutes(),
2623                     connection.getAddress(),
2624                     connection.getAddressPresentation(),
2625                     connection.getCallerDisplayName(),
2626                     connection.getCallerDisplayNamePresentation(),
2627                     connection.getVideoProvider() == null ?
2628                             null : connection.getVideoProvider().getInterface(),
2629                     connection.getVideoState(),
2630                     connection.isRingbackRequested(),
2631                     connection.getAudioModeIsVoip(),
2632                     connection.getConnectTimeMillis(),
2633                     connection.getConnectionStartElapsedRealtimeMillis(),
2634                     connection.getStatusHints(),
2635                     connection.getDisconnectCause(),
2636                     emptyList,
2637                     connection.getExtras(),
2638                     conferenceId,
2639                     connection.getCallDirection(),
2640                     Connection.VERIFICATION_STATUS_NOT_VERIFIED);
2641             mAdapter.addExistingConnection(id, parcelableConnection);
2642         }
2643     }
2644 
2645     /**
2646      * Returns all the active {@code Connection}s for which this {@code ConnectionService}
2647      * has taken responsibility.
2648      *
2649      * @return A collection of {@code Connection}s created by this {@code ConnectionService}.
2650      */
getAllConnections()2651     public final Collection<Connection> getAllConnections() {
2652         return mConnectionById.values();
2653     }
2654 
2655     /**
2656      * Returns all the active {@code Conference}s for which this {@code ConnectionService}
2657      * has taken responsibility.
2658      *
2659      * @return A collection of {@code Conference}s created by this {@code ConnectionService}.
2660      */
getAllConferences()2661     public final Collection<Conference> getAllConferences() {
2662         return mConferenceById.values();
2663     }
2664 
2665     /**
2666      * Create a {@code Connection} given an incoming request. This is used to attach to existing
2667      * incoming calls.
2668      *
2669      * @param connectionManagerPhoneAccount See description at
2670      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
2671      * @param request Details about the incoming call.
2672      * @return The {@code Connection} object to satisfy this call, or {@code null} to
2673      *         not handle the call.
2674      */
onCreateIncomingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2675     public Connection onCreateIncomingConnection(
2676             PhoneAccountHandle connectionManagerPhoneAccount,
2677             ConnectionRequest request) {
2678         return null;
2679     }
2680     /**
2681      * Create a {@code Connection} given an incoming request. This is used to attach to existing
2682      * incoming conference call.
2683      *
2684      * @param connectionManagerPhoneAccount See description at
2685      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
2686      * @param request Details about the incoming call.
2687      * @return The {@code Connection} object to satisfy this call, or {@code null} to
2688      *         not handle the call.
2689      * @hide
2690      */
onCreateIncomingConference( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @Nullable ConnectionRequest request)2691     public @Nullable Conference onCreateIncomingConference(
2692             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
2693             @Nullable ConnectionRequest request) {
2694         return null;
2695     }
2696 
2697     /**
2698      * Called after the {@link Connection} returned by
2699      * {@link #onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}
2700      * or {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} has been
2701      * added to the {@link ConnectionService} and sent to Telecom.
2702      *
2703      * @param connection the {@link Connection}.
2704      * @hide
2705      */
onCreateConnectionComplete(Connection connection)2706     public void onCreateConnectionComplete(Connection connection) {
2707     }
2708 
2709     /**
2710      * Called after the {@link Conference} returned by
2711      * {@link #onCreateIncomingConference(PhoneAccountHandle, ConnectionRequest)}
2712      * or {@link #onCreateOutgoingConference(PhoneAccountHandle, ConnectionRequest)} has been
2713      * added to the {@link ConnectionService} and sent to Telecom.
2714      *
2715      * @param conference the {@link Conference}.
2716      * @hide
2717      */
onCreateConferenceComplete(Conference conference)2718     public void onCreateConferenceComplete(Conference conference) {
2719     }
2720 
2721 
2722     /**
2723      * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
2724      * incoming {@link Connection} was denied.
2725      * <p>
2726      * Used when a self-managed {@link ConnectionService} attempts to create a new incoming
2727      * {@link Connection}, but Telecom has determined that the call cannot be allowed at this time.
2728      * The {@link ConnectionService} is responsible for silently rejecting the new incoming
2729      * {@link Connection}.
2730      * <p>
2731      * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
2732      *
2733      * @param connectionManagerPhoneAccount See description at
2734      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
2735      * @param request The incoming connection request.
2736      */
onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2737     public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
2738                                                  ConnectionRequest request) {
2739     }
2740 
2741     /**
2742      * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
2743      * outgoing {@link Connection} was denied.
2744      * <p>
2745      * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing
2746      * {@link Connection}, 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 Connection} 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      */
onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2756     public void onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
2757                                                  ConnectionRequest request) {
2758     }
2759 
2760     /**
2761      * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
2762      * incoming {@link Conference} was denied.
2763      * <p>
2764      * Used when a self-managed {@link ConnectionService} attempts to create a new incoming
2765      * {@link Conference}, but Telecom has determined that the call cannot be allowed at this time.
2766      * The {@link ConnectionService} is responsible for silently rejecting the new incoming
2767      * {@link Conference}.
2768      * <p>
2769      * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
2770      *
2771      * @param connectionManagerPhoneAccount See description at
2772      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
2773      * @param request The incoming connection request.
2774      * @hide
2775      */
onCreateIncomingConferenceFailed( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @Nullable ConnectionRequest request)2776     public void onCreateIncomingConferenceFailed(
2777             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
2778             @Nullable ConnectionRequest request) {
2779     }
2780 
2781     /**
2782      * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
2783      * outgoing {@link Conference} was denied.
2784      * <p>
2785      * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing
2786      * {@link Conference}, but Telecom has determined that the call cannot be placed at this time.
2787      * The {@link ConnectionService} is responisible for informing the user that the
2788      * {@link Conference} cannot be made at this time.
2789      * <p>
2790      * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
2791      *
2792      * @param connectionManagerPhoneAccount See description at
2793      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
2794      * @param request The outgoing connection request.
2795      * @hide
2796      */
onCreateOutgoingConferenceFailed( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @Nullable ConnectionRequest request)2797     public void onCreateOutgoingConferenceFailed(
2798             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
2799             @Nullable ConnectionRequest request) {
2800     }
2801 
2802 
2803     /**
2804      * Trigger recalculate functinality for conference calls. This is used when a Telephony
2805      * Connection is part of a conference controller but is not yet added to Connection
2806      * Service and hence cannot be added to the conference call.
2807      *
2808      * @hide
2809      */
triggerConferenceRecalculate()2810     public void triggerConferenceRecalculate() {
2811     }
2812 
2813     /**
2814      * Create a {@code Connection} given an outgoing request. This is used to initiate new
2815      * outgoing calls.
2816      *
2817      * @param connectionManagerPhoneAccount The connection manager account to use for managing
2818      *         this call.
2819      *         <p>
2820      *         If this parameter is not {@code null}, it means that this {@code ConnectionService}
2821      *         has registered one or more {@code PhoneAccount}s having
2822      *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
2823      *         one of these {@code PhoneAccount}s, while the {@code request} will contain another
2824      *         (usually but not always distinct) {@code PhoneAccount} to be used for actually
2825      *         making the connection.
2826      *         <p>
2827      *         If this parameter is {@code null}, it means that this {@code ConnectionService} is
2828      *         being asked to make a direct connection. The
2829      *         {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
2830      *         a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
2831      *         making the connection.
2832      * @param request Details about the outgoing call.
2833      * @return The {@code Connection} object to satisfy this call, or the result of an invocation
2834      *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
2835      */
onCreateOutgoingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2836     public Connection onCreateOutgoingConnection(
2837             PhoneAccountHandle connectionManagerPhoneAccount,
2838             ConnectionRequest request) {
2839         return null;
2840     }
2841 
2842     /**
2843      * Create a {@code Conference} given an outgoing request. This is used to initiate new
2844      * outgoing conference call.
2845      *
2846      * @param connectionManagerPhoneAccount The connection manager account to use for managing
2847      *         this call.
2848      *         <p>
2849      *         If this parameter is not {@code null}, it means that this {@code ConnectionService}
2850      *         has registered one or more {@code PhoneAccount}s having
2851      *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
2852      *         one of these {@code PhoneAccount}s, while the {@code request} will contain another
2853      *         (usually but not always distinct) {@code PhoneAccount} to be used for actually
2854      *         making the connection.
2855      *         <p>
2856      *         If this parameter is {@code null}, it means that this {@code ConnectionService} is
2857      *         being asked to make a direct connection. The
2858      *         {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
2859      *         a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
2860      *         making the connection.
2861      * @param request Details about the outgoing call.
2862      * @return The {@code Conference} object to satisfy this call, or the result of an invocation
2863      *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
2864      * @hide
2865      */
onCreateOutgoingConference( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @Nullable ConnectionRequest request)2866     public @Nullable Conference onCreateOutgoingConference(
2867             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
2868             @Nullable ConnectionRequest request) {
2869         return null;
2870     }
2871 
2872 
2873     /**
2874      * Called by Telecom to request that a {@link ConnectionService} creates an instance of an
2875      * outgoing handover {@link Connection}.
2876      * <p>
2877      * A call handover is the process where an ongoing call is transferred from one app (i.e.
2878      * {@link ConnectionService} to another app.  The user could, for example, choose to continue a
2879      * mobile network call in a video calling app.  The mobile network call via the Telephony stack
2880      * is referred to as the source of the handover, and the video calling app is referred to as the
2881      * destination.
2882      * <p>
2883      * When considering a handover scenario the <em>initiating</em> device is where a user initiated
2884      * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo(
2885      * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em>
2886      * device.
2887      * <p>
2888      * This method is called on the destination {@link ConnectionService} on <em>initiating</em>
2889      * device when the user initiates a handover request from one app to another.  The user request
2890      * originates in the {@link InCallService} via
2891      * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
2892      * <p>
2893      * For a full discussion of the handover process and the APIs involved, see
2894      * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
2895      * <p>
2896      * Implementations of this method should return an instance of {@link Connection} which
2897      * represents the handover.  If your app does not wish to accept a handover to it at this time,
2898      * you can return {@code null}.  The code below shows an example of how this is done.
2899      * <pre>
2900      * {@code
2901      * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle
2902      *     fromPhoneAccountHandle, ConnectionRequest request) {
2903      *   if (!isHandoverAvailable()) {
2904      *       return null;
2905      *   }
2906      *   MyConnection connection = new MyConnection();
2907      *   connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
2908      *   connection.setVideoState(request.getVideoState());
2909      *   return connection;
2910      * }
2911      * }
2912      * </pre>
2913      *
2914      * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the
2915      *                               ConnectionService which needs to handover the call.
2916      * @param request Details about the call to handover.
2917      * @return {@link Connection} instance corresponding to the handover call.
2918      */
onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, ConnectionRequest request)2919     public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
2920                                                          ConnectionRequest request) {
2921         return null;
2922     }
2923 
2924     /**
2925      * Called by Telecom to request that a {@link ConnectionService} creates an instance of an
2926      * incoming handover {@link Connection}.
2927      * <p>
2928      * A call handover is the process where an ongoing call is transferred from one app (i.e.
2929      * {@link ConnectionService} to another app.  The user could, for example, choose to continue a
2930      * mobile network call in a video calling app.  The mobile network call via the Telephony stack
2931      * is referred to as the source of the handover, and the video calling app is referred to as the
2932      * destination.
2933      * <p>
2934      * When considering a handover scenario the <em>initiating</em> device is where a user initiated
2935      * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo(
2936      * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em>
2937      * device.
2938      * <p>
2939      * This method is called on the destination app on the <em>receiving</em> device when the
2940      * destination app calls {@link TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)} to
2941      * accept an incoming handover from the <em>initiating</em> device.
2942      * <p>
2943      * For a full discussion of the handover process and the APIs involved, see
2944      * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
2945      * <p>
2946      * Implementations of this method should return an instance of {@link Connection} which
2947      * represents the handover.  The code below shows an example of how this is done.
2948      * <pre>
2949      * {@code
2950      * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle
2951      *     fromPhoneAccountHandle, ConnectionRequest request) {
2952      *   // Given that your app requested to accept the handover, you should not return null here.
2953      *   MyConnection connection = new MyConnection();
2954      *   connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
2955      *   connection.setVideoState(request.getVideoState());
2956      *   return connection;
2957      * }
2958      * }
2959      * </pre>
2960      *
2961      * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the
2962      *                               ConnectionService which needs to handover the call.
2963      * @param request Details about the call which needs to be handover.
2964      * @return {@link Connection} instance corresponding to the handover call.
2965      */
onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, ConnectionRequest request)2966     public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
2967                                                          ConnectionRequest request) {
2968         return null;
2969     }
2970 
2971     /**
2972      * Called by Telecom in response to a {@code TelecomManager#acceptHandover()}
2973      * invocation which failed.
2974      * <p>
2975      * For a full discussion of the handover process and the APIs involved, see
2976      * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}
2977      *
2978      * @param request Details about the call which failed to handover.
2979      * @param error Reason for handover failure.  Will be one of the
2980      */
onHandoverFailed(ConnectionRequest request, @Call.Callback.HandoverFailureErrors int error)2981     public void onHandoverFailed(ConnectionRequest request,
2982             @Call.Callback.HandoverFailureErrors int error) {
2983         return;
2984     }
2985 
2986     /**
2987      * Create a {@code Connection} for a new unknown call. An unknown call is a call originating
2988      * from the ConnectionService that was neither a user-initiated outgoing call, nor an incoming
2989      * call created using
2990      * {@code TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}.
2991      *
2992      * @hide
2993      */
onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)2994     public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
2995             ConnectionRequest request) {
2996         return null;
2997     }
2998 
2999     /**
3000      * Conference two specified connections. Invoked when the user has made a request to merge the
3001      * specified connections into a conference call. In response, the connection service should
3002      * create an instance of {@link Conference} and pass it into {@link #addConference}.
3003      *
3004      * @param connection1 A connection to merge into a conference call.
3005      * @param connection2 A connection to merge into a conference call.
3006      */
onConference(Connection connection1, Connection connection2)3007     public void onConference(Connection connection1, Connection connection2) {}
3008 
3009     /**
3010      * Called when a connection is added.
3011      * @hide
3012      */
onConnectionAdded(Connection connection)3013     public void onConnectionAdded(Connection connection) {}
3014 
3015     /**
3016      * Called when a connection is removed.
3017      * @hide
3018      */
onConnectionRemoved(Connection connection)3019     public void onConnectionRemoved(Connection connection) {}
3020 
3021     /**
3022      * Called when a conference is added.
3023      * @hide
3024      */
onConferenceAdded(Conference conference)3025     public void onConferenceAdded(Conference conference) {}
3026 
3027     /**
3028      * Called when a conference is removed.
3029      * @hide
3030      */
onConferenceRemoved(Conference conference)3031     public void onConferenceRemoved(Conference conference) {}
3032 
3033     /**
3034      * Indicates that a remote conference has been created for existing {@link RemoteConnection}s.
3035      * When this method is invoked, this {@link ConnectionService} should create its own
3036      * representation of the conference call and send it to telecom using {@link #addConference}.
3037      * <p>
3038      * This is only relevant to {@link ConnectionService}s which are registered with
3039      * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
3040      *
3041      * @param conference The remote conference call.
3042      */
onRemoteConferenceAdded(RemoteConference conference)3043     public void onRemoteConferenceAdded(RemoteConference conference) {}
3044 
3045     /**
3046      * Called when an existing connection is added remotely.
3047      * @param connection The existing connection which was added.
3048      */
onRemoteExistingConnectionAdded(RemoteConnection connection)3049     public void onRemoteExistingConnectionAdded(RemoteConnection connection) {}
3050 
3051     /**
3052      * Called when the {@link ConnectionService} has lost the call focus.
3053      * The {@link ConnectionService} should release the call resources and invokes
3054      * {@link ConnectionService#connectionServiceFocusReleased()} to inform telecom that it has
3055      * released the call resources.
3056      */
onConnectionServiceFocusLost()3057     public void onConnectionServiceFocusLost() {}
3058 
3059     /**
3060      * Called when the {@link ConnectionService} has gained the call focus. The
3061      * {@link ConnectionService} can acquire the call resources at this time.
3062      */
onConnectionServiceFocusGained()3063     public void onConnectionServiceFocusGained() {}
3064 
3065     /**
3066      * @hide
3067      */
containsConference(Conference conference)3068     public boolean containsConference(Conference conference) {
3069         return mIdByConference.containsKey(conference);
3070     }
3071 
3072     /** {@hide} */
addRemoteConference(RemoteConference remoteConference)3073     void addRemoteConference(RemoteConference remoteConference) {
3074         onRemoteConferenceAdded(remoteConference);
3075     }
3076 
3077     /** {@hide} */
addRemoteExistingConnection(RemoteConnection remoteConnection)3078     void addRemoteExistingConnection(RemoteConnection remoteConnection) {
3079         onRemoteExistingConnectionAdded(remoteConnection);
3080     }
3081 
onAccountsInitialized()3082     private void onAccountsInitialized() {
3083         mAreAccountsInitialized = true;
3084         for (Runnable r : mPreInitializationConnectionRequests) {
3085             r.run();
3086         }
3087         mPreInitializationConnectionRequests.clear();
3088     }
3089 
3090     /**
3091      * Adds an existing connection to the list of connections, identified by a new call ID unique
3092      * to this connection service.
3093      *
3094      * @param connection The connection.
3095      * @return The ID of the connection (e.g. the call-id).
3096      */
addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection)3097     private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) {
3098         String id;
3099 
3100         if (connection.getExtras() != null && connection.getExtras()
3101                 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
3102             id = connection.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
3103             Log.d(this, "addExistingConnectionInternal - conn %s reusing original id %s",
3104                     connection.getTelecomCallId(), id);
3105         } else if (handle == null) {
3106             // If no phone account handle was provided, we cannot be sure the call ID is unique,
3107             // so just use a random UUID.
3108             id = UUID.randomUUID().toString();
3109         } else {
3110             // Phone account handle was provided, so use the ConnectionService class name as a
3111             // prefix for a unique incremental call ID.
3112             id = handle.getComponentName().getClassName() + "@" + getNextCallId();
3113         }
3114         addConnection(handle, id, connection);
3115         return id;
3116     }
3117 
addConnection(PhoneAccountHandle handle, String callId, Connection connection)3118     private void addConnection(PhoneAccountHandle handle, String callId, Connection connection) {
3119         connection.setTelecomCallId(callId);
3120         mConnectionById.put(callId, connection);
3121         mIdByConnection.put(connection, callId);
3122         connection.addConnectionListener(mConnectionListener);
3123         connection.setConnectionService(this);
3124         connection.setPhoneAccountHandle(handle);
3125         onConnectionAdded(connection);
3126     }
3127 
3128     /** {@hide} */
removeConnection(Connection connection)3129     protected void removeConnection(Connection connection) {
3130         connection.unsetConnectionService(this);
3131         connection.removeConnectionListener(mConnectionListener);
3132         String id = mIdByConnection.get(connection);
3133         if (id != null) {
3134             mConnectionById.remove(id);
3135             mIdByConnection.remove(connection);
3136             mAdapter.removeCall(id);
3137             onConnectionRemoved(connection);
3138         }
3139     }
3140 
addConferenceInternal(Conference conference)3141     private String addConferenceInternal(Conference conference) {
3142         String originalId = null;
3143         if (conference.getExtras() != null && conference.getExtras()
3144                 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
3145             originalId = conference.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
3146             Log.d(this, "addConferenceInternal: conf %s reusing original id %s",
3147                     conference.getTelecomCallId(),
3148                     originalId);
3149         }
3150         if (mIdByConference.containsKey(conference)) {
3151             Log.w(this, "Re-adding an existing conference: %s.", conference);
3152         } else if (conference != null) {
3153             // Conferences do not (yet) have a PhoneAccountHandle associated with them, so we
3154             // cannot determine a ConnectionService class name to associate with the ID, so use
3155             // a unique UUID (for now).
3156             String id = originalId == null ? UUID.randomUUID().toString() : originalId;
3157             mConferenceById.put(id, conference);
3158             mIdByConference.put(conference, id);
3159             conference.addListener(mConferenceListener);
3160             return id;
3161         }
3162 
3163         return null;
3164     }
3165 
removeConference(Conference conference)3166     private void removeConference(Conference conference) {
3167         if (mIdByConference.containsKey(conference)) {
3168             conference.removeListener(mConferenceListener);
3169 
3170             String id = mIdByConference.get(conference);
3171             mConferenceById.remove(id);
3172             mIdByConference.remove(conference);
3173             mAdapter.removeCall(id);
3174 
3175             onConferenceRemoved(conference);
3176         }
3177     }
3178 
findConnectionForAction(String callId, String action)3179     private Connection findConnectionForAction(String callId, String action) {
3180         if (callId != null && mConnectionById.containsKey(callId)) {
3181             return mConnectionById.get(callId);
3182         }
3183         Log.w(this, "%s - Cannot find Connection %s", action, callId);
3184         return getNullConnection();
3185     }
3186 
getNullConnection()3187     static synchronized Connection getNullConnection() {
3188         if (sNullConnection == null) {
3189             sNullConnection = new Connection() {};
3190         }
3191         return sNullConnection;
3192     }
3193 
findConferenceForAction(String conferenceId, String action)3194     private Conference findConferenceForAction(String conferenceId, String action) {
3195         if (mConferenceById.containsKey(conferenceId)) {
3196             return mConferenceById.get(conferenceId);
3197         }
3198         Log.w(this, "%s - Cannot find conference %s", action, conferenceId);
3199         return getNullConference();
3200     }
3201 
createConnectionIdList(List<Connection> connections)3202     private List<String> createConnectionIdList(List<Connection> connections) {
3203         List<String> ids = new ArrayList<>();
3204         for (Connection c : connections) {
3205             if (mIdByConnection.containsKey(c)) {
3206                 ids.add(mIdByConnection.get(c));
3207             }
3208         }
3209         Collections.sort(ids);
3210         return ids;
3211     }
3212 
3213     /**
3214      * Builds a list of {@link Connection} and {@link Conference} IDs based on the list of
3215      * {@link Conferenceable}s passed in.
3216      *
3217      * @param conferenceables The {@link Conferenceable} connections and conferences.
3218      * @return List of string conference and call Ids.
3219      */
createIdList(List<Conferenceable> conferenceables)3220     private List<String> createIdList(List<Conferenceable> conferenceables) {
3221         List<String> ids = new ArrayList<>();
3222         for (Conferenceable c : conferenceables) {
3223             // Only allow Connection and Conference conferenceables.
3224             if (c instanceof Connection) {
3225                 Connection connection = (Connection) c;
3226                 if (mIdByConnection.containsKey(connection)) {
3227                     ids.add(mIdByConnection.get(connection));
3228                 }
3229             } else if (c instanceof Conference) {
3230                 Conference conference = (Conference) c;
3231                 if (mIdByConference.containsKey(conference)) {
3232                     ids.add(mIdByConference.get(conference));
3233                 }
3234             }
3235         }
3236         Collections.sort(ids);
3237         return ids;
3238     }
3239 
getNullConference()3240     private Conference getNullConference() {
3241         if (sNullConference == null) {
3242             sNullConference = new Conference(null) {};
3243         }
3244         return sNullConference;
3245     }
3246 
endAllConnections()3247     private void endAllConnections() {
3248         // Unbound from telecomm.  We should end all connections and conferences.
3249         for (Connection connection : mIdByConnection.keySet()) {
3250             // only operate on top-level calls. Conference calls will be removed on their own.
3251             if (connection.getConference() == null) {
3252                 connection.onDisconnect();
3253             }
3254         }
3255         for (Conference conference : mIdByConference.keySet()) {
3256             conference.onDisconnect();
3257         }
3258     }
3259 
3260     /**
3261      * Retrieves the next call ID as maintainted by the connection service.
3262      *
3263      * @return The call ID.
3264      */
getNextCallId()3265     private int getNextCallId() {
3266         synchronized (mIdSyncRoot) {
3267             return ++mId;
3268         }
3269     }
3270 
3271     /**
3272      * Returns this handler, ONLY FOR TESTING.
3273      * @hide
3274      */
3275     @VisibleForTesting
getHandler()3276     public Handler getHandler() {
3277         return mHandler;
3278     }
3279 }
3280