Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 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.services.telephony;
     18 
     19 import android.content.ActivityNotFoundException;
     20 import android.content.ComponentName;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.net.Uri;
     24 import android.os.Bundle;
     25 import android.telecom.Conference;
     26 import android.telecom.Connection;
     27 import android.telecom.ConnectionRequest;
     28 import android.telecom.ConnectionService;
     29 import android.telecom.DisconnectCause;
     30 import android.telecom.PhoneAccount;
     31 import android.telecom.PhoneAccountHandle;
     32 import android.telecom.TelecomManager;
     33 import android.telecom.VideoProfile;
     34 import android.telephony.CarrierConfigManager;
     35 import android.telephony.PhoneNumberUtils;
     36 import android.telephony.ServiceState;
     37 import android.telephony.SubscriptionManager;
     38 import android.telephony.TelephonyManager;
     39 import android.text.TextUtils;
     40 
     41 import com.android.internal.telephony.Call;
     42 import com.android.internal.telephony.CallStateException;
     43 import com.android.internal.telephony.IccCard;
     44 import com.android.internal.telephony.IccCardConstants;
     45 import com.android.internal.telephony.Phone;
     46 import com.android.internal.telephony.PhoneConstants;
     47 import com.android.internal.telephony.PhoneFactory;
     48 import com.android.internal.telephony.SubscriptionController;
     49 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
     50 import com.android.internal.telephony.imsphone.ImsPhone;
     51 import com.android.phone.MMIDialogActivity;
     52 import com.android.phone.PhoneUtils;
     53 import com.android.phone.R;
     54 
     55 import java.util.ArrayList;
     56 import java.util.Collection;
     57 import java.util.List;
     58 import java.util.regex.Pattern;
     59 
     60 /**
     61  * Service for making GSM and CDMA connections.
     62  */
     63 public class TelephonyConnectionService extends ConnectionService {
     64 
     65     // If configured, reject attempts to dial numbers matching this pattern.
     66     private static final Pattern CDMA_ACTIVATION_CODE_REGEX_PATTERN =
     67             Pattern.compile("\\*228[0-9]{0,2}");
     68 
     69     private final TelephonyConferenceController mTelephonyConferenceController =
     70             new TelephonyConferenceController(this);
     71     private final CdmaConferenceController mCdmaConferenceController =
     72             new CdmaConferenceController(this);
     73     private final ImsConferenceController mImsConferenceController =
     74             new ImsConferenceController(this);
     75 
     76     private ComponentName mExpectedComponentName = null;
     77     private EmergencyCallHelper mEmergencyCallHelper;
     78     private EmergencyTonePlayer mEmergencyTonePlayer;
     79 
     80     /**
     81      * A listener to actionable events specific to the TelephonyConnection.
     82      */
     83     private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
     84             new TelephonyConnection.TelephonyConnectionListener() {
     85         @Override
     86         public void onOriginalConnectionConfigured(TelephonyConnection c) {
     87             addConnectionToConferenceController(c);
     88         }
     89     };
     90 
     91     @Override
     92     public void onCreate() {
     93         super.onCreate();
     94         mExpectedComponentName = new ComponentName(this, this.getClass());
     95         mEmergencyTonePlayer = new EmergencyTonePlayer(this);
     96         TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);
     97     }
     98 
     99     @Override
    100     public Connection onCreateOutgoingConnection(
    101             PhoneAccountHandle connectionManagerPhoneAccount,
    102             final ConnectionRequest request) {
    103         Log.i(this, "onCreateOutgoingConnection, request: " + request);
    104 
    105         Uri handle = request.getAddress();
    106         if (handle == null) {
    107             Log.d(this, "onCreateOutgoingConnection, handle is null");
    108             return Connection.createFailedConnection(
    109                     DisconnectCauseUtil.toTelecomDisconnectCause(
    110                             android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED,
    111                             "No phone number supplied"));
    112         }
    113 
    114         String scheme = handle.getScheme();
    115         final String number;
    116         if (PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
    117             // TODO: We don't check for SecurityException here (requires
    118             // CALL_PRIVILEGED permission).
    119             final Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
    120             if (phone == null) {
    121                 Log.d(this, "onCreateOutgoingConnection, phone is null");
    122                 return Connection.createFailedConnection(
    123                         DisconnectCauseUtil.toTelecomDisconnectCause(
    124                                 android.telephony.DisconnectCause.OUT_OF_SERVICE,
    125                                 "Phone is null"));
    126             }
    127             number = phone.getVoiceMailNumber();
    128             if (TextUtils.isEmpty(number)) {
    129                 Log.d(this, "onCreateOutgoingConnection, no voicemail number set.");
    130                 return Connection.createFailedConnection(
    131                         DisconnectCauseUtil.toTelecomDisconnectCause(
    132                                 android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING,
    133                                 "Voicemail scheme provided but no voicemail number set."));
    134             }
    135 
    136             // Convert voicemail: to tel:
    137             handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
    138         } else {
    139             if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
    140                 Log.d(this, "onCreateOutgoingConnection, Handle %s is not type tel", scheme);
    141                 return Connection.createFailedConnection(
    142                         DisconnectCauseUtil.toTelecomDisconnectCause(
    143                                 android.telephony.DisconnectCause.INVALID_NUMBER,
    144                                 "Handle scheme is not type tel"));
    145             }
    146 
    147             number = handle.getSchemeSpecificPart();
    148             if (TextUtils.isEmpty(number)) {
    149                 Log.d(this, "onCreateOutgoingConnection, unable to parse number");
    150                 return Connection.createFailedConnection(
    151                         DisconnectCauseUtil.toTelecomDisconnectCause(
    152                                 android.telephony.DisconnectCause.INVALID_NUMBER,
    153                                 "Unable to parse number"));
    154             }
    155 
    156             final Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
    157             if (phone != null && CDMA_ACTIVATION_CODE_REGEX_PATTERN.matcher(number).matches()) {
    158                 // Obtain the configuration for the outgoing phone's SIM. If the outgoing number
    159                 // matches the *228 regex pattern, fail the call. This number is used for OTASP, and
    160                 // when dialed could lock LTE SIMs to 3G if not prohibited..
    161                 boolean disableActivation = false;
    162                 CarrierConfigManager cfgManager = (CarrierConfigManager)
    163                         phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
    164                 if (cfgManager != null) {
    165                     disableActivation = cfgManager.getConfigForSubId(phone.getSubId())
    166                             .getBoolean(CarrierConfigManager.KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL);
    167                 }
    168 
    169                 if (disableActivation) {
    170                     return Connection.createFailedConnection(
    171                             DisconnectCauseUtil.toTelecomDisconnectCause(
    172                                     android.telephony.DisconnectCause
    173                                             .CDMA_ALREADY_ACTIVATED,
    174                                     "Tried to dial *228"));
    175                 }
    176             }
    177         }
    178 
    179         final boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(this, number);
    180 
    181         if (isEmergencyNumber && !isRadioOn()) {
    182             final Uri emergencyHandle = handle;
    183             // By default, Connection based on the default Phone, since we need to return to Telecom
    184             // now.
    185             final int defaultPhoneType = PhoneFactory.getDefaultPhone().getPhoneType();
    186             final Connection emergencyConnection = getTelephonyConnection(request, number,
    187                     isEmergencyNumber, emergencyHandle, PhoneFactory.getDefaultPhone());
    188             if (mEmergencyCallHelper == null) {
    189                 mEmergencyCallHelper = new EmergencyCallHelper(this);
    190             }
    191             mEmergencyCallHelper.enableEmergencyCalling(new EmergencyCallStateListener.Callback() {
    192                 @Override
    193                 public void onComplete(EmergencyCallStateListener listener, boolean isRadioReady) {
    194                     // Make sure the Call has not already been canceled by the user.
    195                     if (emergencyConnection.getState() == Connection.STATE_DISCONNECTED) {
    196                         Log.i(this, "Emergency call disconnected before the outgoing call was " +
    197                                 "placed. Skipping emergency call placement.");
    198                         return;
    199                     }
    200                     if (isRadioReady) {
    201                         // Get the right phone object since the radio has been turned on
    202                         // successfully.
    203                         final Phone phone = getPhoneForAccount(request.getAccountHandle(),
    204                                 isEmergencyNumber);
    205                         // If the PhoneType of the Phone being used is different than the Default
    206                         // Phone, then we need create a new Connection using that PhoneType and
    207                         // replace it in Telecom.
    208                         if (phone.getPhoneType() != defaultPhoneType) {
    209                             Connection repConnection = getTelephonyConnection(request, number,
    210                                     isEmergencyNumber, emergencyHandle, phone);
    211                             // If there was a failure, the resulting connection will not be a
    212                             // TelephonyConnection, so don't place the call, just return!
    213                             if (repConnection instanceof TelephonyConnection) {
    214                                 placeOutgoingConnection((TelephonyConnection) repConnection, phone,
    215                                         request);
    216                             }
    217                             // Notify Telecom of the new Connection type.
    218                             // TODO: Switch out the underlying connection instead of creating a new
    219                             // one and causing UI Jank.
    220                             addExistingConnection(PhoneUtils.makePstnPhoneAccountHandle(phone),
    221                                     repConnection);
    222                             // Remove the old connection from Telecom after.
    223                             emergencyConnection.setDisconnected(
    224                                     DisconnectCauseUtil.toTelecomDisconnectCause(
    225                                             android.telephony.DisconnectCause.OUTGOING_CANCELED,
    226                                             "Reconnecting outgoing Emergency Call."));
    227                             emergencyConnection.destroy();
    228                         } else {
    229                             placeOutgoingConnection((TelephonyConnection) emergencyConnection,
    230                                     phone, request);
    231                         }
    232                     } else {
    233                         Log.w(this, "onCreateOutgoingConnection, failed to turn on radio");
    234                         emergencyConnection.setDisconnected(
    235                                 DisconnectCauseUtil.toTelecomDisconnectCause(
    236                                         android.telephony.DisconnectCause.POWER_OFF,
    237                                         "Failed to turn on radio."));
    238                         emergencyConnection.destroy();
    239                     }
    240                 }
    241             });
    242             // Return the still unconnected GsmConnection and wait for the Radios to boot before
    243             // connecting it to the underlying Phone.
    244             return emergencyConnection;
    245         } else {
    246             if (!canAddCall() && !isEmergencyNumber) {
    247                 Log.d(this, "onCreateOutgoingConnection, cannot add call .");
    248                 return Connection.createFailedConnection(
    249                         new DisconnectCause(DisconnectCause.ERROR,
    250                                 getApplicationContext().getText(
    251                                         R.string.incall_error_cannot_add_call),
    252                                 getApplicationContext().getText(
    253                                         R.string.incall_error_cannot_add_call),
    254                                 "Add call restricted due to ongoing video call"));
    255             }
    256 
    257             // Get the right phone object from the account data passed in.
    258             final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
    259             Connection resultConnection = getTelephonyConnection(request, number, isEmergencyNumber,
    260                     handle, phone);
    261             // If there was a failure, the resulting connection will not be a TelephonyConnection,
    262             // so don't place the call!
    263             if(resultConnection instanceof TelephonyConnection) {
    264                 placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
    265             }
    266             return resultConnection;
    267         }
    268     }
    269 
    270     /**
    271      * @return {@code true} if any other call is disabling the ability to add calls, {@code false}
    272      *      otherwise.
    273      */
    274     private boolean canAddCall() {
    275         Collection<Connection> connections = getAllConnections();
    276         for (Connection connection : connections) {
    277             if (connection.getExtras() != null &&
    278                     connection.getExtras().getBoolean(Connection.EXTRA_DISABLE_ADD_CALL, false)) {
    279                 return false;
    280             }
    281         }
    282         return true;
    283     }
    284 
    285     private Connection getTelephonyConnection(final ConnectionRequest request, final String number,
    286             boolean isEmergencyNumber, final Uri handle, Phone phone) {
    287 
    288         if (phone == null) {
    289             final Context context = getApplicationContext();
    290             if (context.getResources().getBoolean(R.bool.config_checkSimStateBeforeOutgoingCall)) {
    291                 // Check SIM card state before the outgoing call.
    292                 // Start the SIM unlock activity if PIN_REQUIRED.
    293                 final Phone defaultPhone = PhoneFactory.getDefaultPhone();
    294                 final IccCard icc = defaultPhone.getIccCard();
    295                 IccCardConstants.State simState = IccCardConstants.State.UNKNOWN;
    296                 if (icc != null) {
    297                     simState = icc.getState();
    298                 }
    299                 if (simState == IccCardConstants.State.PIN_REQUIRED) {
    300                     final String simUnlockUiPackage = context.getResources().getString(
    301                             R.string.config_simUnlockUiPackage);
    302                     final String simUnlockUiClass = context.getResources().getString(
    303                             R.string.config_simUnlockUiClass);
    304                     if (simUnlockUiPackage != null && simUnlockUiClass != null) {
    305                         Intent simUnlockIntent = new Intent().setComponent(new ComponentName(
    306                                 simUnlockUiPackage, simUnlockUiClass));
    307                         simUnlockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    308                         try {
    309                             context.startActivity(simUnlockIntent);
    310                         } catch (ActivityNotFoundException exception) {
    311                             Log.e(this, exception, "Unable to find SIM unlock UI activity.");
    312                         }
    313                     }
    314                     return Connection.createFailedConnection(
    315                             DisconnectCauseUtil.toTelecomDisconnectCause(
    316                                     android.telephony.DisconnectCause.OUT_OF_SERVICE,
    317                                     "SIM_STATE_PIN_REQUIRED"));
    318                 }
    319             }
    320 
    321             Log.d(this, "onCreateOutgoingConnection, phone is null");
    322             return Connection.createFailedConnection(
    323                     DisconnectCauseUtil.toTelecomDisconnectCause(
    324                             android.telephony.DisconnectCause.OUT_OF_SERVICE, "Phone is null"));
    325         }
    326 
    327         // Check both voice & data RAT to enable normal CS call,
    328         // when voice RAT is OOS but Data RAT is present.
    329         int state = phone.getServiceState().getState();
    330         if (state == ServiceState.STATE_OUT_OF_SERVICE) {
    331             int dataNetType = phone.getServiceState().getDataNetworkType();
    332             if (dataNetType == TelephonyManager.NETWORK_TYPE_LTE ||
    333                     dataNetType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
    334                 state = phone.getServiceState().getDataRegState();
    335             }
    336         }
    337 
    338         // If we're dialing a non-emergency number and the phone is in ECM mode, reject the call if
    339         // carrier configuration specifies that we cannot make non-emergency calls in ECM mode.
    340         if (!isEmergencyNumber && phone.isInEcm()) {
    341             boolean allowNonEmergencyCalls = true;
    342             CarrierConfigManager cfgManager = (CarrierConfigManager)
    343                     phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
    344             if (cfgManager != null) {
    345                 allowNonEmergencyCalls = cfgManager.getConfigForSubId(phone.getSubId())
    346                         .getBoolean(CarrierConfigManager.KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL);
    347             }
    348 
    349             if (!allowNonEmergencyCalls) {
    350                 return Connection.createFailedConnection(
    351                         DisconnectCauseUtil.toTelecomDisconnectCause(
    352                                 android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY,
    353                                 "Cannot make non-emergency call in ECM mode."
    354                         ));
    355             }
    356         }
    357 
    358         if (!isEmergencyNumber) {
    359             switch (state) {
    360                 case ServiceState.STATE_IN_SERVICE:
    361                 case ServiceState.STATE_EMERGENCY_ONLY:
    362                     break;
    363                 case ServiceState.STATE_OUT_OF_SERVICE:
    364                     if (phone.isUtEnabled() && number.endsWith("#")) {
    365                         Log.d(this, "onCreateOutgoingConnection dial for UT");
    366                         break;
    367                     } else {
    368                         return Connection.createFailedConnection(
    369                                 DisconnectCauseUtil.toTelecomDisconnectCause(
    370                                         android.telephony.DisconnectCause.OUT_OF_SERVICE,
    371                                         "ServiceState.STATE_OUT_OF_SERVICE"));
    372                     }
    373                 case ServiceState.STATE_POWER_OFF:
    374                     return Connection.createFailedConnection(
    375                             DisconnectCauseUtil.toTelecomDisconnectCause(
    376                                     android.telephony.DisconnectCause.POWER_OFF,
    377                                     "ServiceState.STATE_POWER_OFF"));
    378                 default:
    379                     Log.d(this, "onCreateOutgoingConnection, unknown service state: %d", state);
    380                     return Connection.createFailedConnection(
    381                             DisconnectCauseUtil.toTelecomDisconnectCause(
    382                                     android.telephony.DisconnectCause.OUTGOING_FAILURE,
    383                                     "Unknown service state " + state));
    384             }
    385         }
    386 
    387         final Context context = getApplicationContext();
    388         if (VideoProfile.isVideo(request.getVideoState()) && isTtyModeEnabled(context) &&
    389                 !isEmergencyNumber) {
    390             return Connection.createFailedConnection(DisconnectCauseUtil.toTelecomDisconnectCause(
    391                     android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED));
    392         }
    393 
    394         // Check for additional limits on CDMA phones.
    395         final Connection failedConnection = checkAdditionalOutgoingCallLimits(phone);
    396         if (failedConnection != null) {
    397             return failedConnection;
    398         }
    399 
    400         final TelephonyConnection connection =
    401                 createConnectionFor(phone, null, true /* isOutgoing */, request.getAccountHandle(),
    402                         request.getTelecomCallId(), request.getAddress(), request.getVideoState());
    403         if (connection == null) {
    404             return Connection.createFailedConnection(
    405                     DisconnectCauseUtil.toTelecomDisconnectCause(
    406                             android.telephony.DisconnectCause.OUTGOING_FAILURE,
    407                             "Invalid phone type"));
    408         }
    409         connection.setAddress(handle, PhoneConstants.PRESENTATION_ALLOWED);
    410         connection.setInitializing();
    411         connection.setVideoState(request.getVideoState());
    412 
    413         return connection;
    414     }
    415 
    416     @Override
    417     public Connection onCreateIncomingConnection(
    418             PhoneAccountHandle connectionManagerPhoneAccount,
    419             ConnectionRequest request) {
    420         Log.i(this, "onCreateIncomingConnection, request: " + request);
    421         // If there is an incoming emergency CDMA Call (while the phone is in ECBM w/ No SIM),
    422         // make sure the PhoneAccount lookup retrieves the default Emergency Phone.
    423         PhoneAccountHandle accountHandle = request.getAccountHandle();
    424         boolean isEmergency = false;
    425         if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
    426                 accountHandle.getId())) {
    427             Log.i(this, "Emergency PhoneAccountHandle is being used for incoming call... " +
    428                     "Treat as an Emergency Call.");
    429             isEmergency = true;
    430         }
    431         Phone phone = getPhoneForAccount(accountHandle, isEmergency);
    432         if (phone == null) {
    433             return Connection.createFailedConnection(
    434                     DisconnectCauseUtil.toTelecomDisconnectCause(
    435                             android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
    436                             "Phone is null"));
    437         }
    438 
    439         Call call = phone.getRingingCall();
    440         if (!call.getState().isRinging()) {
    441             Log.i(this, "onCreateIncomingConnection, no ringing call");
    442             return Connection.createFailedConnection(
    443                     DisconnectCauseUtil.toTelecomDisconnectCause(
    444                             android.telephony.DisconnectCause.INCOMING_MISSED,
    445                             "Found no ringing call"));
    446         }
    447 
    448         com.android.internal.telephony.Connection originalConnection =
    449                 call.getState() == Call.State.WAITING ?
    450                     call.getLatestConnection() : call.getEarliestConnection();
    451         if (isOriginalConnectionKnown(originalConnection)) {
    452             Log.i(this, "onCreateIncomingConnection, original connection already registered");
    453             return Connection.createCanceledConnection();
    454         }
    455 
    456         // We should rely on the originalConnection to get the video state.  The request coming
    457         // from Telecom does not know the video state of the incoming call.
    458         int videoState = originalConnection != null ? originalConnection.getVideoState() :
    459                 VideoProfile.STATE_AUDIO_ONLY;
    460 
    461         Connection connection =
    462                 createConnectionFor(phone, originalConnection, false /* isOutgoing */,
    463                         request.getAccountHandle(), request.getTelecomCallId(),
    464                         request.getAddress(), videoState);
    465         if (connection == null) {
    466             return Connection.createCanceledConnection();
    467         } else {
    468             return connection;
    469         }
    470     }
    471 
    472     @Override
    473     public void triggerConferenceRecalculate() {
    474         if (mTelephonyConferenceController.shouldRecalculate()) {
    475             mTelephonyConferenceController.recalculate();
    476         }
    477     }
    478 
    479     @Override
    480     public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
    481             ConnectionRequest request) {
    482         Log.i(this, "onCreateUnknownConnection, request: " + request);
    483         // Use the registered emergency Phone if the PhoneAccountHandle is set to Telephony's
    484         // Emergency PhoneAccount
    485         PhoneAccountHandle accountHandle = request.getAccountHandle();
    486         boolean isEmergency = false;
    487         if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
    488                 accountHandle.getId())) {
    489             Log.i(this, "Emergency PhoneAccountHandle is being used for unknown call... " +
    490                     "Treat as an Emergency Call.");
    491             isEmergency = true;
    492         }
    493         Phone phone = getPhoneForAccount(accountHandle, isEmergency);
    494         if (phone == null) {
    495             return Connection.createFailedConnection(
    496                     DisconnectCauseUtil.toTelecomDisconnectCause(
    497                             android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
    498                             "Phone is null"));
    499         }
    500         Bundle extras = request.getExtras();
    501 
    502         final List<com.android.internal.telephony.Connection> allConnections = new ArrayList<>();
    503 
    504         // Handle the case where an unknown connection has an IMS external call ID specified; we can
    505         // skip the rest of the guesswork and just grad that unknown call now.
    506         if (phone.getImsPhone() != null && extras != null &&
    507                 extras.containsKey(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID)) {
    508 
    509             ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
    510             ImsExternalCallTracker externalCallTracker = imsPhone.getExternalCallTracker();
    511             int externalCallId = extras.getInt(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID,
    512                     -1);
    513 
    514             if (externalCallTracker != null) {
    515                 com.android.internal.telephony.Connection connection =
    516                         externalCallTracker.getConnectionById(externalCallId);
    517 
    518                 if (connection != null) {
    519                     allConnections.add(connection);
    520                 }
    521             }
    522         }
    523 
    524         if (allConnections.isEmpty()) {
    525             final Call ringingCall = phone.getRingingCall();
    526             if (ringingCall.hasConnections()) {
    527                 allConnections.addAll(ringingCall.getConnections());
    528             }
    529             final Call foregroundCall = phone.getForegroundCall();
    530             if ((foregroundCall.getState() != Call.State.DISCONNECTED)
    531                     && (foregroundCall.hasConnections())) {
    532                 allConnections.addAll(foregroundCall.getConnections());
    533             }
    534             if (phone.getImsPhone() != null) {
    535                 final Call imsFgCall = phone.getImsPhone().getForegroundCall();
    536                 if ((imsFgCall.getState() != Call.State.DISCONNECTED) && imsFgCall
    537                         .hasConnections()) {
    538                     allConnections.addAll(imsFgCall.getConnections());
    539                 }
    540             }
    541             final Call backgroundCall = phone.getBackgroundCall();
    542             if (backgroundCall.hasConnections()) {
    543                 allConnections.addAll(phone.getBackgroundCall().getConnections());
    544             }
    545         }
    546 
    547         com.android.internal.telephony.Connection unknownConnection = null;
    548         for (com.android.internal.telephony.Connection telephonyConnection : allConnections) {
    549             if (!isOriginalConnectionKnown(telephonyConnection)) {
    550                 unknownConnection = telephonyConnection;
    551                 Log.d(this, "onCreateUnknownConnection: conn = " + unknownConnection);
    552                 break;
    553             }
    554         }
    555 
    556         if (unknownConnection == null) {
    557             Log.i(this, "onCreateUnknownConnection, did not find previously unknown connection.");
    558             return Connection.createCanceledConnection();
    559         }
    560 
    561         // We should rely on the originalConnection to get the video state.  The request coming
    562         // from Telecom does not know the video state of the unknown call.
    563         int videoState = unknownConnection != null ? unknownConnection.getVideoState() :
    564                 VideoProfile.STATE_AUDIO_ONLY;
    565 
    566         TelephonyConnection connection =
    567                 createConnectionFor(phone, unknownConnection,
    568                         !unknownConnection.isIncoming() /* isOutgoing */,
    569                         request.getAccountHandle(), request.getTelecomCallId(),
    570                         request.getAddress(), videoState);
    571 
    572         if (connection == null) {
    573             return Connection.createCanceledConnection();
    574         } else {
    575             connection.updateState();
    576             return connection;
    577         }
    578     }
    579 
    580     @Override
    581     public void onConference(Connection connection1, Connection connection2) {
    582         if (connection1 instanceof TelephonyConnection &&
    583                 connection2 instanceof TelephonyConnection) {
    584             ((TelephonyConnection) connection1).performConference(
    585                 (TelephonyConnection) connection2);
    586         }
    587 
    588     }
    589 
    590     private boolean isRadioOn() {
    591         boolean result = false;
    592         for (Phone phone : PhoneFactory.getPhones()) {
    593             result |= phone.isRadioOn();
    594         }
    595         return result;
    596     }
    597 
    598     private void placeOutgoingConnection(
    599             TelephonyConnection connection, Phone phone, ConnectionRequest request) {
    600         String number = connection.getAddress().getSchemeSpecificPart();
    601 
    602         com.android.internal.telephony.Connection originalConnection;
    603         try {
    604             originalConnection =
    605                     phone.dial(number, null, request.getVideoState(), request.getExtras());
    606         } catch (CallStateException e) {
    607             Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
    608             int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
    609             if (e.getError() == CallStateException.ERROR_DISCONNECTED) {
    610                 cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
    611             }
    612             connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
    613                     cause, e.getMessage()));
    614             return;
    615         }
    616 
    617         if (originalConnection == null) {
    618             int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
    619             // On GSM phones, null connection means that we dialed an MMI code
    620             if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
    621                 Log.d(this, "dialed MMI code");
    622                 telephonyDisconnectCause = android.telephony.DisconnectCause.DIALED_MMI;
    623                 final Intent intent = new Intent(this, MMIDialogActivity.class);
    624                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
    625                         Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
    626                 startActivity(intent);
    627             }
    628             Log.d(this, "placeOutgoingConnection, phone.dial returned null");
    629             connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
    630                     telephonyDisconnectCause, "Connection is null"));
    631         } else {
    632             connection.setOriginalConnection(originalConnection);
    633         }
    634     }
    635 
    636     private TelephonyConnection createConnectionFor(
    637             Phone phone,
    638             com.android.internal.telephony.Connection originalConnection,
    639             boolean isOutgoing,
    640             PhoneAccountHandle phoneAccountHandle,
    641             String telecomCallId,
    642             Uri address,
    643             int videoState) {
    644         TelephonyConnection returnConnection = null;
    645         int phoneType = phone.getPhoneType();
    646         if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
    647             returnConnection = new GsmConnection(originalConnection, telecomCallId);
    648         } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
    649             boolean allowsMute = allowsMute(phone);
    650             returnConnection = new CdmaConnection(originalConnection, mEmergencyTonePlayer,
    651                     allowsMute, isOutgoing, telecomCallId);
    652         }
    653         if (returnConnection != null) {
    654             // Listen to Telephony specific callbacks from the connection
    655             returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
    656             returnConnection.setVideoPauseSupported(
    657                     TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(
    658                             phoneAccountHandle));
    659         }
    660         return returnConnection;
    661     }
    662 
    663     private boolean isOriginalConnectionKnown(
    664             com.android.internal.telephony.Connection originalConnection) {
    665         for (Connection connection : getAllConnections()) {
    666             if (connection instanceof TelephonyConnection) {
    667                 TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
    668                 if (telephonyConnection.getOriginalConnection() == originalConnection) {
    669                     return true;
    670                 }
    671             }
    672         }
    673         return false;
    674     }
    675 
    676     private Phone getPhoneForAccount(PhoneAccountHandle accountHandle, boolean isEmergency) {
    677         Phone chosenPhone = null;
    678         int subId = PhoneUtils.getSubIdForPhoneAccountHandle(accountHandle);
    679         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
    680             int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
    681             chosenPhone = PhoneFactory.getPhone(phoneId);
    682         }
    683         // If this is an emergency call and the phone we originally planned to make this call
    684         // with is not in service or was invalid, try to find one that is in service, using the
    685         // default as a last chance backup.
    686         if (isEmergency && (chosenPhone == null || ServiceState.STATE_IN_SERVICE != chosenPhone
    687                 .getServiceState().getState())) {
    688             Log.d(this, "getPhoneForAccount: phone for phone acct handle %s is out of service "
    689                     + "or invalid for emergency call.", accountHandle);
    690             chosenPhone = getFirstPhoneForEmergencyCall();
    691             Log.d(this, "getPhoneForAccount: using subId: " +
    692                     (chosenPhone == null ? "null" : chosenPhone.getSubId()));
    693         }
    694         return chosenPhone;
    695     }
    696 
    697     /**
    698      * Retrieves the most sensible Phone to use for an emergency call using the following Priority
    699      *  list (for multi-SIM devices):
    700      *  1) The User's SIM preference for Voice calling
    701      *  2) The First Phone that is currently IN_SERVICE or is available for emergency calling
    702      *  3) The First Phone that has a SIM card in it (Starting from Slot 0...N)
    703      *  4) The Default Phone (Currently set as Slot 0)
    704      */
    705     private Phone getFirstPhoneForEmergencyCall() {
    706         Phone firstPhoneWithSim = null;
    707 
    708         // 1)
    709         int phoneId = SubscriptionManager.getDefaultVoicePhoneId();
    710         if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
    711             Phone defaultPhone = PhoneFactory.getPhone(phoneId);
    712             if (defaultPhone != null && isAvailableForEmergencyCalls(defaultPhone)) {
    713                 return defaultPhone;
    714             }
    715         }
    716 
    717         for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
    718             Phone phone = PhoneFactory.getPhone(i);
    719             if (phone == null)
    720                 continue;
    721             // 2)
    722             if (isAvailableForEmergencyCalls(phone)) {
    723                 // the slot has the radio on & state is in service.
    724                 Log.d(this, "getFirstPhoneForEmergencyCall, radio on & in service, Phone Id:" + i);
    725                 return phone;
    726             }
    727             // 3)
    728             if (firstPhoneWithSim == null && TelephonyManager.getDefault().hasIccCard(i)) {
    729                 // The slot has a SIM card inserted, but is not in service, so keep track of this
    730                 // Phone. Do not return because we want to make sure that none of the other Phones
    731                 // are in service (because that is always faster).
    732                 Log.d(this, "getFirstPhoneForEmergencyCall, SIM card inserted, Phone Id:" + i);
    733                 firstPhoneWithSim = phone;
    734             }
    735         }
    736         // 4)
    737         if (firstPhoneWithSim == null) {
    738             // No SIMs inserted, get the default.
    739             Log.d(this, "getFirstPhoneForEmergencyCall, return default phone");
    740             return PhoneFactory.getDefaultPhone();
    741         } else {
    742             return firstPhoneWithSim;
    743         }
    744     }
    745 
    746     /**
    747      * Returns true if the state of the Phone is IN_SERVICE or available for emergency calling only.
    748      */
    749     private boolean isAvailableForEmergencyCalls(Phone phone) {
    750         return ServiceState.STATE_IN_SERVICE == phone.getServiceState().getState() ||
    751                 phone.getServiceState().isEmergencyOnly();
    752     }
    753 
    754     /**
    755      * Determines if the connection should allow mute.
    756      *
    757      * @param phone The current phone.
    758      * @return {@code True} if the connection should allow mute.
    759      */
    760     private boolean allowsMute(Phone phone) {
    761         // For CDMA phones, check if we are in Emergency Callback Mode (ECM).  Mute is disallowed
    762         // in ECM mode.
    763         if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
    764             if (phone.isInEcm()) {
    765                 return false;
    766             }
    767         }
    768 
    769         return true;
    770     }
    771 
    772     @Override
    773     public void removeConnection(Connection connection) {
    774         super.removeConnection(connection);
    775         if (connection instanceof TelephonyConnection) {
    776             TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
    777             telephonyConnection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
    778         }
    779     }
    780 
    781     /**
    782      * When a {@link TelephonyConnection} has its underlying original connection configured,
    783      * we need to add it to the correct conference controller.
    784      *
    785      * @param connection The connection to be added to the controller
    786      */
    787     public void addConnectionToConferenceController(TelephonyConnection connection) {
    788         // TODO: Do we need to handle the case of the original connection changing
    789         // and triggering this callback multiple times for the same connection?
    790         // If that is the case, we might want to remove this connection from all
    791         // conference controllers first before re-adding it.
    792         if (connection.isImsConnection()) {
    793             Log.d(this, "Adding IMS connection to conference controller: " + connection);
    794             mImsConferenceController.add(connection);
    795         } else {
    796             int phoneType = connection.getCall().getPhone().getPhoneType();
    797             if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
    798                 Log.d(this, "Adding GSM connection to conference controller: " + connection);
    799                 mTelephonyConferenceController.add(connection);
    800             } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA &&
    801                     connection instanceof CdmaConnection) {
    802                 Log.d(this, "Adding CDMA connection to conference controller: " + connection);
    803                 mCdmaConferenceController.add((CdmaConnection)connection);
    804             }
    805             Log.d(this, "Removing connection from IMS conference controller: " + connection);
    806             mImsConferenceController.remove(connection);
    807         }
    808     }
    809 
    810     /**
    811      * Create a new CDMA connection. CDMA connections have additional limitations when creating
    812      * additional calls which are handled in this method.  Specifically, CDMA has a "FLASH" command
    813      * that can be used for three purposes: merging a call, swapping unmerged calls, and adding
    814      * a new outgoing call. The function of the flash command depends on the context of the current
    815      * set of calls. This method will prevent an outgoing call from being made if it is not within
    816      * the right circumstances to support adding a call.
    817      */
    818     private Connection checkAdditionalOutgoingCallLimits(Phone phone) {
    819         if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
    820             // Check to see if any CDMA conference calls exist, and if they do, check them for
    821             // limitations.
    822             for (Conference conference : getAllConferences()) {
    823                 if (conference instanceof CdmaConference) {
    824                     CdmaConference cdmaConf = (CdmaConference) conference;
    825 
    826                     // If the CDMA conference has not been merged, add-call will not work, so fail
    827                     // this request to add a call.
    828                     if (cdmaConf.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
    829                         return Connection.createFailedConnection(new DisconnectCause(
    830                                     DisconnectCause.RESTRICTED,
    831                                     null,
    832                                     getResources().getString(R.string.callFailed_cdma_call_limit),
    833                                     "merge-capable call exists, prevent flash command."));
    834                     }
    835                 }
    836             }
    837         }
    838 
    839         return null; // null means nothing went wrong, and call should continue.
    840     }
    841 
    842     private boolean isTtyModeEnabled(Context context) {
    843         return (android.provider.Settings.Secure.getInt(
    844                 context.getContentResolver(),
    845                 android.provider.Settings.Secure.PREFERRED_TTY_MODE,
    846                 TelecomManager.TTY_MODE_OFF) != TelecomManager.TTY_MODE_OFF);
    847     }
    848 }
    849