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