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