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 static android.Manifest.permission.MODIFY_PHONE_STATE;
20 
21 import android.app.AppOpsManager;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.pm.PackageManager;
25 import android.net.Uri;
26 import android.os.Binder;
27 import android.os.Bundle;
28 import android.os.IBinder;
29 import android.os.ParcelFileDescriptor;
30 import android.os.RemoteException;
31 import android.os.UserHandle;
32 import android.telecom.CallAudioState;
33 import android.telecom.Connection;
34 import android.telecom.ConnectionRequest;
35 import android.telecom.ConnectionService;
36 import android.telecom.DisconnectCause;
37 import android.telecom.GatewayInfo;
38 import android.telecom.Log;
39 import android.telecom.Logging.Session;
40 import android.telecom.ParcelableConference;
41 import android.telecom.ParcelableConnection;
42 import android.telecom.PhoneAccountHandle;
43 import android.telecom.StatusHints;
44 import android.telecom.TelecomManager;
45 import android.telecom.VideoProfile;
46 
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.internal.telecom.IConnectionService;
49 import com.android.internal.telecom.IConnectionServiceAdapter;
50 import com.android.internal.telecom.IVideoProvider;
51 import com.android.internal.telecom.RemoteServiceCallback;
52 import com.android.internal.util.Preconditions;
53 
54 import java.util.ArrayList;
55 import java.util.Collections;
56 import java.util.HashMap;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Set;
60 import java.util.concurrent.ConcurrentHashMap;
61 
62 /**
63  * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps
64  * track of when the object can safely be unbound. Other classes should not use
65  * {@link IConnectionService} directly and instead should use this class to invoke methods of
66  * {@link IConnectionService}.
67  */
68 @VisibleForTesting
69 public class ConnectionServiceWrapper extends ServiceBinder implements
70         ConnectionServiceFocusManager.ConnectionServiceFocus {
71 
72     private final class Adapter extends IConnectionServiceAdapter.Stub {
73 
74         @Override
handleCreateConnectionComplete(String callId, ConnectionRequest request, ParcelableConnection connection, Session.Info sessionInfo)75         public void handleCreateConnectionComplete(String callId, ConnectionRequest request,
76                 ParcelableConnection connection, Session.Info sessionInfo) {
77             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE);
78             long token = Binder.clearCallingIdentity();
79             try {
80                 synchronized (mLock) {
81                     logIncoming("handleCreateConnectionComplete %s", callId);
82                     ConnectionServiceWrapper.this
83                             .handleCreateConnectionComplete(callId, request, connection);
84 
85                     if (mServiceInterface != null) {
86                         logOutgoing("createConnectionComplete %s", callId);
87                         try {
88                             mServiceInterface.createConnectionComplete(callId,
89                                     Log.getExternalSession());
90                         } catch (RemoteException e) {
91                         }
92                     }
93                 }
94             } catch (Throwable t) {
95                 Log.e(ConnectionServiceWrapper.this, t, "");
96                 throw t;
97             } finally {
98                 Binder.restoreCallingIdentity(token);
99                 Log.endSession();
100             }
101         }
102 
103         @Override
handleCreateConferenceComplete(String callId, ConnectionRequest request, ParcelableConference conference, Session.Info sessionInfo)104         public void handleCreateConferenceComplete(String callId, ConnectionRequest request,
105                 ParcelableConference conference, Session.Info sessionInfo) {
106             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE);
107             long token = Binder.clearCallingIdentity();
108             try {
109                 synchronized (mLock) {
110                     logIncoming("handleCreateConferenceComplete %s", callId);
111                     ConnectionServiceWrapper.this
112                             .handleCreateConferenceComplete(callId, request, conference);
113 
114                     if (mServiceInterface != null) {
115                         logOutgoing("createConferenceComplete %s", callId);
116                         try {
117                             mServiceInterface.createConferenceComplete(callId,
118                                     Log.getExternalSession());
119                         } catch (RemoteException e) {
120                         }
121                     }
122                 }
123             } catch (Throwable t) {
124                 Log.e(ConnectionServiceWrapper.this, t, "");
125                 throw t;
126             } finally {
127                 Binder.restoreCallingIdentity(token);
128                 Log.endSession();
129             }
130         }
131 
132 
133         @Override
setActive(String callId, Session.Info sessionInfo)134         public void setActive(String callId, Session.Info sessionInfo) {
135             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_ACTIVE);
136             long token = Binder.clearCallingIdentity();
137             try {
138                 synchronized (mLock) {
139                     logIncoming("setActive %s", callId);
140                     Call call = mCallIdMapper.getCall(callId);
141                     if (call != null) {
142                         mCallsManager.markCallAsActive(call);
143                     } else {
144                         // Log.w(this, "setActive, unknown call id: %s", msg.obj);
145                     }
146                 }
147             } catch (Throwable t) {
148                 Log.e(ConnectionServiceWrapper.this, t, "");
149                 throw t;
150             } finally {
151                 Binder.restoreCallingIdentity(token);
152                 Log.endSession();
153             }
154         }
155 
156         @Override
setRinging(String callId, Session.Info sessionInfo)157         public void setRinging(String callId, Session.Info sessionInfo) {
158             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_RINGING);
159             long token = Binder.clearCallingIdentity();
160             try {
161                 synchronized (mLock) {
162                     logIncoming("setRinging %s", callId);
163                     Call call = mCallIdMapper.getCall(callId);
164                     if (call != null) {
165                         mCallsManager.markCallAsRinging(call);
166                     } else {
167                         // Log.w(this, "setRinging, unknown call id: %s", msg.obj);
168                     }
169                 }
170             } catch (Throwable t) {
171                 Log.e(ConnectionServiceWrapper.this, t, "");
172                 throw t;
173             } finally {
174                 Binder.restoreCallingIdentity(token);
175                 Log.endSession();
176             }
177         }
178 
179         @Override
resetConnectionTime(String callId, Session.Info sessionInfo)180         public void resetConnectionTime(String callId, Session.Info sessionInfo) {
181             Log.startSession(sessionInfo, "CSW.rCCT");
182             long token = Binder.clearCallingIdentity();
183             try {
184                 synchronized (mLock) {
185                     logIncoming("resetConnectionTime %s", callId);
186                     Call call = mCallIdMapper.getCall(callId);
187                     if (call != null) {
188                         mCallsManager.resetConnectionTime(call);
189                     } else {
190                         // Log.w(this, "resetConnectionTime, unknown call id: %s", msg.obj);
191                     }
192                 }
193             } finally {
194                 Binder.restoreCallingIdentity(token);
195                 Log.endSession();
196             }
197         }
198 
199         @Override
setVideoProvider(String callId, IVideoProvider videoProvider, Session.Info sessionInfo)200         public void setVideoProvider(String callId, IVideoProvider videoProvider,
201                 Session.Info sessionInfo) {
202             Log.startSession(sessionInfo, "CSW.sVP");
203             long token = Binder.clearCallingIdentity();
204             try {
205                 synchronized (mLock) {
206                     logIncoming("setVideoProvider %s", callId);
207                     Call call = mCallIdMapper.getCall(callId);
208                     if (call != null) {
209                         call.setVideoProvider(videoProvider);
210                     }
211                 }
212             } catch (Throwable t) {
213                 Log.e(ConnectionServiceWrapper.this, t, "");
214                 throw t;
215             } finally {
216                 Binder.restoreCallingIdentity(token);
217                 Log.endSession();
218             }
219         }
220 
221         @Override
setDialing(String callId, Session.Info sessionInfo)222         public void setDialing(String callId, Session.Info sessionInfo) {
223             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_DIALING);
224             long token = Binder.clearCallingIdentity();
225             try {
226                 synchronized (mLock) {
227                     logIncoming("setDialing %s", callId);
228                     Call call = mCallIdMapper.getCall(callId);
229                     if (call != null) {
230                         mCallsManager.markCallAsDialing(call);
231                     } else {
232                         // Log.w(this, "setDialing, unknown call id: %s", msg.obj);
233                     }
234                 }
235             } catch (Throwable t) {
236                 Log.e(ConnectionServiceWrapper.this, t, "");
237                 throw t;
238             } finally {
239                 Binder.restoreCallingIdentity(token);
240                 Log.endSession();
241             }
242         }
243 
244         @Override
setPulling(String callId, Session.Info sessionInfo)245         public void setPulling(String callId, Session.Info sessionInfo) {
246             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_PULLING);
247             long token = Binder.clearCallingIdentity();
248             try {
249                 synchronized (mLock) {
250                     logIncoming("setPulling %s", callId);
251                     Call call = mCallIdMapper.getCall(callId);
252                     if (call != null) {
253                         mCallsManager.markCallAsPulling(call);
254                     }
255                 }
256             } catch (Throwable t) {
257                 Log.e(ConnectionServiceWrapper.this, t, "");
258                 throw t;
259             } finally {
260                 Binder.restoreCallingIdentity(token);
261                 Log.endSession();
262             }
263         }
264 
265         @Override
setDisconnected(String callId, DisconnectCause disconnectCause, Session.Info sessionInfo)266         public void setDisconnected(String callId, DisconnectCause disconnectCause,
267                 Session.Info sessionInfo) {
268             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_DISCONNECTED);
269             long token = Binder.clearCallingIdentity();
270             try {
271                 synchronized (mLock) {
272                     logIncoming("setDisconnected %s %s", callId, disconnectCause);
273                     Call call = mCallIdMapper.getCall(callId);
274                     Log.d(this, "disconnect call %s %s", disconnectCause, call);
275                     if (call != null) {
276                         mCallsManager.markCallAsDisconnected(call, disconnectCause);
277                     } else {
278                         // Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
279                     }
280                 }
281             } catch (Throwable t) {
282                 Log.e(ConnectionServiceWrapper.this, t, "");
283                 throw t;
284             } finally {
285                 Binder.restoreCallingIdentity(token);
286                 Log.endSession();
287             }
288         }
289 
290         @Override
setOnHold(String callId, Session.Info sessionInfo)291         public void setOnHold(String callId, Session.Info sessionInfo) {
292             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_ON_HOLD);
293             long token = Binder.clearCallingIdentity();
294             try {
295                 synchronized (mLock) {
296                     logIncoming("setOnHold %s", callId);
297                     Call call = mCallIdMapper.getCall(callId);
298                     if (call != null) {
299                         mCallsManager.markCallAsOnHold(call);
300                     } else {
301                         // Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
302                     }
303                 }
304             } catch (Throwable t) {
305                 Log.e(ConnectionServiceWrapper.this, t, "");
306                 throw t;
307             } finally {
308                 Binder.restoreCallingIdentity(token);
309                 Log.endSession();
310             }
311         }
312 
313         @Override
setRingbackRequested(String callId, boolean ringback, Session.Info sessionInfo)314         public void setRingbackRequested(String callId, boolean ringback,
315                 Session.Info sessionInfo) {
316             Log.startSession(sessionInfo, "CSW.SRR");
317             long token = Binder.clearCallingIdentity();
318             try {
319                 synchronized (mLock) {
320                     logIncoming("setRingbackRequested %s %b", callId, ringback);
321                     Call call = mCallIdMapper.getCall(callId);
322                     if (call != null) {
323                         call.setRingbackRequested(ringback);
324                     } else {
325                         // Log.w(this, "setRingback, unknown call id: %s", args.arg1);
326                     }
327                 }
328             } catch (Throwable t) {
329                 Log.e(ConnectionServiceWrapper.this, t, "");
330                 throw t;
331             } finally {
332                 Binder.restoreCallingIdentity(token);
333                 Log.endSession();
334             }
335         }
336 
337         @Override
removeCall(String callId, Session.Info sessionInfo)338         public void removeCall(String callId, Session.Info sessionInfo) {
339             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_REMOVE_CALL);
340             long token = Binder.clearCallingIdentity();
341             try {
342                 synchronized (mLock) {
343                     logIncoming("removeCall %s", callId);
344                     Call call = mCallIdMapper.getCall(callId);
345                     if (call != null) {
346                         if (call.isAlive()) {
347                             mCallsManager.markCallAsDisconnected(
348                                     call, new DisconnectCause(DisconnectCause.REMOTE));
349                         } else {
350                             mCallsManager.markCallAsRemoved(call);
351                         }
352                     }
353                 }
354             } catch (Throwable t) {
355                 Log.e(ConnectionServiceWrapper.this, t, "");
356                 throw t;
357             } finally {
358                 Binder.restoreCallingIdentity(token);
359                 Log.endSession();
360             }
361         }
362 
363         @Override
setConnectionCapabilities(String callId, int connectionCapabilities, Session.Info sessionInfo)364         public void setConnectionCapabilities(String callId, int connectionCapabilities,
365                 Session.Info sessionInfo) {
366             Log.startSession(sessionInfo, "CSW.sCC");
367             long token = Binder.clearCallingIdentity();
368             try {
369                 synchronized (mLock) {
370                     logIncoming("setConnectionCapabilities %s %d", callId, connectionCapabilities);
371                     Call call = mCallIdMapper.getCall(callId);
372                     if (call != null) {
373                         call.setConnectionCapabilities(connectionCapabilities);
374                     } else {
375                         // Log.w(ConnectionServiceWrapper.this,
376                         // "setConnectionCapabilities, unknown call id: %s", msg.obj);
377                     }
378                 }
379             } catch (Throwable t) {
380                 Log.e(ConnectionServiceWrapper.this, t, "");
381                 throw t;
382             } finally {
383                 Binder.restoreCallingIdentity(token);
384                 Log.endSession();
385             }
386         }
387 
388         @Override
setConnectionProperties(String callId, int connectionProperties, Session.Info sessionInfo)389         public void setConnectionProperties(String callId, int connectionProperties,
390                 Session.Info sessionInfo) {
391             Log.startSession("CSW.sCP");
392             long token = Binder.clearCallingIdentity();
393             try {
394                 synchronized (mLock) {
395                     logIncoming("setConnectionProperties %s %d", callId, connectionProperties);
396                     Call call = mCallIdMapper.getCall(callId);
397                     if (call != null) {
398                         call.setConnectionProperties(connectionProperties);
399                     }
400                 }
401             } catch (Throwable t) {
402                 Log.e(ConnectionServiceWrapper.this, t, "");
403                 throw t;
404             } finally {
405                 Binder.restoreCallingIdentity(token);
406                 Log.endSession();
407             }
408         }
409 
410         @Override
setIsConferenced(String callId, String conferenceCallId, Session.Info sessionInfo)411         public void setIsConferenced(String callId, String conferenceCallId,
412                 Session.Info sessionInfo) {
413             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_IS_CONFERENCED);
414             long token = Binder.clearCallingIdentity();
415             try {
416                 synchronized (mLock) {
417                     logIncoming("setIsConferenced %s %s", callId, conferenceCallId);
418                     Call childCall = mCallIdMapper.getCall(callId);
419                     if (childCall != null) {
420                         if (conferenceCallId == null) {
421                             Log.d(this, "unsetting parent: %s", conferenceCallId);
422                             childCall.setParentAndChildCall(null);
423                         } else {
424                             Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
425                             childCall.setParentAndChildCall(conferenceCall);
426                         }
427                     } else {
428                         // Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
429                     }
430                 }
431             } catch (Throwable t) {
432                 Log.e(ConnectionServiceWrapper.this, t, "");
433                 throw t;
434             } finally {
435                 Binder.restoreCallingIdentity(token);
436                 Log.endSession();
437             }
438         }
439 
440         @Override
setConferenceMergeFailed(String callId, Session.Info sessionInfo)441         public void setConferenceMergeFailed(String callId, Session.Info sessionInfo) {
442             Log.startSession(sessionInfo, "CSW.sCMF");
443             long token = Binder.clearCallingIdentity();
444             try {
445                 synchronized (mLock) {
446                     logIncoming("setConferenceMergeFailed %s", callId);
447                     // TODO: we should move the UI for indication a merge failure here
448                     // from CallNotifier.onSuppServiceFailed(). This way the InCallUI can
449                     // deliver the message anyway that they want. b/20530631.
450                     Call call = mCallIdMapper.getCall(callId);
451                     if (call != null) {
452                         call.onConnectionEvent(Connection.EVENT_CALL_MERGE_FAILED, null);
453                     } else {
454                         Log.w(this, "setConferenceMergeFailed, unknown call id: %s", callId);
455                     }
456                 }
457             } catch (Throwable t) {
458                 Log.e(ConnectionServiceWrapper.this, t, "");
459                 throw t;
460             } finally {
461                 Binder.restoreCallingIdentity(token);
462                 Log.endSession();
463             }
464         }
465 
466         @Override
addConferenceCall(String callId, ParcelableConference parcelableConference, Session.Info sessionInfo)467         public void addConferenceCall(String callId, ParcelableConference parcelableConference,
468                 Session.Info sessionInfo) {
469             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_ADD_CONFERENCE_CALL);
470 
471             if (parcelableConference.getConnectElapsedTimeMillis() != 0
472                     && mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
473                             != PackageManager.PERMISSION_GRANTED) {
474                 Log.w(this, "addConferenceCall from caller without permission!");
475                 parcelableConference = new ParcelableConference.Builder(
476                         parcelableConference.getPhoneAccount(),
477                         parcelableConference.getState())
478                         .setConnectionCapabilities(parcelableConference.getConnectionCapabilities())
479                         .setConnectionProperties(parcelableConference.getConnectionProperties())
480                         .setConnectionIds(parcelableConference.getConnectionIds())
481                         .setVideoAttributes(parcelableConference.getVideoProvider(),
482                                 parcelableConference.getVideoState())
483                         .setStatusHints(parcelableConference.getStatusHints())
484                         .setExtras(parcelableConference.getExtras())
485                         .setAddress(parcelableConference.getHandle(),
486                                 parcelableConference.getHandlePresentation())
487                         // no caller display name set.
488                         .setDisconnectCause(parcelableConference.getDisconnectCause())
489                         .setRingbackRequested(parcelableConference.isRingbackRequested())
490                         .build();
491             }
492 
493             long token = Binder.clearCallingIdentity();
494             try {
495                 synchronized (mLock) {
496                     if (mCallIdMapper.getCall(callId) != null) {
497                         Log.w(this, "Attempting to add a conference call using an existing " +
498                                 "call id %s", callId);
499                         return;
500                     }
501                     logIncoming("addConferenceCall %s %s [%s]", callId, parcelableConference,
502                             parcelableConference.getConnectionIds());
503 
504                     // Make sure that there's at least one valid call. For remote connections
505                     // we'll get a add conference msg from both the remote connection service
506                     // and from the real connection service.
507                     boolean hasValidCalls = false;
508                     for (String connId : parcelableConference.getConnectionIds()) {
509                         if (mCallIdMapper.getCall(connId) != null) {
510                             hasValidCalls = true;
511                         }
512                     }
513                     // But don't bail out if the connection count is 0, because that is a valid
514                     // IMS conference state.
515                     if (!hasValidCalls && parcelableConference.getConnectionIds().size() > 0) {
516                         Log.d(this, "Attempting to add a conference with no valid calls");
517                         return;
518                     }
519 
520                     PhoneAccountHandle phAcc = null;
521                     if (parcelableConference != null &&
522                             parcelableConference.getPhoneAccount() != null) {
523                         phAcc = parcelableConference.getPhoneAccount();
524                     }
525 
526                     Bundle connectionExtras = parcelableConference.getExtras();
527 
528                     String connectIdToCheck = null;
529                     if (connectionExtras != null && connectionExtras
530                             .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
531                         // Conference was added via a connection manager, see if its original id is
532                         // known.
533                         connectIdToCheck = connectionExtras
534                                 .getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
535                     } else {
536                         connectIdToCheck = callId;
537                     }
538 
539                     Call conferenceCall;
540                     // Check to see if this conference has already been added.
541                     Call alreadyAddedConnection = mCallsManager
542                             .getAlreadyAddedConnection(connectIdToCheck);
543                     if (alreadyAddedConnection != null && mCallIdMapper.getCall(callId) == null) {
544                         // We are currently attempting to add the conference via a connection mgr,
545                         // and the originating ConnectionService has already added it.  Instead of
546                         // making a new Telecom call, we will simply add it to the ID mapper here,
547                         // and replace the ConnectionService on the call.
548                         mCallIdMapper.addCall(alreadyAddedConnection, callId);
549                         alreadyAddedConnection.replaceConnectionService(
550                                 ConnectionServiceWrapper.this);
551                         conferenceCall = alreadyAddedConnection;
552                     } else {
553                         // need to create a new Call
554                         Call newConferenceCall = mCallsManager.createConferenceCall(callId,
555                                 phAcc, parcelableConference);
556                         mCallIdMapper.addCall(newConferenceCall, callId);
557                         newConferenceCall.setConnectionService(ConnectionServiceWrapper.this);
558                         conferenceCall = newConferenceCall;
559                     }
560 
561                     Log.d(this, "adding children to conference %s phAcc %s",
562                             parcelableConference.getConnectionIds(), phAcc);
563                     for (String connId : parcelableConference.getConnectionIds()) {
564                         Call childCall = mCallIdMapper.getCall(connId);
565                         Log.d(this, "found child: %s", connId);
566                         if (childCall != null) {
567                             childCall.setParentAndChildCall(conferenceCall);
568                         }
569                     }
570                 }
571             } catch (Throwable t) {
572                 Log.e(ConnectionServiceWrapper.this, t, "");
573                 throw t;
574             } finally {
575                 Binder.restoreCallingIdentity(token);
576                 Log.endSession();
577             }
578         }
579 
580         @Override
onPostDialWait(String callId, String remaining, Session.Info sessionInfo)581         public void onPostDialWait(String callId, String remaining,
582                 Session.Info sessionInfo) throws RemoteException {
583             Log.startSession(sessionInfo, "CSW.oPDW");
584             long token = Binder.clearCallingIdentity();
585             try {
586                 synchronized (mLock) {
587                     logIncoming("onPostDialWait %s %s", callId, remaining);
588                     Call call = mCallIdMapper.getCall(callId);
589                     if (call != null) {
590                         call.onPostDialWait(remaining);
591                     } else {
592                         // Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
593                     }
594                 }
595             } catch (Throwable t) {
596                 Log.e(ConnectionServiceWrapper.this, t, "");
597                 throw t;
598             } finally {
599                 Binder.restoreCallingIdentity(token);
600                 Log.endSession();
601             }
602         }
603 
604         @Override
onPostDialChar(String callId, char nextChar, Session.Info sessionInfo)605         public void onPostDialChar(String callId, char nextChar,
606                 Session.Info sessionInfo) throws RemoteException {
607             Log.startSession(sessionInfo, "CSW.oPDC");
608             long token = Binder.clearCallingIdentity();
609             try {
610                 synchronized (mLock) {
611                     logIncoming("onPostDialChar %s %s", callId, nextChar);
612                     Call call = mCallIdMapper.getCall(callId);
613                     if (call != null) {
614                         call.onPostDialChar(nextChar);
615                     } else {
616                         // Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1);
617                     }
618                 }
619             } catch (Throwable t) {
620                 Log.e(ConnectionServiceWrapper.this, t, "");
621                 throw t;
622             } finally {
623                 Binder.restoreCallingIdentity(token);
624                 Log.endSession();
625             }
626         }
627 
628         @Override
queryRemoteConnectionServices(RemoteServiceCallback callback, String callingPackage, Session.Info sessionInfo)629         public void queryRemoteConnectionServices(RemoteServiceCallback callback,
630                 String callingPackage, Session.Info sessionInfo) {
631             final UserHandle callingUserHandle = Binder.getCallingUserHandle();
632             Log.startSession(sessionInfo, "CSW.qRCS");
633             long token = Binder.clearCallingIdentity();
634             try {
635                 synchronized (mLock) {
636                     logIncoming("queryRemoteConnectionServices callingPackage=" + callingPackage);
637                     ConnectionServiceWrapper.this
638                             .queryRemoteConnectionServices(callingUserHandle, callingPackage,
639                                     callback);
640                 }
641             } catch (Throwable t) {
642                 Log.e(ConnectionServiceWrapper.this, t, "");
643                 throw t;
644             } finally {
645                 Binder.restoreCallingIdentity(token);
646                 Log.endSession();
647             }
648         }
649 
650         @Override
setVideoState(String callId, int videoState, Session.Info sessionInfo)651         public void setVideoState(String callId, int videoState, Session.Info sessionInfo) {
652             Log.startSession(sessionInfo, "CSW.sVS");
653             long token = Binder.clearCallingIdentity();
654             try {
655                 synchronized (mLock) {
656                     logIncoming("setVideoState %s %d", callId, videoState);
657                     Call call = mCallIdMapper.getCall(callId);
658                     if (call != null) {
659                         call.setVideoState(videoState);
660                     }
661                 }
662             } catch (Throwable t) {
663                 Log.e(ConnectionServiceWrapper.this, t, "");
664                 throw t;
665             } finally {
666                 Binder.restoreCallingIdentity(token);
667                 Log.endSession();
668             }
669         }
670 
671         @Override
setIsVoipAudioMode(String callId, boolean isVoip, Session.Info sessionInfo)672         public void setIsVoipAudioMode(String callId, boolean isVoip, Session.Info sessionInfo) {
673             Log.startSession(sessionInfo, "CSW.sIVAM");
674             long token = Binder.clearCallingIdentity();
675             try {
676                 synchronized (mLock) {
677                     logIncoming("setIsVoipAudioMode %s %b", callId, isVoip);
678                     Call call = mCallIdMapper.getCall(callId);
679                     if (call != null) {
680                         call.setIsVoipAudioMode(isVoip);
681                     }
682                 }
683             } catch (Throwable t) {
684                 Log.e(ConnectionServiceWrapper.this, t, "");
685                 throw t;
686             } finally {
687                 Binder.restoreCallingIdentity(token);
688                 Log.endSession();
689             }
690         }
691 
692         @Override
setAudioRoute(String callId, int audioRoute, String bluetoothAddress, Session.Info sessionInfo)693         public void setAudioRoute(String callId, int audioRoute,
694                 String bluetoothAddress, Session.Info sessionInfo) {
695             Log.startSession(sessionInfo, "CSW.sAR");
696             long token = Binder.clearCallingIdentity();
697             try {
698                 synchronized (mLock) {
699                     logIncoming("setAudioRoute %s %s", callId,
700                             CallAudioState.audioRouteToString(audioRoute));
701                     mCallsManager.setAudioRoute(audioRoute, bluetoothAddress);
702                 }
703             } catch (Throwable t) {
704                 Log.e(ConnectionServiceWrapper.this, t, "");
705                 throw t;
706             } finally {
707                 Binder.restoreCallingIdentity(token);
708                 Log.endSession();
709             }
710         }
711 
712         @Override
setStatusHints(String callId, StatusHints statusHints, Session.Info sessionInfo)713         public void setStatusHints(String callId, StatusHints statusHints,
714                 Session.Info sessionInfo) {
715             Log.startSession(sessionInfo, "CSW.sSH");
716             long token = Binder.clearCallingIdentity();
717             try {
718                 synchronized (mLock) {
719                     logIncoming("setStatusHints %s %s", callId, statusHints);
720                     Call call = mCallIdMapper.getCall(callId);
721                     if (call != null) {
722                         call.setStatusHints(statusHints);
723                     }
724                 }
725             } catch (Throwable t) {
726                 Log.e(ConnectionServiceWrapper.this, t, "");
727                 throw t;
728             } finally {
729                 Binder.restoreCallingIdentity(token);
730                 Log.endSession();
731             }
732         }
733 
734         @Override
putExtras(String callId, Bundle extras, Session.Info sessionInfo)735         public void putExtras(String callId, Bundle extras, Session.Info sessionInfo) {
736             Log.startSession(sessionInfo, "CSW.pE");
737             long token = Binder.clearCallingIdentity();
738             try {
739                 synchronized (mLock) {
740                     Bundle.setDefusable(extras, true);
741                     Call call = mCallIdMapper.getCall(callId);
742                     if (call != null) {
743                         call.putExtras(Call.SOURCE_CONNECTION_SERVICE, extras);
744                     }
745                 }
746             } catch (Throwable t) {
747                 Log.e(ConnectionServiceWrapper.this, t, "");
748                 throw t;
749             } finally {
750                 Binder.restoreCallingIdentity(token);
751                 Log.endSession();
752             }
753         }
754 
755         @Override
removeExtras(String callId, List<String> keys, Session.Info sessionInfo)756         public void removeExtras(String callId, List<String> keys, Session.Info sessionInfo) {
757             Log.startSession(sessionInfo, "CSW.rE");
758             long token = Binder.clearCallingIdentity();
759             try {
760                 synchronized (mLock) {
761                     logIncoming("removeExtra %s %s", callId, keys);
762                     Call call = mCallIdMapper.getCall(callId);
763                     if (call != null) {
764                         call.removeExtras(Call.SOURCE_CONNECTION_SERVICE, keys);
765                     }
766                 }
767             } catch (Throwable t) {
768                 Log.e(ConnectionServiceWrapper.this, t, "");
769                 throw t;
770             } finally {
771                 Binder.restoreCallingIdentity(token);
772                 Log.endSession();
773             }
774         }
775 
776         @Override
setAddress(String callId, Uri address, int presentation, Session.Info sessionInfo)777         public void setAddress(String callId, Uri address, int presentation,
778                 Session.Info sessionInfo) {
779             Log.startSession(sessionInfo, "CSW.sA");
780 
781             long token = Binder.clearCallingIdentity();
782             try {
783                 synchronized (mLock) {
784                     logIncoming("setAddress %s %s %d", callId, address, presentation);
785                     Call call = mCallIdMapper.getCall(callId);
786                     if (call != null) {
787                         call.setHandle(address, presentation);
788                     }
789                 }
790             } catch (Throwable t) {
791                 Log.e(ConnectionServiceWrapper.this, t, "");
792                 throw t;
793             } finally {
794                 Binder.restoreCallingIdentity(token);
795                 Log.endSession();
796             }
797         }
798 
799         @Override
setCallerDisplayName(String callId, String callerDisplayName, int presentation, Session.Info sessionInfo)800         public void setCallerDisplayName(String callId, String callerDisplayName, int presentation,
801                 Session.Info sessionInfo) {
802             Log.startSession(sessionInfo, "CSW.sCDN");
803             long token = Binder.clearCallingIdentity();
804             try {
805                 synchronized (mLock) {
806                     logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName,
807                             presentation);
808                     Call call = mCallIdMapper.getCall(callId);
809                     if (call != null) {
810                         call.setCallerDisplayName(callerDisplayName, presentation);
811                     }
812                 }
813             } catch (Throwable t) {
814                 Log.e(ConnectionServiceWrapper.this, t, "");
815                 throw t;
816             } finally {
817                 Binder.restoreCallingIdentity(token);
818                 Log.endSession();
819             }
820         }
821 
822         @Override
setConferenceableConnections(String callId, List<String> conferenceableCallIds, Session.Info sessionInfo)823         public void setConferenceableConnections(String callId, List<String> conferenceableCallIds,
824                 Session.Info sessionInfo) {
825             Log.startSession(sessionInfo, "CSW.sCC");
826             long token = Binder.clearCallingIdentity();
827             try {
828                 synchronized (mLock) {
829 
830                     Call call = mCallIdMapper.getCall(callId);
831                     if (call != null) {
832                         logIncoming("setConferenceableConnections %s %s", callId,
833                                 conferenceableCallIds);
834                         List<Call> conferenceableCalls =
835                                 new ArrayList<>(conferenceableCallIds.size());
836                         for (String otherId : conferenceableCallIds) {
837                             Call otherCall = mCallIdMapper.getCall(otherId);
838                             if (otherCall != null && otherCall != call) {
839                                 conferenceableCalls.add(otherCall);
840                             }
841                         }
842                         call.setConferenceableCalls(conferenceableCalls);
843                     }
844                 }
845             } catch (Throwable t) {
846                 Log.e(ConnectionServiceWrapper.this, t, "");
847                 throw t;
848             } finally {
849                 Binder.restoreCallingIdentity(token);
850                 Log.endSession();
851             }
852         }
853 
854         @Override
addExistingConnection(String callId, ParcelableConnection connection, Session.Info sessionInfo)855         public void addExistingConnection(String callId, ParcelableConnection connection,
856                 Session.Info sessionInfo) {
857             Log.startSession(sessionInfo, "CSW.aEC");
858             UserHandle userHandle = Binder.getCallingUserHandle();
859             // Check that the Calling Package matches PhoneAccountHandle's Component Package
860             PhoneAccountHandle callingPhoneAccountHandle = connection.getPhoneAccount();
861             if (callingPhoneAccountHandle != null) {
862                 mAppOpsManager.checkPackage(Binder.getCallingUid(),
863                         callingPhoneAccountHandle.getComponentName().getPackageName());
864             }
865 
866             long token = Binder.clearCallingIdentity();
867             try {
868                 synchronized (mLock) {
869                     // Make sure that the PhoneAccount associated with the incoming
870                     // ParcelableConnection is in fact registered to Telecom and is being called
871                     // from the correct user.
872                     List<PhoneAccountHandle> accountHandles =
873                     // Include CAPABILITY_EMERGENCY_CALLS_ONLY in this list in case we are adding
874                     // an emergency call.
875                             mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null /*uriScheme*/,
876                             false /*includeDisabledAccounts*/, userHandle, 0 /*capabilities*/,
877                             0 /*excludedCapabilities*/);
878                     PhoneAccountHandle phoneAccountHandle = null;
879                     for (PhoneAccountHandle accountHandle : accountHandles) {
880                         if(accountHandle.equals(callingPhoneAccountHandle)) {
881                             phoneAccountHandle = accountHandle;
882                         }
883                     }
884                     // Allow the Sim call manager account as well, even if its disabled.
885                     if (phoneAccountHandle == null && callingPhoneAccountHandle != null) {
886                         // Search all SIM PhoneAccounts to see if there is a SIM call manager
887                         // associated with any of them and verify that the calling handle matches.
888                         for (PhoneAccountHandle handle :
889                                 mPhoneAccountRegistrar.getSimPhoneAccounts(userHandle)) {
890                             int subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(
891                                     handle);
892                             PhoneAccountHandle connectionMgrHandle =
893                                     mPhoneAccountRegistrar.getSimCallManager(subId, userHandle);
894                             if (callingPhoneAccountHandle.equals(connectionMgrHandle)) {
895                                 phoneAccountHandle = connectionMgrHandle;
896                                 break;
897                             }
898                         }
899                     }
900                     if (phoneAccountHandle != null) {
901                         logIncoming("addExistingConnection %s %s", callId, connection);
902 
903                         Bundle connectionExtras = connection.getExtras();
904                         String connectIdToCheck = null;
905                         if (connectionExtras != null && connectionExtras
906                                 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
907                             connectIdToCheck = connectionExtras
908                                     .getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
909                         } else {
910                             connectIdToCheck = callId;
911                         }
912                         // Check to see if this Connection has already been added.
913                         Call alreadyAddedConnection = mCallsManager
914                                 .getAlreadyAddedConnection(connectIdToCheck);
915 
916                         if (alreadyAddedConnection != null
917                                 && mCallIdMapper.getCall(callId) == null) {
918                             mCallIdMapper.addCall(alreadyAddedConnection, callId);
919                             alreadyAddedConnection
920                                     .replaceConnectionService(ConnectionServiceWrapper.this);
921                             return;
922                         }
923 
924                         Call existingCall = mCallsManager
925                                 .createCallForExistingConnection(callId, connection);
926                         mCallIdMapper.addCall(existingCall, callId);
927                         existingCall.setConnectionService(ConnectionServiceWrapper.this);
928                     } else {
929                         Log.e(this, new RemoteException("The PhoneAccount being used is not " +
930                                 "currently registered with Telecom."), "Unable to " +
931                                 "addExistingConnection.");
932                     }
933                 }
934             } catch (Throwable t) {
935                 Log.e(ConnectionServiceWrapper.this, t, "");
936                 throw t;
937             } finally {
938                 Binder.restoreCallingIdentity(token);
939                 Log.endSession();
940             }
941         }
942 
943         @Override
onConnectionEvent(String callId, String event, Bundle extras, Session.Info sessionInfo)944         public void onConnectionEvent(String callId, String event, Bundle extras,
945                 Session.Info sessionInfo) {
946             Log.startSession(sessionInfo, "CSW.oCE");
947             long token = Binder.clearCallingIdentity();
948             try {
949                 synchronized (mLock) {
950                     Bundle.setDefusable(extras, true);
951                     Call call = mCallIdMapper.getCall(callId);
952                     if (call != null) {
953                         call.onConnectionEvent(event, extras);
954                     }
955                 }
956             } catch (Throwable t) {
957                 Log.e(ConnectionServiceWrapper.this, t, "");
958                 throw t;
959             } finally {
960                 Binder.restoreCallingIdentity(token);
961                 Log.endSession();
962             }
963         }
964 
965         @Override
onRttInitiationSuccess(String callId, Session.Info sessionInfo)966         public void onRttInitiationSuccess(String callId, Session.Info sessionInfo)
967                 throws RemoteException {
968 
969         }
970 
971         @Override
onRttInitiationFailure(String callId, int reason, Session.Info sessionInfo)972         public void onRttInitiationFailure(String callId, int reason, Session.Info sessionInfo)
973                 throws RemoteException {
974             Log.startSession(sessionInfo, "CSW.oRIF");
975             long token = Binder.clearCallingIdentity();
976             try {
977                 synchronized (mLock) {
978                     Call call = mCallIdMapper.getCall(callId);
979                     if (call != null) {
980                         call.onRttConnectionFailure(reason);
981                     }
982                 }
983             } catch (Throwable t) {
984                 Log.e(ConnectionServiceWrapper.this, t, "");
985                 throw t;
986             } finally {
987                 Binder.restoreCallingIdentity(token);
988                 Log.endSession();
989             }
990         }
991 
992         @Override
onRttSessionRemotelyTerminated(String callId, Session.Info sessionInfo)993         public void onRttSessionRemotelyTerminated(String callId, Session.Info sessionInfo)
994                 throws RemoteException {
995 
996         }
997 
998         @Override
onRemoteRttRequest(String callId, Session.Info sessionInfo)999         public void onRemoteRttRequest(String callId, Session.Info sessionInfo)
1000                 throws RemoteException {
1001             Log.startSession(sessionInfo, "CSW.oRRR");
1002             long token = Binder.clearCallingIdentity();
1003             try {
1004                 synchronized (mLock) {
1005                     Call call = mCallIdMapper.getCall(callId);
1006                     if (call != null) {
1007                         call.onRemoteRttRequest();
1008                     }
1009                 }
1010             } catch (Throwable t) {
1011                 Log.e(ConnectionServiceWrapper.this, t, "");
1012                 throw t;
1013             } finally {
1014                 Binder.restoreCallingIdentity(token);
1015                 Log.endSession();
1016             }
1017         }
1018 
1019         @Override
onPhoneAccountChanged(String callId, PhoneAccountHandle pHandle, Session.Info sessionInfo)1020         public void onPhoneAccountChanged(String callId, PhoneAccountHandle pHandle,
1021                 Session.Info sessionInfo) throws RemoteException {
1022             // Check that the Calling Package matches PhoneAccountHandle's Component Package
1023             if (pHandle != null) {
1024                 mAppOpsManager.checkPackage(Binder.getCallingUid(),
1025                         pHandle.getComponentName().getPackageName());
1026             }
1027             Log.startSession(sessionInfo, "CSW.oPAC");
1028             long token = Binder.clearCallingIdentity();
1029             try {
1030                 synchronized (mLock) {
1031                     Call call = mCallIdMapper.getCall(callId);
1032                     if (call != null) {
1033                         call.setTargetPhoneAccount(pHandle);
1034                     }
1035                 }
1036             } catch (Throwable t) {
1037                 Log.e(ConnectionServiceWrapper.this, t, "");
1038                 throw t;
1039             } finally {
1040                 Binder.restoreCallingIdentity(token);
1041                 Log.endSession();
1042             }
1043         }
1044 
1045         @Override
onConnectionServiceFocusReleased(Session.Info sessionInfo)1046         public void onConnectionServiceFocusReleased(Session.Info sessionInfo)
1047                 throws RemoteException {
1048             Log.startSession(sessionInfo, "CSW.oCSFR");
1049             long token = Binder.clearCallingIdentity();
1050             try {
1051                 synchronized (mLock) {
1052                     mConnSvrFocusListener.onConnectionServiceReleased(
1053                             ConnectionServiceWrapper.this);
1054                 }
1055             } catch (Throwable t) {
1056                 Log.e(ConnectionServiceWrapper.this, t, "");
1057                 throw t;
1058             } finally {
1059                 Binder.restoreCallingIdentity(token);
1060                 Log.endSession();
1061             }
1062         }
1063 
1064         @Override
setConferenceState(String callId, boolean isConference, Session.Info sessionInfo)1065         public void setConferenceState(String callId, boolean isConference,
1066                 Session.Info sessionInfo) throws RemoteException {
1067             Log.startSession(sessionInfo, "CSW.sCS");
1068 
1069             if (mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
1070                     != PackageManager.PERMISSION_GRANTED) {
1071                 Log.w(this, "setConferenceState from caller without permission.");
1072                 Log.endSession();
1073                 return;
1074             }
1075 
1076             long token = Binder.clearCallingIdentity();
1077             try {
1078                 synchronized (mLock) {
1079                     Call call = mCallIdMapper.getCall(callId);
1080                     if (call != null) {
1081                         call.setConferenceState(isConference);
1082                     }
1083                 }
1084             } catch (Throwable t) {
1085                 Log.e(ConnectionServiceWrapper.this, t, "");
1086                 throw t;
1087             } finally {
1088                 Binder.restoreCallingIdentity(token);
1089                 Log.endSession();
1090             }
1091         }
1092 
1093         @Override
setCallDirection(String callId, int direction, Session.Info sessionInfo)1094         public void setCallDirection(String callId, int direction, Session.Info sessionInfo) {
1095             Log.startSession(sessionInfo, "CSW.sCD");
1096 
1097             if (mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
1098                     != PackageManager.PERMISSION_GRANTED) {
1099                 Log.w(this, "setCallDirection from caller without permission.");
1100                 Log.endSession();
1101                 return;
1102             }
1103 
1104             long token = Binder.clearCallingIdentity();
1105             try {
1106                 synchronized (mLock) {
1107                     logIncoming("setCallDirection %s %d", callId, direction);
1108                     Call call = mCallIdMapper.getCall(callId);
1109                     if (call != null) {
1110                         call.setCallDirection(Call.getRemappedCallDirection(direction));
1111                     }
1112                 }
1113             } catch (Throwable t) {
1114                 Log.e(ConnectionServiceWrapper.this, t, "");
1115                 throw t;
1116             } finally {
1117                 Binder.restoreCallingIdentity(token);
1118                 Log.endSession();
1119             }
1120         }
1121     }
1122 
1123     private final Adapter mAdapter = new Adapter();
1124     private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getConnectionId);
1125     private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
1126 
1127     private Binder2 mBinder = new Binder2();
1128     private IConnectionService mServiceInterface;
1129     private final ConnectionServiceRepository mConnectionServiceRepository;
1130     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
1131     private final CallsManager mCallsManager;
1132     private final AppOpsManager mAppOpsManager;
1133     private final Context mContext;
1134 
1135     private ConnectionServiceFocusManager.ConnectionServiceFocusListener mConnSvrFocusListener;
1136 
1137     /**
1138      * Creates a connection service.
1139      *
1140      * @param componentName The component name of the service with which to bind.
1141      * @param connectionServiceRepository Connection service repository.
1142      * @param phoneAccountRegistrar Phone account registrar
1143      * @param callsManager Calls manager
1144      * @param context The context.
1145      * @param userHandle The {@link UserHandle} to use when binding.
1146      */
ConnectionServiceWrapper( ComponentName componentName, ConnectionServiceRepository connectionServiceRepository, PhoneAccountRegistrar phoneAccountRegistrar, CallsManager callsManager, Context context, TelecomSystem.SyncRoot lock, UserHandle userHandle)1147     ConnectionServiceWrapper(
1148             ComponentName componentName,
1149             ConnectionServiceRepository connectionServiceRepository,
1150             PhoneAccountRegistrar phoneAccountRegistrar,
1151             CallsManager callsManager,
1152             Context context,
1153             TelecomSystem.SyncRoot lock,
1154             UserHandle userHandle) {
1155         super(ConnectionService.SERVICE_INTERFACE, componentName, context, lock, userHandle);
1156         mConnectionServiceRepository = connectionServiceRepository;
1157         phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {
1158             // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections
1159             // To do this, we must proxy remote ConnectionService objects
1160         });
1161         mPhoneAccountRegistrar = phoneAccountRegistrar;
1162         mCallsManager = callsManager;
1163         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
1164         mContext = context;
1165     }
1166 
1167     /** See {@link IConnectionService#addConnectionServiceAdapter}. */
addConnectionServiceAdapter(IConnectionServiceAdapter adapter)1168     private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
1169         if (isServiceValid("addConnectionServiceAdapter")) {
1170             try {
1171                 logOutgoing("addConnectionServiceAdapter %s", adapter);
1172                 mServiceInterface.addConnectionServiceAdapter(adapter, Log.getExternalSession());
1173             } catch (RemoteException e) {
1174             }
1175         }
1176     }
1177 
1178     /** See {@link IConnectionService#removeConnectionServiceAdapter}. */
removeConnectionServiceAdapter(IConnectionServiceAdapter adapter)1179     private void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
1180         if (isServiceValid("removeConnectionServiceAdapter")) {
1181             try {
1182                 logOutgoing("removeConnectionServiceAdapter %s", adapter);
1183                 mServiceInterface.removeConnectionServiceAdapter(adapter, Log.getExternalSession());
1184             } catch (RemoteException e) {
1185             }
1186         }
1187     }
1188 
1189     /**
1190      * Creates a conference for a new outgoing call or attach to an existing incoming call.
1191      */
createConference(final Call call, final CreateConnectionResponse response)1192     public void createConference(final Call call, final CreateConnectionResponse response) {
1193         Log.d(this, "createConference(%s) via %s.", call, getComponentName());
1194         BindCallback callback = new BindCallback() {
1195             @Override
1196             public void onSuccess() {
1197                 String callId = mCallIdMapper.getCallId(call);
1198                 mPendingResponses.put(callId, response);
1199 
1200                 Bundle extras = call.getIntentExtras();
1201 
1202                 Log.addEvent(call, LogUtils.Events.START_CONFERENCE,
1203                         Log.piiHandle(call.getHandle()));
1204 
1205                 ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
1206                         .setAccountHandle(call.getTargetPhoneAccount())
1207                         .setAddress(call.getHandle())
1208                         .setExtras(extras)
1209                         .setVideoState(call.getVideoState())
1210                         .setTelecomCallId(callId)
1211                         // For self-managed incoming calls, if there is another ongoing call Telecom
1212                         // is responsible for showing a UI to ask the user if they'd like to answer
1213                         // this new incoming call.
1214                         .setShouldShowIncomingCallUi(
1215                                 !mCallsManager.shouldShowSystemIncomingCallUi(call))
1216                         .setRttPipeFromInCall(call.getInCallToCsRttPipeForCs())
1217                         .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
1218                         .setParticipants(call.getParticipants())
1219                         .setIsAdhocConferenceCall(call.isAdhocConferenceCall())
1220                         .build();
1221 
1222                 try {
1223                     mServiceInterface.createConference(
1224                             call.getConnectionManagerPhoneAccount(),
1225                             callId,
1226                             connectionRequest,
1227                             call.shouldAttachToExistingConnection(),
1228                             call.isUnknown(),
1229                             Log.getExternalSession());
1230 
1231                 } catch (RemoteException e) {
1232                     Log.e(this, e, "Failure to createConference -- %s", getComponentName());
1233                     mPendingResponses.remove(callId).handleCreateConferenceFailure(
1234                             new DisconnectCause(DisconnectCause.ERROR, e.toString()));
1235                 }
1236             }
1237 
1238             @Override
1239             public void onFailure() {
1240                 Log.e(this, new Exception(), "Failure to conference %s", getComponentName());
1241                 response.handleCreateConferenceFailure(new DisconnectCause(DisconnectCause.ERROR));
1242             }
1243         };
1244 
1245         mBinder.bind(callback, call);
1246 
1247     }
1248 
1249     /**
1250      * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
1251      */
1252     @VisibleForTesting
createConnection(final Call call, final CreateConnectionResponse response)1253     public void createConnection(final Call call, final CreateConnectionResponse response) {
1254         Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
1255         BindCallback callback = new BindCallback() {
1256             @Override
1257             public void onSuccess() {
1258                 String callId = mCallIdMapper.getCallId(call);
1259                 mPendingResponses.put(callId, response);
1260 
1261                 GatewayInfo gatewayInfo = call.getGatewayInfo();
1262                 Bundle extras = call.getIntentExtras();
1263                 if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
1264                         gatewayInfo.getOriginalAddress() != null) {
1265                     extras = (Bundle) extras.clone();
1266                     extras.putString(
1267                             TelecomManager.GATEWAY_PROVIDER_PACKAGE,
1268                             gatewayInfo.getGatewayProviderPackageName());
1269                     extras.putParcelable(
1270                             TelecomManager.GATEWAY_ORIGINAL_ADDRESS,
1271                             gatewayInfo.getOriginalAddress());
1272                 }
1273 
1274                 if (call.isIncoming() && mCallsManager.getEmergencyCallHelper()
1275                         .getLastEmergencyCallTimeMillis() > 0) {
1276                   // Add the last emergency call time to the connection request for incoming calls
1277                   if (extras == call.getIntentExtras()) {
1278                     extras = (Bundle) extras.clone();
1279                   }
1280                   extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS,
1281                       mCallsManager.getEmergencyCallHelper().getLastEmergencyCallTimeMillis());
1282                 }
1283 
1284                 // Call is incoming and added because we're handing over from another; tell CS
1285                 // that its expected to handover.
1286                 if (call.isIncoming() && call.getHandoverSourceCall() != null) {
1287                     extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true);
1288                     extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
1289                             call.getHandoverSourceCall().getTargetPhoneAccount());
1290                 }
1291 
1292                 Log.addEvent(call, LogUtils.Events.START_CONNECTION,
1293                         Log.piiHandle(call.getHandle()));
1294 
1295                 ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
1296                         .setAccountHandle(call.getTargetPhoneAccount())
1297                         .setAddress(call.getHandle())
1298                         .setExtras(extras)
1299                         .setVideoState(call.getVideoState())
1300                         .setTelecomCallId(callId)
1301                         // For self-managed incoming calls, if there is another ongoing call Telecom
1302                         // is responsible for showing a UI to ask the user if they'd like to answer
1303                         // this new incoming call.
1304                         .setShouldShowIncomingCallUi(
1305                                 !mCallsManager.shouldShowSystemIncomingCallUi(call))
1306                         .setRttPipeFromInCall(call.getInCallToCsRttPipeForCs())
1307                         .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
1308                         .build();
1309 
1310                 try {
1311                     mServiceInterface.createConnection(
1312                             call.getConnectionManagerPhoneAccount(),
1313                             callId,
1314                             connectionRequest,
1315                             call.shouldAttachToExistingConnection(),
1316                             call.isUnknown(),
1317                             Log.getExternalSession());
1318 
1319                 } catch (RemoteException e) {
1320                     Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
1321                     mPendingResponses.remove(callId).handleCreateConnectionFailure(
1322                             new DisconnectCause(DisconnectCause.ERROR, e.toString()));
1323                 }
1324             }
1325 
1326             @Override
1327             public void onFailure() {
1328                 Log.e(this, new Exception(), "Failure to call %s", getComponentName());
1329                 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
1330             }
1331         };
1332 
1333         mBinder.bind(callback, call);
1334     }
1335 
1336     /**
1337      * Notifies the {@link ConnectionService} associated with a {@link Call} that the request to
1338      * create a connection has been denied or failed.
1339      * @param call The call.
1340      */
createConnectionFailed(final Call call)1341     void createConnectionFailed(final Call call) {
1342         Log.d(this, "createConnectionFailed(%s) via %s.", call, getComponentName());
1343         BindCallback callback = new BindCallback() {
1344             @Override
1345             public void onSuccess() {
1346                 final String callId = mCallIdMapper.getCallId(call);
1347                 // If still bound, tell the connection service create connection has failed.
1348                 if (callId != null && isServiceValid("createConnectionFailed")) {
1349                     Log.addEvent(call, LogUtils.Events.CREATE_CONNECTION_FAILED,
1350                             Log.piiHandle(call.getHandle()));
1351                     try {
1352                         logOutgoing("createConnectionFailed %s", callId);
1353                         mServiceInterface.createConnectionFailed(
1354                                 call.getConnectionManagerPhoneAccount(),
1355                                 callId,
1356                                 new ConnectionRequest(
1357                                         call.getTargetPhoneAccount(),
1358                                         call.getHandle(),
1359                                         call.getIntentExtras(),
1360                                         call.getVideoState(),
1361                                         callId,
1362                                         false),
1363                                 call.isIncoming(),
1364                                 Log.getExternalSession());
1365                         call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
1366                         call.disconnect();
1367                     } catch (RemoteException e) {
1368                     }
1369                 }
1370             }
1371 
1372             @Override
1373             public void onFailure() {
1374                 // Binding failed.  Oh no.
1375                 Log.w(this, "onFailure - could not bind to CS for call %s", call.getId());
1376             }
1377         };
1378 
1379         mBinder.bind(callback, call);
1380     }
1381 
1382     /**
1383      * Notifies the {@link ConnectionService} associated with a {@link Call} that the request to
1384      * create a conference has been denied or failed.
1385      * @param call The call.
1386      */
createConferenceFailed(final Call call)1387     void createConferenceFailed(final Call call) {
1388         Log.d(this, "createConferenceFailed(%s) via %s.", call, getComponentName());
1389         BindCallback callback = new BindCallback() {
1390             @Override
1391             public void onSuccess() {
1392                 final String callId = mCallIdMapper.getCallId(call);
1393                 // If still bound, tell the connection service create connection has failed.
1394                 if (callId != null && isServiceValid("createConferenceFailed")) {
1395                     Log.addEvent(call, LogUtils.Events.CREATE_CONFERENCE_FAILED,
1396                             Log.piiHandle(call.getHandle()));
1397                     try {
1398                         logOutgoing("createConferenceFailed %s", callId);
1399                         mServiceInterface.createConferenceFailed(
1400                                 call.getConnectionManagerPhoneAccount(),
1401                                 callId,
1402                                 new ConnectionRequest(
1403                                         call.getTargetPhoneAccount(),
1404                                         call.getHandle(),
1405                                         call.getIntentExtras(),
1406                                         call.getVideoState(),
1407                                         callId,
1408                                         false),
1409                                 call.isIncoming(),
1410                                 Log.getExternalSession());
1411                         call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
1412                         call.disconnect();
1413                     } catch (RemoteException e) {
1414                     }
1415                 }
1416             }
1417 
1418             @Override
1419             public void onFailure() {
1420                 // Binding failed.  Oh no.
1421                 Log.w(this, "onFailure - could not bind to CS for conf call %s", call.getId());
1422             }
1423         };
1424 
1425         mBinder.bind(callback, call);
1426     }
1427 
1428 
handoverFailed(final Call call, final int reason)1429     void handoverFailed(final Call call, final int reason) {
1430         Log.d(this, "handoverFailed(%s) via %s.", call, getComponentName());
1431         BindCallback callback = new BindCallback() {
1432             @Override
1433             public void onSuccess() {
1434                 final String callId = mCallIdMapper.getCallId(call);
1435                 // If still bound, tell the connection service create connection has failed.
1436                 if (callId != null && isServiceValid("handoverFailed")) {
1437                     Log.addEvent(call, LogUtils.Events.HANDOVER_FAILED,
1438                             Log.piiHandle(call.getHandle()));
1439                     try {
1440                         mServiceInterface.handoverFailed(
1441                                 callId,
1442                                 new ConnectionRequest(
1443                                         call.getTargetPhoneAccount(),
1444                                         call.getHandle(),
1445                                         call.getIntentExtras(),
1446                                         call.getVideoState(),
1447                                         callId,
1448                                         false), reason, Log.getExternalSession());
1449                     } catch (RemoteException e) {
1450                     }
1451                 }
1452             }
1453 
1454             @Override
1455             public void onFailure() {
1456                 // Binding failed.
1457                 Log.w(this, "onFailure - could not bind to CS for call %s",
1458                         call.getId());
1459             }
1460         };
1461 
1462         mBinder.bind(callback, call);
1463     }
1464 
handoverComplete(final Call call)1465     void handoverComplete(final Call call) {
1466         Log.d(this, "handoverComplete(%s) via %s.", call, getComponentName());
1467         BindCallback callback = new BindCallback() {
1468             @Override
1469             public void onSuccess() {
1470                 final String callId = mCallIdMapper.getCallId(call);
1471                 // If still bound, tell the connection service create connection has failed.
1472                 if (callId != null && isServiceValid("handoverComplete")) {
1473                     try {
1474                         mServiceInterface.handoverComplete(
1475                                 callId,
1476                                 Log.getExternalSession());
1477                     } catch (RemoteException e) {
1478                     }
1479                 }
1480             }
1481 
1482             @Override
1483             public void onFailure() {
1484                 // Binding failed.
1485                 Log.w(this, "onFailure - could not bind to CS for call %s",
1486                         call.getId());
1487             }
1488         };
1489 
1490         mBinder.bind(callback, call);
1491     }
1492 
1493     /** @see IConnectionService#abort(String, Session.Info)  */
abort(Call call)1494     void abort(Call call) {
1495         // Clear out any pending outgoing call data
1496         final String callId = mCallIdMapper.getCallId(call);
1497 
1498         // If still bound, tell the connection service to abort.
1499         if (callId != null && isServiceValid("abort")) {
1500             try {
1501                 logOutgoing("abort %s", callId);
1502                 mServiceInterface.abort(callId, Log.getExternalSession());
1503             } catch (RemoteException e) {
1504             }
1505         }
1506 
1507         removeCall(call, new DisconnectCause(DisconnectCause.LOCAL));
1508     }
1509 
1510     /** @see IConnectionService#silence(String, Session.Info) */
silence(Call call)1511     void silence(Call call) {
1512         final String callId = mCallIdMapper.getCallId(call);
1513         if (callId != null && isServiceValid("silence")) {
1514             try {
1515                 logOutgoing("silence %s", callId);
1516                 mServiceInterface.silence(callId, Log.getExternalSession());
1517             } catch (RemoteException e) {
1518             }
1519         }
1520     }
1521 
1522     /** @see IConnectionService#hold(String, Session.Info) */
hold(Call call)1523     void hold(Call call) {
1524         final String callId = mCallIdMapper.getCallId(call);
1525         if (callId != null && isServiceValid("hold")) {
1526             try {
1527                 logOutgoing("hold %s", callId);
1528                 mServiceInterface.hold(callId, Log.getExternalSession());
1529             } catch (RemoteException e) {
1530             }
1531         }
1532     }
1533 
1534     /** @see IConnectionService#unhold(String, Session.Info) */
unhold(Call call)1535     void unhold(Call call) {
1536         final String callId = mCallIdMapper.getCallId(call);
1537         if (callId != null && isServiceValid("unhold")) {
1538             try {
1539                 logOutgoing("unhold %s", callId);
1540                 mServiceInterface.unhold(callId, Log.getExternalSession());
1541             } catch (RemoteException e) {
1542             }
1543         }
1544     }
1545 
1546     /** @see IConnectionService#onCallAudioStateChanged(String, CallAudioState, Session.Info) */
1547     @VisibleForTesting
onCallAudioStateChanged(Call activeCall, CallAudioState audioState)1548     public void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) {
1549         final String callId = mCallIdMapper.getCallId(activeCall);
1550         if (callId != null && isServiceValid("onCallAudioStateChanged")) {
1551             try {
1552                 logOutgoing("onCallAudioStateChanged %s %s", callId, audioState);
1553                 mServiceInterface.onCallAudioStateChanged(callId, audioState,
1554                         Log.getExternalSession());
1555             } catch (RemoteException e) {
1556             }
1557         }
1558     }
1559 
1560     /** @see IConnectionService#disconnect(String, Session.Info) */
disconnect(Call call)1561     void disconnect(Call call) {
1562         final String callId = mCallIdMapper.getCallId(call);
1563         if (callId != null && isServiceValid("disconnect")) {
1564             try {
1565                 logOutgoing("disconnect %s", callId);
1566                 mServiceInterface.disconnect(callId, Log.getExternalSession());
1567             } catch (RemoteException e) {
1568             }
1569         }
1570     }
1571 
1572     /** @see IConnectionService#answer(String, Session.Info) */
answer(Call call, int videoState)1573     void answer(Call call, int videoState) {
1574         final String callId = mCallIdMapper.getCallId(call);
1575         if (callId != null && isServiceValid("answer")) {
1576             try {
1577                 logOutgoing("answer %s %d", callId, videoState);
1578                 if (VideoProfile.isAudioOnly(videoState)) {
1579                     mServiceInterface.answer(callId, Log.getExternalSession());
1580                 } else {
1581                     mServiceInterface.answerVideo(callId, videoState, Log.getExternalSession());
1582                 }
1583             } catch (RemoteException e) {
1584             }
1585         }
1586     }
1587 
1588     /** @see IConnectionService#deflect(String, Uri , Session.Info) */
deflect(Call call, Uri address)1589     void deflect(Call call, Uri address) {
1590         final String callId = mCallIdMapper.getCallId(call);
1591         if (callId != null && isServiceValid("deflect")) {
1592             try {
1593                 logOutgoing("deflect %s", callId);
1594                 mServiceInterface.deflect(callId, address, Log.getExternalSession());
1595             } catch (RemoteException e) {
1596             }
1597         }
1598     }
1599 
1600     /** @see IConnectionService#reject(String, Session.Info) */
reject(Call call, boolean rejectWithMessage, String message)1601     void reject(Call call, boolean rejectWithMessage, String message) {
1602         final String callId = mCallIdMapper.getCallId(call);
1603         if (callId != null && isServiceValid("reject")) {
1604             try {
1605                 logOutgoing("reject %s", callId);
1606 
1607                 if (rejectWithMessage && call.can(
1608                         Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
1609                     mServiceInterface.rejectWithMessage(callId, message, Log.getExternalSession());
1610                 } else {
1611                     mServiceInterface.reject(callId, Log.getExternalSession());
1612                 }
1613             } catch (RemoteException e) {
1614             }
1615         }
1616     }
1617 
1618     /** @see IConnectionService#reject(String, Session.Info) */
rejectWithReason(Call call, @android.telecom.Call.RejectReason int rejectReason)1619     void rejectWithReason(Call call, @android.telecom.Call.RejectReason int rejectReason) {
1620         final String callId = mCallIdMapper.getCallId(call);
1621         if (callId != null && isServiceValid("rejectReason")) {
1622             try {
1623                 logOutgoing("rejectReason %s, %d", callId, rejectReason);
1624 
1625                 mServiceInterface.rejectWithReason(callId, rejectReason, Log.getExternalSession());
1626             } catch (RemoteException e) {
1627             }
1628         }
1629     }
1630 
1631     /** @see IConnectionService#transfer(String, Uri , boolean, Session.Info) */
transfer(Call call, Uri number, boolean isConfirmationRequired)1632     void transfer(Call call, Uri number, boolean isConfirmationRequired) {
1633         final String callId = mCallIdMapper.getCallId(call);
1634         if (callId != null && isServiceValid("transfer")) {
1635             try {
1636                 logOutgoing("transfer %s", callId);
1637                 mServiceInterface.transfer(callId, number, isConfirmationRequired,
1638                         Log.getExternalSession());
1639             } catch (RemoteException e) {
1640             }
1641         }
1642     }
1643 
1644     /** @see IConnectionService#consultativeTransfer(String, String, Session.Info) */
transfer(Call call, Call otherCall)1645     void transfer(Call call, Call otherCall) {
1646         final String callId = mCallIdMapper.getCallId(call);
1647         final String otherCallId = mCallIdMapper.getCallId(otherCall);
1648         if (callId != null && otherCallId != null && isServiceValid("consultativeTransfer")) {
1649             try {
1650                 logOutgoing("consultativeTransfer %s", callId);
1651                 mServiceInterface.consultativeTransfer(callId, otherCallId,
1652                         Log.getExternalSession());
1653             } catch (RemoteException e) {
1654             }
1655         }
1656     }
1657 
1658     /** @see IConnectionService#playDtmfTone(String, char, Session.Info) */
playDtmfTone(Call call, char digit)1659     void playDtmfTone(Call call, char digit) {
1660         final String callId = mCallIdMapper.getCallId(call);
1661         if (callId != null && isServiceValid("playDtmfTone")) {
1662             try {
1663                 logOutgoing("playDtmfTone %s %c", callId, digit);
1664                 mServiceInterface.playDtmfTone(callId, digit, Log.getExternalSession());
1665             } catch (RemoteException e) {
1666             }
1667         }
1668     }
1669 
1670     /** @see IConnectionService#stopDtmfTone(String, Session.Info) */
stopDtmfTone(Call call)1671     void stopDtmfTone(Call call) {
1672         final String callId = mCallIdMapper.getCallId(call);
1673         if (callId != null && isServiceValid("stopDtmfTone")) {
1674             try {
1675                 logOutgoing("stopDtmfTone %s", callId);
1676                 mServiceInterface.stopDtmfTone(callId, Log.getExternalSession());
1677             } catch (RemoteException e) {
1678             }
1679         }
1680     }
1681 
addCall(Call call)1682     void addCall(Call call) {
1683         if (mCallIdMapper.getCallId(call) == null) {
1684             mCallIdMapper.addCall(call);
1685         }
1686     }
1687 
1688     /**
1689      * Associates newCall with this connection service by replacing callToReplace.
1690      */
replaceCall(Call newCall, Call callToReplace)1691     void replaceCall(Call newCall, Call callToReplace) {
1692         Preconditions.checkState(callToReplace.getConnectionService() == this);
1693         mCallIdMapper.replaceCall(newCall, callToReplace);
1694     }
1695 
removeCall(Call call)1696     void removeCall(Call call) {
1697         removeCall(call, new DisconnectCause(DisconnectCause.ERROR));
1698     }
1699 
removeCall(String callId, DisconnectCause disconnectCause)1700     void removeCall(String callId, DisconnectCause disconnectCause) {
1701         CreateConnectionResponse response = mPendingResponses.remove(callId);
1702         if (response != null) {
1703             response.handleCreateConnectionFailure(disconnectCause);
1704         }
1705 
1706         mCallIdMapper.removeCall(callId);
1707     }
1708 
removeCall(Call call, DisconnectCause disconnectCause)1709     void removeCall(Call call, DisconnectCause disconnectCause) {
1710         CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
1711         if (response != null) {
1712             response.handleCreateConnectionFailure(disconnectCause);
1713         }
1714 
1715         mCallIdMapper.removeCall(call);
1716     }
1717 
onPostDialContinue(Call call, boolean proceed)1718     void onPostDialContinue(Call call, boolean proceed) {
1719         final String callId = mCallIdMapper.getCallId(call);
1720         if (callId != null && isServiceValid("onPostDialContinue")) {
1721             try {
1722                 logOutgoing("onPostDialContinue %s %b", callId, proceed);
1723                 mServiceInterface.onPostDialContinue(callId, proceed, Log.getExternalSession());
1724             } catch (RemoteException ignored) {
1725             }
1726         }
1727     }
1728 
conference(final Call call, Call otherCall)1729     void conference(final Call call, Call otherCall) {
1730         final String callId = mCallIdMapper.getCallId(call);
1731         final String otherCallId = mCallIdMapper.getCallId(otherCall);
1732         if (callId != null && otherCallId != null && isServiceValid("conference")) {
1733             try {
1734                 logOutgoing("conference %s %s", callId, otherCallId);
1735                 mServiceInterface.conference(callId, otherCallId, Log.getExternalSession());
1736             } catch (RemoteException ignored) {
1737             }
1738         }
1739     }
1740 
splitFromConference(Call call)1741     void splitFromConference(Call call) {
1742         final String callId = mCallIdMapper.getCallId(call);
1743         if (callId != null && isServiceValid("splitFromConference")) {
1744             try {
1745                 logOutgoing("splitFromConference %s", callId);
1746                 mServiceInterface.splitFromConference(callId, Log.getExternalSession());
1747             } catch (RemoteException ignored) {
1748             }
1749         }
1750     }
1751 
mergeConference(Call call)1752     void mergeConference(Call call) {
1753         final String callId = mCallIdMapper.getCallId(call);
1754         if (callId != null && isServiceValid("mergeConference")) {
1755             try {
1756                 logOutgoing("mergeConference %s", callId);
1757                 mServiceInterface.mergeConference(callId, Log.getExternalSession());
1758             } catch (RemoteException ignored) {
1759             }
1760         }
1761     }
1762 
swapConference(Call call)1763     void swapConference(Call call) {
1764         final String callId = mCallIdMapper.getCallId(call);
1765         if (callId != null && isServiceValid("swapConference")) {
1766             try {
1767                 logOutgoing("swapConference %s", callId);
1768                 mServiceInterface.swapConference(callId, Log.getExternalSession());
1769             } catch (RemoteException ignored) {
1770             }
1771         }
1772     }
1773 
addConferenceParticipants(Call call, List<Uri> participants)1774     void addConferenceParticipants(Call call, List<Uri> participants) {
1775         final String callId = mCallIdMapper.getCallId(call);
1776         if (callId != null && isServiceValid("addConferenceParticipants")) {
1777             try {
1778                 logOutgoing("addConferenceParticipants %s", callId);
1779                 mServiceInterface.addConferenceParticipants(callId, participants,
1780                         Log.getExternalSession());
1781             } catch (RemoteException ignored) {
1782             }
1783         }
1784     }
1785 
1786     @VisibleForTesting
pullExternalCall(Call call)1787     public void pullExternalCall(Call call) {
1788         final String callId = mCallIdMapper.getCallId(call);
1789         if (callId != null && isServiceValid("pullExternalCall")) {
1790             try {
1791                 logOutgoing("pullExternalCall %s", callId);
1792                 mServiceInterface.pullExternalCall(callId, Log.getExternalSession());
1793             } catch (RemoteException ignored) {
1794             }
1795         }
1796     }
1797 
sendCallEvent(Call call, String event, Bundle extras)1798     void sendCallEvent(Call call, String event, Bundle extras) {
1799         final String callId = mCallIdMapper.getCallId(call);
1800         if (callId != null && isServiceValid("sendCallEvent")) {
1801             try {
1802                 logOutgoing("sendCallEvent %s %s", callId, event);
1803                 mServiceInterface.sendCallEvent(callId, event, extras, Log.getExternalSession());
1804             } catch (RemoteException ignored) {
1805             }
1806         }
1807     }
1808 
onExtrasChanged(Call call, Bundle extras)1809     void onExtrasChanged(Call call, Bundle extras) {
1810         final String callId = mCallIdMapper.getCallId(call);
1811         if (callId != null && isServiceValid("onExtrasChanged")) {
1812             try {
1813                 logOutgoing("onExtrasChanged %s %s", callId, extras);
1814                 mServiceInterface.onExtrasChanged(callId, extras, Log.getExternalSession());
1815             } catch (RemoteException ignored) {
1816             }
1817         }
1818     }
1819 
startRtt(Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall)1820     void startRtt(Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
1821         final String callId = mCallIdMapper.getCallId(call);
1822         if (callId != null && isServiceValid("startRtt")) {
1823             try {
1824                 logOutgoing("startRtt: %s %s %s", callId, fromInCall, toInCall);
1825                 mServiceInterface.startRtt(callId, fromInCall, toInCall, Log.getExternalSession());
1826             } catch (RemoteException ignored) {
1827             }
1828         }
1829     }
1830 
stopRtt(Call call)1831     void stopRtt(Call call) {
1832         final String callId = mCallIdMapper.getCallId(call);
1833         if (callId != null && isServiceValid("stopRtt")) {
1834             try {
1835                 logOutgoing("stopRtt: %s", callId);
1836                 mServiceInterface.stopRtt(callId, Log.getExternalSession());
1837             } catch (RemoteException ignored) {
1838             }
1839         }
1840     }
1841 
respondToRttRequest( Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall)1842     void respondToRttRequest(
1843             Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
1844         final String callId = mCallIdMapper.getCallId(call);
1845         if (callId != null && isServiceValid("respondToRttRequest")) {
1846             try {
1847                 logOutgoing("respondToRttRequest: %s %s %s", callId, fromInCall, toInCall);
1848                 mServiceInterface.respondToRttUpgradeRequest(
1849                         callId, fromInCall, toInCall, Log.getExternalSession());
1850             } catch (RemoteException ignored) {
1851             }
1852         }
1853     }
1854 
1855     /** {@inheritDoc} */
1856     @Override
setServiceInterface(IBinder binder)1857     protected void setServiceInterface(IBinder binder) {
1858         mServiceInterface = IConnectionService.Stub.asInterface(binder);
1859         Log.v(this, "Adding Connection Service Adapter.");
1860         addConnectionServiceAdapter(mAdapter);
1861     }
1862 
1863     /** {@inheritDoc} */
1864     @Override
removeServiceInterface()1865     protected void removeServiceInterface() {
1866         Log.v(this, "Removing Connection Service Adapter.");
1867         removeConnectionServiceAdapter(mAdapter);
1868         // We have lost our service connection. Notify the world that this service is done.
1869         // We must notify the adapter before CallsManager. The adapter will force any pending
1870         // outgoing calls to try the next service. This needs to happen before CallsManager
1871         // tries to clean up any calls still associated with this service.
1872         handleConnectionServiceDeath();
1873         mCallsManager.handleConnectionServiceDeath(this);
1874         mServiceInterface = null;
1875     }
1876 
1877     @Override
connectionServiceFocusLost()1878     public void connectionServiceFocusLost() {
1879         // Immediately response to the Telecom that it has released the call resources.
1880         // TODO(mpq): Change back to the default implementation once b/69651192 done.
1881         if (mConnSvrFocusListener != null) {
1882             mConnSvrFocusListener.onConnectionServiceReleased(ConnectionServiceWrapper.this);
1883         }
1884         BindCallback callback = new BindCallback() {
1885             @Override
1886             public void onSuccess() {
1887                 try {
1888                     mServiceInterface.connectionServiceFocusLost(Log.getExternalSession());
1889                 } catch (RemoteException ignored) {
1890                     Log.d(this, "failed to inform the focus lost event");
1891                 }
1892             }
1893 
1894             @Override
1895             public void onFailure() {}
1896         };
1897         mBinder.bind(callback, null /* null call */);
1898     }
1899 
1900     @Override
connectionServiceFocusGained()1901     public void connectionServiceFocusGained() {
1902         BindCallback callback = new BindCallback() {
1903             @Override
1904             public void onSuccess() {
1905                 try {
1906                     mServiceInterface.connectionServiceFocusGained(Log.getExternalSession());
1907                 } catch (RemoteException ignored) {
1908                     Log.d(this, "failed to inform the focus gained event");
1909                 }
1910             }
1911 
1912             @Override
1913             public void onFailure() {}
1914         };
1915         mBinder.bind(callback, null /* null call */);
1916     }
1917 
1918     @Override
setConnectionServiceFocusListener( ConnectionServiceFocusManager.ConnectionServiceFocusListener listener)1919     public void setConnectionServiceFocusListener(
1920             ConnectionServiceFocusManager.ConnectionServiceFocusListener listener) {
1921         mConnSvrFocusListener = listener;
1922     }
1923 
handleCreateConnectionComplete( String callId, ConnectionRequest request, ParcelableConnection connection)1924     private void handleCreateConnectionComplete(
1925             String callId,
1926             ConnectionRequest request,
1927             ParcelableConnection connection) {
1928         // TODO: Note we are not using parameter "request", which is a side effect of our tacit
1929         // assumption that we have at most one outgoing connection attempt per ConnectionService.
1930         // This may not continue to be the case.
1931         if (connection.getState() == Connection.STATE_DISCONNECTED) {
1932             // A connection that begins in the DISCONNECTED state is an indication of
1933             // failure to connect; we handle all failures uniformly
1934             Call foundCall = mCallIdMapper.getCall(callId);
1935 
1936             if (connection.getConnectTimeMillis() != 0) {
1937                 foundCall.setConnectTimeMillis(connection.getConnectTimeMillis());
1938             }
1939 
1940             if (foundCall != null) {
1941                 // The post-dial digits are created when the call is first created.  Normally
1942                 // the ConnectionService is responsible for stripping them from the address, but
1943                 // since a failed connection will not have done this, we could end up with duplicate
1944                 // post-dial digits.
1945                 foundCall.clearPostDialDigits();
1946             }
1947             removeCall(callId, connection.getDisconnectCause());
1948         } else {
1949             // Successful connection
1950             if (mPendingResponses.containsKey(callId)) {
1951                 mPendingResponses.remove(callId)
1952                         .handleCreateConnectionSuccess(mCallIdMapper, connection);
1953             }
1954         }
1955     }
1956 
handleCreateConferenceComplete( String callId, ConnectionRequest request, ParcelableConference conference)1957     private void handleCreateConferenceComplete(
1958             String callId,
1959             ConnectionRequest request,
1960             ParcelableConference conference) {
1961         // TODO: Note we are not using parameter "request", which is a side effect of our tacit
1962         // assumption that we have at most one outgoing conference attempt per ConnectionService.
1963         // This may not continue to be the case.
1964         if (conference.getState() == Connection.STATE_DISCONNECTED) {
1965             // A conference that begins in the DISCONNECTED state is an indication of
1966             // failure to connect; we handle all failures uniformly
1967             removeCall(callId, conference.getDisconnectCause());
1968         } else {
1969             // Successful connection
1970             if (mPendingResponses.containsKey(callId)) {
1971                 mPendingResponses.remove(callId)
1972                         .handleCreateConferenceSuccess(mCallIdMapper, conference);
1973             }
1974         }
1975     }
1976 
1977     /**
1978      * Called when the associated connection service dies.
1979      */
handleConnectionServiceDeath()1980     private void handleConnectionServiceDeath() {
1981         if (!mPendingResponses.isEmpty()) {
1982             CreateConnectionResponse[] responses = mPendingResponses.values().toArray(
1983                     new CreateConnectionResponse[mPendingResponses.values().size()]);
1984             mPendingResponses.clear();
1985             for (int i = 0; i < responses.length; i++) {
1986                 responses[i].handleCreateConnectionFailure(
1987                         new DisconnectCause(DisconnectCause.ERROR, "CS_DEATH"));
1988             }
1989         }
1990         mCallIdMapper.clear();
1991 
1992         if (mConnSvrFocusListener != null) {
1993             mConnSvrFocusListener.onConnectionServiceDeath(this);
1994         }
1995     }
1996 
logIncoming(String msg, Object... params)1997     private void logIncoming(String msg, Object... params) {
1998         Log.d(this, "ConnectionService -> Telecom[" + mComponentName.flattenToShortString() + "]: "
1999                 + msg, params);
2000     }
2001 
logOutgoing(String msg, Object... params)2002     private void logOutgoing(String msg, Object... params) {
2003         Log.d(this, "Telecom -> ConnectionService[" + mComponentName.flattenToShortString() + "]: "
2004                 + msg, params);
2005     }
2006 
queryRemoteConnectionServices(final UserHandle userHandle, final String callingPackage, final RemoteServiceCallback callback)2007     private void queryRemoteConnectionServices(final UserHandle userHandle,
2008             final String callingPackage, final RemoteServiceCallback callback) {
2009         boolean isCallerConnectionManager = false;
2010         // For each Sim ConnectionService, use its subid to find the correct connection manager for
2011         // that ConnectionService; return those Sim ConnectionServices which match the connection
2012         // manager.
2013         final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap(
2014                 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1));
2015         for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts(userHandle)) {
2016             int subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(handle);
2017             PhoneAccountHandle connectionMgrHandle = mPhoneAccountRegistrar.getSimCallManager(subId,
2018                     userHandle);
2019             if (connectionMgrHandle == null
2020                     || !connectionMgrHandle.getComponentName().getPackageName().equals(
2021                             callingPackage)) {
2022                 Log.v(this, "queryRemoteConnectionServices: callingPackage=%s skipped; "
2023                                 + "doesn't match mgr %s for tfa %s",
2024                         callingPackage, connectionMgrHandle, handle);
2025             } else {
2026                 isCallerConnectionManager = true;
2027             }
2028             ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
2029                     handle.getComponentName(), handle.getUserHandle());
2030             if (service != null && service != this) {
2031                 simServices.add(service);
2032             } else {
2033                 // This is unexpected, normally PhoneAccounts with CAPABILITY_CALL_PROVIDER are not
2034                 // also CAPABILITY_CONNECTION_MANAGER
2035                 Log.w(this, "call provider also detected as SIM call manager: " + service);
2036             }
2037         }
2038 
2039         // Bail early if the caller isn't the sim connection mgr.
2040         if (!isCallerConnectionManager) {
2041             Log.d(this, "queryRemoteConnectionServices: none; not sim call mgr.");
2042             noRemoteServices(callback);
2043             return;
2044         }
2045 
2046         final List<ComponentName> simServiceComponentNames = new ArrayList<>();
2047         final List<IBinder> simServiceBinders = new ArrayList<>();
2048 
2049         Log.i(this, "queryRemoteConnectionServices, simServices = %s", simServices);
2050 
2051         for (ConnectionServiceWrapper simService : simServices) {
2052             final ConnectionServiceWrapper currentSimService = simService;
2053 
2054             currentSimService.mBinder.bind(new BindCallback() {
2055                 @Override
2056                 public void onSuccess() {
2057                     Log.d(this, "queryRemoteConnectionServices: Adding simService %s",
2058                             currentSimService.getComponentName());
2059                     if (currentSimService.mServiceInterface == null) {
2060                         // The remote ConnectionService died, so do not add it.
2061                         // We will still perform maybeComplete() and notify the caller with an empty
2062                         // list of sim services via maybeComplete().
2063                         Log.w(this, "queryRemoteConnectionServices: simService %s died - Skipping.",
2064                                 currentSimService.getComponentName());
2065                     } else {
2066                         simServiceComponentNames.add(currentSimService.getComponentName());
2067                         simServiceBinders.add(currentSimService.mServiceInterface.asBinder());
2068                     }
2069                     maybeComplete();
2070                 }
2071 
2072                 @Override
2073                 public void onFailure() {
2074                     Log.d(this, "queryRemoteConnectionServices: Failed simService %s",
2075                             currentSimService.getComponentName());
2076                     // We know maybeComplete() will always be a no-op from now on, so go ahead and
2077                     // signal failure of the entire request
2078                     noRemoteServices(callback);
2079                 }
2080 
2081                 private void maybeComplete() {
2082                     if (simServiceComponentNames.size() == simServices.size()) {
2083                         setRemoteServices(callback, simServiceComponentNames, simServiceBinders);
2084                     }
2085                 }
2086             }, null);
2087         }
2088     }
2089 
setRemoteServices( RemoteServiceCallback callback, List<ComponentName> componentNames, List<IBinder> binders)2090     private void setRemoteServices(
2091             RemoteServiceCallback callback,
2092             List<ComponentName> componentNames,
2093             List<IBinder> binders) {
2094         try {
2095             callback.onResult(componentNames, binders);
2096         } catch (RemoteException e) {
2097             Log.e(this, e, "setRemoteServices: Contacting ConnectionService %s",
2098                     ConnectionServiceWrapper.this.getComponentName());
2099         }
2100     }
2101 
noRemoteServices(RemoteServiceCallback callback)2102     private void noRemoteServices(RemoteServiceCallback callback) {
2103         setRemoteServices(callback, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
2104     }
2105 
2106     @Override
toString()2107     public String toString() {
2108         StringBuilder sb = new StringBuilder();
2109         sb.append("[ConnectionServiceWrapper componentName=");
2110         sb.append(mComponentName);
2111         sb.append("]");
2112         return sb.toString();
2113     }
2114 }
2115