ConnectionServiceWrapper.java revision 9086fb1d96dfc6581e7e7d4fe756a90ad592311f
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
859    private final Adapter mAdapter = new Adapter();
860    private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getConnectionId);
861    private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
862
863    private Binder2 mBinder = new Binder2();
864    private IConnectionService mServiceInterface;
865    private final ConnectionServiceRepository mConnectionServiceRepository;
866    private final PhoneAccountRegistrar mPhoneAccountRegistrar;
867    private final CallsManager mCallsManager;
868    private final AppOpsManager mAppOpsManager;
869
870    /**
871     * Creates a connection service.
872     *
873     * @param componentName The component name of the service with which to bind.
874     * @param connectionServiceRepository Connection service repository.
875     * @param phoneAccountRegistrar Phone account registrar
876     * @param callsManager Calls manager
877     * @param context The context.
878     * @param userHandle The {@link UserHandle} to use when binding.
879     */
880    ConnectionServiceWrapper(
881            ComponentName componentName,
882            ConnectionServiceRepository connectionServiceRepository,
883            PhoneAccountRegistrar phoneAccountRegistrar,
884            CallsManager callsManager,
885            Context context,
886            TelecomSystem.SyncRoot lock,
887            UserHandle userHandle) {
888        super(ConnectionService.SERVICE_INTERFACE, componentName, context, lock, userHandle);
889        mConnectionServiceRepository = connectionServiceRepository;
890        phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {
891            // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections
892            // To do this, we must proxy remote ConnectionService objects
893        });
894        mPhoneAccountRegistrar = phoneAccountRegistrar;
895        mCallsManager = callsManager;
896        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
897    }
898
899    /** See {@link IConnectionService#addConnectionServiceAdapter}. */
900    private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
901        if (isServiceValid("addConnectionServiceAdapter")) {
902            try {
903                logOutgoing("addConnectionServiceAdapter %s", adapter);
904                mServiceInterface.addConnectionServiceAdapter(adapter, Log.getExternalSession());
905            } catch (RemoteException e) {
906            }
907        }
908    }
909
910    /** See {@link IConnectionService#removeConnectionServiceAdapter}. */
911    private void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
912        if (isServiceValid("removeConnectionServiceAdapter")) {
913            try {
914                logOutgoing("removeConnectionServiceAdapter %s", adapter);
915                mServiceInterface.removeConnectionServiceAdapter(adapter, Log.getExternalSession());
916            } catch (RemoteException e) {
917            }
918        }
919    }
920
921    /**
922     * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
923     */
924    @VisibleForTesting
925    public void createConnection(final Call call, final CreateConnectionResponse response) {
926        Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
927        BindCallback callback = new BindCallback() {
928            @Override
929            public void onSuccess() {
930                String callId = mCallIdMapper.getCallId(call);
931                mPendingResponses.put(callId, response);
932
933                GatewayInfo gatewayInfo = call.getGatewayInfo();
934                Bundle extras = call.getIntentExtras();
935                if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
936                        gatewayInfo.getOriginalAddress() != null) {
937                    extras = (Bundle) extras.clone();
938                    extras.putString(
939                            TelecomManager.GATEWAY_PROVIDER_PACKAGE,
940                            gatewayInfo.getGatewayProviderPackageName());
941                    extras.putParcelable(
942                            TelecomManager.GATEWAY_ORIGINAL_ADDRESS,
943                            gatewayInfo.getOriginalAddress());
944                }
945
946                if (call.isIncoming() && mCallsManager.getEmergencyCallHelper()
947                        .getLastEmergencyCallTimeMillis() > 0) {
948                  // Add the last emergency call time to the connection request for incoming calls
949                  if (extras == call.getIntentExtras()) {
950                    extras = (Bundle) extras.clone();
951                  }
952                  extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS,
953                      mCallsManager.getEmergencyCallHelper().getLastEmergencyCallTimeMillis());
954                }
955
956                // Call is incoming and added because we're handing over from another; tell CS
957                // that its expected to handover.
958                if (call.isIncoming() && call.getHandoverSourceCall() != null) {
959                    extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true);
960                    extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
961                            call.getHandoverSourceCall().getTargetPhoneAccount());
962                }
963
964                Log.addEvent(call, LogUtils.Events.START_CONNECTION,
965                        Log.piiHandle(call.getHandle()));
966
967                ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
968                        .setAccountHandle(call.getTargetPhoneAccount())
969                        .setAddress(call.getHandle())
970                        .setExtras(extras)
971                        .setVideoState(call.getVideoState())
972                        .setTelecomCallId(callId)
973                        // For self-managed incoming calls, if there is another ongoing call Telecom
974                        // is responsible for showing a UI to ask the user if they'd like to answer
975                        // this new incoming call.
976                        .setShouldShowIncomingCallUi(
977                                !mCallsManager.shouldShowSystemIncomingCallUi(call))
978                        .setRttPipeFromInCall(call.getInCallToCsRttPipeForCs())
979                        .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
980                        .build();
981
982                try {
983                    mServiceInterface.createConnection(
984                            call.getConnectionManagerPhoneAccount(),
985                            callId,
986                            connectionRequest,
987                            call.shouldAttachToExistingConnection(),
988                            call.isUnknown(),
989                            Log.getExternalSession());
990
991                } catch (RemoteException e) {
992                    Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
993                    mPendingResponses.remove(callId).handleCreateConnectionFailure(
994                            new DisconnectCause(DisconnectCause.ERROR, e.toString()));
995                }
996            }
997
998            @Override
999            public void onFailure() {
1000                Log.e(this, new Exception(), "Failure to call %s", getComponentName());
1001                response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
1002            }
1003        };
1004
1005        mBinder.bind(callback, call);
1006    }
1007
1008    /**
1009     * Notifies the {@link ConnectionService} associated with a {@link Call} that the request to
1010     * create a connection has been denied or failed.
1011     * @param call The call.
1012     */
1013    void createConnectionFailed(final Call call) {
1014        Log.d(this, "createConnectionFailed(%s) via %s.", call, getComponentName());
1015        BindCallback callback = new BindCallback() {
1016            @Override
1017            public void onSuccess() {
1018                final String callId = mCallIdMapper.getCallId(call);
1019                // If still bound, tell the connection service create connection has failed.
1020                if (callId != null && isServiceValid("createConnectionFailed")) {
1021                    Log.addEvent(call, LogUtils.Events.CREATE_CONNECTION_FAILED,
1022                            Log.piiHandle(call.getHandle()));
1023                    try {
1024                        logOutgoing("createConnectionFailed %s", callId);
1025                        mServiceInterface.createConnectionFailed(
1026                                call.getConnectionManagerPhoneAccount(),
1027                                callId,
1028                                new ConnectionRequest(
1029                                        call.getTargetPhoneAccount(),
1030                                        call.getHandle(),
1031                                        call.getIntentExtras(),
1032                                        call.getVideoState(),
1033                                        callId,
1034                                        false),
1035                                call.isIncoming(),
1036                                Log.getExternalSession());
1037                        call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
1038                        call.disconnect();
1039                    } catch (RemoteException e) {
1040                    }
1041                }
1042            }
1043
1044            @Override
1045            public void onFailure() {
1046                // Binding failed.  Oh no.
1047                Log.w(this, "onFailure - could not bind to CS for call %s", call.getId());
1048            }
1049        };
1050
1051        mBinder.bind(callback, call);
1052    }
1053
1054    /** @see IConnectionService#abort(String, Session.Info)  */
1055    void abort(Call call) {
1056        // Clear out any pending outgoing call data
1057        final String callId = mCallIdMapper.getCallId(call);
1058
1059        // If still bound, tell the connection service to abort.
1060        if (callId != null && isServiceValid("abort")) {
1061            try {
1062                logOutgoing("abort %s", callId);
1063                mServiceInterface.abort(callId, Log.getExternalSession());
1064            } catch (RemoteException e) {
1065            }
1066        }
1067
1068        removeCall(call, new DisconnectCause(DisconnectCause.LOCAL));
1069    }
1070
1071    /** @see IConnectionService#silence(String, Session.Info) */
1072    void silence(Call call) {
1073        final String callId = mCallIdMapper.getCallId(call);
1074        if (callId != null && isServiceValid("silence")) {
1075            try {
1076                logOutgoing("silence %s", callId);
1077                mServiceInterface.silence(callId, Log.getExternalSession());
1078            } catch (RemoteException e) {
1079            }
1080        }
1081    }
1082
1083    /** @see IConnectionService#hold(String, Session.Info) */
1084    void hold(Call call) {
1085        final String callId = mCallIdMapper.getCallId(call);
1086        if (callId != null && isServiceValid("hold")) {
1087            try {
1088                logOutgoing("hold %s", callId);
1089                mServiceInterface.hold(callId, Log.getExternalSession());
1090            } catch (RemoteException e) {
1091            }
1092        }
1093    }
1094
1095    /** @see IConnectionService#unhold(String, Session.Info) */
1096    void unhold(Call call) {
1097        final String callId = mCallIdMapper.getCallId(call);
1098        if (callId != null && isServiceValid("unhold")) {
1099            try {
1100                logOutgoing("unhold %s", callId);
1101                mServiceInterface.unhold(callId, Log.getExternalSession());
1102            } catch (RemoteException e) {
1103            }
1104        }
1105    }
1106
1107    /** @see IConnectionService#onCallAudioStateChanged(String, CallAudioState, Session.Info) */
1108    @VisibleForTesting
1109    public void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) {
1110        final String callId = mCallIdMapper.getCallId(activeCall);
1111        if (callId != null && isServiceValid("onCallAudioStateChanged")) {
1112            try {
1113                logOutgoing("onCallAudioStateChanged %s %s", callId, audioState);
1114                mServiceInterface.onCallAudioStateChanged(callId, audioState,
1115                        Log.getExternalSession());
1116            } catch (RemoteException e) {
1117            }
1118        }
1119    }
1120
1121    /** @see IConnectionService#disconnect(String, Session.Info) */
1122    void disconnect(Call call) {
1123        final String callId = mCallIdMapper.getCallId(call);
1124        if (callId != null && isServiceValid("disconnect")) {
1125            try {
1126                logOutgoing("disconnect %s", callId);
1127                mServiceInterface.disconnect(callId, Log.getExternalSession());
1128            } catch (RemoteException e) {
1129            }
1130        }
1131    }
1132
1133    /** @see IConnectionService#answer(String, Session.Info) */
1134    void answer(Call call, int videoState) {
1135        final String callId = mCallIdMapper.getCallId(call);
1136        if (callId != null && isServiceValid("answer")) {
1137            try {
1138                logOutgoing("answer %s %d", callId, videoState);
1139                if (VideoProfile.isAudioOnly(videoState)) {
1140                    mServiceInterface.answer(callId, Log.getExternalSession());
1141                } else {
1142                    mServiceInterface.answerVideo(callId, videoState, Log.getExternalSession());
1143                }
1144            } catch (RemoteException e) {
1145            }
1146        }
1147    }
1148
1149    /** @see IConnectionService#reject(String, Session.Info) */
1150    void reject(Call call, boolean rejectWithMessage, String message) {
1151        final String callId = mCallIdMapper.getCallId(call);
1152        if (callId != null && isServiceValid("reject")) {
1153            try {
1154                logOutgoing("reject %s", callId);
1155
1156                if (rejectWithMessage && call.can(
1157                        Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
1158                    mServiceInterface.rejectWithMessage(callId, message, Log.getExternalSession());
1159                } else {
1160                    mServiceInterface.reject(callId, Log.getExternalSession());
1161                }
1162            } catch (RemoteException e) {
1163            }
1164        }
1165    }
1166
1167    /** @see IConnectionService#playDtmfTone(String, char, Session.Info) */
1168    void playDtmfTone(Call call, char digit) {
1169        final String callId = mCallIdMapper.getCallId(call);
1170        if (callId != null && isServiceValid("playDtmfTone")) {
1171            try {
1172                logOutgoing("playDtmfTone %s %c", callId, digit);
1173                mServiceInterface.playDtmfTone(callId, digit, Log.getExternalSession());
1174            } catch (RemoteException e) {
1175            }
1176        }
1177    }
1178
1179    /** @see IConnectionService#stopDtmfTone(String, Session.Info) */
1180    void stopDtmfTone(Call call) {
1181        final String callId = mCallIdMapper.getCallId(call);
1182        if (callId != null && isServiceValid("stopDtmfTone")) {
1183            try {
1184                logOutgoing("stopDtmfTone %s", callId);
1185                mServiceInterface.stopDtmfTone(callId, Log.getExternalSession());
1186            } catch (RemoteException e) {
1187            }
1188        }
1189    }
1190
1191    void addCall(Call call) {
1192        if (mCallIdMapper.getCallId(call) == null) {
1193            mCallIdMapper.addCall(call);
1194        }
1195    }
1196
1197    /**
1198     * Associates newCall with this connection service by replacing callToReplace.
1199     */
1200    void replaceCall(Call newCall, Call callToReplace) {
1201        Preconditions.checkState(callToReplace.getConnectionService() == this);
1202        mCallIdMapper.replaceCall(newCall, callToReplace);
1203    }
1204
1205    void removeCall(Call call) {
1206        removeCall(call, new DisconnectCause(DisconnectCause.ERROR));
1207    }
1208
1209    void removeCall(String callId, DisconnectCause disconnectCause) {
1210        CreateConnectionResponse response = mPendingResponses.remove(callId);
1211        if (response != null) {
1212            response.handleCreateConnectionFailure(disconnectCause);
1213        }
1214
1215        mCallIdMapper.removeCall(callId);
1216    }
1217
1218    void removeCall(Call call, DisconnectCause disconnectCause) {
1219        CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
1220        if (response != null) {
1221            response.handleCreateConnectionFailure(disconnectCause);
1222        }
1223
1224        mCallIdMapper.removeCall(call);
1225    }
1226
1227    void onPostDialContinue(Call call, boolean proceed) {
1228        final String callId = mCallIdMapper.getCallId(call);
1229        if (callId != null && isServiceValid("onPostDialContinue")) {
1230            try {
1231                logOutgoing("onPostDialContinue %s %b", callId, proceed);
1232                mServiceInterface.onPostDialContinue(callId, proceed, Log.getExternalSession());
1233            } catch (RemoteException ignored) {
1234            }
1235        }
1236    }
1237
1238    void conference(final Call call, Call otherCall) {
1239        final String callId = mCallIdMapper.getCallId(call);
1240        final String otherCallId = mCallIdMapper.getCallId(otherCall);
1241        if (callId != null && otherCallId != null && isServiceValid("conference")) {
1242            try {
1243                logOutgoing("conference %s %s", callId, otherCallId);
1244                mServiceInterface.conference(callId, otherCallId, Log.getExternalSession());
1245            } catch (RemoteException ignored) {
1246            }
1247        }
1248    }
1249
1250    void splitFromConference(Call call) {
1251        final String callId = mCallIdMapper.getCallId(call);
1252        if (callId != null && isServiceValid("splitFromConference")) {
1253            try {
1254                logOutgoing("splitFromConference %s", callId);
1255                mServiceInterface.splitFromConference(callId, Log.getExternalSession());
1256            } catch (RemoteException ignored) {
1257            }
1258        }
1259    }
1260
1261    void mergeConference(Call call) {
1262        final String callId = mCallIdMapper.getCallId(call);
1263        if (callId != null && isServiceValid("mergeConference")) {
1264            try {
1265                logOutgoing("mergeConference %s", callId);
1266                mServiceInterface.mergeConference(callId, Log.getExternalSession());
1267            } catch (RemoteException ignored) {
1268            }
1269        }
1270    }
1271
1272    void swapConference(Call call) {
1273        final String callId = mCallIdMapper.getCallId(call);
1274        if (callId != null && isServiceValid("swapConference")) {
1275            try {
1276                logOutgoing("swapConference %s", callId);
1277                mServiceInterface.swapConference(callId, Log.getExternalSession());
1278            } catch (RemoteException ignored) {
1279            }
1280        }
1281    }
1282
1283    void pullExternalCall(Call call) {
1284        final String callId = mCallIdMapper.getCallId(call);
1285        if (callId != null && isServiceValid("pullExternalCall")) {
1286            try {
1287                logOutgoing("pullExternalCall %s", callId);
1288                mServiceInterface.pullExternalCall(callId, Log.getExternalSession());
1289            } catch (RemoteException ignored) {
1290            }
1291        }
1292    }
1293
1294    void sendCallEvent(Call call, String event, Bundle extras) {
1295        final String callId = mCallIdMapper.getCallId(call);
1296        if (callId != null && isServiceValid("sendCallEvent")) {
1297            try {
1298                logOutgoing("sendCallEvent %s %s", callId, event);
1299                mServiceInterface.sendCallEvent(callId, event, extras, Log.getExternalSession());
1300            } catch (RemoteException ignored) {
1301            }
1302        }
1303    }
1304
1305    void onExtrasChanged(Call call, Bundle extras) {
1306        final String callId = mCallIdMapper.getCallId(call);
1307        if (callId != null && isServiceValid("onExtrasChanged")) {
1308            try {
1309                logOutgoing("onExtrasChanged %s %s", callId, extras);
1310                mServiceInterface.onExtrasChanged(callId, extras, Log.getExternalSession());
1311            } catch (RemoteException ignored) {
1312            }
1313        }
1314    }
1315
1316    void startRtt(Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
1317        final String callId = mCallIdMapper.getCallId(call);
1318        if (callId != null && isServiceValid("startRtt")) {
1319            try {
1320                logOutgoing("startRtt: %s %s %s", callId, fromInCall, toInCall);
1321                mServiceInterface.startRtt(callId, fromInCall, toInCall, Log.getExternalSession());
1322            } catch (RemoteException ignored) {
1323            }
1324        }
1325    }
1326
1327    void stopRtt(Call call) {
1328        final String callId = mCallIdMapper.getCallId(call);
1329        if (callId != null && isServiceValid("stopRtt")) {
1330            try {
1331                logOutgoing("stopRtt: %s", callId);
1332                mServiceInterface.stopRtt(callId, Log.getExternalSession());
1333            } catch (RemoteException ignored) {
1334            }
1335        }
1336    }
1337
1338    void respondToRttRequest(
1339            Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
1340        final String callId = mCallIdMapper.getCallId(call);
1341        if (callId != null && isServiceValid("respondToRttRequest")) {
1342            try {
1343                logOutgoing("respondToRttRequest: %s %s %s", callId, fromInCall, toInCall);
1344                mServiceInterface.respondToRttUpgradeRequest(
1345                        callId, fromInCall, toInCall, Log.getExternalSession());
1346            } catch (RemoteException ignored) {
1347            }
1348        }
1349    }
1350
1351    /** {@inheritDoc} */
1352    @Override
1353    protected void setServiceInterface(IBinder binder) {
1354        mServiceInterface = IConnectionService.Stub.asInterface(binder);
1355        Log.v(this, "Adding Connection Service Adapter.");
1356        addConnectionServiceAdapter(mAdapter);
1357    }
1358
1359    /** {@inheritDoc} */
1360    @Override
1361    protected void removeServiceInterface() {
1362        Log.v(this, "Removing Connection Service Adapter.");
1363        removeConnectionServiceAdapter(mAdapter);
1364        // We have lost our service connection. Notify the world that this service is done.
1365        // We must notify the adapter before CallsManager. The adapter will force any pending
1366        // outgoing calls to try the next service. This needs to happen before CallsManager
1367        // tries to clean up any calls still associated with this service.
1368        handleConnectionServiceDeath();
1369        mCallsManager.handleConnectionServiceDeath(this);
1370        mServiceInterface = null;
1371    }
1372
1373    private void handleCreateConnectionComplete(
1374            String callId,
1375            ConnectionRequest request,
1376            ParcelableConnection connection) {
1377        // TODO: Note we are not using parameter "request", which is a side effect of our tacit
1378        // assumption that we have at most one outgoing connection attempt per ConnectionService.
1379        // This may not continue to be the case.
1380        if (connection.getState() == Connection.STATE_DISCONNECTED) {
1381            // A connection that begins in the DISCONNECTED state is an indication of
1382            // failure to connect; we handle all failures uniformly
1383            removeCall(callId, connection.getDisconnectCause());
1384        } else {
1385            // Successful connection
1386            if (mPendingResponses.containsKey(callId)) {
1387                mPendingResponses.remove(callId)
1388                        .handleCreateConnectionSuccess(mCallIdMapper, connection);
1389            }
1390        }
1391    }
1392
1393    /**
1394     * Called when the associated connection service dies.
1395     */
1396    private void handleConnectionServiceDeath() {
1397        if (!mPendingResponses.isEmpty()) {
1398            CreateConnectionResponse[] responses = mPendingResponses.values().toArray(
1399                    new CreateConnectionResponse[mPendingResponses.values().size()]);
1400            mPendingResponses.clear();
1401            for (int i = 0; i < responses.length; i++) {
1402                responses[i].handleCreateConnectionFailure(
1403                        new DisconnectCause(DisconnectCause.ERROR, "CS_DEATH"));
1404            }
1405        }
1406        mCallIdMapper.clear();
1407    }
1408
1409    private void logIncoming(String msg, Object... params) {
1410        Log.d(this, "ConnectionService -> Telecom[" + mComponentName.flattenToShortString() + "]: "
1411                + msg, params);
1412    }
1413
1414    private void logOutgoing(String msg, Object... params) {
1415        Log.d(this, "Telecom -> ConnectionService[" + mComponentName.flattenToShortString() + "]: "
1416                + msg, params);
1417    }
1418
1419    private void queryRemoteConnectionServices(final UserHandle userHandle,
1420            final RemoteServiceCallback callback) {
1421        // Only give remote connection services to this connection service if it is listed as
1422        // the connection manager.
1423        PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager(userHandle);
1424        Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager);
1425        if (simCallManager == null ||
1426                !simCallManager.getComponentName().equals(getComponentName())) {
1427            noRemoteServices(callback);
1428            return;
1429        }
1430
1431        // Make a list of ConnectionServices that are listed as being associated with SIM accounts
1432        final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap(
1433                new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1));
1434        for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts(userHandle)) {
1435            ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
1436                    handle.getComponentName(), handle.getUserHandle());
1437            if (service != null) {
1438                simServices.add(service);
1439            }
1440        }
1441
1442        final List<ComponentName> simServiceComponentNames = new ArrayList<>();
1443        final List<IBinder> simServiceBinders = new ArrayList<>();
1444
1445        Log.v(this, "queryRemoteConnectionServices, simServices = %s", simServices);
1446
1447        for (ConnectionServiceWrapper simService : simServices) {
1448            if (simService == this) {
1449                // Only happens in the unlikely case that a SIM service is also a SIM call manager
1450                continue;
1451            }
1452
1453            final ConnectionServiceWrapper currentSimService = simService;
1454
1455            currentSimService.mBinder.bind(new BindCallback() {
1456                @Override
1457                public void onSuccess() {
1458                    Log.d(this, "Adding simService %s", currentSimService.getComponentName());
1459                    simServiceComponentNames.add(currentSimService.getComponentName());
1460                    simServiceBinders.add(currentSimService.mServiceInterface.asBinder());
1461                    maybeComplete();
1462                }
1463
1464                @Override
1465                public void onFailure() {
1466                    Log.d(this, "Failed simService %s", currentSimService.getComponentName());
1467                    // We know maybeComplete() will always be a no-op from now on, so go ahead and
1468                    // signal failure of the entire request
1469                    noRemoteServices(callback);
1470                }
1471
1472                private void maybeComplete() {
1473                    if (simServiceComponentNames.size() == simServices.size()) {
1474                        setRemoteServices(callback, simServiceComponentNames, simServiceBinders);
1475                    }
1476                }
1477            }, null);
1478        }
1479    }
1480
1481    private void setRemoteServices(
1482            RemoteServiceCallback callback,
1483            List<ComponentName> componentNames,
1484            List<IBinder> binders) {
1485        try {
1486            callback.onResult(componentNames, binders);
1487        } catch (RemoteException e) {
1488            Log.e(this, e, "Contacting ConnectionService %s",
1489                    ConnectionServiceWrapper.this.getComponentName());
1490        }
1491    }
1492
1493    private void noRemoteServices(RemoteServiceCallback callback) {
1494        setRemoteServices(callback, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
1495    }
1496}
1497