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