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 
17 package com.android.server.telecom;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.net.Uri;
22 import android.os.Binder;
23 import android.os.Bundle;
24 import android.os.IBinder;
25 import android.os.RemoteException;
26 import android.os.UserHandle;
27 import android.telecom.CallAudioState;
28 import android.telecom.Connection;
29 import android.telecom.ConnectionRequest;
30 import android.telecom.ConnectionService;
31 import android.telecom.DisconnectCause;
32 import android.telecom.GatewayInfo;
33 import android.telecom.ParcelableConference;
34 import android.telecom.ParcelableConnection;
35 import android.telecom.PhoneAccountHandle;
36 import android.telecom.StatusHints;
37 import android.telecom.TelecomManager;
38 import android.telecom.VideoProfile;
39 
40 import com.android.internal.annotations.VisibleForTesting;
41 import com.android.internal.telecom.IConnectionService;
42 import com.android.internal.telecom.IConnectionServiceAdapter;
43 import com.android.internal.telecom.IVideoProvider;
44 import com.android.internal.telecom.RemoteServiceCallback;
45 import com.android.internal.util.Preconditions;
46 
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.HashMap;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Set;
53 import java.util.concurrent.ConcurrentHashMap;
54 
55 /**
56  * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps
57  * track of when the object can safely be unbound. Other classes should not use
58  * {@link IConnectionService} directly and instead should use this class to invoke methods of
59  * {@link IConnectionService}.
60  */
61 @VisibleForTesting
62 public class ConnectionServiceWrapper extends ServiceBinder {
63 
64     private final class Adapter extends IConnectionServiceAdapter.Stub {
65 
66         @Override
handleCreateConnectionComplete(String callId, ConnectionRequest request, ParcelableConnection connection)67         public void handleCreateConnectionComplete(String callId, ConnectionRequest request,
68                 ParcelableConnection connection) {
69             Log.startSession("CSW.hCCC");
70             long token = Binder.clearCallingIdentity();
71             try {
72                 synchronized (mLock) {
73                     logIncoming("handleCreateConnectionComplete %s", callId);
74                     ConnectionServiceWrapper.this
75                             .handleCreateConnectionComplete(callId, request, connection);
76                 }
77             } finally {
78                 Binder.restoreCallingIdentity(token);
79                 Log.endSession();
80             }
81         }
82 
83         @Override
setActive(String callId)84         public void setActive(String callId) {
85             Log.startSession("CSW.sA");
86             long token = Binder.clearCallingIdentity();
87             try {
88                 synchronized (mLock) {
89                     logIncoming("setActive %s", callId);
90                     Call call = mCallIdMapper.getCall(callId);
91                     if (call != null) {
92                         mCallsManager.markCallAsActive(call);
93                     } else {
94                         // Log.w(this, "setActive, unknown call id: %s", msg.obj);
95                     }
96                 }
97             } finally {
98                 Binder.restoreCallingIdentity(token);
99                 Log.endSession();
100             }
101         }
102 
103         @Override
setRinging(String callId)104         public void setRinging(String callId) {
105             Log.startSession("CSW.sR");
106             long token = Binder.clearCallingIdentity();
107             try {
108                 synchronized (mLock) {
109                     logIncoming("setRinging %s", callId);
110                     Call call = mCallIdMapper.getCall(callId);
111                     if (call != null) {
112                         mCallsManager.markCallAsRinging(call);
113                     } else {
114                         // Log.w(this, "setRinging, unknown call id: %s", msg.obj);
115                     }
116                 }
117             } finally {
118                 Binder.restoreCallingIdentity(token);
119                 Log.endSession();
120             }
121         }
122 
123         @Override
setVideoProvider(String callId, IVideoProvider videoProvider)124         public void setVideoProvider(String callId, IVideoProvider videoProvider) {
125             Log.startSession("CSW.sVP");
126             long token = Binder.clearCallingIdentity();
127             try {
128                 synchronized (mLock) {
129                     logIncoming("setVideoProvider %s", callId);
130                     Call call = mCallIdMapper.getCall(callId);
131                     if (call != null) {
132                         call.setVideoProvider(videoProvider);
133                     }
134                 }
135             } finally {
136                 Binder.restoreCallingIdentity(token);
137                 Log.endSession();
138             }
139         }
140 
141         @Override
setDialing(String callId)142         public void setDialing(String callId) {
143             Log.startSession("CSW.sD");
144             long token = Binder.clearCallingIdentity();
145             try {
146                 synchronized (mLock) {
147                     logIncoming("setDialing %s", callId);
148                     Call call = mCallIdMapper.getCall(callId);
149                     if (call != null) {
150                         mCallsManager.markCallAsDialing(call);
151                     } else {
152                         // Log.w(this, "setDialing, unknown call id: %s", msg.obj);
153                     }
154                 }
155             } finally {
156                 Binder.restoreCallingIdentity(token);
157                 Log.endSession();
158             }
159         }
160 
161         @Override
setDisconnected(String callId, DisconnectCause disconnectCause)162         public void setDisconnected(String callId, DisconnectCause disconnectCause) {
163             Log.startSession("CSW.sD");
164             long token = Binder.clearCallingIdentity();
165             try {
166                 synchronized (mLock) {
167                     logIncoming("setDisconnected %s %s", callId, disconnectCause);
168                     Call call = mCallIdMapper.getCall(callId);
169                     Log.d(this, "disconnect call %s %s", disconnectCause, call);
170                     if (call != null) {
171                         mCallsManager.markCallAsDisconnected(call, disconnectCause);
172                     } else {
173                         // Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
174                     }
175                 }
176             } finally {
177                 Binder.restoreCallingIdentity(token);
178                 Log.endSession();
179             }
180         }
181 
182         @Override
setOnHold(String callId)183         public void setOnHold(String callId) {
184             Log.startSession("CSW.sOH");
185             long token = Binder.clearCallingIdentity();
186             try {
187                 synchronized (mLock) {
188                     logIncoming("setOnHold %s", callId);
189                     Call call = mCallIdMapper.getCall(callId);
190                     if (call != null) {
191                         mCallsManager.markCallAsOnHold(call);
192                     } else {
193                         // Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
194                     }
195                 }
196             } finally {
197                 Binder.restoreCallingIdentity(token);
198                 Log.endSession();
199             }
200         }
201 
202         @Override
setRingbackRequested(String callId, boolean ringback)203         public void setRingbackRequested(String callId, boolean ringback) {
204             Log.startSession("CSW.SRR");
205             long token = Binder.clearCallingIdentity();
206             try {
207                 synchronized (mLock) {
208                     logIncoming("setRingbackRequested %s %b", callId, ringback);
209                     Call call = mCallIdMapper.getCall(callId);
210                     if (call != null) {
211                         call.setRingbackRequested(ringback);
212                     } else {
213                         // Log.w(this, "setRingback, unknown call id: %s", args.arg1);
214                     }
215                 }
216             } finally {
217                 Binder.restoreCallingIdentity(token);
218                 Log.endSession();
219             }
220         }
221 
222         @Override
removeCall(String callId)223         public void removeCall(String callId) {
224             Log.startSession("CSW.rC");
225             long token = Binder.clearCallingIdentity();
226             try {
227                 synchronized (mLock) {
228                     logIncoming("removeCall %s", callId);
229                     Call call = mCallIdMapper.getCall(callId);
230                     if (call != null) {
231                         if (call.isAlive()) {
232                             mCallsManager.markCallAsDisconnected(
233                                     call, new DisconnectCause(DisconnectCause.REMOTE));
234                         } else {
235                             mCallsManager.markCallAsRemoved(call);
236                         }
237                     }
238                 }
239             } finally {
240                 Binder.restoreCallingIdentity(token);
241                 Log.endSession();
242             }
243         }
244 
245         @Override
setConnectionCapabilities(String callId, int connectionCapabilities)246         public void setConnectionCapabilities(String callId, int connectionCapabilities) {
247             Log.startSession("CSW.sCC");
248             long token = Binder.clearCallingIdentity();
249             try {
250                 synchronized (mLock) {
251                     logIncoming("setConnectionCapabilities %s %d", callId, connectionCapabilities);
252                     Call call = mCallIdMapper.getCall(callId);
253                     if (call != null) {
254                         call.setConnectionCapabilities(connectionCapabilities);
255                     } else {
256                         // Log.w(ConnectionServiceWrapper.this,
257                         // "setConnectionCapabilities, unknown call id: %s", msg.obj);
258                     }
259                 }
260             } finally {
261                 Binder.restoreCallingIdentity(token);
262                 Log.endSession();
263             }
264         }
265 
266         @Override
setConnectionProperties(String callId, int connectionProperties)267         public void setConnectionProperties(String callId, int connectionProperties) {
268             Log.startSession("CSW.sCP");
269             long token = Binder.clearCallingIdentity();
270             try {
271                 synchronized (mLock) {
272                     logIncoming("setConnectionProperties %s %d", callId, connectionProperties);
273                     Call call = mCallIdMapper.getCall(callId);
274                     if (call != null) {
275                         call.setConnectionProperties(connectionProperties);
276                     }
277                 }
278             } finally {
279                 Binder.restoreCallingIdentity(token);
280                 Log.endSession();
281             }
282         }
283 
284         @Override
setIsConferenced(String callId, String conferenceCallId)285         public void setIsConferenced(String callId, String conferenceCallId) {
286             Log.startSession("CSW.sIC");
287             long token = Binder.clearCallingIdentity();
288             try {
289                 synchronized (mLock) {
290                     logIncoming("setIsConferenced %s %s", callId, conferenceCallId);
291                     Call childCall = mCallIdMapper.getCall(callId);
292                     if (childCall != null) {
293                         if (conferenceCallId == null) {
294                             Log.d(this, "unsetting parent: %s", conferenceCallId);
295                             childCall.setParentCall(null);
296                         } else {
297                             Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
298                             childCall.setParentCall(conferenceCall);
299                         }
300                     } else {
301                         // Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
302                     }
303                 }
304             } finally {
305                 Binder.restoreCallingIdentity(token);
306                 Log.endSession();
307             }
308         }
309 
310         @Override
setConferenceMergeFailed(String callId)311         public void setConferenceMergeFailed(String callId) {
312             Log.startSession("CSW.sCMF");
313             long token = Binder.clearCallingIdentity();
314             try {
315                 synchronized (mLock) {
316                     logIncoming("setConferenceMergeFailed %s", callId);
317                     // TODO: we should move the UI for indication a merge failure here
318                     // from CallNotifier.onSuppServiceFailed(). This way the InCallUI can
319                     // deliver the message anyway that they want. b/20530631.
320                     Call call = mCallIdMapper.getCall(callId);
321                     if (call != null) {
322                         // Just refresh the connection capabilities so that the UI
323                         // is forced to reenable the merge button as the capability
324                         // is still on the connection. Note when b/20530631 is fixed, we need
325                         // to revisit this fix to remove this hacky way of unhiding the merge
326                         // button (side effect of reprocessing the capabilities) and plumb
327                         // the failure event all the way to InCallUI instead of stopping
328                         // it here. That way we can also handle the UI of notifying that
329                         // the merged has failed.
330                         call.setConnectionCapabilities(call.getConnectionCapabilities(), true);
331                     } else {
332                         Log.w(this, "setConferenceMergeFailed, unknown call id: %s", callId);
333                     }
334                 }
335             } finally {
336                 Binder.restoreCallingIdentity(token);
337                 Log.endSession();
338             }
339         }
340 
341         @Override
addConferenceCall(String callId, ParcelableConference parcelableConference)342         public void addConferenceCall(String callId, ParcelableConference parcelableConference) {
343             Log.startSession("CSW.aCC");
344             long token = Binder.clearCallingIdentity();
345             try {
346                 synchronized (mLock) {
347                     if (mCallIdMapper.getCall(callId) != null) {
348                         Log.w(this, "Attempting to add a conference call using an existing " +
349                                 "call id %s", callId);
350                         return;
351                     }
352 
353                     // Make sure that there's at least one valid call. For remote connections
354                     // we'll get a add conference msg from both the remote connection service
355                     // and from the real connection service.
356                     boolean hasValidCalls = false;
357                     for (String connId : parcelableConference.getConnectionIds()) {
358                         if (mCallIdMapper.getCall(connId) != null) {
359                             hasValidCalls = true;
360                         }
361                     }
362                     // But don't bail out if the connection count is 0, because that is a valid
363                     // IMS conference state.
364                     if (!hasValidCalls && parcelableConference.getConnectionIds().size() > 0) {
365                         Log.d(this, "Attempting to add a conference with no valid calls");
366                         return;
367                     }
368 
369                     // need to create a new Call
370                     PhoneAccountHandle phAcc = null;
371                     if (parcelableConference != null &&
372                             parcelableConference.getPhoneAccount() != null) {
373                         phAcc = parcelableConference.getPhoneAccount();
374                     }
375                     Call conferenceCall = mCallsManager.createConferenceCall(callId,
376                             phAcc, parcelableConference);
377                     mCallIdMapper.addCall(conferenceCall, callId);
378                     conferenceCall.setConnectionService(ConnectionServiceWrapper.this);
379 
380                     Log.d(this, "adding children to conference %s phAcc %s",
381                             parcelableConference.getConnectionIds(), phAcc);
382                     for (String connId : parcelableConference.getConnectionIds()) {
383                         Call childCall = mCallIdMapper.getCall(connId);
384                         Log.d(this, "found child: %s", connId);
385                         if (childCall != null) {
386                             childCall.setParentCall(conferenceCall);
387                         }
388                     }
389                 }
390             } finally {
391                 Binder.restoreCallingIdentity(token);
392                 Log.endSession();
393             }
394         }
395 
396         @Override
onPostDialWait(String callId, String remaining)397         public void onPostDialWait(String callId, String remaining) throws RemoteException {
398             Log.startSession("CSW.oPDW");
399             long token = Binder.clearCallingIdentity();
400             try {
401                 synchronized (mLock) {
402                     logIncoming("onPostDialWait %s %s", callId, remaining);
403                     Call call = mCallIdMapper.getCall(callId);
404                     if (call != null) {
405                         call.onPostDialWait(remaining);
406                     } else {
407                         // Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
408                     }
409                 }
410             } finally {
411                 Binder.restoreCallingIdentity(token);
412                 Log.endSession();
413             }
414         }
415 
416         @Override
onPostDialChar(String callId, char nextChar)417         public void onPostDialChar(String callId, char nextChar) throws RemoteException {
418             Log.startSession("CSW.oPDC");
419             long token = Binder.clearCallingIdentity();
420             try {
421                 synchronized (mLock) {
422                     logIncoming("onPostDialChar %s %s", callId, nextChar);
423                     Call call = mCallIdMapper.getCall(callId);
424                     if (call != null) {
425                         call.onPostDialChar(nextChar);
426                     } else {
427                         // Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1);
428                     }
429                 }
430             } finally {
431                 Binder.restoreCallingIdentity(token);
432                 Log.endSession();
433             }
434         }
435 
436         @Override
queryRemoteConnectionServices(RemoteServiceCallback callback)437         public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
438             final UserHandle callingUserHandle = Binder.getCallingUserHandle();
439             Log.startSession("CSW.qRCS");
440             long token = Binder.clearCallingIdentity();
441             try {
442                 synchronized (mLock) {
443                     logIncoming("queryRemoteConnectionServices %s", callback);
444                     ConnectionServiceWrapper.this
445                             .queryRemoteConnectionServices(callingUserHandle, callback);
446                 }
447             } finally {
448                 Binder.restoreCallingIdentity(token);
449                 Log.endSession();
450             }
451         }
452 
453         @Override
setVideoState(String callId, int videoState)454         public void setVideoState(String callId, int videoState) {
455             Log.startSession("CSW.sVS");
456             long token = Binder.clearCallingIdentity();
457             try {
458                 synchronized (mLock) {
459                     logIncoming("setVideoState %s %d", callId, videoState);
460                     Call call = mCallIdMapper.getCall(callId);
461                     if (call != null) {
462                         call.setVideoState(videoState);
463                     }
464                 }
465             } finally {
466                 Binder.restoreCallingIdentity(token);
467                 Log.endSession();
468             }
469         }
470 
471         @Override
setIsVoipAudioMode(String callId, boolean isVoip)472         public void setIsVoipAudioMode(String callId, boolean isVoip) {
473             Log.startSession("CSW.sIVAM");
474             long token = Binder.clearCallingIdentity();
475             try {
476                 synchronized (mLock) {
477                     logIncoming("setIsVoipAudioMode %s %b", callId, isVoip);
478                     Call call = mCallIdMapper.getCall(callId);
479                     if (call != null) {
480                         call.setIsVoipAudioMode(isVoip);
481                     }
482                 }
483             } finally {
484                 Binder.restoreCallingIdentity(token);
485                 Log.endSession();
486             }
487         }
488 
489         @Override
setStatusHints(String callId, StatusHints statusHints)490         public void setStatusHints(String callId, StatusHints statusHints) {
491             Log.startSession("CSW.sSH");
492             long token = Binder.clearCallingIdentity();
493             try {
494                 synchronized (mLock) {
495                     logIncoming("setStatusHints %s %s", callId, statusHints);
496                     Call call = mCallIdMapper.getCall(callId);
497                     if (call != null) {
498                         call.setStatusHints(statusHints);
499                     }
500                 }
501             } finally {
502                 Binder.restoreCallingIdentity(token);
503                 Log.endSession();
504             }
505         }
506 
507         @Override
putExtras(String callId, Bundle extras)508         public void putExtras(String callId, Bundle extras) {
509             Log.startSession("CSW.pE");
510             long token = Binder.clearCallingIdentity();
511             try {
512                 synchronized (mLock) {
513                     Bundle.setDefusable(extras, true);
514                     Call call = mCallIdMapper.getCall(callId);
515                     if (call != null) {
516                         call.putExtras(Call.SOURCE_CONNECTION_SERVICE, extras);
517                     }
518                 }
519             } finally {
520                 Binder.restoreCallingIdentity(token);
521                 Log.endSession();
522             }
523         }
524 
525         @Override
removeExtras(String callId, List<String> keys)526         public void removeExtras(String callId, List<String> keys) {
527             Log.startSession("CSW.rE");
528             long token = Binder.clearCallingIdentity();
529             try {
530                 synchronized (mLock) {
531                     logIncoming("removeExtra %s %s", callId, keys);
532                     Call call = mCallIdMapper.getCall(callId);
533                     if (call != null) {
534                         call.removeExtras(Call.SOURCE_CONNECTION_SERVICE, keys);
535                     }
536                 }
537             } finally {
538                 Binder.restoreCallingIdentity(token);
539                 Log.endSession();
540             }
541         }
542 
543         @Override
setAddress(String callId, Uri address, int presentation)544         public void setAddress(String callId, Uri address, int presentation) {
545             Log.startSession("CSW.sA");
546             long token = Binder.clearCallingIdentity();
547             try {
548                 synchronized (mLock) {
549                     logIncoming("setAddress %s %s %d", callId, address, presentation);
550                     Call call = mCallIdMapper.getCall(callId);
551                     if (call != null) {
552                         call.setHandle(address, presentation);
553                     }
554                 }
555             } finally {
556                 Binder.restoreCallingIdentity(token);
557                 Log.endSession();
558             }
559         }
560 
561         @Override
setCallerDisplayName( String callId, String callerDisplayName, int presentation)562         public void setCallerDisplayName(
563                 String callId, String callerDisplayName, int presentation) {
564             Log.startSession("CSW.sCDN");
565             long token = Binder.clearCallingIdentity();
566             try {
567                 synchronized (mLock) {
568                     logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName,
569                             presentation);
570                     Call call = mCallIdMapper.getCall(callId);
571                     if (call != null) {
572                         call.setCallerDisplayName(callerDisplayName, presentation);
573                     }
574                 }
575             } finally {
576                 Binder.restoreCallingIdentity(token);
577                 Log.endSession();
578             }
579         }
580 
581         @Override
setConferenceableConnections( String callId, List<String> conferenceableCallIds)582         public void setConferenceableConnections(
583                 String callId, List<String> conferenceableCallIds) {
584             Log.startSession("CSW.sCC");
585             long token = Binder.clearCallingIdentity();
586             try {
587                 synchronized (mLock) {
588                     logIncoming("setConferenceableConnections %s %s", callId,
589                             conferenceableCallIds);
590                     Call call = mCallIdMapper.getCall(callId);
591                     if (call != null) {
592                         List<Call> conferenceableCalls =
593                                 new ArrayList<>(conferenceableCallIds.size());
594                         for (String otherId : conferenceableCallIds) {
595                             Call otherCall = mCallIdMapper.getCall(otherId);
596                             if (otherCall != null && otherCall != call) {
597                                 conferenceableCalls.add(otherCall);
598                             }
599                         }
600                         call.setConferenceableCalls(conferenceableCalls);
601                     }
602                 }
603             } finally {
604                 Binder.restoreCallingIdentity(token);
605                 Log.endSession();
606             }
607         }
608 
609         @Override
addExistingConnection(String callId, ParcelableConnection connection)610         public void addExistingConnection(String callId, ParcelableConnection connection) {
611             Log.startSession("CSW.aEC");
612             long token = Binder.clearCallingIdentity();
613             try {
614                 synchronized (mLock) {
615                     logIncoming("addExistingConnection  %s %s", callId, connection);
616                     Call existingCall = mCallsManager
617                             .createCallForExistingConnection(callId, connection);
618                     mCallIdMapper.addCall(existingCall, callId);
619                     existingCall.setConnectionService(ConnectionServiceWrapper.this);
620                 }
621             } finally {
622                 Binder.restoreCallingIdentity(token);
623                 Log.endSession();
624             }
625         }
626 
627         @Override
onConnectionEvent(String callId, String event, Bundle extras)628         public void onConnectionEvent(String callId, String event, Bundle extras) {
629             Log.startSession("CSW.oCE");
630             long token = Binder.clearCallingIdentity();
631             try {
632                 synchronized (mLock) {
633                     Bundle.setDefusable(extras, true);
634                     Call call = mCallIdMapper.getCall(callId);
635                     if (call != null) {
636                         call.onConnectionEvent(event, extras);
637                     }
638                 }
639             } finally {
640                 Binder.restoreCallingIdentity(token);
641                 Log.endSession();
642             }
643         }
644     }
645 
646     private final Adapter mAdapter = new Adapter();
647     private final CallIdMapper mCallIdMapper = new CallIdMapper();
648     private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
649 
650     private Binder2 mBinder = new Binder2();
651     private IConnectionService mServiceInterface;
652     private final ConnectionServiceRepository mConnectionServiceRepository;
653     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
654     private final CallsManager mCallsManager;
655 
656     /**
657      * Creates a connection service.
658      *
659      * @param componentName The component name of the service with which to bind.
660      * @param connectionServiceRepository Connection service repository.
661      * @param phoneAccountRegistrar Phone account registrar
662      * @param callsManager Calls manager
663      * @param context The context.
664      * @param userHandle The {@link UserHandle} to use when binding.
665      */
ConnectionServiceWrapper( ComponentName componentName, ConnectionServiceRepository connectionServiceRepository, PhoneAccountRegistrar phoneAccountRegistrar, CallsManager callsManager, Context context, TelecomSystem.SyncRoot lock, UserHandle userHandle)666     ConnectionServiceWrapper(
667             ComponentName componentName,
668             ConnectionServiceRepository connectionServiceRepository,
669             PhoneAccountRegistrar phoneAccountRegistrar,
670             CallsManager callsManager,
671             Context context,
672             TelecomSystem.SyncRoot lock,
673             UserHandle userHandle) {
674         super(ConnectionService.SERVICE_INTERFACE, componentName, context, lock, userHandle);
675         mConnectionServiceRepository = connectionServiceRepository;
676         phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {
677             // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections
678             // To do this, we must proxy remote ConnectionService objects
679         });
680         mPhoneAccountRegistrar = phoneAccountRegistrar;
681         mCallsManager = callsManager;
682     }
683 
684     /** See {@link IConnectionService#addConnectionServiceAdapter}. */
addConnectionServiceAdapter(IConnectionServiceAdapter adapter)685     private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
686         if (isServiceValid("addConnectionServiceAdapter")) {
687             try {
688                 logOutgoing("addConnectionServiceAdapter %s", adapter);
689                 mServiceInterface.addConnectionServiceAdapter(adapter);
690             } catch (RemoteException e) {
691             }
692         }
693     }
694 
695     /**
696      * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
697      */
698     @VisibleForTesting
createConnection(final Call call, final CreateConnectionResponse response)699     public void createConnection(final Call call, final CreateConnectionResponse response) {
700         Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
701         BindCallback callback = new BindCallback() {
702             @Override
703             public void onSuccess() {
704                 String callId = mCallIdMapper.getCallId(call);
705                 mPendingResponses.put(callId, response);
706 
707                 GatewayInfo gatewayInfo = call.getGatewayInfo();
708                 Bundle extras = call.getIntentExtras();
709                 if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
710                         gatewayInfo.getOriginalAddress() != null) {
711                     extras = (Bundle) extras.clone();
712                     extras.putString(
713                             TelecomManager.GATEWAY_PROVIDER_PACKAGE,
714                             gatewayInfo.getGatewayProviderPackageName());
715                     extras.putParcelable(
716                             TelecomManager.GATEWAY_ORIGINAL_ADDRESS,
717                             gatewayInfo.getOriginalAddress());
718                 }
719 
720                 Log.event(call, Log.Events.START_CONNECTION, Log.piiHandle(call.getHandle()));
721                 try {
722                     mServiceInterface.createConnection(
723                             call.getConnectionManagerPhoneAccount(),
724                             callId,
725                             new ConnectionRequest(
726                                     call.getTargetPhoneAccount(),
727                                     call.getHandle(),
728                                     extras,
729                                     call.getVideoState(),
730                                     callId),
731                             call.shouldAttachToExistingConnection(),
732                             call.isUnknown());
733                 } catch (RemoteException e) {
734                     Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
735                     mPendingResponses.remove(callId).handleCreateConnectionFailure(
736                             new DisconnectCause(DisconnectCause.ERROR, e.toString()));
737                 }
738             }
739 
740             @Override
741             public void onFailure() {
742                 Log.e(this, new Exception(), "Failure to call %s", getComponentName());
743                 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
744             }
745         };
746 
747         mBinder.bind(callback, call);
748     }
749 
750     /** @see IConnectionService#abort(String) */
abort(Call call)751     void abort(Call call) {
752         // Clear out any pending outgoing call data
753         final String callId = mCallIdMapper.getCallId(call);
754 
755         // If still bound, tell the connection service to abort.
756         if (callId != null && isServiceValid("abort")) {
757             try {
758                 logOutgoing("abort %s", callId);
759                 mServiceInterface.abort(callId);
760             } catch (RemoteException e) {
761             }
762         }
763 
764         removeCall(call, new DisconnectCause(DisconnectCause.LOCAL));
765     }
766 
767     /** @see IConnectionService#silence(String) */
silence(Call call)768     void silence(Call call) {
769         final String callId = mCallIdMapper.getCallId(call);
770         if (callId != null && isServiceValid("silence")) {
771             try {
772                 logOutgoing("silence %s", callId);
773                 mServiceInterface.silence(callId);
774             } catch (RemoteException e) {
775             }
776         }
777     }
778 
779     /** @see IConnectionService#hold(String) */
hold(Call call)780     void hold(Call call) {
781         final String callId = mCallIdMapper.getCallId(call);
782         if (callId != null && isServiceValid("hold")) {
783             try {
784                 logOutgoing("hold %s", callId);
785                 mServiceInterface.hold(callId);
786             } catch (RemoteException e) {
787             }
788         }
789     }
790 
791     /** @see IConnectionService#unhold(String) */
unhold(Call call)792     void unhold(Call call) {
793         final String callId = mCallIdMapper.getCallId(call);
794         if (callId != null && isServiceValid("unhold")) {
795             try {
796                 logOutgoing("unhold %s", callId);
797                 mServiceInterface.unhold(callId);
798             } catch (RemoteException e) {
799             }
800         }
801     }
802 
803     /** @see IConnectionService#onCallAudioStateChanged(String, CallAudioState) */
804     @VisibleForTesting
onCallAudioStateChanged(Call activeCall, CallAudioState audioState)805     public void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) {
806         final String callId = mCallIdMapper.getCallId(activeCall);
807         if (callId != null && isServiceValid("onCallAudioStateChanged")) {
808             try {
809                 logOutgoing("onCallAudioStateChanged %s %s", callId, audioState);
810                 mServiceInterface.onCallAudioStateChanged(callId, audioState);
811             } catch (RemoteException e) {
812             }
813         }
814     }
815 
816     /** @see IConnectionService#disconnect(String) */
disconnect(Call call)817     void disconnect(Call call) {
818         final String callId = mCallIdMapper.getCallId(call);
819         if (callId != null && isServiceValid("disconnect")) {
820             try {
821                 logOutgoing("disconnect %s", callId);
822                 mServiceInterface.disconnect(callId);
823             } catch (RemoteException e) {
824             }
825         }
826     }
827 
828     /** @see IConnectionService#answer(String) */
answer(Call call, int videoState)829     void answer(Call call, int videoState) {
830         final String callId = mCallIdMapper.getCallId(call);
831         if (callId != null && isServiceValid("answer")) {
832             try {
833                 logOutgoing("answer %s %d", callId, videoState);
834                 if (VideoProfile.isAudioOnly(videoState)) {
835                     mServiceInterface.answer(callId);
836                 } else {
837                     mServiceInterface.answerVideo(callId, videoState);
838                 }
839             } catch (RemoteException e) {
840             }
841         }
842     }
843 
844     /** @see IConnectionService#reject(String) */
reject(Call call, boolean rejectWithMessage, String message)845     void reject(Call call, boolean rejectWithMessage, String message) {
846         final String callId = mCallIdMapper.getCallId(call);
847         if (callId != null && isServiceValid("reject")) {
848             try {
849                 logOutgoing("reject %s", callId);
850 
851                 if (rejectWithMessage && call.can(
852                         Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
853                     mServiceInterface.rejectWithMessage(callId, message);
854                 } else {
855                     mServiceInterface.reject(callId);
856                 }
857             } catch (RemoteException e) {
858             }
859         }
860     }
861 
862     /** @see IConnectionService#playDtmfTone(String, char) */
playDtmfTone(Call call, char digit)863     void playDtmfTone(Call call, char digit) {
864         final String callId = mCallIdMapper.getCallId(call);
865         if (callId != null && isServiceValid("playDtmfTone")) {
866             try {
867                 logOutgoing("playDtmfTone %s %c", callId, digit);
868                 mServiceInterface.playDtmfTone(callId, digit);
869             } catch (RemoteException e) {
870             }
871         }
872     }
873 
874     /** @see IConnectionService#stopDtmfTone(String) */
stopDtmfTone(Call call)875     void stopDtmfTone(Call call) {
876         final String callId = mCallIdMapper.getCallId(call);
877         if (callId != null && isServiceValid("stopDtmfTone")) {
878             try {
879                 logOutgoing("stopDtmfTone %s", callId);
880                 mServiceInterface.stopDtmfTone(callId);
881             } catch (RemoteException e) {
882             }
883         }
884     }
885 
addCall(Call call)886     void addCall(Call call) {
887         if (mCallIdMapper.getCallId(call) == null) {
888             mCallIdMapper.addCall(call);
889         }
890     }
891 
892     /**
893      * Associates newCall with this connection service by replacing callToReplace.
894      */
replaceCall(Call newCall, Call callToReplace)895     void replaceCall(Call newCall, Call callToReplace) {
896         Preconditions.checkState(callToReplace.getConnectionService() == this);
897         mCallIdMapper.replaceCall(newCall, callToReplace);
898     }
899 
removeCall(Call call)900     void removeCall(Call call) {
901         removeCall(call, new DisconnectCause(DisconnectCause.ERROR));
902     }
903 
removeCall(String callId, DisconnectCause disconnectCause)904     void removeCall(String callId, DisconnectCause disconnectCause) {
905         CreateConnectionResponse response = mPendingResponses.remove(callId);
906         if (response != null) {
907             response.handleCreateConnectionFailure(disconnectCause);
908         }
909 
910         mCallIdMapper.removeCall(callId);
911     }
912 
removeCall(Call call, DisconnectCause disconnectCause)913     void removeCall(Call call, DisconnectCause disconnectCause) {
914         CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
915         if (response != null) {
916             response.handleCreateConnectionFailure(disconnectCause);
917         }
918 
919         mCallIdMapper.removeCall(call);
920     }
921 
onPostDialContinue(Call call, boolean proceed)922     void onPostDialContinue(Call call, boolean proceed) {
923         final String callId = mCallIdMapper.getCallId(call);
924         if (callId != null && isServiceValid("onPostDialContinue")) {
925             try {
926                 logOutgoing("onPostDialContinue %s %b", callId, proceed);
927                 mServiceInterface.onPostDialContinue(callId, proceed);
928             } catch (RemoteException ignored) {
929             }
930         }
931     }
932 
conference(final Call call, Call otherCall)933     void conference(final Call call, Call otherCall) {
934         final String callId = mCallIdMapper.getCallId(call);
935         final String otherCallId = mCallIdMapper.getCallId(otherCall);
936         if (callId != null && otherCallId != null && isServiceValid("conference")) {
937             try {
938                 logOutgoing("conference %s %s", callId, otherCallId);
939                 mServiceInterface.conference(callId, otherCallId);
940             } catch (RemoteException ignored) {
941             }
942         }
943     }
944 
splitFromConference(Call call)945     void splitFromConference(Call call) {
946         final String callId = mCallIdMapper.getCallId(call);
947         if (callId != null && isServiceValid("splitFromConference")) {
948             try {
949                 logOutgoing("splitFromConference %s", callId);
950                 mServiceInterface.splitFromConference(callId);
951             } catch (RemoteException ignored) {
952             }
953         }
954     }
955 
mergeConference(Call call)956     void mergeConference(Call call) {
957         final String callId = mCallIdMapper.getCallId(call);
958         if (callId != null && isServiceValid("mergeConference")) {
959             try {
960                 logOutgoing("mergeConference %s", callId);
961                 mServiceInterface.mergeConference(callId);
962             } catch (RemoteException ignored) {
963             }
964         }
965     }
966 
swapConference(Call call)967     void swapConference(Call call) {
968         final String callId = mCallIdMapper.getCallId(call);
969         if (callId != null && isServiceValid("swapConference")) {
970             try {
971                 logOutgoing("swapConference %s", callId);
972                 mServiceInterface.swapConference(callId);
973             } catch (RemoteException ignored) {
974             }
975         }
976     }
977 
pullExternalCall(Call call)978     void pullExternalCall(Call call) {
979         final String callId = mCallIdMapper.getCallId(call);
980         if (callId != null && isServiceValid("pullExternalCall")) {
981             try {
982                 logOutgoing("pullExternalCall %s", callId);
983                 mServiceInterface.pullExternalCall(callId);
984             } catch (RemoteException ignored) {
985             }
986         }
987     }
988 
sendCallEvent(Call call, String event, Bundle extras)989     void sendCallEvent(Call call, String event, Bundle extras) {
990         final String callId = mCallIdMapper.getCallId(call);
991         if (callId != null && isServiceValid("sendCallEvent")) {
992             try {
993                 logOutgoing("sendCallEvent %s %s", callId, event);
994                 mServiceInterface.sendCallEvent(callId, event, extras);
995             } catch (RemoteException ignored) {
996             }
997         }
998     }
999 
onExtrasChanged(Call call, Bundle extras)1000     void onExtrasChanged(Call call, Bundle extras) {
1001         final String callId = mCallIdMapper.getCallId(call);
1002         if (callId != null && isServiceValid("onExtrasChanged")) {
1003             try {
1004                 logOutgoing("onExtrasChanged %s %s", callId, extras);
1005                 mServiceInterface.onExtrasChanged(callId, extras);
1006             } catch (RemoteException ignored) {
1007             }
1008         }
1009     }
1010 
1011     /** {@inheritDoc} */
1012     @Override
setServiceInterface(IBinder binder)1013     protected void setServiceInterface(IBinder binder) {
1014         if (binder == null) {
1015             // We have lost our service connection. Notify the world that this service is done.
1016             // We must notify the adapter before CallsManager. The adapter will force any pending
1017             // outgoing calls to try the next service. This needs to happen before CallsManager
1018             // tries to clean up any calls still associated with this service.
1019             handleConnectionServiceDeath();
1020             mCallsManager.handleConnectionServiceDeath(this);
1021             mServiceInterface = null;
1022         } else {
1023             mServiceInterface = IConnectionService.Stub.asInterface(binder);
1024             addConnectionServiceAdapter(mAdapter);
1025         }
1026     }
1027 
handleCreateConnectionComplete( String callId, ConnectionRequest request, ParcelableConnection connection)1028     private void handleCreateConnectionComplete(
1029             String callId,
1030             ConnectionRequest request,
1031             ParcelableConnection connection) {
1032         // TODO: Note we are not using parameter "request", which is a side effect of our tacit
1033         // assumption that we have at most one outgoing connection attempt per ConnectionService.
1034         // This may not continue to be the case.
1035         if (connection.getState() == Connection.STATE_DISCONNECTED) {
1036             // A connection that begins in the DISCONNECTED state is an indication of
1037             // failure to connect; we handle all failures uniformly
1038             removeCall(callId, connection.getDisconnectCause());
1039         } else {
1040             // Successful connection
1041             if (mPendingResponses.containsKey(callId)) {
1042                 mPendingResponses.remove(callId)
1043                         .handleCreateConnectionSuccess(mCallIdMapper, connection);
1044             }
1045         }
1046     }
1047 
1048     /**
1049      * Called when the associated connection service dies.
1050      */
handleConnectionServiceDeath()1051     private void handleConnectionServiceDeath() {
1052         if (!mPendingResponses.isEmpty()) {
1053             CreateConnectionResponse[] responses = mPendingResponses.values().toArray(
1054                     new CreateConnectionResponse[mPendingResponses.values().size()]);
1055             mPendingResponses.clear();
1056             for (int i = 0; i < responses.length; i++) {
1057                 responses[i].handleCreateConnectionFailure(
1058                         new DisconnectCause(DisconnectCause.ERROR));
1059             }
1060         }
1061         mCallIdMapper.clear();
1062     }
1063 
logIncoming(String msg, Object... params)1064     private void logIncoming(String msg, Object... params) {
1065         Log.d(this, "ConnectionService -> Telecom: " + msg, params);
1066     }
1067 
logOutgoing(String msg, Object... params)1068     private void logOutgoing(String msg, Object... params) {
1069         Log.d(this, "Telecom -> ConnectionService: " + msg, params);
1070     }
1071 
queryRemoteConnectionServices(final UserHandle userHandle, final RemoteServiceCallback callback)1072     private void queryRemoteConnectionServices(final UserHandle userHandle,
1073             final RemoteServiceCallback callback) {
1074         // Only give remote connection services to this connection service if it is listed as
1075         // the connection manager.
1076         PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager(userHandle);
1077         Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager);
1078         if (simCallManager == null ||
1079                 !simCallManager.getComponentName().equals(getComponentName())) {
1080             noRemoteServices(callback);
1081             return;
1082         }
1083 
1084         // Make a list of ConnectionServices that are listed as being associated with SIM accounts
1085         final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap(
1086                 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1));
1087         for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts(userHandle)) {
1088             ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
1089                     handle.getComponentName(), handle.getUserHandle());
1090             if (service != null) {
1091                 simServices.add(service);
1092             }
1093         }
1094 
1095         final List<ComponentName> simServiceComponentNames = new ArrayList<>();
1096         final List<IBinder> simServiceBinders = new ArrayList<>();
1097 
1098         Log.v(this, "queryRemoteConnectionServices, simServices = %s", simServices);
1099 
1100         for (ConnectionServiceWrapper simService : simServices) {
1101             if (simService == this) {
1102                 // Only happens in the unlikely case that a SIM service is also a SIM call manager
1103                 continue;
1104             }
1105 
1106             final ConnectionServiceWrapper currentSimService = simService;
1107 
1108             currentSimService.mBinder.bind(new BindCallback() {
1109                 @Override
1110                 public void onSuccess() {
1111                     Log.d(this, "Adding simService %s", currentSimService.getComponentName());
1112                     simServiceComponentNames.add(currentSimService.getComponentName());
1113                     simServiceBinders.add(currentSimService.mServiceInterface.asBinder());
1114                     maybeComplete();
1115                 }
1116 
1117                 @Override
1118                 public void onFailure() {
1119                     Log.d(this, "Failed simService %s", currentSimService.getComponentName());
1120                     // We know maybeComplete() will always be a no-op from now on, so go ahead and
1121                     // signal failure of the entire request
1122                     noRemoteServices(callback);
1123                 }
1124 
1125                 private void maybeComplete() {
1126                     if (simServiceComponentNames.size() == simServices.size()) {
1127                         setRemoteServices(callback, simServiceComponentNames, simServiceBinders);
1128                     }
1129                 }
1130             }, null);
1131         }
1132     }
1133 
setRemoteServices( RemoteServiceCallback callback, List<ComponentName> componentNames, List<IBinder> binders)1134     private void setRemoteServices(
1135             RemoteServiceCallback callback,
1136             List<ComponentName> componentNames,
1137             List<IBinder> binders) {
1138         try {
1139             callback.onResult(componentNames, binders);
1140         } catch (RemoteException e) {
1141             Log.e(this, e, "Contacting ConnectionService %s",
1142                     ConnectionServiceWrapper.this.getComponentName());
1143         }
1144     }
1145 
noRemoteServices(RemoteServiceCallback callback)1146     private void noRemoteServices(RemoteServiceCallback callback) {
1147         setRemoteServices(callback, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
1148     }
1149 }
1150