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