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