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 import android.telephony.TelephonyManager;
     44 
     45 import com.android.internal.annotations.VisibleForTesting;
     46 import com.android.internal.telecom.IConnectionService;
     47 import com.android.internal.telecom.IConnectionServiceAdapter;
     48 import com.android.internal.telecom.IVideoProvider;
     49 import com.android.internal.telecom.RemoteServiceCallback;
     50 import com.android.internal.util.Preconditions;
     51 
     52 import java.util.ArrayList;
     53 import java.util.Collections;
     54 import java.util.HashMap;
     55 import java.util.List;
     56 import java.util.Map;
     57 import java.util.Set;
     58 import java.util.concurrent.ConcurrentHashMap;
     59 
     60 /**
     61  * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps
     62  * track of when the object can safely be unbound. Other classes should not use
     63  * {@link IConnectionService} directly and instead should use this class to invoke methods of
     64  * {@link IConnectionService}.
     65  */
     66 @VisibleForTesting
     67 public class ConnectionServiceWrapper extends ServiceBinder implements
     68         ConnectionServiceFocusManager.ConnectionServiceFocus {
     69 
     70     private final class Adapter extends IConnectionServiceAdapter.Stub {
     71 
     72         @Override
     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