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