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