ConnectionServiceWrapper.java revision 62630f727fa055e5d79c8194ed5db697104f116c
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
17package com.android.server.telecom;
18
19import android.app.AppOpsManager;
20import android.content.ComponentName;
21import android.content.Context;
22import android.net.Uri;
23import android.os.Binder;
24import android.os.Bundle;
25import android.os.IBinder;
26import android.os.ParcelFileDescriptor;
27import android.os.RemoteException;
28import android.os.UserHandle;
29import android.telecom.CallAudioState;
30import android.telecom.Connection;
31import android.telecom.ConnectionRequest;
32import android.telecom.ConnectionService;
33import android.telecom.DisconnectCause;
34import android.telecom.GatewayInfo;
35import android.telecom.Log;
36import android.telecom.Logging.Session;
37import android.telecom.ParcelableConference;
38import android.telecom.ParcelableConnection;
39import android.telecom.PhoneAccountHandle;
40import android.telecom.StatusHints;
41import android.telecom.TelecomManager;
42import android.telecom.VideoProfile;
43import android.telephony.TelephonyManager;
44
45import com.android.internal.annotations.VisibleForTesting;
46import com.android.internal.telecom.IConnectionService;
47import com.android.internal.telecom.IConnectionServiceAdapter;
48import com.android.internal.telecom.IVideoProvider;
49import com.android.internal.telecom.RemoteServiceCallback;
50import com.android.internal.util.Preconditions;
51
52import java.util.ArrayList;
53import java.util.Collections;
54import java.util.HashMap;
55import java.util.List;
56import java.util.Map;
57import java.util.Set;
58import 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
67public class ConnectionServiceWrapper extends ServiceBinder implements
68        ConnectionServiceFocusManager.ConnectionServiceFocus {
69
70    private final class Adapter extends IConnectionServiceAdapter.Stub {
71
72        @Override
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
876        public void onRttInitiationSuccess(String callId, Session.Info sessionInfo)
877                throws RemoteException {
878
879        }
880
881        @Override
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
903        public void onRttSessionRemotelyTerminated(String callId, Session.Info sessionInfo)
904                throws RemoteException {
905
906        }
907
908        @Override
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
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
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     */
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}. */
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}. */
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
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     */
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
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
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)  */
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) */
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) */
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) */
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
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) */
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) */
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) */
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) */
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) */
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) */
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
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     */
1394    void replaceCall(Call newCall, Call callToReplace) {
1395        Preconditions.checkState(callToReplace.getConnectionService() == this);
1396        mCallIdMapper.replaceCall(newCall, callToReplace);
1397    }
1398
1399    void removeCall(Call call) {
1400        removeCall(call, new DisconnectCause(DisconnectCause.ERROR));
1401    }
1402
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1609    public void setConnectionServiceFocusListener(
1610            ConnectionServiceFocusManager.ConnectionServiceFocusListener listener) {
1611        mConnSvrFocusListener = listener;
1612    }
1613
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     */
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
1654    private void logIncoming(String msg, Object... params) {
1655        Log.d(this, "ConnectionService -> Telecom[" + mComponentName.flattenToShortString() + "]: "
1656                + msg, params);
1657    }
1658
1659    private void logOutgoing(String msg, Object... params) {
1660        Log.d(this, "Telecom -> ConnectionService[" + mComponentName.flattenToShortString() + "]: "
1661                + msg, params);
1662    }
1663
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
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
1746    private void noRemoteServices(RemoteServiceCallback callback) {
1747        setRemoteServices(callback, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
1748    }
1749}
1750