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