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.content.ComponentName;
     20 import android.content.Context;
     21 import android.net.Uri;
     22 import android.os.Bundle;
     23 import android.os.Handler;
     24 import android.os.IBinder;
     25 import android.os.Message;
     26 import android.os.RemoteException;
     27 import android.telecom.AudioState;
     28 import android.telecom.Connection;
     29 import android.telecom.ConnectionRequest;
     30 import android.telecom.ConnectionService;
     31 import android.telecom.DisconnectCause;
     32 import android.telecom.GatewayInfo;
     33 import android.telecom.ParcelableConference;
     34 import android.telecom.ParcelableConnection;
     35 import android.telecom.PhoneAccount;
     36 import android.telecom.PhoneAccountHandle;
     37 import android.telecom.StatusHints;
     38 import android.telecom.TelecomManager;
     39 import android.telecom.VideoProfile;
     40 
     41 import com.android.internal.os.SomeArgs;
     42 import com.android.internal.telecom.IConnectionService;
     43 import com.android.internal.telecom.IConnectionServiceAdapter;
     44 import com.android.internal.telecom.IVideoProvider;
     45 import com.android.internal.telecom.RemoteServiceCallback;
     46 import com.android.internal.util.Preconditions;
     47 
     48 import java.util.ArrayList;
     49 import java.util.Collections;
     50 import java.util.HashMap;
     51 import java.util.List;
     52 import java.util.Map;
     53 import java.util.Set;
     54 import java.util.concurrent.ConcurrentHashMap;
     55 
     56 /**
     57  * Wrapper for {@link IConnectionService}s, handles binding to {@link IConnectionService} and keeps
     58  * track of when the object can safely be unbound. Other classes should not use
     59  * {@link IConnectionService} directly and instead should use this class to invoke methods of
     60  * {@link IConnectionService}.
     61  */
     62 final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> {
     63     private static final int MSG_HANDLE_CREATE_CONNECTION_COMPLETE = 1;
     64     private static final int MSG_SET_ACTIVE = 2;
     65     private static final int MSG_SET_RINGING = 3;
     66     private static final int MSG_SET_DIALING = 4;
     67     private static final int MSG_SET_DISCONNECTED = 5;
     68     private static final int MSG_SET_ON_HOLD = 6;
     69     private static final int MSG_SET_RINGBACK_REQUESTED = 7;
     70     private static final int MSG_SET_CALL_CAPABILITIES = 8;
     71     private static final int MSG_SET_IS_CONFERENCED = 9;
     72     private static final int MSG_ADD_CONFERENCE_CALL = 10;
     73     private static final int MSG_REMOVE_CALL = 11;
     74     private static final int MSG_ON_POST_DIAL_WAIT = 12;
     75     private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 13;
     76     private static final int MSG_SET_VIDEO_PROVIDER = 14;
     77     private static final int MSG_SET_IS_VOIP_AUDIO_MODE = 15;
     78     private static final int MSG_SET_STATUS_HINTS = 16;
     79     private static final int MSG_SET_ADDRESS = 17;
     80     private static final int MSG_SET_CALLER_DISPLAY_NAME = 18;
     81     private static final int MSG_SET_VIDEO_STATE = 19;
     82     private static final int MSG_SET_CONFERENCEABLE_CONNECTIONS = 20;
     83 
     84     private final Handler mHandler = new Handler() {
     85         @Override
     86         public void handleMessage(Message msg) {
     87             Call call;
     88             switch (msg.what) {
     89                 case MSG_HANDLE_CREATE_CONNECTION_COMPLETE: {
     90                     SomeArgs args = (SomeArgs) msg.obj;
     91                     try {
     92                         String callId = (String) args.arg1;
     93                         ConnectionRequest request = (ConnectionRequest) args.arg2;
     94                         ParcelableConnection connection = (ParcelableConnection) args.arg3;
     95                         handleCreateConnectionComplete(callId, request, connection);
     96                     } finally {
     97                         args.recycle();
     98                     }
     99                     break;
    100                 }
    101                 case MSG_SET_ACTIVE:
    102                     call = mCallIdMapper.getCall(msg.obj);
    103                     if (call != null) {
    104                         mCallsManager.markCallAsActive(call);
    105                     } else {
    106                         //Log.w(this, "setActive, unknown call id: %s", msg.obj);
    107                     }
    108                     break;
    109                 case MSG_SET_RINGING:
    110                     call = mCallIdMapper.getCall(msg.obj);
    111                     if (call != null) {
    112                         mCallsManager.markCallAsRinging(call);
    113                     } else {
    114                         //Log.w(this, "setRinging, unknown call id: %s", msg.obj);
    115                     }
    116                     break;
    117                 case MSG_SET_DIALING:
    118                     call = mCallIdMapper.getCall(msg.obj);
    119                     if (call != null) {
    120                         mCallsManager.markCallAsDialing(call);
    121                     } else {
    122                         //Log.w(this, "setDialing, unknown call id: %s", msg.obj);
    123                     }
    124                     break;
    125                 case MSG_SET_DISCONNECTED: {
    126                     SomeArgs args = (SomeArgs) msg.obj;
    127                     try {
    128                         call = mCallIdMapper.getCall(args.arg1);
    129                         DisconnectCause disconnectCause = (DisconnectCause) args.arg2;
    130                         Log.d(this, "disconnect call %s %s", disconnectCause, call);
    131                         if (call != null) {
    132                             mCallsManager.markCallAsDisconnected(call, disconnectCause);
    133                         } else {
    134                             //Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
    135                         }
    136                     } finally {
    137                         args.recycle();
    138                     }
    139                     break;
    140                 }
    141                 case MSG_SET_ON_HOLD:
    142                     call = mCallIdMapper.getCall(msg.obj);
    143                     if (call != null) {
    144                         mCallsManager.markCallAsOnHold(call);
    145                     } else {
    146                         //Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
    147                     }
    148                     break;
    149                 case MSG_SET_RINGBACK_REQUESTED: {
    150                     call = mCallIdMapper.getCall(msg.obj);
    151                     if (call != null) {
    152                         call.setRingbackRequested(msg.arg1 == 1);
    153                     } else {
    154                         //Log.w(this, "setRingback, unknown call id: %s", args.arg1);
    155                     }
    156                     break;
    157                 }
    158                 case MSG_SET_CALL_CAPABILITIES: {
    159                     call = mCallIdMapper.getCall(msg.obj);
    160                     if (call != null) {
    161                         call.setCallCapabilities(msg.arg1);
    162                     } else {
    163                         //Log.w(ConnectionServiceWrapper.this,
    164                         //      "setCallCapabilities, unknown call id: %s", msg.obj);
    165                     }
    166                     break;
    167                 }
    168                 case MSG_SET_IS_CONFERENCED: {
    169                     SomeArgs args = (SomeArgs) msg.obj;
    170                     try {
    171                         Call childCall = mCallIdMapper.getCall(args.arg1);
    172                         Log.d(this, "SET_IS_CONFERENCE: %s %s", args.arg1, args.arg2);
    173                         if (childCall != null) {
    174                             String conferenceCallId = (String) args.arg2;
    175                             if (conferenceCallId == null) {
    176                                 Log.d(this, "unsetting parent: %s", args.arg1);
    177                                 childCall.setParentCall(null);
    178                             } else {
    179                                 Call conferenceCall = mCallIdMapper.getCall(conferenceCallId);
    180                                 childCall.setParentCall(conferenceCall);
    181                             }
    182                         } else {
    183                             //Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
    184                         }
    185                     } finally {
    186                         args.recycle();
    187                     }
    188                     break;
    189                 }
    190                 case MSG_ADD_CONFERENCE_CALL: {
    191                     SomeArgs args = (SomeArgs) msg.obj;
    192                     try {
    193                         String id = (String) args.arg1;
    194                         if (mCallIdMapper.getCall(id) != null) {
    195                             Log.w(this, "Attempting to add a conference call using an existing " +
    196                                     "call id %s", id);
    197                             break;
    198                         }
    199                         ParcelableConference parcelableConference =
    200                                 (ParcelableConference) args.arg2;
    201 
    202                         // Make sure that there's at least one valid call. For remote connections
    203                         // we'll get a add conference msg from both the remote connection service
    204                         // and from the real connection service.
    205                         boolean hasValidCalls = false;
    206                         for (String callId : parcelableConference.getConnectionIds()) {
    207                             if (mCallIdMapper.getCall(callId) != null) {
    208                                 hasValidCalls = true;
    209                             }
    210                         }
    211                         if (!hasValidCalls) {
    212                             Log.d(this, "Attempting to add a conference with no valid calls");
    213                             break;
    214                         }
    215 
    216                         // need to create a new Call
    217                         Call conferenceCall = mCallsManager.createConferenceCall(
    218                                 null, parcelableConference);
    219                         mCallIdMapper.addCall(conferenceCall, id);
    220                         conferenceCall.setConnectionService(ConnectionServiceWrapper.this);
    221 
    222                         Log.d(this, "adding children to conference %s",
    223                                 parcelableConference.getConnectionIds());
    224                         for (String callId : parcelableConference.getConnectionIds()) {
    225                             Call childCall = mCallIdMapper.getCall(callId);
    226                             Log.d(this, "found child: %s", callId);
    227                             if (childCall != null) {
    228                                 childCall.setParentCall(conferenceCall);
    229                             }
    230                         }
    231                     } finally {
    232                         args.recycle();
    233                     }
    234                     break;
    235                 }
    236                 case MSG_REMOVE_CALL: {
    237                     call = mCallIdMapper.getCall(msg.obj);
    238                     if (call != null) {
    239                         if (call.isActive()) {
    240                             mCallsManager.markCallAsDisconnected(
    241                                     call, new DisconnectCause(DisconnectCause.REMOTE));
    242                         } else {
    243                             mCallsManager.markCallAsRemoved(call);
    244                         }
    245                     }
    246                     break;
    247                 }
    248                 case MSG_ON_POST_DIAL_WAIT: {
    249                     SomeArgs args = (SomeArgs) msg.obj;
    250                     try {
    251                         call = mCallIdMapper.getCall(args.arg1);
    252                         if (call != null) {
    253                             String remaining = (String) args.arg2;
    254                             call.onPostDialWait(remaining);
    255                         } else {
    256                             //Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
    257                         }
    258                     } finally {
    259                         args.recycle();
    260                     }
    261                     break;
    262                 }
    263                 case MSG_QUERY_REMOTE_CALL_SERVICES: {
    264                     queryRemoteConnectionServices((RemoteServiceCallback) msg.obj);
    265                     break;
    266                 }
    267                 case MSG_SET_VIDEO_PROVIDER: {
    268                     SomeArgs args = (SomeArgs) msg.obj;
    269                     try {
    270                         call = mCallIdMapper.getCall(args.arg1);
    271                         IVideoProvider videoProvider = (IVideoProvider) args.arg2;
    272                         if (call != null) {
    273                             call.setVideoProvider(videoProvider);
    274                         }
    275                     } finally {
    276                         args.recycle();
    277                     }
    278                     break;
    279                 }
    280                 case MSG_SET_IS_VOIP_AUDIO_MODE: {
    281                     call = mCallIdMapper.getCall(msg.obj);
    282                     if (call != null) {
    283                         call.setIsVoipAudioMode(msg.arg1 == 1);
    284                     }
    285                     break;
    286                 }
    287                 case MSG_SET_STATUS_HINTS: {
    288                     SomeArgs args = (SomeArgs) msg.obj;
    289                     try {
    290                         call = mCallIdMapper.getCall(args.arg1);
    291                         StatusHints statusHints = (StatusHints) args.arg2;
    292                         if (call != null) {
    293                             call.setStatusHints(statusHints);
    294                         }
    295                     } finally {
    296                         args.recycle();
    297                     }
    298                     break;
    299                 }
    300                 case MSG_SET_ADDRESS: {
    301                     SomeArgs args = (SomeArgs) msg.obj;
    302                     try {
    303                         call = mCallIdMapper.getCall(args.arg1);
    304                         if (call != null) {
    305                             call.setHandle((Uri) args.arg2, args.argi1);
    306                         }
    307                     } finally {
    308                         args.recycle();
    309                     }
    310                     break;
    311                 }
    312                 case MSG_SET_CALLER_DISPLAY_NAME: {
    313                     SomeArgs args = (SomeArgs) msg.obj;
    314                     try {
    315                         call = mCallIdMapper.getCall(args.arg1);
    316                         if (call != null) {
    317                             call.setCallerDisplayName((String) args.arg2, args.argi1);
    318                         }
    319                     } finally {
    320                         args.recycle();
    321                     }
    322                     break;
    323                 }
    324                 case MSG_SET_VIDEO_STATE: {
    325                     call = mCallIdMapper.getCall(msg.obj);
    326                     if (call != null) {
    327                         call.setVideoState(msg.arg1);
    328                     }
    329                     break;
    330                 }
    331                 case MSG_SET_CONFERENCEABLE_CONNECTIONS: {
    332                     SomeArgs args = (SomeArgs) msg.obj;
    333                     try {
    334                         call = mCallIdMapper.getCall(args.arg1);
    335                         if (call != null ){
    336                             @SuppressWarnings("unchecked")
    337                             List<String> conferenceableIds = (List<String>) args.arg2;
    338                             List<Call> conferenceableCalls =
    339                                     new ArrayList<>(conferenceableIds.size());
    340                             for (String otherId : (List<String>) args.arg2) {
    341                                 Call otherCall = mCallIdMapper.getCall(otherId);
    342                                 if (otherCall != null && otherCall != call) {
    343                                     conferenceableCalls.add(otherCall);
    344                                 }
    345                             }
    346                             call.setConferenceableCalls(conferenceableCalls);
    347                         }
    348                     } finally {
    349                         args.recycle();
    350                     }
    351                     break;
    352                 }
    353             }
    354         }
    355     };
    356 
    357     private final class Adapter extends IConnectionServiceAdapter.Stub {
    358 
    359         @Override
    360         public void handleCreateConnectionComplete(
    361                 String callId,
    362                 ConnectionRequest request,
    363                 ParcelableConnection connection) {
    364             logIncoming("handleCreateConnectionComplete %s", request);
    365             if (mCallIdMapper.isValidCallId(callId)) {
    366                 SomeArgs args = SomeArgs.obtain();
    367                 args.arg1 = callId;
    368                 args.arg2 = request;
    369                 args.arg3 = connection;
    370                 mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_COMPLETE, args)
    371                         .sendToTarget();
    372             }
    373         }
    374 
    375         @Override
    376         public void setActive(String callId) {
    377             logIncoming("setActive %s", callId);
    378             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
    379                 mHandler.obtainMessage(MSG_SET_ACTIVE, callId).sendToTarget();
    380             }
    381         }
    382 
    383         @Override
    384         public void setRinging(String callId) {
    385             logIncoming("setRinging %s", callId);
    386             if (mCallIdMapper.isValidCallId(callId)) {
    387                 mHandler.obtainMessage(MSG_SET_RINGING, callId).sendToTarget();
    388             }
    389         }
    390 
    391         @Override
    392         public void setVideoProvider(String callId, IVideoProvider videoProvider) {
    393             logIncoming("setVideoProvider %s", callId);
    394             if (mCallIdMapper.isValidCallId(callId)) {
    395                 SomeArgs args = SomeArgs.obtain();
    396                 args.arg1 = callId;
    397                 args.arg2 = videoProvider;
    398                 mHandler.obtainMessage(MSG_SET_VIDEO_PROVIDER, args).sendToTarget();
    399             }
    400         }
    401 
    402         @Override
    403         public void setDialing(String callId) {
    404             logIncoming("setDialing %s", callId);
    405             if (mCallIdMapper.isValidCallId(callId)) {
    406                 mHandler.obtainMessage(MSG_SET_DIALING, callId).sendToTarget();
    407             }
    408         }
    409 
    410         @Override
    411         public void setDisconnected(String callId, DisconnectCause disconnectCause) {
    412             logIncoming("setDisconnected %s %s", callId, disconnectCause);
    413             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
    414                 Log.d(this, "disconnect call %s", callId);
    415                 SomeArgs args = SomeArgs.obtain();
    416                 args.arg1 = callId;
    417                 args.arg2 = disconnectCause;
    418                 mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
    419             }
    420         }
    421 
    422         @Override
    423         public void setOnHold(String callId) {
    424             logIncoming("setOnHold %s", callId);
    425             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
    426                 mHandler.obtainMessage(MSG_SET_ON_HOLD, callId).sendToTarget();
    427             }
    428         }
    429 
    430         @Override
    431         public void setRingbackRequested(String callId, boolean ringback) {
    432             logIncoming("setRingbackRequested %s %b", callId, ringback);
    433             if (mCallIdMapper.isValidCallId(callId)) {
    434                 mHandler.obtainMessage(MSG_SET_RINGBACK_REQUESTED, ringback ? 1 : 0, 0, callId)
    435                         .sendToTarget();
    436             }
    437         }
    438 
    439         @Override
    440         public void removeCall(String callId) {
    441             logIncoming("removeCall %s", callId);
    442             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
    443                 mHandler.obtainMessage(MSG_REMOVE_CALL, callId).sendToTarget();
    444             }
    445         }
    446 
    447         @Override
    448         public void setCallCapabilities(String callId, int callCapabilities) {
    449             logIncoming("setCallCapabilities %s %d", callId, callCapabilities);
    450             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
    451                 mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, callCapabilities, 0, callId)
    452                         .sendToTarget();
    453             } else {
    454                 Log.w(this, "ID not valid for setCallCapabilities");
    455             }
    456         }
    457 
    458         @Override
    459         public void setIsConferenced(String callId, String conferenceCallId) {
    460             logIncoming("setIsConferenced %s %s", callId, conferenceCallId);
    461             if (mCallIdMapper.isValidCallId(callId)) {
    462                 SomeArgs args = SomeArgs.obtain();
    463                 args.arg1 = callId;
    464                 args.arg2 = conferenceCallId;
    465                 mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget();
    466             }
    467         }
    468 
    469         @Override
    470         public void addConferenceCall(String callId, ParcelableConference parcelableConference) {
    471             logIncoming("addConferenceCall %s %s", callId, parcelableConference);
    472             // We do not check call Ids here because we do not yet know the call ID for new
    473             // conference calls.
    474             SomeArgs args = SomeArgs.obtain();
    475             args.arg1 = callId;
    476             args.arg2 = parcelableConference;
    477             mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, args).sendToTarget();
    478         }
    479 
    480         @Override
    481         public void onPostDialWait(String callId, String remaining) throws RemoteException {
    482             logIncoming("onPostDialWait %s %s", callId, remaining);
    483             if (mCallIdMapper.isValidCallId(callId)) {
    484                 SomeArgs args = SomeArgs.obtain();
    485                 args.arg1 = callId;
    486                 args.arg2 = remaining;
    487                 mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget();
    488             }
    489         }
    490 
    491         @Override
    492         public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
    493             logIncoming("queryRemoteCSs");
    494             mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget();
    495         }
    496 
    497         @Override
    498         public void setVideoState(String callId, int videoState) {
    499             logIncoming("setVideoState %s %d", callId, videoState);
    500             if (mCallIdMapper.isValidCallId(callId)) {
    501                 mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState, 0, callId).sendToTarget();
    502             }
    503         }
    504 
    505         @Override
    506         public void setIsVoipAudioMode(String callId, boolean isVoip) {
    507             logIncoming("setIsVoipAudioMode %s %b", callId, isVoip);
    508             if (mCallIdMapper.isValidCallId(callId)) {
    509                 mHandler.obtainMessage(MSG_SET_IS_VOIP_AUDIO_MODE, isVoip ? 1 : 0, 0,
    510                         callId).sendToTarget();
    511             }
    512         }
    513 
    514         @Override
    515         public void setStatusHints(String callId, StatusHints statusHints) {
    516             logIncoming("setStatusHints %s %s", callId, statusHints);
    517             if (mCallIdMapper.isValidCallId(callId)) {
    518                 SomeArgs args = SomeArgs.obtain();
    519                 args.arg1 = callId;
    520                 args.arg2 = statusHints;
    521                 mHandler.obtainMessage(MSG_SET_STATUS_HINTS, args).sendToTarget();
    522             }
    523         }
    524 
    525         @Override
    526         public void setAddress(String callId, Uri address, int presentation) {
    527             logIncoming("setAddress %s %s %d", callId, address, presentation);
    528             if (mCallIdMapper.isValidCallId(callId)) {
    529                 SomeArgs args = SomeArgs.obtain();
    530                 args.arg1 = callId;
    531                 args.arg2 = address;
    532                 args.argi1 = presentation;
    533                 mHandler.obtainMessage(MSG_SET_ADDRESS, args).sendToTarget();
    534             }
    535         }
    536 
    537         @Override
    538         public void setCallerDisplayName(
    539                 String callId, String callerDisplayName, int presentation) {
    540             logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName, presentation);
    541             if (mCallIdMapper.isValidCallId(callId)) {
    542                 SomeArgs args = SomeArgs.obtain();
    543                 args.arg1 = callId;
    544                 args.arg2 = callerDisplayName;
    545                 args.argi1 = presentation;
    546                 mHandler.obtainMessage(MSG_SET_CALLER_DISPLAY_NAME, args).sendToTarget();
    547             }
    548         }
    549 
    550         @Override
    551         public void setConferenceableConnections(
    552                 String callId, List<String> conferenceableCallIds) {
    553             logIncoming("setConferenceableConnections %s %s", callId, conferenceableCallIds);
    554             if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
    555                 SomeArgs args = SomeArgs.obtain();
    556                 args.arg1 = callId;
    557                 args.arg2 = conferenceableCallIds;
    558                 mHandler.obtainMessage(MSG_SET_CONFERENCEABLE_CONNECTIONS, args).sendToTarget();
    559             }
    560         }
    561     }
    562 
    563     private final Adapter mAdapter = new Adapter();
    564     private final CallsManager mCallsManager = CallsManager.getInstance();
    565     /**
    566      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
    567      * load factor before resizing, 1 means we only expect a single thread to
    568      * access the map so make only a single shard
    569      */
    570     private final Set<Call> mPendingConferenceCalls = Collections.newSetFromMap(
    571             new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
    572     private final CallIdMapper mCallIdMapper = new CallIdMapper("ConnectionService");
    573     private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
    574 
    575     private Binder mBinder = new Binder();
    576     private IConnectionService mServiceInterface;
    577     private final ConnectionServiceRepository mConnectionServiceRepository;
    578     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
    579 
    580     /**
    581      * Creates a connection service.
    582      *
    583      * @param componentName The component name of the service with which to bind.
    584      * @param connectionServiceRepository Connection service repository.
    585      * @param phoneAccountRegistrar Phone account registrar
    586      * @param context The context.
    587      */
    588     ConnectionServiceWrapper(
    589             ComponentName componentName,
    590             ConnectionServiceRepository connectionServiceRepository,
    591             PhoneAccountRegistrar phoneAccountRegistrar,
    592             Context context) {
    593         super(ConnectionService.SERVICE_INTERFACE, componentName, context);
    594         mConnectionServiceRepository = connectionServiceRepository;
    595         phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {
    596             // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections
    597             // To do this, we must proxy remote ConnectionService objects
    598         });
    599         mPhoneAccountRegistrar = phoneAccountRegistrar;
    600     }
    601 
    602     /** See {@link IConnectionService#addConnectionServiceAdapter}. */
    603     private void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
    604         if (isServiceValid("addConnectionServiceAdapter")) {
    605             try {
    606                 logOutgoing("addConnectionServiceAdapter %s", adapter);
    607                 mServiceInterface.addConnectionServiceAdapter(adapter);
    608             } catch (RemoteException e) {
    609             }
    610         }
    611     }
    612 
    613     /**
    614      * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
    615      */
    616     void createConnection(final Call call, final CreateConnectionResponse response) {
    617         Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
    618         BindCallback callback = new BindCallback() {
    619             @Override
    620             public void onSuccess() {
    621                 String callId = mCallIdMapper.getCallId(call);
    622                 mPendingResponses.put(callId, response);
    623 
    624                 GatewayInfo gatewayInfo = call.getGatewayInfo();
    625                 Bundle extras = call.getExtras();
    626                 if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
    627                         gatewayInfo.getOriginalAddress() != null) {
    628                     extras = (Bundle) extras.clone();
    629                     extras.putString(
    630                             TelecomManager.GATEWAY_PROVIDER_PACKAGE,
    631                             gatewayInfo.getGatewayProviderPackageName());
    632                     extras.putParcelable(
    633                             TelecomManager.GATEWAY_ORIGINAL_ADDRESS,
    634                             gatewayInfo.getOriginalAddress());
    635                 }
    636 
    637                 try {
    638                     mServiceInterface.createConnection(
    639                             call.getConnectionManagerPhoneAccount(),
    640                             callId,
    641                             new ConnectionRequest(
    642                                     call.getTargetPhoneAccount(),
    643                                     call.getHandle(),
    644                                     extras,
    645                                     call.getVideoState()),
    646                             call.isIncoming(),
    647                             call.isUnknown());
    648                 } catch (RemoteException e) {
    649                     Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
    650                     mPendingResponses.remove(callId).handleCreateConnectionFailure(
    651                             new DisconnectCause(DisconnectCause.ERROR, e.toString()));
    652                 }
    653             }
    654 
    655             @Override
    656             public void onFailure() {
    657                 Log.e(this, new Exception(), "Failure to call %s", getComponentName());
    658                 response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
    659             }
    660         };
    661 
    662         mBinder.bind(callback);
    663     }
    664 
    665     /** @see ConnectionService#abort(String) */
    666     void abort(Call call) {
    667         // Clear out any pending outgoing call data
    668         final String callId = mCallIdMapper.getCallId(call);
    669 
    670         // If still bound, tell the connection service to abort.
    671         if (callId != null && isServiceValid("abort")) {
    672             try {
    673                 logOutgoing("abort %s", callId);
    674                 mServiceInterface.abort(callId);
    675             } catch (RemoteException e) {
    676             }
    677         }
    678 
    679         removeCall(call, new DisconnectCause(DisconnectCause.LOCAL));
    680     }
    681 
    682     /** @see ConnectionService#hold(String) */
    683     void hold(Call call) {
    684         final String callId = mCallIdMapper.getCallId(call);
    685         if (callId != null && isServiceValid("hold")) {
    686             try {
    687                 logOutgoing("hold %s", callId);
    688                 mServiceInterface.hold(callId);
    689             } catch (RemoteException e) {
    690             }
    691         }
    692     }
    693 
    694     /** @see ConnectionService#unhold(String) */
    695     void unhold(Call call) {
    696         final String callId = mCallIdMapper.getCallId(call);
    697         if (callId != null && isServiceValid("unhold")) {
    698             try {
    699                 logOutgoing("unhold %s", callId);
    700                 mServiceInterface.unhold(callId);
    701             } catch (RemoteException e) {
    702             }
    703         }
    704     }
    705 
    706     /** @see ConnectionService#onAudioStateChanged(String,AudioState) */
    707     void onAudioStateChanged(Call activeCall, AudioState audioState) {
    708         final String callId = mCallIdMapper.getCallId(activeCall);
    709         if (callId != null && isServiceValid("onAudioStateChanged")) {
    710             try {
    711                 logOutgoing("onAudioStateChanged %s %s", callId, audioState);
    712                 mServiceInterface.onAudioStateChanged(callId, audioState);
    713             } catch (RemoteException e) {
    714             }
    715         }
    716     }
    717 
    718     /** @see ConnectionService#disconnect(String) */
    719     void disconnect(Call call) {
    720         final String callId = mCallIdMapper.getCallId(call);
    721         if (callId != null && isServiceValid("disconnect")) {
    722             try {
    723                 logOutgoing("disconnect %s", callId);
    724                 mServiceInterface.disconnect(callId);
    725             } catch (RemoteException e) {
    726             }
    727         }
    728     }
    729 
    730     /** @see ConnectionService#answer(String,int) */
    731     void answer(Call call, int videoState) {
    732         final String callId = mCallIdMapper.getCallId(call);
    733         if (callId != null && isServiceValid("answer")) {
    734             try {
    735                 logOutgoing("answer %s %d", callId, videoState);
    736                 if (videoState == VideoProfile.VideoState.AUDIO_ONLY) {
    737                     mServiceInterface.answer(callId);
    738                 } else {
    739                     mServiceInterface.answerVideo(callId, videoState);
    740                 }
    741             } catch (RemoteException e) {
    742             }
    743         }
    744     }
    745 
    746     /** @see ConnectionService#reject(String) */
    747     void reject(Call call) {
    748         final String callId = mCallIdMapper.getCallId(call);
    749         if (callId != null && isServiceValid("reject")) {
    750             try {
    751                 logOutgoing("reject %s", callId);
    752                 mServiceInterface.reject(callId);
    753             } catch (RemoteException e) {
    754             }
    755         }
    756     }
    757 
    758     /** @see ConnectionService#playDtmfTone(String,char) */
    759     void playDtmfTone(Call call, char digit) {
    760         final String callId = mCallIdMapper.getCallId(call);
    761         if (callId != null && isServiceValid("playDtmfTone")) {
    762             try {
    763                 logOutgoing("playDtmfTone %s %c", callId, digit);
    764                 mServiceInterface.playDtmfTone(callId, digit);
    765             } catch (RemoteException e) {
    766             }
    767         }
    768     }
    769 
    770     /** @see ConnectionService#stopDtmfTone(String) */
    771     void stopDtmfTone(Call call) {
    772         final String callId = mCallIdMapper.getCallId(call);
    773         if (callId != null && isServiceValid("stopDtmfTone")) {
    774             try {
    775                 logOutgoing("stopDtmfTone %s",callId);
    776                 mServiceInterface.stopDtmfTone(callId);
    777             } catch (RemoteException e) {
    778             }
    779         }
    780     }
    781 
    782     void addCall(Call call) {
    783         if (mCallIdMapper.getCallId(call) == null) {
    784             mCallIdMapper.addCall(call);
    785         }
    786     }
    787 
    788     /**
    789      * Associates newCall with this connection service by replacing callToReplace.
    790      */
    791     void replaceCall(Call newCall, Call callToReplace) {
    792         Preconditions.checkState(callToReplace.getConnectionService() == this);
    793         mCallIdMapper.replaceCall(newCall, callToReplace);
    794     }
    795 
    796     void removeCall(Call call) {
    797         removeCall(call, new DisconnectCause(DisconnectCause.ERROR));
    798     }
    799 
    800     void removeCall(String callId, DisconnectCause disconnectCause) {
    801         CreateConnectionResponse response = mPendingResponses.remove(callId);
    802         if (response != null) {
    803             response.handleCreateConnectionFailure(disconnectCause);
    804         }
    805 
    806         mCallIdMapper.removeCall(callId);
    807     }
    808 
    809     void removeCall(Call call, DisconnectCause disconnectCause) {
    810         CreateConnectionResponse response = mPendingResponses.remove(mCallIdMapper.getCallId(call));
    811         if (response != null) {
    812             response.handleCreateConnectionFailure(disconnectCause);
    813         }
    814 
    815         mCallIdMapper.removeCall(call);
    816     }
    817 
    818     void onPostDialContinue(Call call, boolean proceed) {
    819         final String callId = mCallIdMapper.getCallId(call);
    820         if (callId != null && isServiceValid("onPostDialContinue")) {
    821             try {
    822                 logOutgoing("onPostDialContinue %s %b", callId, proceed);
    823                 mServiceInterface.onPostDialContinue(callId, proceed);
    824             } catch (RemoteException ignored) {
    825             }
    826         }
    827     }
    828 
    829     void conference(final Call call, Call otherCall) {
    830         final String callId = mCallIdMapper.getCallId(call);
    831         final String otherCallId = mCallIdMapper.getCallId(otherCall);
    832         if (callId != null && otherCallId != null && isServiceValid("conference")) {
    833             try {
    834                 logOutgoing("conference %s %s", callId, otherCallId);
    835                 mServiceInterface.conference(callId, otherCallId);
    836             } catch (RemoteException ignored) {
    837             }
    838         }
    839     }
    840 
    841     void splitFromConference(Call call) {
    842         final String callId = mCallIdMapper.getCallId(call);
    843         if (callId != null && isServiceValid("splitFromConference")) {
    844             try {
    845                 logOutgoing("splitFromConference %s", callId);
    846                 mServiceInterface.splitFromConference(callId);
    847             } catch (RemoteException ignored) {
    848             }
    849         }
    850     }
    851 
    852     void mergeConference(Call call) {
    853         final String callId = mCallIdMapper.getCallId(call);
    854         if (callId != null && isServiceValid("mergeConference")) {
    855             try {
    856                 logOutgoing("mergeConference %s", callId);
    857                 mServiceInterface.mergeConference(callId);
    858             } catch (RemoteException ignored) {
    859             }
    860         }
    861     }
    862 
    863     void swapConference(Call call) {
    864         final String callId = mCallIdMapper.getCallId(call);
    865         if (callId != null && isServiceValid("swapConference")) {
    866             try {
    867                 logOutgoing("swapConference %s", callId);
    868                 mServiceInterface.swapConference(callId);
    869             } catch (RemoteException ignored) {
    870             }
    871         }
    872     }
    873 
    874     /** {@inheritDoc} */
    875     @Override
    876     protected void setServiceInterface(IBinder binder) {
    877         if (binder == null) {
    878             // We have lost our service connection. Notify the world that this service is done.
    879             // We must notify the adapter before CallsManager. The adapter will force any pending
    880             // outgoing calls to try the next service. This needs to happen before CallsManager
    881             // tries to clean up any calls still associated with this service.
    882             handleConnectionServiceDeath();
    883             CallsManager.getInstance().handleConnectionServiceDeath(this);
    884             mServiceInterface = null;
    885         } else {
    886             mServiceInterface = IConnectionService.Stub.asInterface(binder);
    887             addConnectionServiceAdapter(mAdapter);
    888         }
    889     }
    890 
    891     private void handleCreateConnectionComplete(
    892             String callId,
    893             ConnectionRequest request,
    894             ParcelableConnection connection) {
    895         // TODO: Note we are not using parameter "request", which is a side effect of our tacit
    896         // assumption that we have at most one outgoing connection attempt per ConnectionService.
    897         // This may not continue to be the case.
    898         if (connection.getState() == Connection.STATE_DISCONNECTED) {
    899             // A connection that begins in the DISCONNECTED state is an indication of
    900             // failure to connect; we handle all failures uniformly
    901             removeCall(callId, connection.getDisconnectCause());
    902         } else {
    903             // Successful connection
    904             if (mPendingResponses.containsKey(callId)) {
    905                 mPendingResponses.remove(callId)
    906                         .handleCreateConnectionSuccess(mCallIdMapper, connection);
    907             }
    908         }
    909     }
    910 
    911     /**
    912      * Called when the associated connection service dies.
    913      */
    914     private void handleConnectionServiceDeath() {
    915         if (!mPendingResponses.isEmpty()) {
    916             CreateConnectionResponse[] responses = mPendingResponses.values().toArray(
    917                     new CreateConnectionResponse[mPendingResponses.values().size()]);
    918             mPendingResponses.clear();
    919             for (int i = 0; i < responses.length; i++) {
    920                 responses[i].handleCreateConnectionFailure(
    921                         new DisconnectCause(DisconnectCause.ERROR));
    922             }
    923         }
    924         mCallIdMapper.clear();
    925     }
    926 
    927     private void logIncoming(String msg, Object... params) {
    928         Log.d(this, "ConnectionService -> Telecom: " + msg, params);
    929     }
    930 
    931     private void logOutgoing(String msg, Object... params) {
    932         Log.d(this, "Telecom -> ConnectionService: " + msg, params);
    933     }
    934 
    935     private void queryRemoteConnectionServices(final RemoteServiceCallback callback) {
    936         // Only give remote connection services to this connection service if it is listed as
    937         // the connection manager.
    938         PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager();
    939         Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager);
    940         if (simCallManager == null ||
    941                 !simCallManager.getComponentName().equals(getComponentName())) {
    942             noRemoteServices(callback);
    943             return;
    944         }
    945 
    946         // Make a list of ConnectionServices that are listed as being associated with SIM accounts
    947         final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap(
    948                 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1));
    949         for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getCallCapablePhoneAccounts()) {
    950             PhoneAccount account = mPhoneAccountRegistrar.getPhoneAccount(handle);
    951             if ((account.getCapabilities() & PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) != 0) {
    952                 ConnectionServiceWrapper service =
    953                         mConnectionServiceRepository.getService(handle.getComponentName());
    954                 if (service != null) {
    955                     simServices.add(service);
    956                 }
    957             }
    958         }
    959 
    960         final List<ComponentName> simServiceComponentNames = new ArrayList<>();
    961         final List<IBinder> simServiceBinders = new ArrayList<>();
    962 
    963         Log.v(this, "queryRemoteConnectionServices, simServices = %s", simServices);
    964 
    965         for (ConnectionServiceWrapper simService : simServices) {
    966             if (simService == this) {
    967                 // Only happens in the unlikely case that a SIM service is also a SIM call manager
    968                 continue;
    969             }
    970 
    971             final ConnectionServiceWrapper currentSimService = simService;
    972 
    973             currentSimService.mBinder.bind(new BindCallback() {
    974                 @Override
    975                 public void onSuccess() {
    976                     Log.d(this, "Adding simService %s", currentSimService.getComponentName());
    977                     simServiceComponentNames.add(currentSimService.getComponentName());
    978                     simServiceBinders.add(currentSimService.mServiceInterface.asBinder());
    979                     maybeComplete();
    980                 }
    981 
    982                 @Override
    983                 public void onFailure() {
    984                     Log.d(this, "Failed simService %s", currentSimService.getComponentName());
    985                     // We know maybeComplete() will always be a no-op from now on, so go ahead and
    986                     // signal failure of the entire request
    987                     noRemoteServices(callback);
    988                 }
    989 
    990                 private void maybeComplete() {
    991                     if (simServiceComponentNames.size() == simServices.size()) {
    992                         setRemoteServices(callback, simServiceComponentNames, simServiceBinders);
    993                     }
    994                 }
    995             });
    996         }
    997     }
    998 
    999     private void setRemoteServices(
   1000             RemoteServiceCallback callback,
   1001             List<ComponentName> componentNames,
   1002             List<IBinder> binders) {
   1003         try {
   1004             callback.onResult(componentNames, binders);
   1005         } catch (RemoteException e) {
   1006             Log.e(this, e, "Contacting ConnectionService %s",
   1007                     ConnectionServiceWrapper.this.getComponentName());
   1008         }
   1009     }
   1010 
   1011     private void noRemoteServices(RemoteServiceCallback callback) {
   1012         try {
   1013             callback.onResult(Collections.EMPTY_LIST, Collections.EMPTY_LIST);
   1014         } catch (RemoteException e) {
   1015             Log.e(this, e, "Contacting ConnectionService %s", this.getComponentName());
   1016         }
   1017     }
   1018 }
   1019