Home | History | Annotate | Download | only in telecom
      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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    786         public void onRttInitiationSuccess(String callId, Session.Info sessionInfo)
    787                 throws RemoteException {
    788 
    789         }
    790 
    791         @Override
    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
    810         public void onRttSessionRemotelyTerminated(String callId, Session.Info sessionInfo)
    811                 throws RemoteException {
    812 
    813         }
    814 
    815         @Override
    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      */
    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}. */
    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}. */
    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
    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                 // Call is incoming and added because we're handing over from another; tell CS
    932                 // that its expected to handover.
    933                 if (call.isIncoming() && call.getHandoverSourceCall() != null) {
    934                     extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true);
    935                     extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
    936                             call.getHandoverSourceCall().getTargetPhoneAccount());
    937                 }
    938 
    939                 Log.addEvent(call, LogUtils.Events.START_CONNECTION,
    940                         Log.piiHandle(call.getHandle()));
    941 
    942                 ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
    943                         .setAccountHandle(call.getTargetPhoneAccount())
    944                         .setAddress(call.getHandle())
    945                         .setExtras(extras)
    946                         .setVideoState(call.getVideoState())
    947                         .setTelecomCallId(callId)
    948                         // For self-managed incoming calls, if there is another ongoing call Telecom
    949                         // is responsible for showing a UI to ask the user if they'd like to answer
    950                         // this new incoming call.
    951                         .setShouldShowIncomingCallUi(
    952                                 !mCallsManager.shouldShowSystemIncomingCallUi(call))
    953                         .setRttPipeFromInCall(call.getInCallToCsRttPipeForCs())
    954                         .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
    955                         .build();
    956 
    957                 try {
    958                     mServiceInterface.createConnection(
    959                             call.getConnectionManagerPhoneAccount(),
    960                             callId,
    961                             connectionRequest,
    962                             call.shouldAttachToExistingConnection(),
    963                             call.isUnknown(),
    964                             Log.getExternalSession());
    965 
    966                 } catch (RemoteException e) {
    967                     Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
    968                     mPendingResponses.remove(callId).handleCreateConnectionFailure(
    969                             new DisconnectCause(DisconnectCause.ERROR, e.toString()));
    970                 }
    971             }
    972 
    973             @Override
    974             public void onFailure() {
    975                 Log.e(this, new Exception(), "Failure to call %s", getComponentName());
    976                 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
    977             }
    978         };
    979 
    980         mBinder.bind(callback, call);
    981     }
    982 
    983     /**
    984      * Notifies the {@link ConnectionService} associated with a {@link Call} that the request to
    985      * create a connection has been denied or failed.
    986      * @param call The call.
    987      */
    988     void createConnectionFailed(final Call call) {
    989         Log.d(this, "createConnectionFailed(%s) via %s.", call, getComponentName());
    990         BindCallback callback = new BindCallback() {
    991             @Override
    992             public void onSuccess() {
    993                 final String callId = mCallIdMapper.getCallId(call);
    994                 // If still bound, tell the connection service create connection has failed.
    995                 if (callId != null && isServiceValid("createConnectionFailed")) {
    996                     Log.addEvent(call, LogUtils.Events.CREATE_CONNECTION_FAILED,
    997                             Log.piiHandle(call.getHandle()));
    998                     try {
    999                         logOutgoing("createConnectionFailed %s", callId);
   1000                         mServiceInterface.createConnectionFailed(
   1001                                 call.getConnectionManagerPhoneAccount(),
   1002                                 callId,
   1003                                 new ConnectionRequest(
   1004                                         call.getTargetPhoneAccount(),
   1005                                         call.getHandle(),
   1006                                         call.getIntentExtras(),
   1007                                         call.getVideoState(),
   1008                                         callId,
   1009                                         false),
   1010                                 call.isIncoming(),
   1011                                 Log.getExternalSession());
   1012                         call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
   1013                         call.disconnect();
   1014                     } catch (RemoteException e) {
   1015                     }
   1016                 }
   1017             }
   1018 
   1019             @Override
   1020             public void onFailure() {
   1021                 // Binding failed.  Oh no.
   1022                 Log.w(this, "onFailure - could not bind to CS for call %s", call.getId());
   1023             }
   1024         };
   1025 
   1026         mBinder.bind(callback, call);
   1027     }
   1028 
   1029     /** @see IConnectionService#abort(String, Session.Info)  */
   1030     void abort(Call call) {
   1031         // Clear out any pending outgoing call data
   1032         final String callId = mCallIdMapper.getCallId(call);
   1033 
   1034         // If still bound, tell the connection service to abort.
   1035         if (callId != null && isServiceValid("abort")) {
   1036             try {
   1037                 logOutgoing("abort %s", callId);
   1038                 mServiceInterface.abort(callId, Log.getExternalSession());
   1039             } catch (RemoteException e) {
   1040             }
   1041         }
   1042 
   1043         removeCall(call, new DisconnectCause(DisconnectCause.LOCAL));
   1044     }
   1045 
   1046     /** @see IConnectionService#silence(String, Session.Info) */
   1047     void silence(Call call) {
   1048         final String callId = mCallIdMapper.getCallId(call);
   1049         if (callId != null && isServiceValid("silence")) {
   1050             try {
   1051                 logOutgoing("silence %s", callId);
   1052                 mServiceInterface.silence(callId, Log.getExternalSession());
   1053             } catch (RemoteException e) {
   1054             }
   1055         }
   1056     }
   1057 
   1058     /** @see IConnectionService#hold(String, Session.Info) */
   1059     void hold(Call call) {
   1060         final String callId = mCallIdMapper.getCallId(call);
   1061         if (callId != null && isServiceValid("hold")) {
   1062             try {
   1063                 logOutgoing("hold %s", callId);
   1064                 mServiceInterface.hold(callId, Log.getExternalSession());
   1065             } catch (RemoteException e) {
   1066             }
   1067         }
   1068     }
   1069 
   1070     /** @see IConnectionService#unhold(String, Session.Info) */
   1071     void unhold(Call call) {
   1072         final String callId = mCallIdMapper.getCallId(call);
   1073         if (callId != null && isServiceValid("unhold")) {
   1074             try {
   1075                 logOutgoing("unhold %s", callId);
   1076                 mServiceInterface.unhold(callId, Log.getExternalSession());
   1077             } catch (RemoteException e) {
   1078             }
   1079         }
   1080     }
   1081 
   1082     /** @see IConnectionService#onCallAudioStateChanged(String, CallAudioState, Session.Info) */
   1083     @VisibleForTesting
   1084     public void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) {
   1085         final String callId = mCallIdMapper.getCallId(activeCall);
   1086         if (callId != null && isServiceValid("onCallAudioStateChanged")) {
   1087             try {
   1088                 logOutgoing("onCallAudioStateChanged %s %s", callId, audioState);
   1089                 mServiceInterface.onCallAudioStateChanged(callId, audioState,
   1090                         Log.getExternalSession());
   1091             } catch (RemoteException e) {
   1092             }
   1093         }
   1094     }
   1095 
   1096     /** @see IConnectionService#disconnect(String, Session.Info) */
   1097     void disconnect(Call call) {
   1098         final String callId = mCallIdMapper.getCallId(call);
   1099         if (callId != null && isServiceValid("disconnect")) {
   1100             try {
   1101                 logOutgoing("disconnect %s", callId);
   1102                 mServiceInterface.disconnect(callId, Log.getExternalSession());
   1103             } catch (RemoteException e) {
   1104             }
   1105         }
   1106     }
   1107 
   1108     /** @see IConnectionService#answer(String, Session.Info) */
   1109     void answer(Call call, int videoState) {
   1110         final String callId = mCallIdMapper.getCallId(call);
   1111         if (callId != null && isServiceValid("answer")) {
   1112             try {
   1113                 logOutgoing("answer %s %d", callId, videoState);
   1114                 if (VideoProfile.isAudioOnly(videoState)) {
   1115                     mServiceInterface.answer(callId, Log.getExternalSession());
   1116                 } else {
   1117                     mServiceInterface.answerVideo(callId, videoState, Log.getExternalSession());
   1118                 }
   1119             } catch (RemoteException e) {
   1120             }
   1121         }
   1122     }
   1123 
   1124     /** @see IConnectionService#reject(String, Session.Info) */
   1125     void reject(Call call, boolean rejectWithMessage, String message) {
   1126         final String callId = mCallIdMapper.getCallId(call);
   1127         if (callId != null && isServiceValid("reject")) {
   1128             try {
   1129                 logOutgoing("reject %s", callId);
   1130 
   1131                 if (rejectWithMessage && call.can(
   1132                         Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
   1133                     mServiceInterface.rejectWithMessage(callId, message, Log.getExternalSession());
   1134                 } else {
   1135                     mServiceInterface.reject(callId, Log.getExternalSession());
   1136                 }
   1137             } catch (RemoteException e) {
   1138             }
   1139         }
   1140     }
   1141 
   1142     /** @see IConnectionService#playDtmfTone(String, char, Session.Info) */
   1143     void playDtmfTone(Call call, char digit) {
   1144         final String callId = mCallIdMapper.getCallId(call);
   1145         if (callId != null && isServiceValid("playDtmfTone")) {
   1146             try {
   1147                 logOutgoing("playDtmfTone %s %c", callId, digit);
   1148                 mServiceInterface.playDtmfTone(callId, digit, Log.getExternalSession());
   1149             } catch (RemoteException e) {
   1150             }
   1151         }
   1152     }
   1153 
   1154     /** @see IConnectionService#stopDtmfTone(String, Session.Info) */
   1155     void stopDtmfTone(Call call) {
   1156         final String callId = mCallIdMapper.getCallId(call);
   1157         if (callId != null && isServiceValid("stopDtmfTone")) {
   1158             try {
   1159                 logOutgoing("stopDtmfTone %s", callId);
   1160                 mServiceInterface.stopDtmfTone(callId, Log.getExternalSession());
   1161             } catch (RemoteException e) {
   1162             }
   1163         }
   1164     }
   1165 
   1166     void addCall(Call call) {
   1167         if (mCallIdMapper.getCallId(call) == null) {
   1168             mCallIdMapper.addCall(call);
   1169         }
   1170     }
   1171 
   1172     /**
   1173      * Associates newCall with this connection service by replacing callToReplace.
   1174      */
   1175     void replaceCall(Call newCall, Call callToReplace) {
   1176         Preconditions.checkState(callToReplace.getConnectionService() == this);
   1177         mCallIdMapper.replaceCall(newCall, callToReplace);
   1178     }
   1179 
   1180     void removeCall(Call call) {
   1181         removeCall(call, new DisconnectCause(DisconnectCause.ERROR));
   1182     }
   1183 
   1184     void removeCall(String callId, DisconnectCause disconnectCause) {
   1185         CreateConnectionResponse response = mPendingResponses.remove(callId);
   1186         if (response != null) {
   1187             response.handleCreateConnectionFailure(disconnectCause);
   1188         }
   1189 
   1190         mCallIdMapper.removeCall(callId);
   1191     }
   1192 
   1193     void removeCall(Call call, DisconnectCause disconnectCause) {
   1194         CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
   1195         if (response != null) {
   1196             response.handleCreateConnectionFailure(disconnectCause);
   1197         }
   1198 
   1199         mCallIdMapper.removeCall(call);
   1200     }
   1201 
   1202     void onPostDialContinue(Call call, boolean proceed) {
   1203         final String callId = mCallIdMapper.getCallId(call);
   1204         if (callId != null && isServiceValid("onPostDialContinue")) {
   1205             try {
   1206                 logOutgoing("onPostDialContinue %s %b", callId, proceed);
   1207                 mServiceInterface.onPostDialContinue(callId, proceed, Log.getExternalSession());
   1208             } catch (RemoteException ignored) {
   1209             }
   1210         }
   1211     }
   1212 
   1213     void conference(final Call call, Call otherCall) {
   1214         final String callId = mCallIdMapper.getCallId(call);
   1215         final String otherCallId = mCallIdMapper.getCallId(otherCall);
   1216         if (callId != null && otherCallId != null && isServiceValid("conference")) {
   1217             try {
   1218                 logOutgoing("conference %s %s", callId, otherCallId);
   1219                 mServiceInterface.conference(callId, otherCallId, Log.getExternalSession());
   1220             } catch (RemoteException ignored) {
   1221             }
   1222         }
   1223     }
   1224 
   1225     void splitFromConference(Call call) {
   1226         final String callId = mCallIdMapper.getCallId(call);
   1227         if (callId != null && isServiceValid("splitFromConference")) {
   1228             try {
   1229                 logOutgoing("splitFromConference %s", callId);
   1230                 mServiceInterface.splitFromConference(callId, Log.getExternalSession());
   1231             } catch (RemoteException ignored) {
   1232             }
   1233         }
   1234     }
   1235 
   1236     void mergeConference(Call call) {
   1237         final String callId = mCallIdMapper.getCallId(call);
   1238         if (callId != null && isServiceValid("mergeConference")) {
   1239             try {
   1240                 logOutgoing("mergeConference %s", callId);
   1241                 mServiceInterface.mergeConference(callId, Log.getExternalSession());
   1242             } catch (RemoteException ignored) {
   1243             }
   1244         }
   1245     }
   1246 
   1247     void swapConference(Call call) {
   1248         final String callId = mCallIdMapper.getCallId(call);
   1249         if (callId != null && isServiceValid("swapConference")) {
   1250             try {
   1251                 logOutgoing("swapConference %s", callId);
   1252                 mServiceInterface.swapConference(callId, Log.getExternalSession());
   1253             } catch (RemoteException ignored) {
   1254             }
   1255         }
   1256     }
   1257 
   1258     void pullExternalCall(Call call) {
   1259         final String callId = mCallIdMapper.getCallId(call);
   1260         if (callId != null && isServiceValid("pullExternalCall")) {
   1261             try {
   1262                 logOutgoing("pullExternalCall %s", callId);
   1263                 mServiceInterface.pullExternalCall(callId, Log.getExternalSession());
   1264             } catch (RemoteException ignored) {
   1265             }
   1266         }
   1267     }
   1268 
   1269     void sendCallEvent(Call call, String event, Bundle extras) {
   1270         final String callId = mCallIdMapper.getCallId(call);
   1271         if (callId != null && isServiceValid("sendCallEvent")) {
   1272             try {
   1273                 logOutgoing("sendCallEvent %s %s", callId, event);
   1274                 mServiceInterface.sendCallEvent(callId, event, extras, Log.getExternalSession());
   1275             } catch (RemoteException ignored) {
   1276             }
   1277         }
   1278     }
   1279 
   1280     void onExtrasChanged(Call call, Bundle extras) {
   1281         final String callId = mCallIdMapper.getCallId(call);
   1282         if (callId != null && isServiceValid("onExtrasChanged")) {
   1283             try {
   1284                 logOutgoing("onExtrasChanged %s %s", callId, extras);
   1285                 mServiceInterface.onExtrasChanged(callId, extras, Log.getExternalSession());
   1286             } catch (RemoteException ignored) {
   1287             }
   1288         }
   1289     }
   1290 
   1291     void startRtt(Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
   1292         final String callId = mCallIdMapper.getCallId(call);
   1293         if (callId != null && isServiceValid("startRtt")) {
   1294             try {
   1295                 logOutgoing("startRtt: %s %s %s", callId, fromInCall, toInCall);
   1296                 mServiceInterface.startRtt(callId, fromInCall, toInCall, Log.getExternalSession());
   1297             } catch (RemoteException ignored) {
   1298             }
   1299         }
   1300     }
   1301 
   1302     void stopRtt(Call call) {
   1303         final String callId = mCallIdMapper.getCallId(call);
   1304         if (callId != null && isServiceValid("stopRtt")) {
   1305             try {
   1306                 logOutgoing("stopRtt: %s", callId);
   1307                 mServiceInterface.stopRtt(callId, Log.getExternalSession());
   1308             } catch (RemoteException ignored) {
   1309             }
   1310         }
   1311     }
   1312 
   1313     void respondToRttRequest(
   1314             Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
   1315         final String callId = mCallIdMapper.getCallId(call);
   1316         if (callId != null && isServiceValid("respondToRttRequest")) {
   1317             try {
   1318                 logOutgoing("respondToRttRequest: %s %s %s", callId, fromInCall, toInCall);
   1319                 mServiceInterface.respondToRttUpgradeRequest(
   1320                         callId, fromInCall, toInCall, Log.getExternalSession());
   1321             } catch (RemoteException ignored) {
   1322             }
   1323         }
   1324     }
   1325 
   1326     /** {@inheritDoc} */
   1327     @Override
   1328     protected void setServiceInterface(IBinder binder) {
   1329         mServiceInterface = IConnectionService.Stub.asInterface(binder);
   1330         Log.v(this, "Adding Connection Service Adapter.");
   1331         addConnectionServiceAdapter(mAdapter);
   1332     }
   1333 
   1334     /** {@inheritDoc} */
   1335     @Override
   1336     protected void removeServiceInterface() {
   1337         Log.v(this, "Removing Connection Service Adapter.");
   1338         removeConnectionServiceAdapter(mAdapter);
   1339         // We have lost our service connection. Notify the world that this service is done.
   1340         // We must notify the adapter before CallsManager. The adapter will force any pending
   1341         // outgoing calls to try the next service. This needs to happen before CallsManager
   1342         // tries to clean up any calls still associated with this service.
   1343         handleConnectionServiceDeath();
   1344         mCallsManager.handleConnectionServiceDeath(this);
   1345         mServiceInterface = null;
   1346     }
   1347 
   1348     private void handleCreateConnectionComplete(
   1349             String callId,
   1350             ConnectionRequest request,
   1351             ParcelableConnection connection) {
   1352         // TODO: Note we are not using parameter "request", which is a side effect of our tacit
   1353         // assumption that we have at most one outgoing connection attempt per ConnectionService.
   1354         // This may not continue to be the case.
   1355         if (connection.getState() == Connection.STATE_DISCONNECTED) {
   1356             // A connection that begins in the DISCONNECTED state is an indication of
   1357             // failure to connect; we handle all failures uniformly
   1358             removeCall(callId, connection.getDisconnectCause());
   1359         } else {
   1360             // Successful connection
   1361             if (mPendingResponses.containsKey(callId)) {
   1362                 mPendingResponses.remove(callId)
   1363                         .handleCreateConnectionSuccess(mCallIdMapper, connection);
   1364             }
   1365         }
   1366     }
   1367 
   1368     /**
   1369      * Called when the associated connection service dies.
   1370      */
   1371     private void handleConnectionServiceDeath() {
   1372         if (!mPendingResponses.isEmpty()) {
   1373             CreateConnectionResponse[] responses = mPendingResponses.values().toArray(
   1374                     new CreateConnectionResponse[mPendingResponses.values().size()]);
   1375             mPendingResponses.clear();
   1376             for (int i = 0; i < responses.length; i++) {
   1377                 responses[i].handleCreateConnectionFailure(
   1378                         new DisconnectCause(DisconnectCause.ERROR, "CS_DEATH"));
   1379             }
   1380         }
   1381         mCallIdMapper.clear();
   1382     }
   1383 
   1384     private void logIncoming(String msg, Object... params) {
   1385         Log.d(this, "ConnectionService -> Telecom[" + mComponentName.flattenToShortString() + "]: "
   1386                 + msg, params);
   1387     }
   1388 
   1389     private void logOutgoing(String msg, Object... params) {
   1390         Log.d(this, "Telecom -> ConnectionService[" + mComponentName.flattenToShortString() + "]: "
   1391                 + msg, params);
   1392     }
   1393 
   1394     private void queryRemoteConnectionServices(final UserHandle userHandle,
   1395             final RemoteServiceCallback callback) {
   1396         // Only give remote connection services to this connection service if it is listed as
   1397         // the connection manager.
   1398         PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager(userHandle);
   1399         Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager);
   1400         if (simCallManager == null ||
   1401                 !simCallManager.getComponentName().equals(getComponentName())) {
   1402             noRemoteServices(callback);
   1403             return;
   1404         }
   1405 
   1406         // Make a list of ConnectionServices that are listed as being associated with SIM accounts
   1407         final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap(
   1408                 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1));
   1409         for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts(userHandle)) {
   1410             ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
   1411                     handle.getComponentName(), handle.getUserHandle());
   1412             if (service != null) {
   1413                 simServices.add(service);
   1414             }
   1415         }
   1416 
   1417         final List<ComponentName> simServiceComponentNames = new ArrayList<>();
   1418         final List<IBinder> simServiceBinders = new ArrayList<>();
   1419 
   1420         Log.v(this, "queryRemoteConnectionServices, simServices = %s", simServices);
   1421 
   1422         for (ConnectionServiceWrapper simService : simServices) {
   1423             if (simService == this) {
   1424                 // Only happens in the unlikely case that a SIM service is also a SIM call manager
   1425                 continue;
   1426             }
   1427 
   1428             final ConnectionServiceWrapper currentSimService = simService;
   1429 
   1430             currentSimService.mBinder.bind(new BindCallback() {
   1431                 @Override
   1432                 public void onSuccess() {
   1433                     Log.d(this, "Adding simService %s", currentSimService.getComponentName());
   1434                     simServiceComponentNames.add(currentSimService.getComponentName());
   1435                     simServiceBinders.add(currentSimService.mServiceInterface.asBinder());
   1436                     maybeComplete();
   1437                 }
   1438 
   1439                 @Override
   1440                 public void onFailure() {
   1441                     Log.d(this, "Failed simService %s", currentSimService.getComponentName());
   1442                     // We know maybeComplete() will always be a no-op from now on, so go ahead and
   1443                     // signal failure of the entire request
   1444                     noRemoteServices(callback);
   1445                 }
   1446 
   1447                 private void maybeComplete() {
   1448                     if (simServiceComponentNames.size() == simServices.size()) {
   1449                         setRemoteServices(callback, simServiceComponentNames, simServiceBinders);
   1450                     }
   1451                 }
   1452             }, null);
   1453         }
   1454     }
   1455 
   1456     private void setRemoteServices(
   1457             RemoteServiceCallback callback,
   1458             List<ComponentName> componentNames,
   1459             List<IBinder> binders) {
   1460         try {
   1461             callback.onResult(componentNames, binders);
   1462         } catch (RemoteException e) {
   1463             Log.e(this, e, "Contacting ConnectionService %s",
   1464                     ConnectionServiceWrapper.this.getComponentName());
   1465         }
   1466     }
   1467 
   1468     private void noRemoteServices(RemoteServiceCallback callback) {
   1469         setRemoteServices(callback, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
   1470     }
   1471 }
   1472