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