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