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