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