ConnectionServiceWrapper.java revision 4845c9bc370433ae34533a21d7a3d7de8deaf465
1/*
2 * Copyright 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
17package com.android.server.telecom;
18
19import android.app.AppOpsManager;
20import android.content.ComponentName;
21import android.content.Context;
22import android.net.Uri;
23import android.os.Binder;
24import android.os.Bundle;
25import android.os.IBinder;
26import android.os.ParcelFileDescriptor;
27import android.os.RemoteException;
28import android.os.UserHandle;
29import android.telecom.CallAudioState;
30import android.telecom.Connection;
31import android.telecom.ConnectionRequest;
32import android.telecom.ConnectionService;
33import android.telecom.DisconnectCause;
34import android.telecom.GatewayInfo;
35import android.telecom.Log;
36import android.telecom.Logging.Session;
37import android.telecom.ParcelableConference;
38import android.telecom.ParcelableConnection;
39import android.telecom.PhoneAccountHandle;
40import android.telecom.StatusHints;
41import android.telecom.TelecomManager;
42import android.telecom.VideoProfile;
43import android.telephony.TelephonyManager;
44
45import com.android.internal.annotations.VisibleForTesting;
46import com.android.internal.telecom.IConnectionService;
47import com.android.internal.telecom.IConnectionServiceAdapter;
48import com.android.internal.telecom.IVideoProvider;
49import com.android.internal.telecom.RemoteServiceCallback;
50import com.android.internal.util.Preconditions;
51
52import java.util.ArrayList;
53import java.util.Collections;
54import java.util.HashMap;
55import java.util.List;
56import java.util.Map;
57import java.util.Set;
58import java.util.concurrent.ConcurrentHashMap;
59
60/**
61 * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps
62 * track of when the object can safely be unbound. Other classes should not use
63 * {@link IConnectionService} directly and instead should use this class to invoke methods of
64 * {@link IConnectionService}.
65 */
66@VisibleForTesting
67public class ConnectionServiceWrapper extends ServiceBinder {
68
69    private final class Adapter extends IConnectionServiceAdapter.Stub {
70
71        @Override
72        public void handleCreateConnectionComplete(String callId, ConnectionRequest request,
73                ParcelableConnection connection, Session.Info sessionInfo) {
74            Log.startSession(sessionInfo, LogUtils.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE);
75            long token = Binder.clearCallingIdentity();
76            try {
77                synchronized (mLock) {
78                    logIncoming("handleCreateConnectionComplete %s", callId);
79                    ConnectionServiceWrapper.this
80                            .handleCreateConnectionComplete(callId, request, connection);
81
82                    if (mServiceInterface != null) {
83                        logOutgoing("createConnectionComplete %s", callId);
84                        try {
85                            mServiceInterface.createConnectionComplete(callId,
86                                    Log.getExternalSession());
87                        } catch (RemoteException e) {
88                        }
89                    }
90                }
91            } finally {
92                Binder.restoreCallingIdentity(token);
93                Log.endSession();
94            }
95        }
96
97        @Override
98        public void setActive(String callId, Session.Info sessionInfo) {
99            Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_ACTIVE);
100            long token = Binder.clearCallingIdentity();
101            try {
102                synchronized (mLock) {
103                    logIncoming("setActive %s", callId);
104                    Call call = mCallIdMapper.getCall(callId);
105                    if (call != null) {
106                        mCallsManager.markCallAsActive(call);
107                    } else {
108                        // Log.w(this, "setActive, unknown call id: %s", msg.obj);
109                    }
110                }
111            } finally {
112                Binder.restoreCallingIdentity(token);
113                Log.endSession();
114            }
115        }
116
117        @Override
118        public void setRinging(String callId, Session.Info sessionInfo) {
119            Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_RINGING);
120            long token = Binder.clearCallingIdentity();
121            try {
122                synchronized (mLock) {
123                    logIncoming("setRinging %s", callId);
124                    Call call = mCallIdMapper.getCall(callId);
125                    if (call != null) {
126                        mCallsManager.markCallAsRinging(call);
127                    } else {
128                        // Log.w(this, "setRinging, unknown call id: %s", msg.obj);
129                    }
130                }
131            } finally {
132                Binder.restoreCallingIdentity(token);
133                Log.endSession();
134            }
135        }
136
137        @Override
138        public void setVideoProvider(String callId, IVideoProvider videoProvider,
139                Session.Info sessionInfo) {
140            Log.startSession(sessionInfo, "CSW.sVP");
141            long token = Binder.clearCallingIdentity();
142            try {
143                synchronized (mLock) {
144                    logIncoming("setVideoProvider %s", callId);
145                    Call call = mCallIdMapper.getCall(callId);
146                    if (call != null) {
147                        call.setVideoProvider(videoProvider);
148                    }
149                }
150            } finally {
151                Binder.restoreCallingIdentity(token);
152                Log.endSession();
153            }
154        }
155
156        @Override
157        public void setDialing(String callId, Session.Info sessionInfo) {
158            Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_DIALING);
159            long token = Binder.clearCallingIdentity();
160            try {
161                synchronized (mLock) {
162                    logIncoming("setDialing %s", callId);
163                    Call call = mCallIdMapper.getCall(callId);
164                    if (call != null) {
165                        mCallsManager.markCallAsDialing(call);
166                    } else {
167                        // Log.w(this, "setDialing, unknown call id: %s", msg.obj);
168                    }
169                }
170            } finally {
171                Binder.restoreCallingIdentity(token);
172                Log.endSession();
173            }
174        }
175
176        @Override
177	    public void setPulling(String callId, Session.Info sessionInfo) {
178            Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_PULLING);
179            long token = Binder.clearCallingIdentity();
180            try {
181                synchronized (mLock) {
182                    logIncoming("setPulling %s", callId);
183                    Call call = mCallIdMapper.getCall(callId);
184                    if (call != null) {
185                        mCallsManager.markCallAsPulling(call);
186                    }
187                }
188            } finally {
189                Binder.restoreCallingIdentity(token);
190                Log.endSession();
191            }
192        }
193
194        @Override
195        public void setDisconnected(String callId, DisconnectCause disconnectCause,
196                Session.Info sessionInfo) {
197            Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_DISCONNECTED);
198            long token = Binder.clearCallingIdentity();
199            try {
200                synchronized (mLock) {
201                    logIncoming("setDisconnected %s %s", callId, disconnectCause);
202                    Call call = mCallIdMapper.getCall(callId);
203                    Log.d(this, "disconnect call %s %s", disconnectCause, call);
204                    if (call != null) {
205                        mCallsManager.markCallAsDisconnected(call, disconnectCause);
206                    } else {
207                        // Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
208                    }
209                }
210            } finally {
211                Binder.restoreCallingIdentity(token);
212                Log.endSession();
213            }
214        }
215
216        @Override
217        public void setOnHold(String callId, Session.Info sessionInfo) {
218            Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_ON_HOLD);
219            long token = Binder.clearCallingIdentity();
220            try {
221                synchronized (mLock) {
222                    logIncoming("setOnHold %s", callId);
223                    Call call = mCallIdMapper.getCall(callId);
224                    if (call != null) {
225                        mCallsManager.markCallAsOnHold(call);
226                    } else {
227                        // Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
228                    }
229                }
230            } finally {
231                Binder.restoreCallingIdentity(token);
232                Log.endSession();
233            }
234        }
235
236        @Override
237        public void setRingbackRequested(String callId, boolean ringback,
238                Session.Info sessionInfo) {
239            Log.startSession(sessionInfo, "CSW.SRR");
240            long token = Binder.clearCallingIdentity();
241            try {
242                synchronized (mLock) {
243                    logIncoming("setRingbackRequested %s %b", callId, ringback);
244                    Call call = mCallIdMapper.getCall(callId);
245                    if (call != null) {
246                        call.setRingbackRequested(ringback);
247                    } else {
248                        // Log.w(this, "setRingback, unknown call id: %s", args.arg1);
249                    }
250                }
251            } finally {
252                Binder.restoreCallingIdentity(token);
253                Log.endSession();
254            }
255        }
256
257        @Override
258        public void removeCall(String callId, Session.Info sessionInfo) {
259            Log.startSession(sessionInfo, LogUtils.Sessions.CSW_REMOVE_CALL);
260            long token = Binder.clearCallingIdentity();
261            try {
262                synchronized (mLock) {
263                    logIncoming("removeCall %s", callId);
264                    Call call = mCallIdMapper.getCall(callId);
265                    if (call != null) {
266                        if (call.isAlive()) {
267                            mCallsManager.markCallAsDisconnected(
268                                    call, new DisconnectCause(DisconnectCause.REMOTE));
269                        } else {
270                            mCallsManager.markCallAsRemoved(call);
271                        }
272                    }
273                }
274            } finally {
275                Binder.restoreCallingIdentity(token);
276                Log.endSession();
277            }
278        }
279
280        @Override
281        public void setConnectionCapabilities(String callId, int connectionCapabilities,
282                Session.Info sessionInfo) {
283            Log.startSession(sessionInfo, "CSW.sCC");
284            long token = Binder.clearCallingIdentity();
285            try {
286                synchronized (mLock) {
287                    logIncoming("setConnectionCapabilities %s %d", callId, connectionCapabilities);
288                    Call call = mCallIdMapper.getCall(callId);
289                    if (call != null) {
290                        call.setConnectionCapabilities(connectionCapabilities);
291                    } else {
292                        // Log.w(ConnectionServiceWrapper.this,
293                        // "setConnectionCapabilities, unknown call id: %s", msg.obj);
294                    }
295                }
296            } finally {
297                Binder.restoreCallingIdentity(token);
298                Log.endSession();
299            }
300        }
301
302        @Override
303        public void setConnectionProperties(String callId, int connectionProperties,
304                Session.Info sessionInfo) {
305            Log.startSession("CSW.sCP");
306            long token = Binder.clearCallingIdentity();
307            try {
308                synchronized (mLock) {
309                    logIncoming("setConnectionProperties %s %d", callId, connectionProperties);
310                    Call call = mCallIdMapper.getCall(callId);
311                    if (call != null) {
312                        call.setConnectionProperties(connectionProperties);
313                    }
314                }
315            } finally {
316                Binder.restoreCallingIdentity(token);
317                Log.endSession();
318            }
319        }
320
321        @Override
322        public void setIsConferenced(String callId, String conferenceCallId,
323                Session.Info sessionInfo) {
324            Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_IS_CONFERENCED);
325            long token = Binder.clearCallingIdentity();
326            try {
327                synchronized (mLock) {
328                    logIncoming("setIsConferenced %s %s", callId, conferenceCallId);
329                    Call childCall = mCallIdMapper.getCall(callId);
330                    if (childCall != null) {
331                        if (conferenceCallId == null) {
332                            Log.d(this, "unsetting parent: %s", conferenceCallId);
333                            childCall.setParentAndChildCall(null);
334                        } else {
335                            Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
336                            childCall.setParentAndChildCall(conferenceCall);
337                        }
338                    } else {
339                        // Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
340                    }
341                }
342            } finally {
343                Binder.restoreCallingIdentity(token);
344                Log.endSession();
345            }
346        }
347
348        @Override
349        public void setConferenceMergeFailed(String callId, Session.Info sessionInfo) {
350            Log.startSession(sessionInfo, "CSW.sCMF");
351            long token = Binder.clearCallingIdentity();
352            try {
353                synchronized (mLock) {
354                    logIncoming("setConferenceMergeFailed %s", callId);
355                    // TODO: we should move the UI for indication a merge failure here
356                    // from CallNotifier.onSuppServiceFailed(). This way the InCallUI can
357                    // deliver the message anyway that they want. b/20530631.
358                    Call call = mCallIdMapper.getCall(callId);
359                    if (call != null) {
360                        call.onConnectionEvent(Connection.EVENT_CALL_MERGE_FAILED, null);
361                    } else {
362                        Log.w(this, "setConferenceMergeFailed, unknown call id: %s", callId);
363                    }
364                }
365            } finally {
366                Binder.restoreCallingIdentity(token);
367                Log.endSession();
368            }
369        }
370
371        @Override
372        public void addConferenceCall(String callId, ParcelableConference parcelableConference,
373                Session.Info sessionInfo) {
374            Log.startSession(sessionInfo, LogUtils.Sessions.CSW_ADD_CONFERENCE_CALL);
375            long token = Binder.clearCallingIdentity();
376            try {
377                synchronized (mLock) {
378                    if (mCallIdMapper.getCall(callId) != null) {
379                        Log.w(this, "Attempting to add a conference call using an existing " +
380                                "call id %s", callId);
381                        return;
382                    }
383                    logIncoming("addConferenceCall %s %s [%s]", callId, parcelableConference,
384                            parcelableConference.getConnectionIds());
385
386                    // Make sure that there's at least one valid call. For remote connections
387                    // we'll get a add conference msg from both the remote connection service
388                    // and from the real connection service.
389                    boolean hasValidCalls = false;
390                    for (String connId : parcelableConference.getConnectionIds()) {
391                        if (mCallIdMapper.getCall(connId) != null) {
392                            hasValidCalls = true;
393                        }
394                    }
395                    // But don't bail out if the connection count is 0, because that is a valid
396                    // IMS conference state.
397                    if (!hasValidCalls && parcelableConference.getConnectionIds().size() > 0) {
398                        Log.d(this, "Attempting to add a conference with no valid calls");
399                        return;
400                    }
401
402                    PhoneAccountHandle phAcc = null;
403                    if (parcelableConference != null &&
404                            parcelableConference.getPhoneAccount() != null) {
405                        phAcc = parcelableConference.getPhoneAccount();
406                    }
407
408                    Bundle connectionExtras = parcelableConference.getExtras();
409
410                    String connectIdToCheck = null;
411                    if (connectionExtras != null && connectionExtras
412                            .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
413                        // Conference was added via a connection manager, see if its original id is
414                        // known.
415                        connectIdToCheck = connectionExtras
416                                .getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
417                    } else {
418                        connectIdToCheck = callId;
419                    }
420
421                    Call conferenceCall;
422                    // Check to see if this conference has already been added.
423                    Call alreadyAddedConnection = mCallsManager
424                            .getAlreadyAddedConnection(connectIdToCheck);
425                    if (alreadyAddedConnection != null && mCallIdMapper.getCall(callId) == null) {
426                        // We are currently attempting to add the conference via a connection mgr,
427                        // and the originating ConnectionService has already added it.  Instead of
428                        // making a new Telecom call, we will simply add it to the ID mapper here,
429                        // and replace the ConnectionService on the call.
430                        mCallIdMapper.addCall(alreadyAddedConnection, callId);
431                        alreadyAddedConnection.replaceConnectionService(
432                                ConnectionServiceWrapper.this);
433                        conferenceCall = alreadyAddedConnection;
434                    } else {
435                        // need to create a new Call
436                        Call newConferenceCall = mCallsManager.createConferenceCall(callId,
437                                phAcc, parcelableConference);
438                        mCallIdMapper.addCall(newConferenceCall, callId);
439                        newConferenceCall.setConnectionService(ConnectionServiceWrapper.this);
440                        conferenceCall = newConferenceCall;
441                    }
442
443                    Log.d(this, "adding children to conference %s phAcc %s",
444                            parcelableConference.getConnectionIds(), phAcc);
445                    for (String connId : parcelableConference.getConnectionIds()) {
446                        Call childCall = mCallIdMapper.getCall(connId);
447                        Log.d(this, "found child: %s", connId);
448                        if (childCall != null) {
449                            childCall.setParentAndChildCall(conferenceCall);
450                        }
451                    }
452                }
453            } finally {
454                Binder.restoreCallingIdentity(token);
455                Log.endSession();
456            }
457        }
458
459        @Override
460        public void onPostDialWait(String callId, String remaining,
461                Session.Info sessionInfo) throws RemoteException {
462            Log.startSession(sessionInfo, "CSW.oPDW");
463            long token = Binder.clearCallingIdentity();
464            try {
465                synchronized (mLock) {
466                    logIncoming("onPostDialWait %s %s", callId, remaining);
467                    Call call = mCallIdMapper.getCall(callId);
468                    if (call != null) {
469                        call.onPostDialWait(remaining);
470                    } else {
471                        // Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
472                    }
473                }
474            } finally {
475                Binder.restoreCallingIdentity(token);
476                Log.endSession();
477            }
478        }
479
480        @Override
481        public void onPostDialChar(String callId, char nextChar,
482                Session.Info sessionInfo) throws RemoteException {
483            Log.startSession(sessionInfo, "CSW.oPDC");
484            long token = Binder.clearCallingIdentity();
485            try {
486                synchronized (mLock) {
487                    logIncoming("onPostDialChar %s %s", callId, nextChar);
488                    Call call = mCallIdMapper.getCall(callId);
489                    if (call != null) {
490                        call.onPostDialChar(nextChar);
491                    } else {
492                        // Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1);
493                    }
494                }
495            } finally {
496                Binder.restoreCallingIdentity(token);
497                Log.endSession();
498            }
499        }
500
501        @Override
502        public void queryRemoteConnectionServices(RemoteServiceCallback callback,
503                Session.Info sessionInfo) {
504            final UserHandle callingUserHandle = Binder.getCallingUserHandle();
505            Log.startSession(sessionInfo, "CSW.qRCS");
506            long token = Binder.clearCallingIdentity();
507            try {
508                synchronized (mLock) {
509                    logIncoming("queryRemoteConnectionServices %s", callback);
510                    ConnectionServiceWrapper.this
511                            .queryRemoteConnectionServices(callingUserHandle, callback);
512                }
513            } finally {
514                Binder.restoreCallingIdentity(token);
515                Log.endSession();
516            }
517        }
518
519        @Override
520        public void setVideoState(String callId, int videoState, Session.Info sessionInfo) {
521            Log.startSession(sessionInfo, "CSW.sVS");
522            long token = Binder.clearCallingIdentity();
523            try {
524                synchronized (mLock) {
525                    logIncoming("setVideoState %s %d", callId, videoState);
526                    Call call = mCallIdMapper.getCall(callId);
527                    if (call != null) {
528                        call.setVideoState(videoState);
529                    }
530                }
531            } finally {
532                Binder.restoreCallingIdentity(token);
533                Log.endSession();
534            }
535        }
536
537        @Override
538        public void setIsVoipAudioMode(String callId, boolean isVoip, Session.Info sessionInfo) {
539            Log.startSession(sessionInfo, "CSW.sIVAM");
540            long token = Binder.clearCallingIdentity();
541            try {
542                synchronized (mLock) {
543                    logIncoming("setIsVoipAudioMode %s %b", callId, isVoip);
544                    Call call = mCallIdMapper.getCall(callId);
545                    if (call != null) {
546                        call.setIsVoipAudioMode(isVoip);
547                    }
548                }
549            } finally {
550                Binder.restoreCallingIdentity(token);
551                Log.endSession();
552            }
553        }
554
555        @Override
556        public void setAudioRoute(String callId, int audioRoute,
557                String bluetoothAddress, Session.Info sessionInfo) {
558            Log.startSession(sessionInfo, "CSW.sAR");
559            long token = Binder.clearCallingIdentity();
560            try {
561                synchronized (mLock) {
562                    logIncoming("setAudioRoute %s %s", callId,
563                            CallAudioState.audioRouteToString(audioRoute));
564                    mCallsManager.setAudioRoute(audioRoute, bluetoothAddress);
565                }
566            } finally {
567                Binder.restoreCallingIdentity(token);
568                Log.endSession();
569            }
570        }
571
572        @Override
573        public void setStatusHints(String callId, StatusHints statusHints,
574                Session.Info sessionInfo) {
575            Log.startSession(sessionInfo, "CSW.sSH");
576            long token = Binder.clearCallingIdentity();
577            try {
578                synchronized (mLock) {
579                    logIncoming("setStatusHints %s %s", callId, statusHints);
580                    Call call = mCallIdMapper.getCall(callId);
581                    if (call != null) {
582                        call.setStatusHints(statusHints);
583                    }
584                }
585            } finally {
586                Binder.restoreCallingIdentity(token);
587                Log.endSession();
588            }
589        }
590
591        @Override
592        public void putExtras(String callId, Bundle extras, Session.Info sessionInfo) {
593            Log.startSession(sessionInfo, "CSW.pE");
594            long token = Binder.clearCallingIdentity();
595            try {
596                synchronized (mLock) {
597                    Bundle.setDefusable(extras, true);
598                    Call call = mCallIdMapper.getCall(callId);
599                    if (call != null) {
600                        call.putExtras(Call.SOURCE_CONNECTION_SERVICE, extras);
601                    }
602                }
603            } finally {
604                Binder.restoreCallingIdentity(token);
605                Log.endSession();
606            }
607        }
608
609        @Override
610        public void removeExtras(String callId, List<String> keys, Session.Info sessionInfo) {
611            Log.startSession(sessionInfo, "CSW.rE");
612            long token = Binder.clearCallingIdentity();
613            try {
614                synchronized (mLock) {
615                    logIncoming("removeExtra %s %s", callId, keys);
616                    Call call = mCallIdMapper.getCall(callId);
617                    if (call != null) {
618                        call.removeExtras(Call.SOURCE_CONNECTION_SERVICE, keys);
619                    }
620                }
621            } finally {
622                Binder.restoreCallingIdentity(token);
623                Log.endSession();
624            }
625        }
626
627        @Override
628        public void setAddress(String callId, Uri address, int presentation,
629                Session.Info sessionInfo) {
630            Log.startSession(sessionInfo, "CSW.sA");
631            long token = Binder.clearCallingIdentity();
632            try {
633                synchronized (mLock) {
634                    logIncoming("setAddress %s %s %d", callId, address, presentation);
635                    Call call = mCallIdMapper.getCall(callId);
636                    if (call != null) {
637                        call.setHandle(address, presentation);
638                    }
639                }
640            } finally {
641                Binder.restoreCallingIdentity(token);
642                Log.endSession();
643            }
644        }
645
646        @Override
647        public void setCallerDisplayName(String callId, String callerDisplayName, int presentation,
648                Session.Info sessionInfo) {
649            Log.startSession(sessionInfo, "CSW.sCDN");
650            long token = Binder.clearCallingIdentity();
651            try {
652                synchronized (mLock) {
653                    logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName,
654                            presentation);
655                    Call call = mCallIdMapper.getCall(callId);
656                    if (call != null) {
657                        call.setCallerDisplayName(callerDisplayName, presentation);
658                    }
659                }
660            } finally {
661                Binder.restoreCallingIdentity(token);
662                Log.endSession();
663            }
664        }
665
666        @Override
667        public void setConferenceableConnections(String callId, List<String> conferenceableCallIds,
668                Session.Info sessionInfo) {
669            Log.startSession(sessionInfo, "CSW.sCC");
670            long token = Binder.clearCallingIdentity();
671            try {
672                synchronized (mLock) {
673
674                    Call call = mCallIdMapper.getCall(callId);
675                    if (call != null) {
676                        logIncoming("setConferenceableConnections %s %s", callId,
677                                conferenceableCallIds);
678                        List<Call> conferenceableCalls =
679                                new ArrayList<>(conferenceableCallIds.size());
680                        for (String otherId : conferenceableCallIds) {
681                            Call otherCall = mCallIdMapper.getCall(otherId);
682                            if (otherCall != null && otherCall != call) {
683                                conferenceableCalls.add(otherCall);
684                            }
685                        }
686                        call.setConferenceableCalls(conferenceableCalls);
687                    }
688                }
689            } finally {
690                Binder.restoreCallingIdentity(token);
691                Log.endSession();
692            }
693        }
694
695        @Override
696	public void addExistingConnection(String callId, ParcelableConnection connection,
697	        Session.Info sessionInfo) {
698            Log.startSession(sessionInfo, "CSW.aEC");
699            UserHandle userHandle = Binder.getCallingUserHandle();
700            // Check that the Calling Package matches PhoneAccountHandle's Component Package
701            PhoneAccountHandle callingPhoneAccountHandle = connection.getPhoneAccount();
702            if (callingPhoneAccountHandle != null) {
703                mAppOpsManager.checkPackage(Binder.getCallingUid(),
704                        callingPhoneAccountHandle.getComponentName().getPackageName());
705            }
706            long token = Binder.clearCallingIdentity();
707            try {
708                synchronized (mLock) {
709                    // Make sure that the PhoneAccount associated with the incoming
710                    // ParcelableConnection is in fact registered to Telecom and is being called
711                    // from the correct user.
712                    List<PhoneAccountHandle> accountHandles =
713                            mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null /*uriScheme*/,
714                                    false /*includeDisabledAccounts*/, userHandle);
715                    PhoneAccountHandle phoneAccountHandle = null;
716                    for (PhoneAccountHandle accountHandle : accountHandles) {
717                        if(accountHandle.equals(callingPhoneAccountHandle)) {
718                            phoneAccountHandle = accountHandle;
719                        }
720                    }
721                    // Allow the Sim call manager account as well, even if its disabled.
722                    if (phoneAccountHandle == null && callingPhoneAccountHandle != null) {
723                        if (callingPhoneAccountHandle.equals(
724                                mPhoneAccountRegistrar.getSimCallManager(userHandle))) {
725                            phoneAccountHandle = callingPhoneAccountHandle;
726                        }
727                    }
728                    if (phoneAccountHandle != null) {
729                        logIncoming("addExistingConnection %s %s", callId, connection);
730
731                        Bundle connectionExtras = connection.getExtras();
732                        String connectIdToCheck = null;
733                        if (connectionExtras != null && connectionExtras
734                                .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
735                            connectIdToCheck = connectionExtras
736                                    .getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
737                        } else {
738                            connectIdToCheck = callId;
739                        }
740                        // Check to see if this Connection has already been added.
741                        Call alreadyAddedConnection = mCallsManager
742                                .getAlreadyAddedConnection(connectIdToCheck);
743
744                        if (alreadyAddedConnection != null
745                                && mCallIdMapper.getCall(callId) == null) {
746                            mCallIdMapper.addCall(alreadyAddedConnection, callId);
747                            alreadyAddedConnection
748                                    .replaceConnectionService(ConnectionServiceWrapper.this);
749                            return;
750                        }
751
752                        Call existingCall = mCallsManager
753                                .createCallForExistingConnection(callId, connection);
754                        mCallIdMapper.addCall(existingCall, callId);
755                        existingCall.setConnectionService(ConnectionServiceWrapper.this);
756                    } else {
757                        Log.e(this, new RemoteException("The PhoneAccount being used is not " +
758                                "currently registered with Telecom."), "Unable to " +
759                                "addExistingConnection.");
760                    }
761                }
762            } finally {
763                Binder.restoreCallingIdentity(token);
764                Log.endSession();
765            }
766        }
767
768        @Override
769        public void onConnectionEvent(String callId, String event, Bundle extras,
770                Session.Info sessionInfo) {
771            Log.startSession(sessionInfo, "CSW.oCE");
772            long token = Binder.clearCallingIdentity();
773            try {
774                synchronized (mLock) {
775                    Bundle.setDefusable(extras, true);
776                    Call call = mCallIdMapper.getCall(callId);
777                    if (call != null) {
778                        call.onConnectionEvent(event, extras);
779                    }
780                }
781            } finally {
782                Binder.restoreCallingIdentity(token);
783                Log.endSession();
784            }
785        }
786
787        @Override
788        public void onRttInitiationSuccess(String callId, Session.Info sessionInfo)
789                throws RemoteException {
790
791        }
792
793        @Override
794        public void onRttInitiationFailure(String callId, int reason, Session.Info sessionInfo)
795                throws RemoteException {
796            Log.startSession(sessionInfo, "CSW.oRIF");
797            long token = Binder.clearCallingIdentity();
798            try {
799                synchronized (mLock) {
800                    Call call = mCallIdMapper.getCall(callId);
801                    if (call != null) {
802                        call.onRttConnectionFailure(reason);
803                    }
804                }
805            } finally {
806                Binder.restoreCallingIdentity(token);
807                Log.endSession();
808            }
809        }
810
811        @Override
812        public void onRttSessionRemotelyTerminated(String callId, Session.Info sessionInfo)
813                throws RemoteException {
814
815        }
816
817        @Override
818        public void onRemoteRttRequest(String callId, Session.Info sessionInfo)
819                throws RemoteException {
820            Log.startSession(sessionInfo, "CSW.oRRR");
821            long token = Binder.clearCallingIdentity();
822            try {
823                synchronized (mLock) {
824                    Call call = mCallIdMapper.getCall(callId);
825                    if (call != null) {
826                        call.onRemoteRttRequest();
827                    }
828                }
829            } finally {
830                Binder.restoreCallingIdentity(token);
831                Log.endSession();
832            }
833        }
834
835        @Override
836        public void onPhoneAccountChanged(String callId, PhoneAccountHandle pHandle,
837                Session.Info sessionInfo) throws RemoteException {
838            // Check that the Calling Package matches PhoneAccountHandle's Component Package
839            if (pHandle != null) {
840                mAppOpsManager.checkPackage(Binder.getCallingUid(),
841                        pHandle.getComponentName().getPackageName());
842            }
843            Log.startSession(sessionInfo, "CSW.oPAC");
844            long token = Binder.clearCallingIdentity();
845            try {
846                synchronized (mLock) {
847                    Call call = mCallIdMapper.getCall(callId);
848                    if (call != null) {
849                        call.setTargetPhoneAccount(pHandle);
850                    }
851                }
852            } finally {
853                Binder.restoreCallingIdentity(token);
854                Log.endSession();
855            }
856        }
857
858        @Override
859        public void onConnectionServiceFocusReleased(Session.Info sessionInfo)
860                throws RemoteException {
861            // TODO(mpq): This method is added to avoid the compiled error. Add the real
862            // implementation once ag/3273964 done.
863        }
864    }
865
866    private final Adapter mAdapter = new Adapter();
867    private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getConnectionId);
868    private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
869
870    private Binder2 mBinder = new Binder2();
871    private IConnectionService mServiceInterface;
872    private final ConnectionServiceRepository mConnectionServiceRepository;
873    private final PhoneAccountRegistrar mPhoneAccountRegistrar;
874    private final CallsManager mCallsManager;
875    private final AppOpsManager mAppOpsManager;
876
877    /**
878     * Creates a connection service.
879     *
880     * @param componentName The component name of the service with which to bind.
881     * @param connectionServiceRepository Connection service repository.
882     * @param phoneAccountRegistrar Phone account registrar
883     * @param callsManager Calls manager
884     * @param context The context.
885     * @param userHandle The {@link UserHandle} to use when binding.
886     */
887    ConnectionServiceWrapper(
888            ComponentName componentName,
889            ConnectionServiceRepository connectionServiceRepository,
890            PhoneAccountRegistrar phoneAccountRegistrar,
891            CallsManager callsManager,
892            Context context,
893            TelecomSystem.SyncRoot lock,
894            UserHandle userHandle) {
895        super(ConnectionService.SERVICE_INTERFACE, componentName, context, lock, userHandle);
896        mConnectionServiceRepository = connectionServiceRepository;
897        phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {
898            // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections
899            // To do this, we must proxy remote ConnectionService objects
900        });
901        mPhoneAccountRegistrar = phoneAccountRegistrar;
902        mCallsManager = callsManager;
903        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
904    }
905
906    /** See {@link IConnectionService#addConnectionServiceAdapter}. */
907    private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
908        if (isServiceValid("addConnectionServiceAdapter")) {
909            try {
910                logOutgoing("addConnectionServiceAdapter %s", adapter);
911                mServiceInterface.addConnectionServiceAdapter(adapter, Log.getExternalSession());
912            } catch (RemoteException e) {
913            }
914        }
915    }
916
917    /** See {@link IConnectionService#removeConnectionServiceAdapter}. */
918    private void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
919        if (isServiceValid("removeConnectionServiceAdapter")) {
920            try {
921                logOutgoing("removeConnectionServiceAdapter %s", adapter);
922                mServiceInterface.removeConnectionServiceAdapter(adapter, Log.getExternalSession());
923            } catch (RemoteException e) {
924            }
925        }
926    }
927
928    /**
929     * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
930     */
931    @VisibleForTesting
932    public void createConnection(final Call call, final CreateConnectionResponse response) {
933        Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
934        BindCallback callback = new BindCallback() {
935            @Override
936            public void onSuccess() {
937                String callId = mCallIdMapper.getCallId(call);
938                mPendingResponses.put(callId, response);
939
940                GatewayInfo gatewayInfo = call.getGatewayInfo();
941                Bundle extras = call.getIntentExtras();
942                if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
943                        gatewayInfo.getOriginalAddress() != null) {
944                    extras = (Bundle) extras.clone();
945                    extras.putString(
946                            TelecomManager.GATEWAY_PROVIDER_PACKAGE,
947                            gatewayInfo.getGatewayProviderPackageName());
948                    extras.putParcelable(
949                            TelecomManager.GATEWAY_ORIGINAL_ADDRESS,
950                            gatewayInfo.getOriginalAddress());
951                }
952
953                if (call.isIncoming() && mCallsManager.getEmergencyCallHelper()
954                        .getLastEmergencyCallTimeMillis() > 0) {
955                  // Add the last emergency call time to the connection request for incoming calls
956                  if (extras == call.getIntentExtras()) {
957                    extras = (Bundle) extras.clone();
958                  }
959                  extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS,
960                      mCallsManager.getEmergencyCallHelper().getLastEmergencyCallTimeMillis());
961                }
962
963                // Call is incoming and added because we're handing over from another; tell CS
964                // that its expected to handover.
965                if (call.isIncoming() && call.getHandoverSourceCall() != null) {
966                    extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true);
967                    extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
968                            call.getHandoverSourceCall().getTargetPhoneAccount());
969                }
970
971                Log.addEvent(call, LogUtils.Events.START_CONNECTION,
972                        Log.piiHandle(call.getHandle()));
973
974                ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
975                        .setAccountHandle(call.getTargetPhoneAccount())
976                        .setAddress(call.getHandle())
977                        .setExtras(extras)
978                        .setVideoState(call.getVideoState())
979                        .setTelecomCallId(callId)
980                        // For self-managed incoming calls, if there is another ongoing call Telecom
981                        // is responsible for showing a UI to ask the user if they'd like to answer
982                        // this new incoming call.
983                        .setShouldShowIncomingCallUi(
984                                !mCallsManager.shouldShowSystemIncomingCallUi(call))
985                        .setRttPipeFromInCall(call.getInCallToCsRttPipeForCs())
986                        .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
987                        .build();
988
989                try {
990                    mServiceInterface.createConnection(
991                            call.getConnectionManagerPhoneAccount(),
992                            callId,
993                            connectionRequest,
994                            call.shouldAttachToExistingConnection(),
995                            call.isUnknown(),
996                            Log.getExternalSession());
997
998                } catch (RemoteException e) {
999                    Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
1000                    mPendingResponses.remove(callId).handleCreateConnectionFailure(
1001                            new DisconnectCause(DisconnectCause.ERROR, e.toString()));
1002                }
1003            }
1004
1005            @Override
1006            public void onFailure() {
1007                Log.e(this, new Exception(), "Failure to call %s", getComponentName());
1008                response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
1009            }
1010        };
1011
1012        mBinder.bind(callback, call);
1013    }
1014
1015    /**
1016     * Notifies the {@link ConnectionService} associated with a {@link Call} that the request to
1017     * create a connection has been denied or failed.
1018     * @param call The call.
1019     */
1020    void createConnectionFailed(final Call call) {
1021        Log.d(this, "createConnectionFailed(%s) via %s.", call, getComponentName());
1022        BindCallback callback = new BindCallback() {
1023            @Override
1024            public void onSuccess() {
1025                final String callId = mCallIdMapper.getCallId(call);
1026                // If still bound, tell the connection service create connection has failed.
1027                if (callId != null && isServiceValid("createConnectionFailed")) {
1028                    Log.addEvent(call, LogUtils.Events.CREATE_CONNECTION_FAILED,
1029                            Log.piiHandle(call.getHandle()));
1030                    try {
1031                        logOutgoing("createConnectionFailed %s", callId);
1032                        mServiceInterface.createConnectionFailed(
1033                                call.getConnectionManagerPhoneAccount(),
1034                                callId,
1035                                new ConnectionRequest(
1036                                        call.getTargetPhoneAccount(),
1037                                        call.getHandle(),
1038                                        call.getIntentExtras(),
1039                                        call.getVideoState(),
1040                                        callId,
1041                                        false),
1042                                call.isIncoming(),
1043                                Log.getExternalSession());
1044                        call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
1045                        call.disconnect();
1046                    } catch (RemoteException e) {
1047                    }
1048                }
1049            }
1050
1051            @Override
1052            public void onFailure() {
1053                // Binding failed.  Oh no.
1054                Log.w(this, "onFailure - could not bind to CS for call %s", call.getId());
1055            }
1056        };
1057
1058        mBinder.bind(callback, call);
1059    }
1060
1061    void handoverFailed(final Call call, final int reason) {
1062        Log.d(this, "handoverFailed(%s) via %s.", call, getComponentName());
1063        BindCallback callback = new BindCallback() {
1064            @Override
1065            public void onSuccess() {
1066                final String callId = mCallIdMapper.getCallId(call);
1067                // If still bound, tell the connection service create connection has failed.
1068                if (callId != null && isServiceValid("handoverFailed")) {
1069                    Log.addEvent(call, LogUtils.Events.HANDOVER_FAILED,
1070                            Log.piiHandle(call.getHandle()));
1071                    try {
1072                        mServiceInterface.handoverFailed(
1073                                callId,
1074                                new ConnectionRequest(
1075                                        call.getTargetPhoneAccount(),
1076                                        call.getHandle(),
1077                                        call.getIntentExtras(),
1078                                        call.getVideoState(),
1079                                        callId,
1080                                        false), reason, Log.getExternalSession());
1081                    } catch (RemoteException e) {
1082                    }
1083                }
1084            }
1085
1086            @Override
1087            public void onFailure() {
1088                // Binding failed.
1089                Log.w(this, "onFailure - could not bind to CS for call %s",
1090                        call.getId());
1091            }
1092        };
1093
1094        mBinder.bind(callback, call);
1095    }
1096
1097    /** @see IConnectionService#abort(String, Session.Info)  */
1098    void abort(Call call) {
1099        // Clear out any pending outgoing call data
1100        final String callId = mCallIdMapper.getCallId(call);
1101
1102        // If still bound, tell the connection service to abort.
1103        if (callId != null && isServiceValid("abort")) {
1104            try {
1105                logOutgoing("abort %s", callId);
1106                mServiceInterface.abort(callId, Log.getExternalSession());
1107            } catch (RemoteException e) {
1108            }
1109        }
1110
1111        removeCall(call, new DisconnectCause(DisconnectCause.LOCAL));
1112    }
1113
1114    /** @see IConnectionService#silence(String, Session.Info) */
1115    void silence(Call call) {
1116        final String callId = mCallIdMapper.getCallId(call);
1117        if (callId != null && isServiceValid("silence")) {
1118            try {
1119                logOutgoing("silence %s", callId);
1120                mServiceInterface.silence(callId, Log.getExternalSession());
1121            } catch (RemoteException e) {
1122            }
1123        }
1124    }
1125
1126    /** @see IConnectionService#hold(String, Session.Info) */
1127    void hold(Call call) {
1128        final String callId = mCallIdMapper.getCallId(call);
1129        if (callId != null && isServiceValid("hold")) {
1130            try {
1131                logOutgoing("hold %s", callId);
1132                mServiceInterface.hold(callId, Log.getExternalSession());
1133            } catch (RemoteException e) {
1134            }
1135        }
1136    }
1137
1138    /** @see IConnectionService#unhold(String, Session.Info) */
1139    void unhold(Call call) {
1140        final String callId = mCallIdMapper.getCallId(call);
1141        if (callId != null && isServiceValid("unhold")) {
1142            try {
1143                logOutgoing("unhold %s", callId);
1144                mServiceInterface.unhold(callId, Log.getExternalSession());
1145            } catch (RemoteException e) {
1146            }
1147        }
1148    }
1149
1150    /** @see IConnectionService#onCallAudioStateChanged(String, CallAudioState, Session.Info) */
1151    @VisibleForTesting
1152    public void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) {
1153        final String callId = mCallIdMapper.getCallId(activeCall);
1154        if (callId != null && isServiceValid("onCallAudioStateChanged")) {
1155            try {
1156                logOutgoing("onCallAudioStateChanged %s %s", callId, audioState);
1157                mServiceInterface.onCallAudioStateChanged(callId, audioState,
1158                        Log.getExternalSession());
1159            } catch (RemoteException e) {
1160            }
1161        }
1162    }
1163
1164    /** @see IConnectionService#disconnect(String, Session.Info) */
1165    void disconnect(Call call) {
1166        final String callId = mCallIdMapper.getCallId(call);
1167        if (callId != null && isServiceValid("disconnect")) {
1168            try {
1169                logOutgoing("disconnect %s", callId);
1170                mServiceInterface.disconnect(callId, Log.getExternalSession());
1171            } catch (RemoteException e) {
1172            }
1173        }
1174    }
1175
1176    /** @see IConnectionService#answer(String, Session.Info) */
1177    void answer(Call call, int videoState) {
1178        final String callId = mCallIdMapper.getCallId(call);
1179        if (callId != null && isServiceValid("answer")) {
1180            try {
1181                logOutgoing("answer %s %d", callId, videoState);
1182                if (VideoProfile.isAudioOnly(videoState)) {
1183                    mServiceInterface.answer(callId, Log.getExternalSession());
1184                } else {
1185                    mServiceInterface.answerVideo(callId, videoState, Log.getExternalSession());
1186                }
1187            } catch (RemoteException e) {
1188            }
1189        }
1190    }
1191
1192    /** @see IConnectionService#reject(String, Session.Info) */
1193    void reject(Call call, boolean rejectWithMessage, String message) {
1194        final String callId = mCallIdMapper.getCallId(call);
1195        if (callId != null && isServiceValid("reject")) {
1196            try {
1197                logOutgoing("reject %s", callId);
1198
1199                if (rejectWithMessage && call.can(
1200                        Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
1201                    mServiceInterface.rejectWithMessage(callId, message, Log.getExternalSession());
1202                } else {
1203                    mServiceInterface.reject(callId, Log.getExternalSession());
1204                }
1205            } catch (RemoteException e) {
1206            }
1207        }
1208    }
1209
1210    /** @see IConnectionService#playDtmfTone(String, char, Session.Info) */
1211    void playDtmfTone(Call call, char digit) {
1212        final String callId = mCallIdMapper.getCallId(call);
1213        if (callId != null && isServiceValid("playDtmfTone")) {
1214            try {
1215                logOutgoing("playDtmfTone %s %c", callId, digit);
1216                mServiceInterface.playDtmfTone(callId, digit, Log.getExternalSession());
1217            } catch (RemoteException e) {
1218            }
1219        }
1220    }
1221
1222    /** @see IConnectionService#stopDtmfTone(String, Session.Info) */
1223    void stopDtmfTone(Call call) {
1224        final String callId = mCallIdMapper.getCallId(call);
1225        if (callId != null && isServiceValid("stopDtmfTone")) {
1226            try {
1227                logOutgoing("stopDtmfTone %s", callId);
1228                mServiceInterface.stopDtmfTone(callId, Log.getExternalSession());
1229            } catch (RemoteException e) {
1230            }
1231        }
1232    }
1233
1234    void addCall(Call call) {
1235        if (mCallIdMapper.getCallId(call) == null) {
1236            mCallIdMapper.addCall(call);
1237        }
1238    }
1239
1240    /**
1241     * Associates newCall with this connection service by replacing callToReplace.
1242     */
1243    void replaceCall(Call newCall, Call callToReplace) {
1244        Preconditions.checkState(callToReplace.getConnectionService() == this);
1245        mCallIdMapper.replaceCall(newCall, callToReplace);
1246    }
1247
1248    void removeCall(Call call) {
1249        removeCall(call, new DisconnectCause(DisconnectCause.ERROR));
1250    }
1251
1252    void removeCall(String callId, DisconnectCause disconnectCause) {
1253        CreateConnectionResponse response = mPendingResponses.remove(callId);
1254        if (response != null) {
1255            response.handleCreateConnectionFailure(disconnectCause);
1256        }
1257
1258        mCallIdMapper.removeCall(callId);
1259    }
1260
1261    void removeCall(Call call, DisconnectCause disconnectCause) {
1262        CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
1263        if (response != null) {
1264            response.handleCreateConnectionFailure(disconnectCause);
1265        }
1266
1267        mCallIdMapper.removeCall(call);
1268    }
1269
1270    void onPostDialContinue(Call call, boolean proceed) {
1271        final String callId = mCallIdMapper.getCallId(call);
1272        if (callId != null && isServiceValid("onPostDialContinue")) {
1273            try {
1274                logOutgoing("onPostDialContinue %s %b", callId, proceed);
1275                mServiceInterface.onPostDialContinue(callId, proceed, Log.getExternalSession());
1276            } catch (RemoteException ignored) {
1277            }
1278        }
1279    }
1280
1281    void conference(final Call call, Call otherCall) {
1282        final String callId = mCallIdMapper.getCallId(call);
1283        final String otherCallId = mCallIdMapper.getCallId(otherCall);
1284        if (callId != null && otherCallId != null && isServiceValid("conference")) {
1285            try {
1286                logOutgoing("conference %s %s", callId, otherCallId);
1287                mServiceInterface.conference(callId, otherCallId, Log.getExternalSession());
1288            } catch (RemoteException ignored) {
1289            }
1290        }
1291    }
1292
1293    void splitFromConference(Call call) {
1294        final String callId = mCallIdMapper.getCallId(call);
1295        if (callId != null && isServiceValid("splitFromConference")) {
1296            try {
1297                logOutgoing("splitFromConference %s", callId);
1298                mServiceInterface.splitFromConference(callId, Log.getExternalSession());
1299            } catch (RemoteException ignored) {
1300            }
1301        }
1302    }
1303
1304    void mergeConference(Call call) {
1305        final String callId = mCallIdMapper.getCallId(call);
1306        if (callId != null && isServiceValid("mergeConference")) {
1307            try {
1308                logOutgoing("mergeConference %s", callId);
1309                mServiceInterface.mergeConference(callId, Log.getExternalSession());
1310            } catch (RemoteException ignored) {
1311            }
1312        }
1313    }
1314
1315    void swapConference(Call call) {
1316        final String callId = mCallIdMapper.getCallId(call);
1317        if (callId != null && isServiceValid("swapConference")) {
1318            try {
1319                logOutgoing("swapConference %s", callId);
1320                mServiceInterface.swapConference(callId, Log.getExternalSession());
1321            } catch (RemoteException ignored) {
1322            }
1323        }
1324    }
1325
1326    void pullExternalCall(Call call) {
1327        final String callId = mCallIdMapper.getCallId(call);
1328        if (callId != null && isServiceValid("pullExternalCall")) {
1329            try {
1330                logOutgoing("pullExternalCall %s", callId);
1331                mServiceInterface.pullExternalCall(callId, Log.getExternalSession());
1332            } catch (RemoteException ignored) {
1333            }
1334        }
1335    }
1336
1337    void sendCallEvent(Call call, String event, Bundle extras) {
1338        final String callId = mCallIdMapper.getCallId(call);
1339        if (callId != null && isServiceValid("sendCallEvent")) {
1340            try {
1341                logOutgoing("sendCallEvent %s %s", callId, event);
1342                mServiceInterface.sendCallEvent(callId, event, extras, Log.getExternalSession());
1343            } catch (RemoteException ignored) {
1344            }
1345        }
1346    }
1347
1348    void onExtrasChanged(Call call, Bundle extras) {
1349        final String callId = mCallIdMapper.getCallId(call);
1350        if (callId != null && isServiceValid("onExtrasChanged")) {
1351            try {
1352                logOutgoing("onExtrasChanged %s %s", callId, extras);
1353                mServiceInterface.onExtrasChanged(callId, extras, Log.getExternalSession());
1354            } catch (RemoteException ignored) {
1355            }
1356        }
1357    }
1358
1359    void startRtt(Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
1360        final String callId = mCallIdMapper.getCallId(call);
1361        if (callId != null && isServiceValid("startRtt")) {
1362            try {
1363                logOutgoing("startRtt: %s %s %s", callId, fromInCall, toInCall);
1364                mServiceInterface.startRtt(callId, fromInCall, toInCall, Log.getExternalSession());
1365            } catch (RemoteException ignored) {
1366            }
1367        }
1368    }
1369
1370    void stopRtt(Call call) {
1371        final String callId = mCallIdMapper.getCallId(call);
1372        if (callId != null && isServiceValid("stopRtt")) {
1373            try {
1374                logOutgoing("stopRtt: %s", callId);
1375                mServiceInterface.stopRtt(callId, Log.getExternalSession());
1376            } catch (RemoteException ignored) {
1377            }
1378        }
1379    }
1380
1381    void respondToRttRequest(
1382            Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
1383        final String callId = mCallIdMapper.getCallId(call);
1384        if (callId != null && isServiceValid("respondToRttRequest")) {
1385            try {
1386                logOutgoing("respondToRttRequest: %s %s %s", callId, fromInCall, toInCall);
1387                mServiceInterface.respondToRttUpgradeRequest(
1388                        callId, fromInCall, toInCall, Log.getExternalSession());
1389            } catch (RemoteException ignored) {
1390            }
1391        }
1392    }
1393
1394    /** {@inheritDoc} */
1395    @Override
1396    protected void setServiceInterface(IBinder binder) {
1397        mServiceInterface = IConnectionService.Stub.asInterface(binder);
1398        Log.v(this, "Adding Connection Service Adapter.");
1399        addConnectionServiceAdapter(mAdapter);
1400    }
1401
1402    /** {@inheritDoc} */
1403    @Override
1404    protected void removeServiceInterface() {
1405        Log.v(this, "Removing Connection Service Adapter.");
1406        removeConnectionServiceAdapter(mAdapter);
1407        // We have lost our service connection. Notify the world that this service is done.
1408        // We must notify the adapter before CallsManager. The adapter will force any pending
1409        // outgoing calls to try the next service. This needs to happen before CallsManager
1410        // tries to clean up any calls still associated with this service.
1411        handleConnectionServiceDeath();
1412        mCallsManager.handleConnectionServiceDeath(this);
1413        mServiceInterface = null;
1414    }
1415
1416    private void handleCreateConnectionComplete(
1417            String callId,
1418            ConnectionRequest request,
1419            ParcelableConnection connection) {
1420        // TODO: Note we are not using parameter "request", which is a side effect of our tacit
1421        // assumption that we have at most one outgoing connection attempt per ConnectionService.
1422        // This may not continue to be the case.
1423        if (connection.getState() == Connection.STATE_DISCONNECTED) {
1424            // A connection that begins in the DISCONNECTED state is an indication of
1425            // failure to connect; we handle all failures uniformly
1426            removeCall(callId, connection.getDisconnectCause());
1427        } else {
1428            // Successful connection
1429            if (mPendingResponses.containsKey(callId)) {
1430                mPendingResponses.remove(callId)
1431                        .handleCreateConnectionSuccess(mCallIdMapper, connection);
1432            }
1433        }
1434    }
1435
1436    /**
1437     * Called when the associated connection service dies.
1438     */
1439    private void handleConnectionServiceDeath() {
1440        if (!mPendingResponses.isEmpty()) {
1441            CreateConnectionResponse[] responses = mPendingResponses.values().toArray(
1442                    new CreateConnectionResponse[mPendingResponses.values().size()]);
1443            mPendingResponses.clear();
1444            for (int i = 0; i < responses.length; i++) {
1445                responses[i].handleCreateConnectionFailure(
1446                        new DisconnectCause(DisconnectCause.ERROR, "CS_DEATH"));
1447            }
1448        }
1449        mCallIdMapper.clear();
1450    }
1451
1452    private void logIncoming(String msg, Object... params) {
1453        Log.d(this, "ConnectionService -> Telecom[" + mComponentName.flattenToShortString() + "]: "
1454                + msg, params);
1455    }
1456
1457    private void logOutgoing(String msg, Object... params) {
1458        Log.d(this, "Telecom -> ConnectionService[" + mComponentName.flattenToShortString() + "]: "
1459                + msg, params);
1460    }
1461
1462    private void queryRemoteConnectionServices(final UserHandle userHandle,
1463            final RemoteServiceCallback callback) {
1464        // Only give remote connection services to this connection service if it is listed as
1465        // the connection manager.
1466        PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager(userHandle);
1467        Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager);
1468        if (simCallManager == null ||
1469                !simCallManager.getComponentName().equals(getComponentName())) {
1470            noRemoteServices(callback);
1471            return;
1472        }
1473
1474        // Make a list of ConnectionServices that are listed as being associated with SIM accounts
1475        final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap(
1476                new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1));
1477        for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts(userHandle)) {
1478            ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
1479                    handle.getComponentName(), handle.getUserHandle());
1480            if (service != null) {
1481                simServices.add(service);
1482            }
1483        }
1484
1485        final List<ComponentName> simServiceComponentNames = new ArrayList<>();
1486        final List<IBinder> simServiceBinders = new ArrayList<>();
1487
1488        Log.v(this, "queryRemoteConnectionServices, simServices = %s", simServices);
1489
1490        for (ConnectionServiceWrapper simService : simServices) {
1491            if (simService == this) {
1492                // Only happens in the unlikely case that a SIM service is also a SIM call manager
1493                continue;
1494            }
1495
1496            final ConnectionServiceWrapper currentSimService = simService;
1497
1498            currentSimService.mBinder.bind(new BindCallback() {
1499                @Override
1500                public void onSuccess() {
1501                    Log.d(this, "Adding simService %s", currentSimService.getComponentName());
1502                    simServiceComponentNames.add(currentSimService.getComponentName());
1503                    simServiceBinders.add(currentSimService.mServiceInterface.asBinder());
1504                    maybeComplete();
1505                }
1506
1507                @Override
1508                public void onFailure() {
1509                    Log.d(this, "Failed simService %s", currentSimService.getComponentName());
1510                    // We know maybeComplete() will always be a no-op from now on, so go ahead and
1511                    // signal failure of the entire request
1512                    noRemoteServices(callback);
1513                }
1514
1515                private void maybeComplete() {
1516                    if (simServiceComponentNames.size() == simServices.size()) {
1517                        setRemoteServices(callback, simServiceComponentNames, simServiceBinders);
1518                    }
1519                }
1520            }, null);
1521        }
1522    }
1523
1524    private void setRemoteServices(
1525            RemoteServiceCallback callback,
1526            List<ComponentName> componentNames,
1527            List<IBinder> binders) {
1528        try {
1529            callback.onResult(componentNames, binders);
1530        } catch (RemoteException e) {
1531            Log.e(this, e, "Contacting ConnectionService %s",
1532                    ConnectionServiceWrapper.this.getComponentName());
1533        }
1534    }
1535
1536    private void noRemoteServices(RemoteServiceCallback callback) {
1537        setRemoteServices(callback, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
1538    }
1539}
1540