• 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 static android.Manifest.permission.MODIFY_PHONE_STATE;
20 
21 import android.Manifest;
22 import android.app.AppOpsManager;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.pm.PackageManager;
26 import android.graphics.drawable.Icon;
27 import android.location.Location;
28 import android.location.LocationManager;
29 import android.location.LocationRequest;
30 import android.net.Uri;
31 import android.os.Binder;
32 import android.os.Bundle;
33 import android.os.CancellationSignal;
34 import android.os.IBinder;
35 import android.os.ParcelFileDescriptor;
36 import android.os.RemoteException;
37 import android.os.ResultReceiver;
38 import android.os.UserHandle;
39 import android.telecom.CallAudioState;
40 import android.telecom.CallEndpoint;
41 import android.telecom.Connection;
42 import android.telecom.ConnectionRequest;
43 import android.telecom.ConnectionService;
44 import android.telecom.DisconnectCause;
45 import android.telecom.GatewayInfo;
46 import android.telecom.Log;
47 import android.telecom.Logging.Session;
48 import android.telecom.Logging.Runnable;
49 import android.telecom.ParcelableConference;
50 import android.telecom.ParcelableConnection;
51 import android.telecom.PhoneAccountHandle;
52 import android.telecom.QueryLocationException;
53 import android.telecom.StatusHints;
54 import android.telecom.TelecomManager;
55 import android.telecom.VideoProfile;
56 import android.telephony.CellIdentity;
57 import android.telephony.TelephonyManager;
58 import android.util.Pair;
59 
60 import androidx.annotation.Nullable;
61 
62 import com.android.internal.annotations.VisibleForTesting;
63 import com.android.internal.telecom.IConnectionService;
64 import com.android.internal.telecom.IConnectionServiceAdapter;
65 import com.android.internal.telecom.IVideoProvider;
66 import com.android.internal.telecom.RemoteServiceCallback;
67 import com.android.internal.util.Preconditions;
68 import com.android.server.telecom.flags.FeatureFlags;
69 
70 import java.util.ArrayList;
71 import java.util.Collection;
72 import java.util.Collections;
73 import java.util.HashMap;
74 import java.util.List;
75 import java.util.Map;
76 import java.util.Set;
77 import java.util.UUID;
78 import java.util.concurrent.CompletableFuture;
79 import java.util.concurrent.ConcurrentHashMap;
80 import java.util.concurrent.ExecutorService;
81 import java.util.concurrent.Executors;
82 import java.util.concurrent.RejectedExecutionException;
83 import java.util.concurrent.ScheduledExecutorService;
84 import java.util.concurrent.ScheduledFuture;
85 import java.util.concurrent.TimeUnit;
86 import java.util.Objects;
87 
88 /**
89  * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps
90  * track of when the object can safely be unbound. Other classes should not use
91  * {@link IConnectionService} directly and instead should use this class to invoke methods of
92  * {@link IConnectionService}.
93  */
94 @VisibleForTesting
95 public class ConnectionServiceWrapper extends ServiceBinder implements
96         ConnectionServiceFocusManager.ConnectionServiceFocus, CallSourceService {
97 
98     /**
99      * Anomaly Report UUIDs and corresponding error descriptions specific to
100      * ConnectionServiceWrapper.
101      */
102     public static final UUID CREATE_CONNECTION_TIMEOUT_ERROR_UUID =
103             UUID.fromString("54b7203d-a79f-4cbd-b639-85cd93a39cbb");
104     public static final String CREATE_CONNECTION_TIMEOUT_ERROR_MSG =
105             "Timeout expired before Telecom connection was created.";
106     public static final UUID CREATE_CONFERENCE_TIMEOUT_ERROR_UUID =
107             UUID.fromString("caafe5ea-2472-4c61-b2d8-acb9d47e13dd");
108     public static final String CREATE_CONFERENCE_TIMEOUT_ERROR_MSG =
109             "Timeout expired before Telecom conference was created.";
110     public static final UUID NULL_SCHEDULED_EXECUTOR_ERROR_UUID =
111             UUID.fromString("af6b293b-239f-4ccf-bf3a-db212594e29d");
112     public static final String NULL_SCHEDULED_EXECUTOR_ERROR_MSG =
113             "Scheduled executor is null when creating connection/conference.";
114     public static final UUID EXECUTOR_REJECTED_EXECUTION_ERROR_UUID =
115             UUID.fromString("649b348c-9d3f-451e-bae9-d9920e7b422c");
116 
117     public static final String EXECUTOR_REJECTED_EXECUTION_ERROR_MSG =
118             "Scheduled executor caused a Rejected Execution Exception when creating connection.";
119 
120     private static final String TELECOM_ABBREVIATION = "cast";
121     private static final long SERVICE_BINDING_TIMEOUT = 15000L;
122     private CompletableFuture<Pair<Integer, Location>> mQueryLocationFuture = null;
123     private @Nullable CancellationSignal mOngoingQueryLocationRequest = null;
124     private final ExecutorService mQueryLocationExecutor = Executors.newSingleThreadExecutor();
125     private ScheduledExecutorService mScheduledExecutor =
126             Executors.newSingleThreadScheduledExecutor();
127     // Pre-allocate space for 2 calls; realistically thats all we should ever need (tm)
128     private final Map<Call, ScheduledFuture<?>> mScheduledFutureMap = new ConcurrentHashMap<>(2);
129     private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
130 
131     private final class Adapter extends IConnectionServiceAdapter.Stub {
132 
133         @Override
handleCreateConnectionComplete(String callId, ConnectionRequest request, ParcelableConnection connection, Session.Info sessionInfo)134         public void handleCreateConnectionComplete(String callId, ConnectionRequest request,
135                 ParcelableConnection connection, Session.Info sessionInfo) {
136             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE,
137                     mPackageAbbreviation);
138             UserHandle callingUserHandle = Binder.getCallingUserHandle();
139             long token = Binder.clearCallingIdentity();
140             try {
141                 synchronized (mLock) {
142                     logIncoming("handleCreateConnectionComplete %s", callId);
143                     Call call = mCallIdMapper.getCall(callId);
144                     maybeRemoveCleanupFuture(call);
145                     // Check status hints image for cross user access
146                     if (connection.getStatusHints() != null) {
147                         Icon icon = connection.getStatusHints().getIcon();
148                         connection.getStatusHints().setIcon(StatusHints.
149                                 validateAccountIconUserBoundary(icon, callingUserHandle));
150                     }
151                     ConnectionServiceWrapper.this
152                             .handleCreateConnectionComplete(callId, request, connection);
153 
154                     if (mServiceInterface != null) {
155                         logOutgoing("createConnectionComplete %s", callId);
156                         try {
157                             mServiceInterface.createConnectionComplete(callId,
158                                     Log.getExternalSession());
159                         } catch (RemoteException e) {
160                             logOutgoing("createConnectionComplete remote exception=%s", e);
161                         }
162                     }
163                 }
164             } catch (Throwable t) {
165                 Log.e(ConnectionServiceWrapper.this, t, "");
166                 throw t;
167             } finally {
168                 Binder.restoreCallingIdentity(token);
169                 Log.endSession();
170             }
171         }
172 
173         @Override
handleCreateConferenceComplete(String callId, ConnectionRequest request, ParcelableConference conference, Session.Info sessionInfo)174         public void handleCreateConferenceComplete(String callId, ConnectionRequest request,
175                 ParcelableConference conference, Session.Info sessionInfo) {
176             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_HANDLE_CREATE_CONNECTION_COMPLETE,
177                     mPackageAbbreviation);
178             UserHandle callingUserHandle = Binder.getCallingUserHandle();
179             long token = Binder.clearCallingIdentity();
180             try {
181                 synchronized (mLock) {
182                     logIncoming("handleCreateConferenceComplete %s", callId);
183                     Call call = mCallIdMapper.getCall(callId);
184                     maybeRemoveCleanupFuture(call);
185                     // Check status hints image for cross user access
186                     if (conference.getStatusHints() != null) {
187                         Icon icon = conference.getStatusHints().getIcon();
188                         conference.getStatusHints().setIcon(StatusHints.
189                                 validateAccountIconUserBoundary(icon, callingUserHandle));
190                     }
191                     ConnectionServiceWrapper.this
192                             .handleCreateConferenceComplete(callId, request, conference);
193 
194                     if (mServiceInterface != null) {
195                         logOutgoing("createConferenceComplete %s", callId);
196                         try {
197                             mServiceInterface.createConferenceComplete(callId,
198                                     Log.getExternalSession());
199                         } catch (RemoteException e) {
200                         }
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 
213         @Override
setActive(String callId, Session.Info sessionInfo)214         public void setActive(String callId, Session.Info sessionInfo) {
215             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_ACTIVE,
216                     mPackageAbbreviation);
217             long token = Binder.clearCallingIdentity();
218             try {
219                 synchronized (mLock) {
220                     logIncoming("setActive %s", callId);
221                     Call call = mCallIdMapper.getCall(callId);
222                     if (call != null) {
223                         mCallsManager.markCallAsActive(call);
224                     } else {
225                         // Log.w(this, "setActive, unknown call id: %s", msg.obj);
226                     }
227                 }
228             } catch (Throwable t) {
229                 Log.e(ConnectionServiceWrapper.this, t, "");
230                 throw t;
231             } finally {
232                 Binder.restoreCallingIdentity(token);
233                 Log.endSession();
234             }
235         }
236 
237         @Override
setRinging(String callId, Session.Info sessionInfo)238         public void setRinging(String callId, Session.Info sessionInfo) {
239             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_RINGING, mPackageAbbreviation);
240             long token = Binder.clearCallingIdentity();
241             try {
242                 synchronized (mLock) {
243                     logIncoming("setRinging %s", callId);
244                     Call call = mCallIdMapper.getCall(callId);
245                     if (call != null) {
246                         mCallsManager.markCallAsRinging(call);
247                     } else {
248                         // Log.w(this, "setRinging, unknown call id: %s", msg.obj);
249                     }
250                 }
251             } catch (Throwable t) {
252                 Log.e(ConnectionServiceWrapper.this, t, "");
253                 throw t;
254             } finally {
255                 Binder.restoreCallingIdentity(token);
256                 Log.endSession();
257             }
258         }
259 
260         @Override
resetConnectionTime(String callId, Session.Info sessionInfo)261         public void resetConnectionTime(String callId, Session.Info sessionInfo) {
262             Log.startSession(sessionInfo, "CSW.rCCT", mPackageAbbreviation);
263             long token = Binder.clearCallingIdentity();
264             try {
265                 synchronized (mLock) {
266                     logIncoming("resetConnectionTime %s", callId);
267                     Call call = mCallIdMapper.getCall(callId);
268                     if (call != null) {
269                         mCallsManager.resetConnectionTime(call);
270                     } else {
271                         // Log.w(this, "resetConnectionTime, unknown call id: %s", msg.obj);
272                     }
273                 }
274             } finally {
275                 Binder.restoreCallingIdentity(token);
276                 Log.endSession();
277             }
278         }
279 
280         @Override
setVideoProvider(String callId, IVideoProvider videoProvider, Session.Info sessionInfo)281         public void setVideoProvider(String callId, IVideoProvider videoProvider,
282                 Session.Info sessionInfo) {
283             Log.startSession(sessionInfo, "CSW.sVP", mPackageAbbreviation);
284             long token = Binder.clearCallingIdentity();
285             try {
286                 synchronized (mLock) {
287                     logIncoming("setVideoProvider %s", callId);
288                     Call call = mCallIdMapper.getCall(callId);
289                     if (call != null) {
290                         call.setVideoProvider(videoProvider);
291                     }
292                 }
293             } catch (Throwable t) {
294                 Log.e(ConnectionServiceWrapper.this, t, "");
295                 throw t;
296             } finally {
297                 Binder.restoreCallingIdentity(token);
298                 Log.endSession();
299             }
300         }
301 
302         @Override
setDialing(String callId, Session.Info sessionInfo)303         public void setDialing(String callId, Session.Info sessionInfo) {
304             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_DIALING, mPackageAbbreviation);
305             long token = Binder.clearCallingIdentity();
306             try {
307                 synchronized (mLock) {
308                     logIncoming("setDialing %s", callId);
309                     Call call = mCallIdMapper.getCall(callId);
310                     if (call != null) {
311                         mCallsManager.markCallAsDialing(call);
312                     } else {
313                         // Log.w(this, "setDialing, unknown call id: %s", msg.obj);
314                     }
315                 }
316             } catch (Throwable t) {
317                 Log.e(ConnectionServiceWrapper.this, t, "");
318                 throw t;
319             } finally {
320                 Binder.restoreCallingIdentity(token);
321                 Log.endSession();
322             }
323         }
324 
325         @Override
setPulling(String callId, Session.Info sessionInfo)326         public void setPulling(String callId, Session.Info sessionInfo) {
327             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_PULLING, mPackageAbbreviation);
328             long token = Binder.clearCallingIdentity();
329             try {
330                 synchronized (mLock) {
331                     logIncoming("setPulling %s", callId);
332                     Call call = mCallIdMapper.getCall(callId);
333                     if (call != null) {
334                         mCallsManager.markCallAsPulling(call);
335                     }
336                 }
337             } catch (Throwable t) {
338                 Log.e(ConnectionServiceWrapper.this, t, "");
339                 throw t;
340             } finally {
341                 Binder.restoreCallingIdentity(token);
342                 Log.endSession();
343             }
344         }
345 
346         @Override
setDisconnected(String callId, DisconnectCause disconnectCause, Session.Info sessionInfo)347         public void setDisconnected(String callId, DisconnectCause disconnectCause,
348                 Session.Info sessionInfo) {
349             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_DISCONNECTED,
350                     mPackageAbbreviation);
351             long token = Binder.clearCallingIdentity();
352             try {
353                 synchronized (mLock) {
354                     logIncoming("setDisconnected %s %s", callId, disconnectCause);
355                     Call call = mCallIdMapper.getCall(callId);
356                     Log.d(this, "disconnect call %s %s", disconnectCause, call);
357                     if (call != null) {
358                         mCallsManager.markCallAsDisconnected(call, disconnectCause);
359                     } else {
360                         // Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
361                     }
362                 }
363             } catch (Throwable t) {
364                 Log.e(ConnectionServiceWrapper.this, t, "");
365                 throw t;
366             } finally {
367                 Binder.restoreCallingIdentity(token);
368                 Log.endSession();
369             }
370         }
371 
372         @Override
setOnHold(String callId, Session.Info sessionInfo)373         public void setOnHold(String callId, Session.Info sessionInfo) {
374             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_ON_HOLD, mPackageAbbreviation);
375             long token = Binder.clearCallingIdentity();
376             try {
377                 synchronized (mLock) {
378                     logIncoming("setOnHold %s", callId);
379                     Call call = mCallIdMapper.getCall(callId);
380                     if (call != null) {
381                         mCallsManager.markCallAsOnHold(call);
382                     } else {
383                         // Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
384                     }
385                 }
386             } catch (Throwable t) {
387                 Log.e(ConnectionServiceWrapper.this, t, "");
388                 throw t;
389             } finally {
390                 Binder.restoreCallingIdentity(token);
391                 Log.endSession();
392             }
393         }
394 
395         @Override
setRingbackRequested(String callId, boolean ringback, Session.Info sessionInfo)396         public void setRingbackRequested(String callId, boolean ringback,
397                 Session.Info sessionInfo) {
398             Log.startSession(sessionInfo, "CSW.SRR", mPackageAbbreviation);
399             long token = Binder.clearCallingIdentity();
400             try {
401                 synchronized (mLock) {
402                     logIncoming("setRingbackRequested %s %b", callId, ringback);
403                     Call call = mCallIdMapper.getCall(callId);
404                     if (call != null) {
405                         call.setRingbackRequested(ringback);
406                     } else {
407                         // Log.w(this, "setRingback, unknown call id: %s", args.arg1);
408                     }
409                 }
410             } catch (Throwable t) {
411                 Log.e(ConnectionServiceWrapper.this, t, "");
412                 throw t;
413             } finally {
414                 Binder.restoreCallingIdentity(token);
415                 Log.endSession();
416             }
417         }
418 
419         @Override
removeCall(String callId, Session.Info sessionInfo)420         public void removeCall(String callId, Session.Info sessionInfo) {
421             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_REMOVE_CALL, mPackageAbbreviation);
422             long token = Binder.clearCallingIdentity();
423             try {
424                 synchronized (mLock) {
425                     logIncoming("removeCall %s", callId);
426                     Call call = mCallIdMapper.getCall(callId);
427                     if (call != null) {
428                         boolean isRemovalPending = mFlags.cancelRemovalOnEmergencyRedial()
429                                 && call.isRemovalPending();
430                         if (call.isAlive() && !call.isDisconnectHandledViaFuture()
431                                 && !isRemovalPending) {
432                             Log.w(this, "call not disconnected when removeCall"
433                                     + " called, marking disconnected first.");
434                             mCallsManager.markCallAsDisconnected(
435                                     call, new DisconnectCause(DisconnectCause.REMOTE));
436                         }
437                         mCallsManager.markCallAsRemoved(call);
438                     }
439                 }
440             } catch (Throwable t) {
441                 Log.e(ConnectionServiceWrapper.this, t, "");
442                 throw t;
443             } finally {
444                 Binder.restoreCallingIdentity(token);
445                 Log.endSession();
446             }
447         }
448 
449         @Override
setConnectionCapabilities(String callId, int connectionCapabilities, Session.Info sessionInfo)450         public void setConnectionCapabilities(String callId, int connectionCapabilities,
451                 Session.Info sessionInfo) {
452             Log.startSession(sessionInfo, "CSW.sCC", mPackageAbbreviation);
453             long token = Binder.clearCallingIdentity();
454             try {
455                 synchronized (mLock) {
456                     logIncoming("setConnectionCapabilities %s %d", callId, connectionCapabilities);
457                     Call call = mCallIdMapper.getCall(callId);
458                     if (call != null) {
459                         call.setConnectionCapabilities(connectionCapabilities);
460                     } else {
461                         // Log.w(ConnectionServiceWrapper.this,
462                         // "setConnectionCapabilities, unknown call id: %s", msg.obj);
463                     }
464                 }
465             } catch (Throwable t) {
466                 Log.e(ConnectionServiceWrapper.this, t, "");
467                 throw t;
468             } finally {
469                 Binder.restoreCallingIdentity(token);
470                 Log.endSession();
471             }
472         }
473 
474         @Override
setConnectionProperties(String callId, int connectionProperties, Session.Info sessionInfo)475         public void setConnectionProperties(String callId, int connectionProperties,
476                 Session.Info sessionInfo) {
477             Log.startSession("CSW.sCP", mPackageAbbreviation);
478             long token = Binder.clearCallingIdentity();
479             try {
480                 synchronized (mLock) {
481                     logIncoming("setConnectionProperties %s %d", callId, connectionProperties);
482                     Call call = mCallIdMapper.getCall(callId);
483                     if (call != null) {
484                         call.setConnectionProperties(connectionProperties);
485                     }
486                 }
487             } catch (Throwable t) {
488                 Log.e(ConnectionServiceWrapper.this, t, "");
489                 throw t;
490             } finally {
491                 Binder.restoreCallingIdentity(token);
492                 Log.endSession();
493             }
494         }
495 
496         @Override
setIsConferenced(String callId, String conferenceCallId, Session.Info sessionInfo)497         public void setIsConferenced(String callId, String conferenceCallId,
498                 Session.Info sessionInfo) {
499             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_IS_CONFERENCED,
500                     mPackageAbbreviation);
501             long token = Binder.clearCallingIdentity();
502             try {
503                 synchronized (mLock) {
504                     logIncoming("setIsConferenced %s %s", callId, conferenceCallId);
505                     Call childCall = mCallIdMapper.getCall(callId);
506                     if (childCall != null) {
507                         if (conferenceCallId == null) {
508                             Log.d(this, "unsetting parent: %s", conferenceCallId);
509                             childCall.setParentAndChildCall(null);
510                         } else {
511                             Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
512                             // In a situation where a cmgr is used, the conference should be tracked
513                             // by that cmgr's instance of CSW. The cmgr instance of CSW will track
514                             // and properly set the parent and child calls so the request from the
515                             // original Telephony instance of CSW can be ignored.
516                             if (conferenceCall != null){
517                                 childCall.setParentAndChildCall(conferenceCall);
518                             }
519                         }
520                     } else {
521                         // Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
522                     }
523                 }
524             } catch (Throwable t) {
525                 Log.e(ConnectionServiceWrapper.this, t, "");
526                 throw t;
527             } finally {
528                 Binder.restoreCallingIdentity(token);
529                 Log.endSession();
530             }
531         }
532 
533         @Override
setConferenceMergeFailed(String callId, Session.Info sessionInfo)534         public void setConferenceMergeFailed(String callId, Session.Info sessionInfo) {
535             Log.startSession(sessionInfo, "CSW.sCMF", mPackageAbbreviation);
536             long token = Binder.clearCallingIdentity();
537             try {
538                 synchronized (mLock) {
539                     logIncoming("setConferenceMergeFailed %s", callId);
540                     // TODO: we should move the UI for indication a merge failure here
541                     // from CallNotifier.onSuppServiceFailed(). This way the InCallUI can
542                     // deliver the message anyway that they want. b/20530631.
543                     Call call = mCallIdMapper.getCall(callId);
544                     if (call != null) {
545                         call.onConnectionEvent(Connection.EVENT_CALL_MERGE_FAILED, null);
546                     } else {
547                         Log.w(this, "setConferenceMergeFailed, unknown call id: %s", callId);
548                     }
549                 }
550             } catch (Throwable t) {
551                 Log.e(ConnectionServiceWrapper.this, t, "");
552                 throw t;
553             } finally {
554                 Binder.restoreCallingIdentity(token);
555                 Log.endSession();
556             }
557         }
558 
559         @Override
addConferenceCall(String callId, ParcelableConference parcelableConference, Session.Info sessionInfo)560         public void addConferenceCall(String callId, ParcelableConference parcelableConference,
561                 Session.Info sessionInfo) {
562             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_ADD_CONFERENCE_CALL,
563                     mPackageAbbreviation);
564 
565             UserHandle callingUserHandle = Binder.getCallingUserHandle();
566             // Check status hints image for cross user access
567             if (parcelableConference.getStatusHints() != null) {
568                 Icon icon = parcelableConference.getStatusHints().getIcon();
569                 parcelableConference.getStatusHints().setIcon(StatusHints
570                         .validateAccountIconUserBoundary(icon, callingUserHandle));
571             }
572 
573             if (parcelableConference.getConnectElapsedTimeMillis() != 0
574                     && mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
575                             != PackageManager.PERMISSION_GRANTED) {
576                 Log.w(this, "addConferenceCall from caller without permission!");
577                 parcelableConference = new ParcelableConference.Builder(
578                         parcelableConference.getPhoneAccount(),
579                         parcelableConference.getState())
580                         .setConnectionCapabilities(parcelableConference.getConnectionCapabilities())
581                         .setConnectionProperties(parcelableConference.getConnectionProperties())
582                         .setConnectionIds(parcelableConference.getConnectionIds())
583                         .setVideoAttributes(parcelableConference.getVideoProvider(),
584                                 parcelableConference.getVideoState())
585                         .setStatusHints(parcelableConference.getStatusHints())
586                         .setExtras(parcelableConference.getExtras())
587                         .setAddress(parcelableConference.getHandle(),
588                                 parcelableConference.getHandlePresentation())
589                         // no caller display name set.
590                         .setDisconnectCause(parcelableConference.getDisconnectCause())
591                         .setRingbackRequested(parcelableConference.isRingbackRequested())
592                         .build();
593             }
594 
595             long token = Binder.clearCallingIdentity();
596             try {
597                 synchronized (mLock) {
598                     if (mCallIdMapper.getCall(callId) != null) {
599                         Log.w(this, "Attempting to add a conference call using an existing " +
600                                 "call id %s", callId);
601                         return;
602                     }
603                     logIncoming("addConferenceCall %s %s [%s]", callId, parcelableConference,
604                             parcelableConference.getConnectionIds());
605 
606                     // Make sure that there's at least one valid call. For remote connections
607                     // we'll get a add conference msg from both the remote connection service
608                     // and from the real connection service.
609                     boolean hasValidCalls = false;
610                     for (String connId : parcelableConference.getConnectionIds()) {
611                         if (mCallIdMapper.getCall(connId) != null) {
612                             hasValidCalls = true;
613                         }
614                     }
615                     // But don't bail out if the connection count is 0, because that is a valid
616                     // IMS conference state.
617                     if (!hasValidCalls && parcelableConference.getConnectionIds().size() > 0) {
618                         Log.d(this, "Attempting to add a conference with no valid calls");
619                         return;
620                     }
621 
622                     PhoneAccountHandle phAcc = null;
623                     if (parcelableConference != null &&
624                             parcelableConference.getPhoneAccount() != null) {
625                         phAcc = parcelableConference.getPhoneAccount();
626                     }
627 
628                     Bundle connectionExtras = parcelableConference.getExtras();
629 
630                     String connectIdToCheck = null;
631                     if (connectionExtras != null && connectionExtras
632                             .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
633                         // Conference was added via a connection manager, see if its original id is
634                         // known.
635                         connectIdToCheck = connectionExtras
636                                 .getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
637                     } else {
638                         connectIdToCheck = callId;
639                     }
640 
641                     Call conferenceCall;
642                     // Check to see if this conference has already been added.
643                     Call alreadyAddedConnection = mCallsManager
644                             .getAlreadyAddedConnection(connectIdToCheck);
645                     if (alreadyAddedConnection != null && mCallIdMapper.getCall(callId) == null) {
646                         // We are currently attempting to add the conference via a connection mgr,
647                         // and the originating ConnectionService has already added it.  Instead of
648                         // making a new Telecom call, we will simply add it to the ID mapper here,
649                         // and replace the ConnectionService on the call.
650                         mCallIdMapper.addCall(alreadyAddedConnection, callId);
651                         alreadyAddedConnection.replaceConnectionService(
652                                 ConnectionServiceWrapper.this);
653                         conferenceCall = alreadyAddedConnection;
654                     } else {
655                         // need to create a new Call
656                         Call newConferenceCall = mCallsManager.createConferenceCall(callId,
657                                 phAcc, parcelableConference);
658                         mCallIdMapper.addCall(newConferenceCall, callId);
659                         newConferenceCall.setConnectionService(ConnectionServiceWrapper.this);
660                         conferenceCall = newConferenceCall;
661                     }
662 
663                     Log.d(this, "adding children to conference %s phAcc %s",
664                             parcelableConference.getConnectionIds(), phAcc);
665                     for (String connId : parcelableConference.getConnectionIds()) {
666                         Call childCall = mCallIdMapper.getCall(connId);
667                         Log.d(this, "found child: %s", connId);
668                         if (childCall != null) {
669                             childCall.setParentAndChildCall(conferenceCall);
670                         }
671                     }
672                 }
673             } catch (Throwable t) {
674                 Log.e(ConnectionServiceWrapper.this, t, "");
675                 throw t;
676             } finally {
677                 Binder.restoreCallingIdentity(token);
678                 Log.endSession();
679             }
680         }
681 
682         @Override
onPostDialWait(String callId, String remaining, Session.Info sessionInfo)683         public void onPostDialWait(String callId, String remaining,
684                 Session.Info sessionInfo) throws RemoteException {
685             Log.startSession(sessionInfo, "CSW.oPDW", mPackageAbbreviation);
686             long token = Binder.clearCallingIdentity();
687             try {
688                 synchronized (mLock) {
689                     logIncoming("onPostDialWait %s %s", callId, remaining);
690                     Call call = mCallIdMapper.getCall(callId);
691                     if (call != null) {
692                         call.onPostDialWait(remaining);
693                     } else {
694                         // Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
695                     }
696                 }
697             } catch (Throwable t) {
698                 Log.e(ConnectionServiceWrapper.this, t, "");
699                 throw t;
700             } finally {
701                 Binder.restoreCallingIdentity(token);
702                 Log.endSession();
703             }
704         }
705 
706         @Override
onPostDialChar(String callId, char nextChar, Session.Info sessionInfo)707         public void onPostDialChar(String callId, char nextChar,
708                 Session.Info sessionInfo) throws RemoteException {
709             Log.startSession(sessionInfo, "CSW.oPDC", mPackageAbbreviation);
710             long token = Binder.clearCallingIdentity();
711             try {
712                 synchronized (mLock) {
713                     logIncoming("onPostDialChar %s %s", callId, nextChar);
714                     Call call = mCallIdMapper.getCall(callId);
715                     if (call != null) {
716                         call.onPostDialChar(nextChar);
717                     } else {
718                         // Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1);
719                     }
720                 }
721             } catch (Throwable t) {
722                 Log.e(ConnectionServiceWrapper.this, t, "");
723                 throw t;
724             } finally {
725                 Binder.restoreCallingIdentity(token);
726                 Log.endSession();
727             }
728         }
729 
730         @Override
queryRemoteConnectionServices(RemoteServiceCallback callback, String callingPackage, Session.Info sessionInfo)731         public void queryRemoteConnectionServices(RemoteServiceCallback callback,
732                 String callingPackage, Session.Info sessionInfo) {
733             final UserHandle callingUserHandle = Binder.getCallingUserHandle();
734             Log.startSession(sessionInfo, "CSW.qRCS", mPackageAbbreviation);
735             long token = Binder.clearCallingIdentity();
736             try {
737                 synchronized (mLock) {
738                     logIncoming("queryRemoteConnectionServices callingPackage=" + callingPackage);
739                     ConnectionServiceWrapper.this
740                             .queryRemoteConnectionServices(callingUserHandle, callingPackage,
741                                     callback);
742                 }
743             } catch (Throwable t) {
744                 Log.e(ConnectionServiceWrapper.this, t, "");
745                 throw t;
746             } finally {
747                 Binder.restoreCallingIdentity(token);
748                 Log.endSession();
749             }
750         }
751 
752         @Override
setVideoState(String callId, int videoState, Session.Info sessionInfo)753         public void setVideoState(String callId, int videoState, Session.Info sessionInfo) {
754             Log.startSession(sessionInfo, "CSW.sVS", mPackageAbbreviation);
755             long token = Binder.clearCallingIdentity();
756             try {
757                 synchronized (mLock) {
758                     logIncoming("setVideoState %s %d", callId, videoState);
759                     Call call = mCallIdMapper.getCall(callId);
760                     if (call != null) {
761                         call.setVideoState(videoState);
762                     }
763                 }
764             } catch (Throwable t) {
765                 Log.e(ConnectionServiceWrapper.this, t, "");
766                 throw t;
767             } finally {
768                 Binder.restoreCallingIdentity(token);
769                 Log.endSession();
770             }
771         }
772 
773         @Override
setIsVoipAudioMode(String callId, boolean isVoip, Session.Info sessionInfo)774         public void setIsVoipAudioMode(String callId, boolean isVoip, Session.Info sessionInfo) {
775             Log.startSession(sessionInfo, "CSW.sIVAM", mPackageAbbreviation);
776             long token = Binder.clearCallingIdentity();
777             try {
778                 synchronized (mLock) {
779                     logIncoming("setIsVoipAudioMode %s %b", callId, isVoip);
780                     Call call = mCallIdMapper.getCall(callId);
781                     if (call != null) {
782                         call.setIsVoipAudioMode(isVoip);
783                     }
784                 }
785             } catch (Throwable t) {
786                 Log.e(ConnectionServiceWrapper.this, t, "");
787                 throw t;
788             } finally {
789                 Binder.restoreCallingIdentity(token);
790                 Log.endSession();
791             }
792         }
793 
794         @Override
setAudioRoute(String callId, int audioRoute, String bluetoothAddress, Session.Info sessionInfo)795         public void setAudioRoute(String callId, int audioRoute,
796                 String bluetoothAddress, Session.Info sessionInfo) {
797             Log.startSession(sessionInfo, "CSW.sAR", mPackageAbbreviation);
798             long token = Binder.clearCallingIdentity();
799             try {
800                 synchronized (mLock) {
801                     logIncoming("setAudioRoute %s %s", callId,
802                             CallAudioState.audioRouteToString(audioRoute));
803                     mCallsManager.setAudioRoute(audioRoute, bluetoothAddress);
804                 }
805             } catch (Throwable t) {
806                 Log.e(ConnectionServiceWrapper.this, t, "");
807                 throw t;
808             } finally {
809                 Binder.restoreCallingIdentity(token);
810                 Log.endSession();
811             }
812         }
813 
814         @Override
requestCallEndpointChange(String callId, CallEndpoint endpoint, ResultReceiver callback, Session.Info sessionInfo)815         public void requestCallEndpointChange(String callId, CallEndpoint endpoint,
816                 ResultReceiver callback, Session.Info sessionInfo) {
817             Log.startSession(sessionInfo, "CSW.rCEC", mPackageAbbreviation);
818             long token = Binder.clearCallingIdentity();
819             try {
820                 synchronized (mLock) {
821                     logIncoming("requestCallEndpointChange %s %s", callId,
822                             endpoint.getEndpointName());
823                     mCallsManager.requestCallEndpointChange(endpoint, callback);
824                 }
825             } catch (Throwable t) {
826                 Log.e(ConnectionServiceWrapper.this, t, "");
827                 throw t;
828             } finally {
829                 Binder.restoreCallingIdentity(token);
830                 Log.endSession();
831             }
832         }
833 
834         @Override
setStatusHints(String callId, StatusHints statusHints, Session.Info sessionInfo)835         public void setStatusHints(String callId, StatusHints statusHints,
836                 Session.Info sessionInfo) {
837             Log.startSession(sessionInfo, "CSW.sSH", mPackageAbbreviation);
838             UserHandle callingUserHandle = Binder.getCallingUserHandle();
839             long token = Binder.clearCallingIdentity();
840             try {
841                 synchronized (mLock) {
842                     logIncoming("setStatusHints %s %s", callId, statusHints);
843                     // Check status hints image for cross user access
844                     if (statusHints != null) {
845                         Icon icon = statusHints.getIcon();
846                         statusHints.setIcon(StatusHints.validateAccountIconUserBoundary(
847                                 icon, callingUserHandle));
848                     }
849                     Call call = mCallIdMapper.getCall(callId);
850                     if (call != null) {
851                         call.setStatusHints(statusHints);
852                     }
853                 }
854             } catch (Throwable t) {
855                 Log.e(ConnectionServiceWrapper.this, t, "");
856                 throw t;
857             } finally {
858                 Binder.restoreCallingIdentity(token);
859                 Log.endSession();
860             }
861         }
862 
863         @Override
putExtras(String callId, Bundle extras, Session.Info sessionInfo)864         public void putExtras(String callId, Bundle extras, Session.Info sessionInfo) {
865             Log.startSession(sessionInfo, "CSW.pE", mPackageAbbreviation);
866             long token = Binder.clearCallingIdentity();
867             try {
868                 synchronized (mLock) {
869                     Bundle.setDefusable(extras, true);
870                     Call call = mCallIdMapper.getCall(callId);
871                     if (call != null) {
872                         call.putConnectionServiceExtras(extras);
873                     }
874                 }
875             } catch (Throwable t) {
876                 Log.e(ConnectionServiceWrapper.this, t, "");
877                 throw t;
878             } finally {
879                 Binder.restoreCallingIdentity(token);
880                 Log.endSession();
881             }
882         }
883 
884         @Override
removeExtras(String callId, List<String> keys, Session.Info sessionInfo)885         public void removeExtras(String callId, List<String> keys, Session.Info sessionInfo) {
886             Log.startSession(sessionInfo, "CSW.rE", mPackageAbbreviation);
887             long token = Binder.clearCallingIdentity();
888             try {
889                 synchronized (mLock) {
890                     logIncoming("removeExtra %s %s", callId, keys);
891                     Call call = mCallIdMapper.getCall(callId);
892                     if (call != null) {
893                         call.removeExtras(Call.SOURCE_CONNECTION_SERVICE, keys);
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
setAddress(String callId, Uri address, int presentation, Session.Info sessionInfo)906         public void setAddress(String callId, Uri address, int presentation,
907                 Session.Info sessionInfo) {
908             Log.startSession(sessionInfo, "CSW.sA", mPackageAbbreviation);
909 
910             long token = Binder.clearCallingIdentity();
911             try {
912                 synchronized (mLock) {
913                     logIncoming("setAddress %s %s %d", callId, address, presentation);
914                     Call call = mCallIdMapper.getCall(callId);
915                     if (call != null) {
916                         call.setHandle(address, presentation);
917                     }
918                 }
919             } catch (Throwable t) {
920                 Log.e(ConnectionServiceWrapper.this, t, "");
921                 throw t;
922             } finally {
923                 Binder.restoreCallingIdentity(token);
924                 Log.endSession();
925             }
926         }
927 
928         @Override
setCallerDisplayName(String callId, String callerDisplayName, int presentation, Session.Info sessionInfo)929         public void setCallerDisplayName(String callId, String callerDisplayName, int presentation,
930                 Session.Info sessionInfo) {
931             Log.startSession(sessionInfo, "CSW.sCDN", mPackageAbbreviation);
932             long token = Binder.clearCallingIdentity();
933             try {
934                 synchronized (mLock) {
935                     logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName,
936                             presentation);
937                     Call call = mCallIdMapper.getCall(callId);
938                     if (call != null) {
939                         call.setCallerDisplayName(callerDisplayName, presentation);
940                     }
941                 }
942             } catch (Throwable t) {
943                 Log.e(ConnectionServiceWrapper.this, t, "");
944                 throw t;
945             } finally {
946                 Binder.restoreCallingIdentity(token);
947                 Log.endSession();
948             }
949         }
950 
951         @Override
setConferenceableConnections(String callId, List<String> conferenceableCallIds, Session.Info sessionInfo)952         public void setConferenceableConnections(String callId, List<String> conferenceableCallIds,
953                 Session.Info sessionInfo) {
954             Log.startSession(sessionInfo, "CSW.sCC", mPackageAbbreviation);
955             long token = Binder.clearCallingIdentity();
956             try {
957                 synchronized (mLock) {
958 
959                     Call call = mCallIdMapper.getCall(callId);
960                     if (call != null) {
961                         logIncoming("setConferenceableConnections %s %s", callId,
962                                 conferenceableCallIds);
963                         List<Call> conferenceableCalls =
964                                 new ArrayList<>(conferenceableCallIds.size());
965                         for (String otherId : conferenceableCallIds) {
966                             Call otherCall = mCallIdMapper.getCall(otherId);
967                             if (otherCall != null && otherCall != call) {
968                                 conferenceableCalls.add(otherCall);
969                             }
970                         }
971                         call.setConferenceableCalls(conferenceableCalls);
972                     }
973                 }
974             } catch (Throwable t) {
975                 Log.e(ConnectionServiceWrapper.this, t, "");
976                 throw t;
977             } finally {
978                 Binder.restoreCallingIdentity(token);
979                 Log.endSession();
980             }
981         }
982 
983         @Override
addExistingConnection(String callId, ParcelableConnection connection, Session.Info sessionInfo)984         public void addExistingConnection(String callId, ParcelableConnection connection,
985                 Session.Info sessionInfo) {
986             Log.startSession(sessionInfo, "CSW.aEC", mPackageAbbreviation);
987             UserHandle userHandle = Binder.getCallingUserHandle();
988             // Check that the Calling Package matches PhoneAccountHandle's Component Package
989             PhoneAccountHandle callingPhoneAccountHandle = connection.getPhoneAccount();
990             if (callingPhoneAccountHandle != null) {
991                 mAppOpsManager.checkPackage(Binder.getCallingUid(),
992                         callingPhoneAccountHandle.getComponentName().getPackageName());
993             }
994 
995             boolean hasCrossUserAccess = mContext.checkCallingOrSelfPermission(
996                     android.Manifest.permission.INTERACT_ACROSS_USERS)
997                     == PackageManager.PERMISSION_GRANTED;
998             long token = Binder.clearCallingIdentity();
999             try {
1000                 synchronized (mLock) {
1001                     // Make sure that the PhoneAccount associated with the incoming
1002                     // ParcelableConnection is in fact registered to Telecom and is being called
1003                     // from the correct user.
1004                     List<PhoneAccountHandle> accountHandles =
1005                     // Include CAPABILITY_EMERGENCY_CALLS_ONLY in this list in case we are adding
1006                     // an emergency call.
1007                             mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null /*uriScheme*/,
1008                             false /*includeDisabledAccounts*/, userHandle, 0 /*capabilities*/,
1009                             0 /*excludedCapabilities*/, hasCrossUserAccess);
1010                     PhoneAccountHandle phoneAccountHandle = null;
1011                     for (PhoneAccountHandle accountHandle : accountHandles) {
1012                         if(accountHandle.equals(callingPhoneAccountHandle)) {
1013                             phoneAccountHandle = accountHandle;
1014                         }
1015                     }
1016                     // Allow the Sim call manager account as well, even if its disabled.
1017                     if (phoneAccountHandle == null && callingPhoneAccountHandle != null) {
1018                         // Search all SIM PhoneAccounts to see if there is a SIM call manager
1019                         // associated with any of them and verify that the calling handle matches.
1020                         for (PhoneAccountHandle handle :
1021                                 mPhoneAccountRegistrar.getSimPhoneAccounts(userHandle)) {
1022                             int subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(
1023                                     handle);
1024                             PhoneAccountHandle connectionMgrHandle =
1025                                     mPhoneAccountRegistrar.getSimCallManager(subId, userHandle);
1026                             if (callingPhoneAccountHandle.equals(connectionMgrHandle)) {
1027                                 phoneAccountHandle = connectionMgrHandle;
1028                                 break;
1029                             }
1030                         }
1031                     }
1032                     if (phoneAccountHandle != null) {
1033                         logIncoming("addExistingConnection %s %s", callId, connection);
1034 
1035                         Bundle connectionExtras = connection.getExtras();
1036                         String connectIdToCheck = null;
1037                         if (connectionExtras != null && connectionExtras
1038                                 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
1039                             connectIdToCheck = connectionExtras
1040                                     .getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID);
1041                         } else {
1042                             connectIdToCheck = callId;
1043                         }
1044 
1045                         // Check status hints image for cross user access
1046                         if (connection.getStatusHints() != null) {
1047                             Icon icon = connection.getStatusHints().getIcon();
1048                             connection.getStatusHints().setIcon(StatusHints.
1049                                     validateAccountIconUserBoundary(icon, userHandle));
1050                         }
1051                         // Handle the case where an existing connection was added by Telephony via
1052                         // a connection manager.  The remote connection service API does not include
1053                         // the ability to specify a parent connection when adding an existing
1054                         // connection, so we stash the desired parent in the connection extras.
1055                         if (connectionExtras != null
1056                                 && connectionExtras.containsKey(
1057                                         Connection.EXTRA_ADD_TO_CONFERENCE_ID)
1058                                 && connection.getParentCallId() == null) {
1059                             String parentId = connectionExtras.getString(
1060                                     Connection.EXTRA_ADD_TO_CONFERENCE_ID);
1061                             Log.i(ConnectionServiceWrapper.this, "addExistingConnection: remote "
1062                                     + "connection will auto-add to parent %s", parentId);
1063                             // Replace parcelable connection instance, swapping the new desired
1064                             // parent in.
1065                             connection = new ParcelableConnection(
1066                                     connection.getPhoneAccount(),
1067                                     connection.getState(),
1068                                     connection.getConnectionCapabilities(),
1069                                     connection.getConnectionProperties(),
1070                                     connection.getSupportedAudioRoutes(),
1071                                     connection.getHandle(),
1072                                     connection.getHandlePresentation(),
1073                                     connection.getCallerDisplayName(),
1074                                     connection.getCallerDisplayNamePresentation(),
1075                                     connection.getVideoProvider(),
1076                                     connection.getVideoState(),
1077                                     connection.isRingbackRequested(),
1078                                     connection.getIsVoipAudioMode(),
1079                                     connection.getConnectTimeMillis(),
1080                                     connection.getConnectElapsedTimeMillis(),
1081                                     connection.getStatusHints(),
1082                                     connection.getDisconnectCause(),
1083                                     connection.getConferenceableConnectionIds(),
1084                                     connection.getExtras(),
1085                                     parentId,
1086                                     connection.getCallDirection(),
1087                                     connection.getCallerNumberVerificationStatus());
1088                         }
1089                         // Check to see if this Connection has already been added.
1090                         Call alreadyAddedConnection = mCallsManager
1091                                 .getAlreadyAddedConnection(connectIdToCheck);
1092 
1093                         if (alreadyAddedConnection != null
1094                                 && mCallIdMapper.getCall(callId) == null) {
1095                             if (!Objects.equals(connection.getHandle(),
1096                                     alreadyAddedConnection.getHandle())) {
1097                                 alreadyAddedConnection.setHandle(connection.getHandle());
1098                             }
1099                             if (connection.getHandlePresentation() !=
1100                                     alreadyAddedConnection.getHandlePresentation()) {
1101                                 alreadyAddedConnection.setHandle(connection.getHandle(),
1102                                         connection.getHandlePresentation());
1103                             }
1104                             if (!Objects.equals(connection.getCallerDisplayName(),
1105                                     alreadyAddedConnection.getCallerDisplayName())) {
1106                                 alreadyAddedConnection.setCallerDisplayName(connection
1107                                                 .getCallerDisplayName(),
1108                                         connection.getCallerDisplayNamePresentation());
1109                             }
1110                             if (connection.getConnectionCapabilities() !=
1111                                     alreadyAddedConnection.getConnectionCapabilities()) {
1112                                 alreadyAddedConnection.setConnectionCapabilities(connection
1113                                         .getConnectionCapabilities());
1114                             }
1115                             if (connection.getConnectionProperties() !=
1116                                     alreadyAddedConnection.getConnectionProperties()) {
1117                                 alreadyAddedConnection.setConnectionCapabilities(connection
1118                                         .getConnectionProperties());
1119                             }
1120                             mCallIdMapper.addCall(alreadyAddedConnection, callId);
1121                             alreadyAddedConnection
1122                                     .replaceConnectionService(ConnectionServiceWrapper.this);
1123                             return;
1124                         }
1125 
1126                         Call existingCall = mCallsManager
1127                                 .createCallForExistingConnection(callId, connection);
1128                         mCallIdMapper.addCall(existingCall, callId);
1129                         existingCall.setConnectionService(ConnectionServiceWrapper.this);
1130                     } else {
1131                         Log.e(this, new RemoteException("The PhoneAccount being used is not " +
1132                                 "currently registered with Telecom."), "Unable to " +
1133                                 "addExistingConnection.");
1134                     }
1135                 }
1136             } catch (Throwable t) {
1137                 Log.e(ConnectionServiceWrapper.this, t, "");
1138                 throw t;
1139             } finally {
1140                 Binder.restoreCallingIdentity(token);
1141                 Log.endSession();
1142             }
1143         }
1144 
1145         @Override
onConnectionEvent(String callId, String event, Bundle extras, Session.Info sessionInfo)1146         public void onConnectionEvent(String callId, String event, Bundle extras,
1147                 Session.Info sessionInfo) {
1148             Log.startSession(sessionInfo, "CSW.oCE", mPackageAbbreviation);
1149             long token = Binder.clearCallingIdentity();
1150             try {
1151                 synchronized (mLock) {
1152                     Bundle.setDefusable(extras, true);
1153                     Call call = mCallIdMapper.getCall(callId);
1154                     if (call != null) {
1155                         call.onConnectionEvent(event, extras);
1156                     }
1157                 }
1158             } catch (Throwable t) {
1159                 Log.e(ConnectionServiceWrapper.this, t, "");
1160                 throw t;
1161             } finally {
1162                 Binder.restoreCallingIdentity(token);
1163                 Log.endSession();
1164             }
1165         }
1166 
1167         @Override
onRttInitiationSuccess(String callId, Session.Info sessionInfo)1168         public void onRttInitiationSuccess(String callId, Session.Info sessionInfo)
1169                 throws RemoteException {
1170 
1171         }
1172 
1173         @Override
onRttInitiationFailure(String callId, int reason, Session.Info sessionInfo)1174         public void onRttInitiationFailure(String callId, int reason, Session.Info sessionInfo)
1175                 throws RemoteException {
1176             Log.startSession(sessionInfo, "CSW.oRIF", mPackageAbbreviation);
1177             long token = Binder.clearCallingIdentity();
1178             try {
1179                 synchronized (mLock) {
1180                     Call call = mCallIdMapper.getCall(callId);
1181                     if (call != null) {
1182                         call.onRttConnectionFailure(reason);
1183                     }
1184                 }
1185             } catch (Throwable t) {
1186                 Log.e(ConnectionServiceWrapper.this, t, "");
1187                 throw t;
1188             } finally {
1189                 Binder.restoreCallingIdentity(token);
1190                 Log.endSession();
1191             }
1192         }
1193 
1194         @Override
onRttSessionRemotelyTerminated(String callId, Session.Info sessionInfo)1195         public void onRttSessionRemotelyTerminated(String callId, Session.Info sessionInfo)
1196                 throws RemoteException {
1197 
1198         }
1199 
1200         @Override
onRemoteRttRequest(String callId, Session.Info sessionInfo)1201         public void onRemoteRttRequest(String callId, Session.Info sessionInfo)
1202                 throws RemoteException {
1203             Log.startSession(sessionInfo, "CSW.oRRR", mPackageAbbreviation);
1204             long token = Binder.clearCallingIdentity();
1205             try {
1206                 synchronized (mLock) {
1207                     Call call = mCallIdMapper.getCall(callId);
1208                     if (call != null) {
1209                         call.onRemoteRttRequest();
1210                     }
1211                 }
1212             } catch (Throwable t) {
1213                 Log.e(ConnectionServiceWrapper.this, t, "");
1214                 throw t;
1215             } finally {
1216                 Binder.restoreCallingIdentity(token);
1217                 Log.endSession();
1218             }
1219         }
1220 
1221         @Override
onPhoneAccountChanged(String callId, PhoneAccountHandle pHandle, Session.Info sessionInfo)1222         public void onPhoneAccountChanged(String callId, PhoneAccountHandle pHandle,
1223                 Session.Info sessionInfo) throws RemoteException {
1224             // Check that the Calling Package matches PhoneAccountHandle's Component Package
1225             if (pHandle != null) {
1226                 mAppOpsManager.checkPackage(Binder.getCallingUid(),
1227                         pHandle.getComponentName().getPackageName());
1228             }
1229             Log.startSession(sessionInfo, "CSW.oPAC", mPackageAbbreviation);
1230             long token = Binder.clearCallingIdentity();
1231             try {
1232                 synchronized (mLock) {
1233                     Call call = mCallIdMapper.getCall(callId);
1234                     if (call != null) {
1235                         call.setTargetPhoneAccount(pHandle);
1236                     }
1237                 }
1238             } catch (Throwable t) {
1239                 Log.e(ConnectionServiceWrapper.this, t, "");
1240                 throw t;
1241             } finally {
1242                 Binder.restoreCallingIdentity(token);
1243                 Log.endSession();
1244             }
1245         }
1246 
1247         @Override
onConnectionServiceFocusReleased(Session.Info sessionInfo)1248         public void onConnectionServiceFocusReleased(Session.Info sessionInfo)
1249                 throws RemoteException {
1250             Log.startSession(sessionInfo, "CSW.oCSFR", mPackageAbbreviation);
1251             long token = Binder.clearCallingIdentity();
1252             try {
1253                 synchronized (mLock) {
1254                     mConnSvrFocusListener.onConnectionServiceReleased(
1255                             ConnectionServiceWrapper.this);
1256                 }
1257             } catch (Throwable t) {
1258                 Log.e(ConnectionServiceWrapper.this, t, "");
1259                 throw t;
1260             } finally {
1261                 Binder.restoreCallingIdentity(token);
1262                 Log.endSession();
1263             }
1264         }
1265 
1266         @Override
setConferenceState(String callId, boolean isConference, Session.Info sessionInfo)1267         public void setConferenceState(String callId, boolean isConference,
1268                 Session.Info sessionInfo) throws RemoteException {
1269             Log.startSession(sessionInfo, "CSW.sCS", mPackageAbbreviation);
1270 
1271             if (mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
1272                     != PackageManager.PERMISSION_GRANTED) {
1273                 Log.w(this, "setConferenceState from caller without permission.");
1274                 Log.endSession();
1275                 return;
1276             }
1277 
1278             long token = Binder.clearCallingIdentity();
1279             try {
1280                 synchronized (mLock) {
1281                     Call call = mCallIdMapper.getCall(callId);
1282                     if (call != null) {
1283                         call.setConferenceState(isConference);
1284                     }
1285                 }
1286             } catch (Throwable t) {
1287                 Log.e(ConnectionServiceWrapper.this, t, "");
1288                 throw t;
1289             } finally {
1290                 Binder.restoreCallingIdentity(token);
1291                 Log.endSession();
1292             }
1293         }
1294 
1295         @Override
setCallDirection(String callId, int direction, Session.Info sessionInfo)1296         public void setCallDirection(String callId, int direction, Session.Info sessionInfo) {
1297             Log.startSession(sessionInfo, "CSW.sCD", mPackageAbbreviation);
1298 
1299             if (mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
1300                     != PackageManager.PERMISSION_GRANTED) {
1301                 Log.w(this, "setCallDirection from caller without permission.");
1302                 Log.endSession();
1303                 return;
1304             }
1305 
1306             long token = Binder.clearCallingIdentity();
1307             try {
1308                 synchronized (mLock) {
1309                     logIncoming("setCallDirection %s %d", callId, direction);
1310                     Call call = mCallIdMapper.getCall(callId);
1311                     if (call != null) {
1312                         call.setCallDirection(Call.getRemappedCallDirection(direction));
1313                     }
1314                 }
1315             } catch (Throwable t) {
1316                 Log.e(ConnectionServiceWrapper.this, t, "");
1317                 throw t;
1318             } finally {
1319                 Binder.restoreCallingIdentity(token);
1320                 Log.endSession();
1321             }
1322         }
1323 
1324         @Override
queryLocation(String callId, long timeoutMillis, String provider, ResultReceiver callback, Session.Info sessionInfo)1325         public void queryLocation(String callId, long timeoutMillis, String provider,
1326                 ResultReceiver callback, Session.Info sessionInfo) {
1327             Log.startSession(sessionInfo, "CSW.qL", mPackageAbbreviation);
1328 
1329             TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
1330             if (telecomManager == null || !telecomManager.getSimCallManager().getComponentName()
1331                     .equals(getComponentName())) {
1332                 callback.send(0 /* isSuccess */,
1333                         getQueryLocationErrorResult(QueryLocationException.ERROR_NOT_PERMITTED));
1334                 Log.endSession();
1335                 return;
1336             }
1337 
1338             String opPackageName = mContext.getOpPackageName();
1339             int packageUid = -1;
1340             try {
1341                 packageUid = mContext.getPackageManager().getPackageUid(opPackageName,
1342                         PackageManager.PackageInfoFlags.of(0));
1343             } catch (PackageManager.NameNotFoundException e) {
1344                 // packageUid is -1
1345             }
1346 
1347             try {
1348                 mAppOpsManager.noteProxyOp(
1349                         AppOpsManager.OPSTR_FINE_LOCATION,
1350                         opPackageName,
1351                         packageUid,
1352                         null,
1353                         null);
1354             } catch (SecurityException e) {
1355                 Log.e(ConnectionServiceWrapper.this, e, "");
1356             }
1357 
1358             if (!callingUidMatchesPackageManagerRecords(getComponentName().getPackageName())) {
1359                 throw new SecurityException(String.format("queryCurrentLocation: "
1360                                 + "uid mismatch found : callingPackageName=[%s], callingUid=[%d]",
1361                         getComponentName().getPackageName(), Binder.getCallingUid()));
1362             }
1363 
1364             Call call = mCallIdMapper.getCall(callId);
1365             if (call == null || !call.isEmergencyCall()) {
1366                 callback.send(0 /* isSuccess */,
1367                         getQueryLocationErrorResult(QueryLocationException
1368                                 .ERROR_NOT_ALLOWED_FOR_NON_EMERGENCY_CONNECTIONS));
1369                 Log.endSession();
1370                 return;
1371             }
1372 
1373             long token = Binder.clearCallingIdentity();
1374             try {
1375                 synchronized (mLock) {
1376                     logIncoming("queryLocation %s %d", callId, timeoutMillis);
1377                     ConnectionServiceWrapper.this.queryCurrentLocation(timeoutMillis, provider,
1378                             callback);
1379                 }
1380             } catch (Throwable t) {
1381                 Log.e(ConnectionServiceWrapper.this, t, "");
1382                 throw t;
1383             } finally {
1384                 Binder.restoreCallingIdentity(token);
1385                 Log.endSession();
1386             }
1387         }
1388     }
1389 
1390     private final Adapter mAdapter = new Adapter();
1391     private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getConnectionId);
1392     private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
1393 
1394     private Binder2 mBinder = new Binder2();
1395     private IConnectionService mServiceInterface;
1396     private final ConnectionServiceRepository mConnectionServiceRepository;
1397     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
1398     private final CallsManager mCallsManager;
1399     private final AppOpsManager mAppOpsManager;
1400     private final Context mContext;
1401 
1402     private ConnectionServiceFocusManager.ConnectionServiceFocusListener mConnSvrFocusListener;
1403 
1404     /**
1405      * Creates a connection service.
1406      *
1407      * @param componentName The component name of the service with which to bind.
1408      * @param connectionServiceRepository Connection service repository.
1409      * @param phoneAccountRegistrar Phone account registrar
1410      * @param callsManager Calls manager
1411      * @param context The context.
1412      * @param userHandle The {@link UserHandle} to use when binding.
1413      */
1414     @VisibleForTesting
ConnectionServiceWrapper( ComponentName componentName, ConnectionServiceRepository connectionServiceRepository, PhoneAccountRegistrar phoneAccountRegistrar, CallsManager callsManager, Context context, TelecomSystem.SyncRoot lock, UserHandle userHandle, FeatureFlags featureFlags)1415     public ConnectionServiceWrapper(
1416             ComponentName componentName,
1417             ConnectionServiceRepository connectionServiceRepository,
1418             PhoneAccountRegistrar phoneAccountRegistrar,
1419             CallsManager callsManager,
1420             Context context,
1421             TelecomSystem.SyncRoot lock,
1422             UserHandle userHandle,
1423             FeatureFlags featureFlags) {
1424         super(ConnectionService.SERVICE_INTERFACE, componentName, context, lock, userHandle,
1425                 featureFlags);
1426         mConnectionServiceRepository = connectionServiceRepository;
1427         phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {
1428             // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections
1429             // To do this, we must proxy remote ConnectionService objects
1430         });
1431         mPhoneAccountRegistrar = phoneAccountRegistrar;
1432         mCallsManager = callsManager;
1433         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
1434         mContext = context;
1435     }
1436 
1437     /** See {@link IConnectionService#addConnectionServiceAdapter}. */
addConnectionServiceAdapter(IConnectionServiceAdapter adapter)1438     private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
1439         if (isServiceValid("addConnectionServiceAdapter")) {
1440             try {
1441                 logOutgoing("addConnectionServiceAdapter %s", adapter);
1442                 mServiceInterface.addConnectionServiceAdapter(adapter, Log.getExternalSession());
1443             } catch (RemoteException e) {
1444             }
1445         }
1446     }
1447 
1448     /** See {@link IConnectionService#removeConnectionServiceAdapter}. */
removeConnectionServiceAdapter(IConnectionServiceAdapter adapter)1449     private void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
1450         if (isServiceValid("removeConnectionServiceAdapter")) {
1451             try {
1452                 logOutgoing("removeConnectionServiceAdapter %s", adapter);
1453                 mServiceInterface.removeConnectionServiceAdapter(adapter, Log.getExternalSession());
1454             } catch (RemoteException e) {
1455             }
1456         }
1457     }
1458 
1459     @VisibleForTesting
getLastKnownCellIdentity()1460     public CellIdentity getLastKnownCellIdentity() {
1461         TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
1462         if (telephonyManager != null) {
1463             try {
1464                 CellIdentity lastKnownCellIdentity = telephonyManager.getLastKnownCellIdentity();
1465                 mAppOpsManager.noteOp(AppOpsManager.OP_FINE_LOCATION,
1466                         mContext.getPackageManager().getPackageUid(
1467                                 getComponentName().getPackageName(), 0),
1468                         getComponentName().getPackageName());
1469                 return lastKnownCellIdentity;
1470             } catch (UnsupportedOperationException ignored) {
1471                 Log.w(this, "getLastKnownCellIdentity - no telephony on this device");
1472             } catch (PackageManager.NameNotFoundException nameNotFoundException) {
1473                 Log.e(this, nameNotFoundException, "could not find the package -- %s",
1474                         getComponentName().getPackageName());
1475             }
1476         }
1477         return null;
1478     }
1479 
1480     @VisibleForTesting
1481     @SuppressWarnings("FutureReturnValueIgnored")
queryCurrentLocation(long timeoutMillis, String provider, ResultReceiver callback)1482     public void queryCurrentLocation(long timeoutMillis, String provider, ResultReceiver callback) {
1483 
1484         if (mQueryLocationFuture != null && !mQueryLocationFuture.isDone()) {
1485             callback.send(0 /* isSuccess */,
1486                     getQueryLocationErrorResult(
1487                             QueryLocationException.ERROR_PREVIOUS_REQUEST_EXISTS));
1488             return;
1489         }
1490 
1491         LocationManager locationManager = (LocationManager) mContext.createAttributionContext(
1492                 ConnectionServiceWrapper.class.getSimpleName()).getSystemService(
1493                 Context.LOCATION_SERVICE);
1494 
1495         if (locationManager == null) {
1496             callback.send(0 /* isSuccess */,
1497                     getQueryLocationErrorResult(QueryLocationException.ERROR_SERVICE_UNAVAILABLE));
1498         }
1499 
1500         mQueryLocationFuture = new CompletableFuture<Pair<Integer, Location>>()
1501                 .completeOnTimeout(
1502                         Pair.create(QueryLocationException.ERROR_REQUEST_TIME_OUT, null),
1503                         timeoutMillis, TimeUnit.MILLISECONDS);
1504 
1505         mOngoingQueryLocationRequest = new CancellationSignal();
1506         locationManager.getCurrentLocation(
1507                 provider,
1508                 new LocationRequest.Builder(0)
1509                         .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
1510                         .setLocationSettingsIgnored(true)
1511                         .build(),
1512                 mOngoingQueryLocationRequest,
1513                 mQueryLocationExecutor,
1514                 (location) -> mQueryLocationFuture.complete(Pair.create(null, location)));
1515 
1516         mQueryLocationFuture.whenComplete((result, e) -> {
1517             if (e != null) {
1518                 callback.send(0,
1519                         getQueryLocationErrorResult(QueryLocationException.ERROR_UNSPECIFIED));
1520             }
1521             //make sure we don't pass mock locations diretly, always reset() mock locations
1522             if (result.second != null) {
1523                 if(result.second.isMock()) {
1524                     result.second.reset();
1525                 }
1526                 callback.send(1, getQueryLocationResult(result.second));
1527             } else {
1528                 callback.send(0, getQueryLocationErrorResult(result.first));
1529             }
1530 
1531             if (mOngoingQueryLocationRequest != null) {
1532                 mOngoingQueryLocationRequest.cancel();
1533                 mOngoingQueryLocationRequest = null;
1534             }
1535 
1536             if (mQueryLocationFuture != null) {
1537                 mQueryLocationFuture = null;
1538             }
1539         });
1540     }
1541 
getQueryLocationResult(Location location)1542     private Bundle getQueryLocationResult(Location location) {
1543         Bundle extras = new Bundle();
1544         extras.putParcelable(Connection.EXTRA_KEY_QUERY_LOCATION, location);
1545         return extras;
1546     }
1547 
getQueryLocationErrorResult(int result)1548     private Bundle getQueryLocationErrorResult(int result) {
1549         String message;
1550 
1551         switch (result) {
1552             case QueryLocationException.ERROR_REQUEST_TIME_OUT:
1553                 message = "The operation was not completed on time";
1554                 break;
1555             case QueryLocationException.ERROR_PREVIOUS_REQUEST_EXISTS:
1556                 message = "The operation was rejected due to a previous request exists";
1557                 break;
1558             case QueryLocationException.ERROR_NOT_PERMITTED:
1559                 message = "The operation is not permitted";
1560                 break;
1561             case QueryLocationException.ERROR_NOT_ALLOWED_FOR_NON_EMERGENCY_CONNECTIONS:
1562                 message = "Non-emergency call connection are not allowed";
1563                 break;
1564             case QueryLocationException.ERROR_SERVICE_UNAVAILABLE:
1565                 message = "The operation has failed due to service is not available";
1566                 break;
1567             default:
1568                 message = "The operation has failed due to an unknown or unspecified error";
1569         }
1570 
1571         QueryLocationException exception = new QueryLocationException(message, result);
1572         Bundle extras = new Bundle();
1573         extras.putParcelable(QueryLocationException.QUERY_LOCATION_ERROR, exception);
1574         return extras;
1575     }
1576 
1577     /**
1578      * helper method that compares the binder_uid to what the packageManager_uid reports for the
1579      * passed in packageName.
1580      *
1581      * returns true if the binder_uid matches the packageManager_uid records
1582      */
callingUidMatchesPackageManagerRecords(String packageName)1583     private boolean callingUidMatchesPackageManagerRecords(String packageName) {
1584         int packageUid = -1;
1585         int callingUid = Binder.getCallingUid();
1586 
1587         PackageManager pm;
1588         try{
1589             pm = mContext.createContextAsUser(
1590                     UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
1591         }
1592         catch (Exception e){
1593             Log.i(this, "callingUidMatchesPackageManagerRecords:"
1594                     + " createContextAsUser hit exception=[%s]", e.toString());
1595             return false;
1596         }
1597 
1598         if (pm != null) {
1599             try {
1600                 packageUid = pm.getPackageUid(packageName, PackageManager.PackageInfoFlags.of(0));
1601             } catch (PackageManager.NameNotFoundException e) {
1602                 // packageUid is -1.
1603             }
1604         }
1605 
1606         if (packageUid != callingUid) {
1607             Log.i(this, "callingUidMatchesPackageManagerRecords: uid mismatch found for "
1608                     + "packageName=[%s]. packageManager reports packageUid=[%d] but "
1609                     + "binder reports callingUid=[%d]", packageName, packageUid, callingUid);
1610         }
1611 
1612         return packageUid == callingUid;
1613     }
1614 
1615     /**
1616      * Creates a conference for a new outgoing call or attach to an existing incoming call.
1617      */
createConference(final Call call, final CreateConnectionResponse response)1618     public void createConference(final Call call, final CreateConnectionResponse response) {
1619         Log.d(this, "createConference(%s) via %s.", call, getComponentName());
1620         BindCallback callback = new BindCallback() {
1621             @Override
1622             public void onSuccess() {
1623                 String callId = mCallIdMapper.getCallId(call);
1624                 mPendingResponses.put(callId, response);
1625 
1626                 Bundle extras = call.getIntentExtras();
1627                 if (extras == null) {
1628                     extras = new Bundle();
1629                 }
1630                 extras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
1631 
1632                 Log.addEvent(call, LogUtils.Events.START_CONFERENCE,
1633                         Log.piiHandle(call.getHandle()));
1634 
1635                 ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
1636                         .setAccountHandle(call.getTargetPhoneAccount())
1637                         .setAddress(call.getHandle())
1638                         .setExtras(extras)
1639                         .setVideoState(call.getVideoState())
1640                         .setTelecomCallId(callId)
1641                         // For self-managed incoming calls, if there is another ongoing call Telecom
1642                         // is responsible for showing a UI to ask the user if they'd like to answer
1643                         // this new incoming call.
1644                         .setShouldShowIncomingCallUi(
1645                                 !mCallsManager.shouldShowSystemIncomingCallUi(call))
1646                         .setRttPipeFromInCall(call.getInCallToCsRttPipeForCs())
1647                         .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
1648                         .setParticipants(call.getParticipants())
1649                         .setIsAdhocConferenceCall(call.isAdhocConferenceCall())
1650                         .build();
1651                 Runnable r = new Runnable("CSW.cC", mLock) {
1652                     @Override
1653                     public void loggedRun() {
1654                         if (!call.isCreateConnectionComplete()) {
1655                             Log.e(this, new Exception(),
1656                                     "Conference %s creation timeout",
1657                                     getComponentName());
1658                             Log.addEvent(call, LogUtils.Events.CREATE_CONFERENCE_TIMEOUT,
1659                                     Log.piiHandle(call.getHandle()) + " via:" +
1660                                             getComponentName().getPackageName());
1661                             mAnomalyReporter.reportAnomaly(
1662                                     CREATE_CONFERENCE_TIMEOUT_ERROR_UUID,
1663                                     CREATE_CONFERENCE_TIMEOUT_ERROR_MSG);
1664                             response.handleCreateConferenceFailure(
1665                                     new DisconnectCause(DisconnectCause.ERROR));
1666                         }
1667                     }
1668                 };
1669                 if (mScheduledExecutor != null && !mScheduledExecutor.isShutdown()) {
1670                     try {
1671                         // Post cleanup to the executor service and cache the future,
1672                         // so we can cancel it if needed.
1673                         ScheduledFuture<?> future = mScheduledExecutor.schedule(
1674                                 r.getRunnableToCancel(),SERVICE_BINDING_TIMEOUT,
1675                                 TimeUnit.MILLISECONDS);
1676                         mScheduledFutureMap.put(call, future);
1677                     } catch (RejectedExecutionException e) {
1678                         Log.e(this, e, "createConference: mScheduledExecutor was "
1679                                 + "already shutdown");
1680                         mAnomalyReporter.reportAnomaly(
1681                                 EXECUTOR_REJECTED_EXECUTION_ERROR_UUID,
1682                                 EXECUTOR_REJECTED_EXECUTION_ERROR_MSG);
1683                     }
1684                 } else {
1685                     Log.w(this, "createConference: Scheduled executor is null or shutdown");
1686                     mAnomalyReporter.reportAnomaly(
1687                         NULL_SCHEDULED_EXECUTOR_ERROR_UUID,
1688                         NULL_SCHEDULED_EXECUTOR_ERROR_MSG);
1689                 }
1690                 try {
1691                     mServiceInterface.createConference(
1692                             call.getConnectionManagerPhoneAccount(),
1693                             callId,
1694                             connectionRequest,
1695                             call.shouldAttachToExistingConnection(),
1696                             call.isUnknown(),
1697                             Log.getExternalSession(TELECOM_ABBREVIATION));
1698                 } catch (RemoteException e) {
1699                     Log.e(this, e, "Failure to createConference -- %s", getComponentName());
1700                     if (mFlags.dontTimeoutDestroyedCalls()) {
1701                         maybeRemoveCleanupFuture(call);
1702                     }
1703                     mPendingResponses.remove(callId).handleCreateConferenceFailure(
1704                             new DisconnectCause(DisconnectCause.ERROR, e.toString()));
1705                 }
1706             }
1707 
1708             @Override
1709             public void onFailure() {
1710                 Log.e(this, new Exception(), "Failure to conference %s", getComponentName());
1711                 response.handleCreateConferenceFailure(new DisconnectCause(DisconnectCause.ERROR));
1712             }
1713         };
1714 
1715         mBinder.bind(callback, call);
1716 
1717     }
1718 
1719     /**
1720      * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
1721      */
1722     @VisibleForTesting
createConnection(final Call call, final CreateConnectionResponse response)1723     public void createConnection(final Call call, final CreateConnectionResponse response) {
1724         Log.i(this, "createConnection(%s) via %s.", call, getComponentName());
1725         BindCallback callback = new BindCallback() {
1726             @Override
1727             public void onSuccess() {
1728                 String callId = mCallIdMapper.getCallId(call);
1729                 if (callId == null) {
1730                     Log.i(ConnectionServiceWrapper.this, "Call not present"
1731                             + " in call id mapper, maybe it was aborted before the bind"
1732                             + " completed successfully?");
1733                     if (mFlags.dontTimeoutDestroyedCalls()) {
1734                         maybeRemoveCleanupFuture(call);
1735                     }
1736                     response.handleCreateConnectionFailure(
1737                             new DisconnectCause(DisconnectCause.CANCELED));
1738                     return;
1739                 }
1740                 mPendingResponses.put(callId, response);
1741 
1742                 GatewayInfo gatewayInfo = call.getGatewayInfo();
1743                 Bundle extras = call.getIntentExtras();
1744                 if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
1745                         gatewayInfo.getOriginalAddress() != null) {
1746                     extras = (Bundle) extras.clone();
1747                     extras.putString(
1748                             TelecomManager.GATEWAY_PROVIDER_PACKAGE,
1749                             gatewayInfo.getGatewayProviderPackageName());
1750                     extras.putParcelable(
1751                             TelecomManager.GATEWAY_ORIGINAL_ADDRESS,
1752                             gatewayInfo.getOriginalAddress());
1753                 }
1754 
1755                 if (call.isIncoming() && mCallsManager.getEmergencyCallHelper()
1756                         .getLastEmergencyCallTimeMillis() > 0) {
1757                   // Add the last emergency call time to the connection request for incoming calls
1758                   if (extras == call.getIntentExtras()) {
1759                     extras = (Bundle) extras.clone();
1760                   }
1761                   extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS,
1762                       mCallsManager.getEmergencyCallHelper().getLastEmergencyCallTimeMillis());
1763                 }
1764 
1765                 // Call is incoming and added because we're handing over from another; tell CS
1766                 // that its expected to handover.
1767                 if (call.isIncoming() && call.getHandoverSourceCall() != null) {
1768                     extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true);
1769                     extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
1770                             call.getHandoverSourceCall().getTargetPhoneAccount());
1771                 }
1772 
1773                 Log.addEvent(call, LogUtils.Events.START_CONNECTION,
1774                         Log.piiHandle(call.getHandle()) + " via:" +
1775                                 getComponentName().getPackageName());
1776 
1777                 if (call.isEmergencyCall()) {
1778                     extras.putParcelable(Connection.EXTRA_LAST_KNOWN_CELL_IDENTITY,
1779                             getLastKnownCellIdentity());
1780                 }
1781 
1782                 ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
1783                         .setAccountHandle(call.getTargetPhoneAccount())
1784                         .setAddress(call.getHandle())
1785                         .setExtras(extras)
1786                         .setVideoState(call.getVideoState())
1787                         .setTelecomCallId(callId)
1788                         // For self-managed incoming calls, if there is another ongoing call Telecom
1789                         // is responsible for showing a UI to ask the user if they'd like to answer
1790                         // this new incoming call.
1791                         .setShouldShowIncomingCallUi(
1792                                 !mCallsManager.shouldShowSystemIncomingCallUi(call))
1793                         .setRttPipeFromInCall(call.getInCallToCsRttPipeForCs())
1794                         .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
1795                         .build();
1796                 Runnable r = new Runnable("CSW.cC", mLock) {
1797                     @Override
1798                     public void loggedRun() {
1799                         if (!call.isCreateConnectionComplete()) {
1800                             Log.e(this, new Exception(),
1801                                     "Connection %s creation timeout",
1802                                     getComponentName());
1803                             Log.addEvent(call, LogUtils.Events.CREATE_CONNECTION_TIMEOUT,
1804                                     Log.piiHandle(call.getHandle()) + " via:" +
1805                                             getComponentName().getPackageName());
1806                             mAnomalyReporter.reportAnomaly(
1807                                     CREATE_CONNECTION_TIMEOUT_ERROR_UUID,
1808                                     CREATE_CONNECTION_TIMEOUT_ERROR_MSG);
1809                             response.handleCreateConnectionFailure(
1810                                     new DisconnectCause(DisconnectCause.ERROR));
1811                         }
1812                     }
1813                 };
1814                 if (mScheduledExecutor != null && !mScheduledExecutor.isShutdown()) {
1815                     try {
1816                         // Post cleanup to the executor service and cache the future,
1817                         // so we can cancel it if needed.
1818                         ScheduledFuture<?> future = mScheduledExecutor.schedule(
1819                                 r.getRunnableToCancel(),SERVICE_BINDING_TIMEOUT,
1820                                 TimeUnit.MILLISECONDS);
1821                         mScheduledFutureMap.put(call, future);
1822                     } catch (RejectedExecutionException e) {
1823                         Log.e(this, e, "createConnection: mScheduledExecutor was "
1824                                 + "already shutdown");
1825                         mAnomalyReporter.reportAnomaly(
1826                                 EXECUTOR_REJECTED_EXECUTION_ERROR_UUID,
1827                                 EXECUTOR_REJECTED_EXECUTION_ERROR_MSG);
1828                     }
1829                 } else {
1830                     Log.w(this, "createConnection: Scheduled executor is null or shutdown");
1831                     mAnomalyReporter.reportAnomaly(
1832                         NULL_SCHEDULED_EXECUTOR_ERROR_UUID,
1833                         NULL_SCHEDULED_EXECUTOR_ERROR_MSG);
1834                 }
1835                 try {
1836                     if (mFlags.cswServiceInterfaceIsNull() && mServiceInterface == null) {
1837                         if (mFlags.dontTimeoutDestroyedCalls()) {
1838                             maybeRemoveCleanupFuture(call);
1839                         }
1840                         mPendingResponses.remove(callId).handleCreateConnectionFailure(
1841                                 new DisconnectCause(DisconnectCause.ERROR,
1842                                         "CSW#oCC ServiceInterface is null"));
1843                     } else {
1844                         mServiceInterface.createConnection(
1845                                 call.getConnectionManagerPhoneAccount(),
1846                                 callId,
1847                                 connectionRequest,
1848                                 call.shouldAttachToExistingConnection(),
1849                                 call.isUnknown(),
1850                                 Log.getExternalSession(TELECOM_ABBREVIATION));
1851                     }
1852                 } catch (RemoteException e) {
1853                     Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
1854                     if (mFlags.dontTimeoutDestroyedCalls()) {
1855                         maybeRemoveCleanupFuture(call);
1856                     }
1857                     mPendingResponses.remove(callId).handleCreateConnectionFailure(
1858                             new DisconnectCause(DisconnectCause.ERROR, e.toString()));
1859                 }
1860             }
1861 
1862             @Override
1863             public void onFailure() {
1864                 Log.e(this, new Exception(), "Failure to call %s", getComponentName());
1865                 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
1866             }
1867         };
1868 
1869         mBinder.bind(callback, call);
1870     }
1871 
1872     /**
1873      * Notifies the {@link ConnectionService} associated with a {@link Call} that the request to
1874      * create a connection has been denied or failed.
1875      * @param call The call.
1876      */
1877     @VisibleForTesting
createConnectionFailed(final Call call)1878     public void createConnectionFailed(final Call call) {
1879         Log.d(this, "createConnectionFailed(%s) via %s.", call, getComponentName());
1880         BindCallback callback = new BindCallback() {
1881             @Override
1882             public void onSuccess() {
1883                 final String callId = mCallIdMapper.getCallId(call);
1884                 // If still bound, tell the connection service create connection has failed.
1885                 if (callId != null && isServiceValid("createConnectionFailed")) {
1886                     Log.addEvent(call, LogUtils.Events.CREATE_CONNECTION_FAILED,
1887                             Log.piiHandle(call.getHandle()));
1888                     try {
1889                         logOutgoing("createConnectionFailed %s", callId);
1890                         mServiceInterface.createConnectionFailed(
1891                                 call.getConnectionManagerPhoneAccount(),
1892                                 callId,
1893                                 new ConnectionRequest(
1894                                         call.getTargetPhoneAccount(),
1895                                         call.getHandle(),
1896                                         call.getIntentExtras(),
1897                                         call.getVideoState(),
1898                                         callId,
1899                                         false),
1900                                 call.isIncoming(),
1901                                 Log.getExternalSession(TELECOM_ABBREVIATION));
1902                         call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
1903                         call.disconnect();
1904                     } catch (RemoteException e) {
1905                     }
1906                 }
1907             }
1908 
1909             @Override
1910             public void onFailure() {
1911                 // Binding failed.  Oh no.
1912                 Log.w(this, "onFailure - could not bind to CS for call %s", call.getId());
1913             }
1914         };
1915 
1916         mBinder.bind(callback, call);
1917     }
1918 
1919     /**
1920      * Notifies the {@link ConnectionService} associated with a {@link Call} that the request to
1921      * create a conference has been denied or failed.
1922      * @param call The call.
1923      */
createConferenceFailed(final Call call)1924     void createConferenceFailed(final Call call) {
1925         Log.d(this, "createConferenceFailed(%s) via %s.", call, getComponentName());
1926         BindCallback callback = new BindCallback() {
1927             @Override
1928             public void onSuccess() {
1929                 final String callId = mCallIdMapper.getCallId(call);
1930                 // If still bound, tell the connection service create connection has failed.
1931                 if (callId != null && isServiceValid("createConferenceFailed")) {
1932                     Log.addEvent(call, LogUtils.Events.CREATE_CONFERENCE_FAILED,
1933                             Log.piiHandle(call.getHandle()));
1934                     try {
1935                         logOutgoing("createConferenceFailed %s", callId);
1936                         mServiceInterface.createConferenceFailed(
1937                                 call.getConnectionManagerPhoneAccount(),
1938                                 callId,
1939                                 new ConnectionRequest(
1940                                         call.getTargetPhoneAccount(),
1941                                         call.getHandle(),
1942                                         call.getIntentExtras(),
1943                                         call.getVideoState(),
1944                                         callId,
1945                                         false),
1946                                 call.isIncoming(),
1947                                 Log.getExternalSession(TELECOM_ABBREVIATION));
1948                         call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
1949                         call.disconnect();
1950                     } catch (RemoteException e) {
1951                     }
1952                 }
1953             }
1954 
1955             @Override
1956             public void onFailure() {
1957                 // Binding failed.  Oh no.
1958                 Log.w(this, "onFailure - could not bind to CS for conf call %s", call.getId());
1959             }
1960         };
1961 
1962         mBinder.bind(callback, call);
1963     }
1964 
1965 
handoverFailed(final Call call, final int reason)1966     void handoverFailed(final Call call, final int reason) {
1967         Log.d(this, "handoverFailed(%s) via %s.", call, getComponentName());
1968         BindCallback callback = new BindCallback() {
1969             @Override
1970             public void onSuccess() {
1971                 final String callId = mCallIdMapper.getCallId(call);
1972                 // If still bound, tell the connection service create connection has failed.
1973                 if (callId != null && isServiceValid("handoverFailed")) {
1974                     Log.addEvent(call, LogUtils.Events.HANDOVER_FAILED,
1975                             Log.piiHandle(call.getHandle()));
1976                     try {
1977                         mServiceInterface.handoverFailed(
1978                                 callId,
1979                                 new ConnectionRequest(
1980                                         call.getTargetPhoneAccount(),
1981                                         call.getHandle(),
1982                                         call.getIntentExtras(),
1983                                         call.getVideoState(),
1984                                         callId,
1985                                         false),
1986                                 reason,
1987                                 Log.getExternalSession(TELECOM_ABBREVIATION));
1988                     } catch (RemoteException e) {
1989                     }
1990                 }
1991             }
1992 
1993             @Override
1994             public void onFailure() {
1995                 // Binding failed.
1996                 Log.w(this, "onFailure - could not bind to CS for call %s",
1997                         call.getId());
1998             }
1999         };
2000 
2001         mBinder.bind(callback, call);
2002     }
2003 
handoverComplete(final Call call)2004     void handoverComplete(final Call call) {
2005         Log.d(this, "handoverComplete(%s) via %s.", call, getComponentName());
2006         BindCallback callback = new BindCallback() {
2007             @Override
2008             public void onSuccess() {
2009                 final String callId = mCallIdMapper.getCallId(call);
2010                 // If still bound, tell the connection service create connection has failed.
2011                 if (callId != null && isServiceValid("handoverComplete")) {
2012                     try {
2013                         mServiceInterface.handoverComplete(
2014                                 callId,
2015                                 Log.getExternalSession(TELECOM_ABBREVIATION));
2016                     } catch (RemoteException e) {
2017                     }
2018                 }
2019             }
2020 
2021             @Override
2022             public void onFailure() {
2023                 // Binding failed.
2024                 Log.w(this, "onFailure - could not bind to CS for call %s",
2025                         call.getId());
2026             }
2027         };
2028 
2029         mBinder.bind(callback, call);
2030     }
2031 
2032     /** @see IConnectionService#abort(String, Session.Info)  */
abort(Call call)2033     void abort(Call call) {
2034         // Clear out any pending outgoing call data
2035         final String callId = mCallIdMapper.getCallId(call);
2036 
2037         // If still bound, tell the connection service to abort.
2038         if (callId != null && isServiceValid("abort")) {
2039             try {
2040                 logOutgoing("abort %s", callId);
2041                 mServiceInterface.abort(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
2042             } catch (RemoteException e) {
2043             }
2044         }
2045 
2046         removeCall(call, new DisconnectCause(DisconnectCause.LOCAL));
2047     }
2048 
2049     /** @see IConnectionService#silence(String, Session.Info) */
silence(Call call)2050     void silence(Call call) {
2051         final String callId = mCallIdMapper.getCallId(call);
2052         if (callId != null && isServiceValid("silence")) {
2053             try {
2054                 logOutgoing("silence %s", callId);
2055                 mServiceInterface.silence(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
2056             } catch (RemoteException e) {
2057             }
2058         }
2059     }
2060 
2061     /** @see IConnectionService#hold(String, Session.Info) */
hold(Call call)2062     void hold(Call call) {
2063         final String callId = mCallIdMapper.getCallId(call);
2064         if (callId != null && isServiceValid("hold")) {
2065             try {
2066                 logOutgoing("hold %s", callId);
2067                 mServiceInterface.hold(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
2068             } catch (RemoteException e) {
2069             }
2070         }
2071     }
2072 
2073     /** @see IConnectionService#unhold(String, Session.Info) */
unhold(Call call)2074     void unhold(Call call) {
2075         final String callId = mCallIdMapper.getCallId(call);
2076         if (callId != null && isServiceValid("unhold")) {
2077             try {
2078                 logOutgoing("unhold %s", callId);
2079                 mServiceInterface.unhold(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
2080             } catch (RemoteException e) {
2081             }
2082         }
2083     }
2084 
2085     /** @see IConnectionService#onCallAudioStateChanged(String, CallAudioState, Session.Info) */
2086     @VisibleForTesting
onCallAudioStateChanged(Call activeCall, CallAudioState audioState)2087     public void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) {
2088         final String callId = mCallIdMapper.getCallId(activeCall);
2089         if (callId != null && isServiceValid("onCallAudioStateChanged")) {
2090             try {
2091                 logOutgoing("onCallAudioStateChanged %s %s", callId, audioState);
2092                 mServiceInterface.onCallAudioStateChanged(callId, audioState,
2093                         Log.getExternalSession(TELECOM_ABBREVIATION));
2094             } catch (RemoteException e) {
2095             }
2096         }
2097     }
2098 
2099     /** @see IConnectionService#onCallEndpointChanged(String, CallEndpoint, Session.Info) */
2100     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
2101     @Override
onCallEndpointChanged(Call activeCall, CallEndpoint callEndpoint)2102     public void onCallEndpointChanged(Call activeCall, CallEndpoint callEndpoint) {
2103         final String callId = mCallIdMapper.getCallId(activeCall);
2104         if (callId != null && isServiceValid("onCallEndpointChanged")) {
2105             try {
2106                 logOutgoing("onCallEndpointChanged %s %s", callId, callEndpoint);
2107                 mServiceInterface.onCallEndpointChanged(callId, callEndpoint,
2108                         Log.getExternalSession(TELECOM_ABBREVIATION));
2109             } catch (RemoteException e) {
2110                 Log.d(this, "Remote exception calling onCallEndpointChanged");
2111             }
2112         }
2113     }
2114 
2115     /** @see IConnectionService#onAvailableCallEndpointsChanged(String, List, Session.Info) */
2116     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
2117     @Override
onAvailableCallEndpointsChanged(Call activeCall, Set<CallEndpoint> availableCallEndpoints)2118     public void onAvailableCallEndpointsChanged(Call activeCall,
2119             Set<CallEndpoint> availableCallEndpoints) {
2120         final String callId = mCallIdMapper.getCallId(activeCall);
2121         if (callId != null && isServiceValid("onAvailableCallEndpointsChanged")) {
2122             try {
2123                 logOutgoing("onAvailableCallEndpointsChanged %s", callId);
2124                 List<CallEndpoint> availableEndpoints = new ArrayList<>(availableCallEndpoints);
2125                 mServiceInterface.onAvailableCallEndpointsChanged(callId, availableEndpoints,
2126                         Log.getExternalSession(TELECOM_ABBREVIATION));
2127             } catch (RemoteException e) {
2128                 Log.d(this,
2129                         "Remote exception calling onAvailableCallEndpointsChanged");
2130             }
2131         }
2132     }
2133 
2134     @Override
onVideoStateChanged(Call call, int videoState)2135     public void onVideoStateChanged(Call call, int videoState){
2136         // pass through. ConnectionService does not implement this method.
2137     }
2138 
2139     /** @see IConnectionService#onMuteStateChanged(String, boolean, Session.Info) */
2140     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
2141     @Override
onMuteStateChanged(Call activeCall, boolean isMuted)2142     public void onMuteStateChanged(Call activeCall, boolean isMuted) {
2143         final String callId = mCallIdMapper.getCallId(activeCall);
2144         if (callId != null && isServiceValid("onMuteStateChanged")) {
2145             try {
2146                 logOutgoing("onMuteStateChanged %s %s", callId, isMuted);
2147                 mServiceInterface.onMuteStateChanged(callId, isMuted,
2148                         Log.getExternalSession(TELECOM_ABBREVIATION));
2149             } catch (RemoteException e) {
2150                 Log.d(this, "Remote exception calling onMuteStateChanged");
2151             }
2152         }
2153     }
2154 
2155     /** @see IConnectionService#onUsingAlternativeUi(String, boolean, Session.Info) */
2156     @VisibleForTesting
onUsingAlternativeUi(Call activeCall, boolean isUsingAlternativeUi)2157     public void onUsingAlternativeUi(Call activeCall, boolean isUsingAlternativeUi) {
2158         final String callId = mCallIdMapper.getCallId(activeCall);
2159         if (callId != null && isServiceValid("onUsingAlternativeUi")) {
2160             try {
2161                 logOutgoing("onUsingAlternativeUi %s", isUsingAlternativeUi);
2162                 mServiceInterface.onUsingAlternativeUi(callId, isUsingAlternativeUi,
2163                         Log.getExternalSession(TELECOM_ABBREVIATION));
2164             } catch (RemoteException e) {
2165             }
2166         }
2167     }
2168 
2169     /** @see IConnectionService#onTrackedByNonUiService(String, boolean, Session.Info) */
2170     @VisibleForTesting
onTrackedByNonUiService(Call activeCall, boolean isTracked)2171     public void onTrackedByNonUiService(Call activeCall, boolean isTracked) {
2172         final String callId = mCallIdMapper.getCallId(activeCall);
2173         if (callId != null && isServiceValid("onTrackedByNonUiService")) {
2174             try {
2175                 logOutgoing("onTrackedByNonUiService %s", isTracked);
2176                 mServiceInterface.onTrackedByNonUiService(callId, isTracked,
2177                         Log.getExternalSession(TELECOM_ABBREVIATION));
2178             } catch (RemoteException e) {
2179             }
2180         }
2181     }
2182 
2183     /** @see IConnectionService#disconnect(String, Session.Info) */
2184     @VisibleForTesting
disconnect(Call call)2185     public void disconnect(Call call) {
2186         final String callId = mCallIdMapper.getCallId(call);
2187         if (callId != null && isServiceValid("disconnect")) {
2188             try {
2189                 logOutgoing("disconnect %s", callId);
2190                 mServiceInterface.disconnect(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
2191             } catch (RemoteException e) {
2192             }
2193         }
2194     }
2195 
2196     /** @see IConnectionService#answer(String, Session.Info) */
answer(Call call, int videoState)2197     void answer(Call call, int videoState) {
2198         final String callId = mCallIdMapper.getCallId(call);
2199         if (callId != null && isServiceValid("answer")) {
2200             try {
2201                 logOutgoing("answer %s %d", callId, videoState);
2202                 if (VideoProfile.isAudioOnly(videoState)) {
2203                     mServiceInterface.answer(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
2204                 } else {
2205                     mServiceInterface.answerVideo(callId, videoState,
2206                             Log.getExternalSession(TELECOM_ABBREVIATION));
2207                 }
2208             } catch (RemoteException e) {
2209             }
2210         }
2211     }
2212 
2213     /** @see IConnectionService#deflect(String, Uri , Session.Info) */
deflect(Call call, Uri address)2214     void deflect(Call call, Uri address) {
2215         final String callId = mCallIdMapper.getCallId(call);
2216         if (callId != null && isServiceValid("deflect")) {
2217             try {
2218                 logOutgoing("deflect %s", callId);
2219                 mServiceInterface.deflect(callId, address,
2220                         Log.getExternalSession(TELECOM_ABBREVIATION));
2221             } catch (RemoteException e) {
2222             }
2223         }
2224     }
2225 
2226     /** @see IConnectionService#reject(String, Session.Info) */
reject(Call call, boolean rejectWithMessage, String message)2227     void reject(Call call, boolean rejectWithMessage, String message) {
2228         final String callId = mCallIdMapper.getCallId(call);
2229         if (callId != null && isServiceValid("reject")) {
2230             try {
2231                 logOutgoing("reject %s", callId);
2232 
2233                 if (rejectWithMessage && call.can(
2234                         Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
2235                     mServiceInterface.rejectWithMessage(callId, message,
2236                             Log.getExternalSession(TELECOM_ABBREVIATION));
2237                 } else {
2238                     mServiceInterface.reject(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
2239                 }
2240             } catch (RemoteException e) {
2241             }
2242         }
2243     }
2244 
2245     /** @see IConnectionService#reject(String, Session.Info) */
rejectWithReason(Call call, @android.telecom.Call.RejectReason int rejectReason)2246     void rejectWithReason(Call call, @android.telecom.Call.RejectReason int rejectReason) {
2247         final String callId = mCallIdMapper.getCallId(call);
2248         if (callId != null && isServiceValid("rejectReason")) {
2249             try {
2250                 logOutgoing("rejectReason %s, %d", callId, rejectReason);
2251 
2252                 mServiceInterface.rejectWithReason(callId, rejectReason,
2253                         Log.getExternalSession(TELECOM_ABBREVIATION));
2254             } catch (RemoteException e) {
2255             }
2256         }
2257     }
2258 
2259     /** @see IConnectionService#transfer(String, Uri , boolean, Session.Info) */
transfer(Call call, Uri number, boolean isConfirmationRequired)2260     void transfer(Call call, Uri number, boolean isConfirmationRequired) {
2261         final String callId = mCallIdMapper.getCallId(call);
2262         if (callId != null && isServiceValid("transfer")) {
2263             try {
2264                 logOutgoing("transfer %s", callId);
2265                 mServiceInterface.transfer(callId, number, isConfirmationRequired,
2266                         Log.getExternalSession(TELECOM_ABBREVIATION));
2267             } catch (RemoteException e) {
2268             }
2269         }
2270     }
2271 
2272     /** @see IConnectionService#consultativeTransfer(String, String, Session.Info) */
transfer(Call call, Call otherCall)2273     void transfer(Call call, Call otherCall) {
2274         final String callId = mCallIdMapper.getCallId(call);
2275         final String otherCallId = mCallIdMapper.getCallId(otherCall);
2276         if (callId != null && otherCallId != null && isServiceValid("consultativeTransfer")) {
2277             try {
2278                 logOutgoing("consultativeTransfer %s", callId);
2279                 mServiceInterface.consultativeTransfer(callId, otherCallId,
2280                         Log.getExternalSession(TELECOM_ABBREVIATION));
2281             } catch (RemoteException e) {
2282             }
2283         }
2284     }
2285 
2286     /** @see IConnectionService#playDtmfTone(String, char, Session.Info) */
playDtmfTone(Call call, char digit)2287     void playDtmfTone(Call call, char digit) {
2288         final String callId = mCallIdMapper.getCallId(call);
2289         if (callId != null && isServiceValid("playDtmfTone")) {
2290             try {
2291                 logOutgoing("playDtmfTone %s %c", callId, digit);
2292                 mServiceInterface.playDtmfTone(callId, digit,
2293                         Log.getExternalSession(TELECOM_ABBREVIATION));
2294             } catch (RemoteException e) {
2295             }
2296         }
2297     }
2298 
2299     /** @see IConnectionService#stopDtmfTone(String, Session.Info) */
stopDtmfTone(Call call)2300     void stopDtmfTone(Call call) {
2301         final String callId = mCallIdMapper.getCallId(call);
2302         if (callId != null && isServiceValid("stopDtmfTone")) {
2303             try {
2304                 logOutgoing("stopDtmfTone %s", callId);
2305                 mServiceInterface.stopDtmfTone(callId,
2306                         Log.getExternalSession(TELECOM_ABBREVIATION));
2307             } catch (RemoteException e) {
2308             }
2309         }
2310     }
2311 
2312     @VisibleForTesting
addCall(Call call)2313     public void addCall(Call call) {
2314         if (mCallIdMapper.getCallId(call) == null) {
2315             mCallIdMapper.addCall(call);
2316         }
2317     }
2318 
2319     /**
2320      * Associates newCall with this connection service by replacing callToReplace.
2321      */
replaceCall(Call newCall, Call callToReplace)2322     void replaceCall(Call newCall, Call callToReplace) {
2323         Preconditions.checkState(callToReplace.getConnectionService() == this);
2324         mCallIdMapper.replaceCall(newCall, callToReplace);
2325     }
2326 
removeCall(Call call)2327     void removeCall(Call call) {
2328         removeCall(call, new DisconnectCause(DisconnectCause.ERROR));
2329     }
2330 
removeCall(String callId, DisconnectCause disconnectCause)2331     void removeCall(String callId, DisconnectCause disconnectCause) {
2332         CreateConnectionResponse response = mPendingResponses.remove(callId);
2333         if (response != null) {
2334             response.handleCreateConnectionFailure(disconnectCause);
2335         }
2336         if (mFlags.dontTimeoutDestroyedCalls()) {
2337             maybeRemoveCleanupFuture(mCallIdMapper.getCall(callId));
2338         }
2339 
2340         mCallIdMapper.removeCall(callId);
2341     }
2342 
removeCall(Call call, DisconnectCause disconnectCause)2343     void removeCall(Call call, DisconnectCause disconnectCause) {
2344         CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
2345         if (response != null) {
2346             response.handleCreateConnectionFailure(disconnectCause);
2347         }
2348         if (mFlags.dontTimeoutDestroyedCalls()) {
2349             maybeRemoveCleanupFuture(call);
2350         }
2351 
2352         mCallIdMapper.removeCall(call);
2353     }
2354 
onPostDialContinue(Call call, boolean proceed)2355     void onPostDialContinue(Call call, boolean proceed) {
2356         final String callId = mCallIdMapper.getCallId(call);
2357         if (callId != null && isServiceValid("onPostDialContinue")) {
2358             try {
2359                 logOutgoing("onPostDialContinue %s %b", callId, proceed);
2360                 mServiceInterface.onPostDialContinue(callId, proceed,
2361                         Log.getExternalSession(TELECOM_ABBREVIATION));
2362             } catch (RemoteException ignored) {
2363             }
2364         }
2365     }
2366 
conference(final Call call, Call otherCall)2367     void conference(final Call call, Call otherCall) {
2368         final String callId = mCallIdMapper.getCallId(call);
2369         final String otherCallId = mCallIdMapper.getCallId(otherCall);
2370         if (callId != null && otherCallId != null && isServiceValid("conference")) {
2371             try {
2372                 logOutgoing("conference %s %s", callId, otherCallId);
2373                 mServiceInterface.conference(callId, otherCallId,
2374                         Log.getExternalSession(TELECOM_ABBREVIATION));
2375             } catch (RemoteException ignored) {
2376             }
2377         }
2378     }
2379 
splitFromConference(Call call)2380     void splitFromConference(Call call) {
2381         final String callId = mCallIdMapper.getCallId(call);
2382         if (callId != null && isServiceValid("splitFromConference")) {
2383             try {
2384                 logOutgoing("splitFromConference %s", callId);
2385                 mServiceInterface.splitFromConference(callId,
2386                         Log.getExternalSession(TELECOM_ABBREVIATION));
2387             } catch (RemoteException ignored) {
2388             }
2389         }
2390     }
2391 
mergeConference(Call call)2392     void mergeConference(Call call) {
2393         final String callId = mCallIdMapper.getCallId(call);
2394         if (callId != null && isServiceValid("mergeConference")) {
2395             try {
2396                 logOutgoing("mergeConference %s", callId);
2397                 mServiceInterface.mergeConference(callId,
2398                         Log.getExternalSession(TELECOM_ABBREVIATION));
2399             } catch (RemoteException ignored) {
2400             }
2401         }
2402     }
2403 
swapConference(Call call)2404     void swapConference(Call call) {
2405         final String callId = mCallIdMapper.getCallId(call);
2406         if (callId != null && isServiceValid("swapConference")) {
2407             try {
2408                 logOutgoing("swapConference %s", callId);
2409                 mServiceInterface.swapConference(callId,
2410                         Log.getExternalSession(TELECOM_ABBREVIATION));
2411             } catch (RemoteException ignored) {
2412             }
2413         }
2414     }
2415 
addConferenceParticipants(Call call, List<Uri> participants)2416     void addConferenceParticipants(Call call, List<Uri> participants) {
2417         final String callId = mCallIdMapper.getCallId(call);
2418         if (callId != null && isServiceValid("addConferenceParticipants")) {
2419             try {
2420                 logOutgoing("addConferenceParticipants %s", callId);
2421                 mServiceInterface.addConferenceParticipants(callId, participants,
2422                         Log.getExternalSession(TELECOM_ABBREVIATION));
2423             } catch (RemoteException ignored) {
2424             }
2425         }
2426     }
2427 
2428     @VisibleForTesting
pullExternalCall(Call call)2429     public void pullExternalCall(Call call) {
2430         final String callId = mCallIdMapper.getCallId(call);
2431         if (callId != null && isServiceValid("pullExternalCall")) {
2432             try {
2433                 logOutgoing("pullExternalCall %s", callId);
2434                 mServiceInterface.pullExternalCall(callId,
2435                         Log.getExternalSession(TELECOM_ABBREVIATION));
2436             } catch (RemoteException ignored) {
2437             }
2438         }
2439     }
2440 
2441     @Override
sendCallEvent(Call call, String event, Bundle extras)2442     public void sendCallEvent(Call call, String event, Bundle extras) {
2443         final String callId = mCallIdMapper.getCallId(call);
2444         if (callId != null && isServiceValid("sendCallEvent")) {
2445             try {
2446                 logOutgoing("sendCallEvent %s %s", callId, event);
2447                 mServiceInterface.sendCallEvent(callId, event, extras,
2448                         Log.getExternalSession(TELECOM_ABBREVIATION));
2449             } catch (RemoteException ignored) {
2450             }
2451         }
2452     }
2453 
onCallFilteringCompleted(Call call, Connection.CallFilteringCompletionInfo completionInfo)2454     void onCallFilteringCompleted(Call call,
2455             Connection.CallFilteringCompletionInfo completionInfo) {
2456         final String callId = mCallIdMapper.getCallId(call);
2457         if (callId != null && isServiceValid("onCallFilteringCompleted")) {
2458             try {
2459                 logOutgoing("onCallFilteringCompleted %s", completionInfo);
2460                 int contactsPermission = mContext.getPackageManager()
2461                         .checkPermission(Manifest.permission.READ_CONTACTS,
2462                                 getComponentName().getPackageName());
2463                 if (contactsPermission == PackageManager.PERMISSION_GRANTED) {
2464                     mServiceInterface.onCallFilteringCompleted(callId, completionInfo,
2465                             Log.getExternalSession(TELECOM_ABBREVIATION));
2466                 } else {
2467                     logOutgoing("Skipping call filtering complete message for %s due"
2468                             + " to lack of READ_CONTACTS", getComponentName().getPackageName());
2469                 }
2470             } catch (RemoteException e) {
2471                 Log.e(this, e, "Remote exception calling onCallFilteringCompleted");
2472             }
2473         }
2474     }
2475 
onExtrasChanged(Call call, Bundle extras)2476     void onExtrasChanged(Call call, Bundle extras) {
2477         final String callId = mCallIdMapper.getCallId(call);
2478         if (callId != null && isServiceValid("onExtrasChanged")) {
2479             try {
2480                 logOutgoing("onExtrasChanged %s %s", callId, extras);
2481                 mServiceInterface.onExtrasChanged(callId, extras,
2482                         Log.getExternalSession(TELECOM_ABBREVIATION));
2483             } catch (RemoteException ignored) {
2484             }
2485         }
2486     }
2487 
startRtt(Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall)2488     void startRtt(Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
2489         final String callId = mCallIdMapper.getCallId(call);
2490         if (callId != null && isServiceValid("startRtt")) {
2491             try {
2492                 logOutgoing("startRtt: %s %s %s", callId, fromInCall, toInCall);
2493                 mServiceInterface.startRtt(callId, fromInCall, toInCall,
2494                         Log.getExternalSession(TELECOM_ABBREVIATION));
2495             } catch (RemoteException ignored) {
2496             }
2497         }
2498     }
2499 
stopRtt(Call call)2500     void stopRtt(Call call) {
2501         final String callId = mCallIdMapper.getCallId(call);
2502         if (callId != null && isServiceValid("stopRtt")) {
2503             try {
2504                 logOutgoing("stopRtt: %s", callId);
2505                 mServiceInterface.stopRtt(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
2506             } catch (RemoteException ignored) {
2507             }
2508         }
2509     }
2510 
respondToRttRequest( Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall)2511     void respondToRttRequest(
2512             Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
2513         final String callId = mCallIdMapper.getCallId(call);
2514         if (callId != null && isServiceValid("respondToRttRequest")) {
2515             try {
2516                 logOutgoing("respondToRttRequest: %s %s %s", callId, fromInCall, toInCall);
2517                 mServiceInterface.respondToRttUpgradeRequest(
2518                         callId, fromInCall, toInCall, Log.getExternalSession(TELECOM_ABBREVIATION));
2519             } catch (RemoteException ignored) {
2520             }
2521         }
2522     }
2523 
2524     /** {@inheritDoc} */
2525     @Override
setServiceInterface(IBinder binder)2526     protected void setServiceInterface(IBinder binder) {
2527         mServiceInterface = IConnectionService.Stub.asInterface(binder);
2528         Log.v(this, "Adding Connection Service Adapter.");
2529         addConnectionServiceAdapter(mAdapter);
2530     }
2531 
2532     /** {@inheritDoc} */
2533     @Override
removeServiceInterface()2534     protected void removeServiceInterface() {
2535         Log.v(this, "Removing Connection Service Adapter.");
2536         if (mServiceInterface == null) {
2537             // In some cases, we may receive multiple calls to
2538             // remoteServiceInterface, such as when the remote process crashes
2539             // (onBinderDied & onServiceDisconnected)
2540             Log.w(this, "removeServiceInterface: mServiceInterface is null");
2541             return;
2542         }
2543         removeConnectionServiceAdapter(mAdapter);
2544         // We have lost our service connection. Notify the world that this service is done.
2545         // We must notify the adapter before CallsManager. The adapter will force any pending
2546         // outgoing calls to try the next service. This needs to happen before CallsManager
2547         // tries to clean up any calls still associated with this service.
2548         handleConnectionServiceDeath();
2549         mCallsManager.handleConnectionServiceDeath(this);
2550         mServiceInterface = null;
2551         if (mScheduledExecutor != null) {
2552             mScheduledExecutor.shutdown();
2553             mScheduledExecutor = null;
2554         }
2555     }
2556 
2557     @Override
connectionServiceFocusLost()2558     public void connectionServiceFocusLost() {
2559         // Immediately response to the Telecom that it has released the call resources.
2560         // TODO(mpq): Change back to the default implementation once b/69651192 done.
2561         if (mConnSvrFocusListener != null) {
2562             mConnSvrFocusListener.onConnectionServiceReleased(ConnectionServiceWrapper.this);
2563         }
2564         BindCallback callback = new BindCallback() {
2565             @Override
2566             public void onSuccess() {
2567                 if (!isServiceValid("connectionServiceFocusLost")) return;
2568                 try {
2569                     mServiceInterface.connectionServiceFocusLost(
2570                             Log.getExternalSession(TELECOM_ABBREVIATION));
2571                 } catch (RemoteException ignored) {
2572                     Log.d(this, "failed to inform the focus lost event");
2573                 }
2574             }
2575 
2576             @Override
2577             public void onFailure() {}
2578         };
2579         mBinder.bind(callback, null /* null call */);
2580     }
2581 
2582     @Override
connectionServiceFocusGained()2583     public void connectionServiceFocusGained() {
2584         BindCallback callback = new BindCallback() {
2585             @Override
2586             public void onSuccess() {
2587                 if (!isServiceValid("connectionServiceFocusGained")) return;
2588                 try {
2589                     mServiceInterface.connectionServiceFocusGained(
2590                             Log.getExternalSession(TELECOM_ABBREVIATION));
2591                 } catch (RemoteException ignored) {
2592                     Log.d(this, "failed to inform the focus gained event");
2593                 }
2594             }
2595 
2596             @Override
2597             public void onFailure() {}
2598         };
2599         mBinder.bind(callback, null /* null call */);
2600     }
2601 
2602     @Override
setConnectionServiceFocusListener( ConnectionServiceFocusManager.ConnectionServiceFocusListener listener)2603     public void setConnectionServiceFocusListener(
2604             ConnectionServiceFocusManager.ConnectionServiceFocusListener listener) {
2605         mConnSvrFocusListener = listener;
2606     }
2607 
handleCreateConnectionComplete( String callId, ConnectionRequest request, ParcelableConnection connection)2608     private void handleCreateConnectionComplete(
2609             String callId,
2610             ConnectionRequest request,
2611             ParcelableConnection connection) {
2612         // TODO: Note we are not using parameter "request", which is a side effect of our tacit
2613         // assumption that we have at most one outgoing connection attempt per ConnectionService.
2614         // This may not continue to be the case.
2615         if (connection.getState() == Connection.STATE_DISCONNECTED) {
2616             // A connection that begins in the DISCONNECTED state is an indication of
2617             // failure to connect; we handle all failures uniformly
2618             Call foundCall = mCallIdMapper.getCall(callId);
2619 
2620             if (foundCall != null) {
2621                 if (connection.getConnectTimeMillis() != 0) {
2622                     foundCall.setConnectTimeMillis(connection.getConnectTimeMillis());
2623                 }
2624 
2625                 // The post-dial digits are created when the call is first created.  Normally
2626                 // the ConnectionService is responsible for stripping them from the address, but
2627                 // since a failed connection will not have done this, we could end up with duplicate
2628                 // post-dial digits.
2629                 foundCall.clearPostDialDigits();
2630             }
2631             removeCall(callId, connection.getDisconnectCause());
2632         } else {
2633             // Successful connection
2634             if (mPendingResponses.containsKey(callId)) {
2635                 mPendingResponses.remove(callId)
2636                         .handleCreateConnectionSuccess(mCallIdMapper, connection);
2637             }
2638         }
2639     }
2640 
handleCreateConferenceComplete( String callId, ConnectionRequest request, ParcelableConference conference)2641     private void handleCreateConferenceComplete(
2642             String callId,
2643             ConnectionRequest request,
2644             ParcelableConference conference) {
2645         // TODO: Note we are not using parameter "request", which is a side effect of our tacit
2646         // assumption that we have at most one outgoing conference attempt per ConnectionService.
2647         // This may not continue to be the case.
2648         if (conference.getState() == Connection.STATE_DISCONNECTED) {
2649             // A conference that begins in the DISCONNECTED state is an indication of
2650             // failure to connect; we handle all failures uniformly
2651             removeCall(callId, conference.getDisconnectCause());
2652         } else {
2653             // Successful connection
2654             if (mPendingResponses.containsKey(callId)) {
2655                 mPendingResponses.remove(callId)
2656                         .handleCreateConferenceSuccess(mCallIdMapper, conference);
2657             }
2658         }
2659     }
2660 
2661     /**
2662      * Called when the associated connection service dies.
2663      */
handleConnectionServiceDeath()2664     private void handleConnectionServiceDeath() {
2665         if (!mPendingResponses.isEmpty()) {
2666             Collection<CreateConnectionResponse> responses = mPendingResponses.values();
2667             mPendingResponses.clear();
2668             for (CreateConnectionResponse response : responses) {
2669                 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR,
2670                         "CS_DEATH"));
2671             }
2672         }
2673         mCallIdMapper.clear();
2674         mScheduledFutureMap.clear();
2675 
2676         if (mConnSvrFocusListener != null) {
2677             mConnSvrFocusListener.onConnectionServiceDeath(this);
2678         }
2679     }
2680 
logIncoming(String msg, Object... params)2681     private void logIncoming(String msg, Object... params) {
2682         // Keep these as debug; the incoming logging is traced on a package level through the
2683         // session logging.
2684         Log.d(this, "CS -> TC[" + Log.getPackageAbbreviation(mComponentName) + "]: "
2685                 + msg, params);
2686     }
2687 
logOutgoing(String msg, Object... params)2688     private void logOutgoing(String msg, Object... params) {
2689         Log.d(this, "TC -> CS[" + Log.getPackageAbbreviation(mComponentName) + "]: "
2690                 + msg, params);
2691     }
2692 
queryRemoteConnectionServices(final UserHandle userHandle, final String callingPackage, final RemoteServiceCallback callback)2693     private void queryRemoteConnectionServices(final UserHandle userHandle,
2694             final String callingPackage, final RemoteServiceCallback callback) {
2695         boolean isCallerConnectionManager = false;
2696         // For each Sim ConnectionService, use its subid to find the correct connection manager for
2697         // that ConnectionService; return those Sim ConnectionServices which match the connection
2698         // manager.
2699         final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap(
2700                 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1));
2701         for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts(userHandle)) {
2702             int subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(handle);
2703             PhoneAccountHandle connectionMgrHandle = mPhoneAccountRegistrar.getSimCallManager(subId,
2704                     userHandle);
2705             if (connectionMgrHandle == null
2706                     || !connectionMgrHandle.getComponentName().getPackageName().equals(
2707                             callingPackage)) {
2708                 Log.v(this, "queryRemoteConnectionServices: callingPackage=%s skipped; "
2709                                 + "doesn't match mgr %s for tfa %s",
2710                         callingPackage, connectionMgrHandle, handle);
2711             } else {
2712                 isCallerConnectionManager = true;
2713             }
2714             ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
2715                     handle.getComponentName(), handle.getUserHandle());
2716             if (service != null && service != this) {
2717                 simServices.add(service);
2718             } else {
2719                 // This is unexpected, normally PhoneAccounts with CAPABILITY_CALL_PROVIDER are not
2720                 // also CAPABILITY_CONNECTION_MANAGER
2721                 Log.w(this, "call provider also detected as SIM call manager: " + service);
2722             }
2723         }
2724 
2725         Log.i(this, "queryRemoteConnectionServices, simServices = %s", simServices);
2726         // Bail early if the caller isn't the sim connection mgr or no sim connection service
2727         // other than caller available.
2728         if (!isCallerConnectionManager || simServices.isEmpty()) {
2729             Log.d(this, "queryRemoteConnectionServices: not sim call mgr or no simservices.");
2730             noRemoteServices(callback);
2731             return;
2732         }
2733 
2734         final List<ComponentName> simServiceComponentNames = new ArrayList<>();
2735         final List<IBinder> simServiceBinders = new ArrayList<>();
2736 
2737         for (ConnectionServiceWrapper simService : simServices) {
2738             final ConnectionServiceWrapper currentSimService = simService;
2739 
2740             currentSimService.mBinder.bind(new BindCallback() {
2741                 @Override
2742                 public void onSuccess() {
2743                     Log.d(this, "queryRemoteConnectionServices: Adding simService %s",
2744                             currentSimService.getComponentName());
2745                     if (currentSimService.mServiceInterface == null) {
2746                         // The remote ConnectionService died, so do not add it.
2747                         // We will still perform maybeComplete() and notify the caller with an empty
2748                         // list of sim services via maybeComplete().
2749                         Log.w(this, "queryRemoteConnectionServices: simService %s died - Skipping.",
2750                                 currentSimService.getComponentName());
2751                     } else {
2752                         simServiceComponentNames.add(currentSimService.getComponentName());
2753                         simServiceBinders.add(currentSimService.mServiceInterface.asBinder());
2754                     }
2755                     maybeComplete();
2756                 }
2757 
2758                 @Override
2759                 public void onFailure() {
2760                     Log.d(this, "queryRemoteConnectionServices: Failed simService %s",
2761                             currentSimService.getComponentName());
2762                     // We know maybeComplete() will always be a no-op from now on, so go ahead and
2763                     // signal failure of the entire request
2764                     noRemoteServices(callback);
2765                 }
2766 
2767                 private void maybeComplete() {
2768                     if (simServiceComponentNames.size() == simServices.size()) {
2769                         setRemoteServices(callback, simServiceComponentNames, simServiceBinders);
2770                     }
2771                 }
2772             }, null);
2773         }
2774     }
2775 
setRemoteServices( RemoteServiceCallback callback, List<ComponentName> componentNames, List<IBinder> binders)2776     private void setRemoteServices(
2777             RemoteServiceCallback callback,
2778             List<ComponentName> componentNames,
2779             List<IBinder> binders) {
2780         try {
2781             callback.onResult(componentNames, binders);
2782         } catch (RemoteException e) {
2783             Log.e(this, e, "setRemoteServices: Contacting ConnectionService %s",
2784                     ConnectionServiceWrapper.this.getComponentName());
2785         }
2786     }
2787 
noRemoteServices(RemoteServiceCallback callback)2788     private void noRemoteServices(RemoteServiceCallback callback) {
2789         setRemoteServices(callback, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
2790     }
2791 
2792     @Override
toString()2793     public String toString() {
2794         StringBuilder sb = new StringBuilder();
2795         sb.append("[ConnectionServiceWrapper componentName=");
2796         sb.append(mComponentName);
2797         sb.append("]");
2798         return sb.toString();
2799     }
2800 
2801     @VisibleForTesting
setScheduledExecutorService(ScheduledExecutorService service)2802     public void setScheduledExecutorService(ScheduledExecutorService service) {
2803         mScheduledExecutor = service;
2804     }
2805 
2806     @VisibleForTesting
setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter)2807     public void setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter){
2808         mAnomalyReporter = mAnomalyReporterAdapter;
2809     }
2810 
2811     /**
2812      * Given a call, unschedule and cancel the cleanup future.
2813      * @param call the call.
2814      */
maybeRemoveCleanupFuture(Call call)2815     private void maybeRemoveCleanupFuture(Call call) {
2816         if (call == null) {
2817             return;
2818         }
2819         ScheduledFuture<?> future = mScheduledFutureMap.remove(call);
2820         if (future == null) {
2821             return;
2822         }
2823         future.cancel(false /* interrupt */);
2824 
2825     }
2826 }
2827