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.provider.Settings;
     26 import android.telecom.Conference;
     27 import android.telecom.Connection;
     28 import android.telecom.ConnectionRequest;
     29 import android.telecom.ConnectionService;
     30 import android.telecom.DisconnectCause;
     31 import android.telecom.PhoneAccount;
     32 import android.telecom.PhoneAccountHandle;
     33 import android.telecom.TelecomManager;
     34 import android.telecom.VideoProfile;
     35 import android.telephony.CarrierConfigManager;
     36 import android.telephony.PhoneNumberUtils;
     37 import android.telephony.RadioAccessFamily;
     38 import android.telephony.ServiceState;
     39 import android.telephony.SubscriptionManager;
     40 import android.telephony.TelephonyManager;
     41 import android.text.TextUtils;
     42 import android.util.Pair;
     43 
     44 import com.android.internal.annotations.VisibleForTesting;
     45 import com.android.internal.telephony.Call;
     46 import com.android.internal.telephony.CallStateException;
     47 import com.android.internal.telephony.GsmCdmaPhone;
     48 import com.android.internal.telephony.IccCard;
     49 import com.android.internal.telephony.IccCardConstants;
     50 import com.android.internal.telephony.Phone;
     51 import com.android.internal.telephony.PhoneConstants;
     52 import com.android.internal.telephony.PhoneFactory;
     53 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
     54 import com.android.internal.telephony.imsphone.ImsPhone;
     55 import com.android.phone.MMIDialogActivity;
     56 import com.android.phone.PhoneUtils;
     57 import com.android.phone.R;
     58 
     59 import java.lang.ref.WeakReference;
     60 import java.util.ArrayList;
     61 import java.util.Arrays;
     62 import java.util.Collection;
     63 import java.util.Collections;
     64 import java.util.List;
     65 import java.util.regex.Pattern;
     66 
     67 /**
     68  * Service for making GSM and CDMA connections.
     69  */
     70 public class TelephonyConnectionService extends ConnectionService {
     71 
     72     // If configured, reject attempts to dial numbers matching this pattern.
     73     private static final Pattern CDMA_ACTIVATION_CODE_REGEX_PATTERN =
     74             Pattern.compile("\\*228[0-9]{0,2}");
     75 
     76     private final TelephonyConnectionServiceProxy mTelephonyConnectionServiceProxy =
     77             new TelephonyConnectionServiceProxy() {
     78         @Override
     79         public Collection<Connection> getAllConnections() {
     80             return TelephonyConnectionService.this.getAllConnections();
     81         }
     82         @Override
     83         public void addConference(TelephonyConference mTelephonyConference) {
     84             TelephonyConnectionService.this.addConference(mTelephonyConference);
     85         }
     86         @Override
     87         public void addConference(ImsConference mImsConference) {
     88             TelephonyConnectionService.this.addConference(mImsConference);
     89         }
     90         @Override
     91         public void removeConnection(Connection connection) {
     92             TelephonyConnectionService.this.removeConnection(connection);
     93         }
     94         @Override
     95         public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
     96                                           Connection connection) {
     97             TelephonyConnectionService.this
     98                     .addExistingConnection(phoneAccountHandle, connection);
     99         }
    100         @Override
    101         public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
    102                 Connection connection, Conference conference) {
    103             TelephonyConnectionService.this
    104                     .addExistingConnection(phoneAccountHandle, connection, conference);
    105         }
    106         @Override
    107         public void addConnectionToConferenceController(TelephonyConnection connection) {
    108             TelephonyConnectionService.this.addConnectionToConferenceController(connection);
    109         }
    110     };
    111 
    112     private final TelephonyConferenceController mTelephonyConferenceController =
    113             new TelephonyConferenceController(mTelephonyConnectionServiceProxy);
    114     private final CdmaConferenceController mCdmaConferenceController =
    115             new CdmaConferenceController(this);
    116     private final ImsConferenceController mImsConferenceController =
    117             new ImsConferenceController(TelecomAccountRegistry.getInstance(this),
    118                     mTelephonyConnectionServiceProxy);
    119 
    120     private ComponentName mExpectedComponentName = null;
    121     private EmergencyCallHelper mEmergencyCallHelper;
    122     private EmergencyTonePlayer mEmergencyTonePlayer;
    123 
    124     // Contains one TelephonyConnection that has placed a call and a memory of which Phones it has
    125     // already tried to connect with. There should be only one TelephonyConnection trying to place a
    126     // call at one time. We also only access this cache from a TelephonyConnection that wishes to
    127     // redial, so we use a WeakReference that will become stale once the TelephonyConnection is
    128     // destroyed.
    129     private Pair<WeakReference<TelephonyConnection>, List<Phone>> mEmergencyRetryCache;
    130 
    131     /**
    132      * Keeps track of the status of a SIM slot.
    133      */
    134     private static class SlotStatus {
    135         public int slotId;
    136         // RAT capabilities
    137         public int capabilities;
    138         // By default, we will assume that the slots are not locked.
    139         public boolean isLocked = false;
    140 
    141         public SlotStatus(int slotId, int capabilities) {
    142             this.slotId = slotId;
    143             this.capabilities = capabilities;
    144         }
    145     }
    146 
    147     // SubscriptionManager Proxy interface for testing
    148     public interface SubscriptionManagerProxy {
    149         int getDefaultVoicePhoneId();
    150         int getSimStateForSlotIdx(int slotId);
    151         int getPhoneId(int subId);
    152     }
    153 
    154     private SubscriptionManagerProxy mSubscriptionManagerProxy = new SubscriptionManagerProxy() {
    155         @Override
    156         public int getDefaultVoicePhoneId() {
    157             return SubscriptionManager.getDefaultVoicePhoneId();
    158         }
    159 
    160         @Override
    161         public int getSimStateForSlotIdx(int slotId) {
    162             return SubscriptionManager.getSimStateForSlotIndex(slotId);
    163         }
    164 
    165         @Override
    166         public int getPhoneId(int subId) {
    167             return SubscriptionManager.getPhoneId(subId);
    168         }
    169     };
    170 
    171     // TelephonyManager Proxy interface for testing
    172     public interface TelephonyManagerProxy {
    173         int getPhoneCount();
    174         boolean hasIccCard(int slotId);
    175     }
    176 
    177     private TelephonyManagerProxy mTelephonyManagerProxy = new TelephonyManagerProxy() {
    178         private final TelephonyManager sTelephonyManager = TelephonyManager.getDefault();
    179 
    180         @Override
    181         public int getPhoneCount() {
    182             return sTelephonyManager.getPhoneCount();
    183         }
    184 
    185         @Override
    186         public boolean hasIccCard(int slotId) {
    187             return sTelephonyManager.hasIccCard(slotId);
    188         }
    189     };
    190 
    191     //PhoneFactory proxy interface for testing
    192     public interface PhoneFactoryProxy {
    193         Phone getPhone(int index);
    194         Phone getDefaultPhone();
    195         Phone[] getPhones();
    196     }
    197 
    198     private PhoneFactoryProxy mPhoneFactoryProxy = new PhoneFactoryProxy() {
    199         @Override
    200         public Phone getPhone(int index) {
    201             return PhoneFactory.getPhone(index);
    202         }
    203 
    204         @Override
    205         public Phone getDefaultPhone() {
    206             return PhoneFactory.getDefaultPhone();
    207         }
    208 
    209         @Override
    210         public Phone[] getPhones() {
    211             return PhoneFactory.getPhones();
    212         }
    213     };
    214 
    215     @VisibleForTesting
    216     public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) {
    217         mSubscriptionManagerProxy = proxy;
    218     }
    219 
    220     @VisibleForTesting
    221     public void setTelephonyManagerProxy(TelephonyManagerProxy proxy) {
    222         mTelephonyManagerProxy = proxy;
    223     }
    224 
    225     @VisibleForTesting
    226     public void setPhoneFactoryProxy(PhoneFactoryProxy proxy) {
    227         mPhoneFactoryProxy = proxy;
    228     }
    229 
    230     /**
    231      * A listener to actionable events specific to the TelephonyConnection.
    232      */
    233     private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
    234             new TelephonyConnection.TelephonyConnectionListener() {
    235         @Override
    236         public void onOriginalConnectionConfigured(TelephonyConnection c) {
    237             addConnectionToConferenceController(c);
    238         }
    239 
    240         @Override
    241         public void onOriginalConnectionRetry(TelephonyConnection c) {
    242             retryOutgoingOriginalConnection(c);
    243         }
    244     };
    245 
    246     @Override
    247     public void onCreate() {
    248         super.onCreate();
    249         Log.initLogging(this);
    250         mExpectedComponentName = new ComponentName(this, this.getClass());
    251         mEmergencyTonePlayer = new EmergencyTonePlayer(this);
    252         TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);
    253     }
    254 
    255     @Override
    256     public Connection onCreateOutgoingConnection(
    257             PhoneAccountHandle connectionManagerPhoneAccount,
    258             final ConnectionRequest request) {
    259         Log.i(this, "onCreateOutgoingConnection, request: " + request);
    260 
    261         Uri handle = request.getAddress();
    262         if (handle == null) {
    263             Log.d(this, "onCreateOutgoingConnection, handle is null");
    264             return Connection.createFailedConnection(
    265                     DisconnectCauseUtil.toTelecomDisconnectCause(
    266                             android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED,
    267                             "No phone number supplied"));
    268         }
    269 
    270         String scheme = handle.getScheme();
    271         String number;
    272         if (PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
    273             // TODO: We don't check for SecurityException here (requires
    274             // CALL_PRIVILEGED permission).
    275             final Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
    276             if (phone == null) {
    277                 Log.d(this, "onCreateOutgoingConnection, phone is null");
    278                 return Connection.createFailedConnection(
    279                         DisconnectCauseUtil.toTelecomDisconnectCause(
    280                                 android.telephony.DisconnectCause.OUT_OF_SERVICE,
    281                                 "Phone is null"));
    282             }
    283             number = phone.getVoiceMailNumber();
    284             if (TextUtils.isEmpty(number)) {
    285                 Log.d(this, "onCreateOutgoingConnection, no voicemail number set.");
    286                 return Connection.createFailedConnection(
    287                         DisconnectCauseUtil.toTelecomDisconnectCause(
    288                                 android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING,
    289                                 "Voicemail scheme provided but no voicemail number set."));
    290             }
    291 
    292             // Convert voicemail: to tel:
    293             handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
    294         } else {
    295             if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
    296                 Log.d(this, "onCreateOutgoingConnection, Handle %s is not type tel", scheme);
    297                 return Connection.createFailedConnection(
    298                         DisconnectCauseUtil.toTelecomDisconnectCause(
    299                                 android.telephony.DisconnectCause.INVALID_NUMBER,
    300                                 "Handle scheme is not type tel"));
    301             }
    302 
    303             number = handle.getSchemeSpecificPart();
    304             if (TextUtils.isEmpty(number)) {
    305                 Log.d(this, "onCreateOutgoingConnection, unable to parse number");
    306                 return Connection.createFailedConnection(
    307                         DisconnectCauseUtil.toTelecomDisconnectCause(
    308                                 android.telephony.DisconnectCause.INVALID_NUMBER,
    309                                 "Unable to parse number"));
    310             }
    311 
    312             final Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
    313             if (phone != null && CDMA_ACTIVATION_CODE_REGEX_PATTERN.matcher(number).matches()) {
    314                 // Obtain the configuration for the outgoing phone's SIM. If the outgoing number
    315                 // matches the *228 regex pattern, fail the call. This number is used for OTASP, and
    316                 // when dialed could lock LTE SIMs to 3G if not prohibited..
    317                 boolean disableActivation = false;
    318                 CarrierConfigManager cfgManager = (CarrierConfigManager)
    319                         phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
    320                 if (cfgManager != null) {
    321                     disableActivation = cfgManager.getConfigForSubId(phone.getSubId())
    322                             .getBoolean(CarrierConfigManager.KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL);
    323                 }
    324 
    325                 if (disableActivation) {
    326                     return Connection.createFailedConnection(
    327                             DisconnectCauseUtil.toTelecomDisconnectCause(
    328                                     android.telephony.DisconnectCause
    329                                             .CDMA_ALREADY_ACTIVATED,
    330                                     "Tried to dial *228"));
    331                 }
    332             }
    333         }
    334 
    335         // Convert into emergency number if necessary
    336         // This is required in some regions (e.g. Taiwan).
    337         if (!PhoneNumberUtils.isLocalEmergencyNumber(this, number) &&
    338                 PhoneNumberUtils.isConvertToEmergencyNumberEnabled()) {
    339             final Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
    340             // We only do the conversion if the phone is not in service. The un-converted
    341             // emergency numbers will go to the correct destination when the phone is in-service,
    342             // so they will only need the special emergency call setup when the phone is out of
    343             // service.
    344             if (phone == null || phone.getServiceState().getState()
    345                     != ServiceState.STATE_IN_SERVICE) {
    346                 String convertedNumber = PhoneNumberUtils.convertToEmergencyNumber(number);
    347                 if (!TextUtils.equals(convertedNumber, number)) {
    348                     Log.i(this, "onCreateOutgoingConnection, converted to emergency number");
    349                     number = convertedNumber;
    350                     handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
    351                 }
    352             }
    353         }
    354         final String numberToDial = number;
    355 
    356         final boolean isEmergencyNumber =
    357                 PhoneNumberUtils.isLocalEmergencyNumber(this, numberToDial);
    358 
    359         final boolean isAirplaneModeOn = Settings.Global.getInt(getContentResolver(),
    360                 Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
    361 
    362         if (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn)) {
    363             final Uri emergencyHandle = handle;
    364             // By default, Connection based on the default Phone, since we need to return to Telecom
    365             // now.
    366             final int defaultPhoneType = mPhoneFactoryProxy.getDefaultPhone().getPhoneType();
    367             final Connection emergencyConnection = getTelephonyConnection(request, numberToDial,
    368                     isEmergencyNumber, emergencyHandle, mPhoneFactoryProxy.getDefaultPhone());
    369             if (mEmergencyCallHelper == null) {
    370                 mEmergencyCallHelper = new EmergencyCallHelper(this);
    371             }
    372             mEmergencyCallHelper.enableEmergencyCalling(new EmergencyCallStateListener.Callback() {
    373                 @Override
    374                 public void onComplete(EmergencyCallStateListener listener, boolean isRadioReady) {
    375                     // Make sure the Call has not already been canceled by the user.
    376                     if (emergencyConnection.getState() == Connection.STATE_DISCONNECTED) {
    377                         Log.i(this, "Emergency call disconnected before the outgoing call was " +
    378                                 "placed. Skipping emergency call placement.");
    379                         return;
    380                     }
    381                     if (isRadioReady) {
    382                         // Get the right phone object since the radio has been turned on
    383                         // successfully.
    384                         final Phone phone = getPhoneForAccount(request.getAccountHandle(),
    385                                 isEmergencyNumber);
    386                         // If the PhoneType of the Phone being used is different than the Default
    387                         // Phone, then we need create a new Connection using that PhoneType and
    388                         // replace it in Telecom.
    389                         if (phone.getPhoneType() != defaultPhoneType) {
    390                             Connection repConnection = getTelephonyConnection(request, numberToDial,
    391                                     isEmergencyNumber, emergencyHandle, phone);
    392                             // If there was a failure, the resulting connection will not be a
    393                             // TelephonyConnection, so don't place the call, just return!
    394                             if (repConnection instanceof TelephonyConnection) {
    395                                 placeOutgoingConnection((TelephonyConnection) repConnection, phone,
    396                                         request);
    397                             }
    398                             // Notify Telecom of the new Connection type.
    399                             // TODO: Switch out the underlying connection instead of creating a new
    400                             // one and causing UI Jank.
    401                             addExistingConnection(PhoneUtils.makePstnPhoneAccountHandle(phone),
    402                                     repConnection);
    403                             // Remove the old connection from Telecom after.
    404                             emergencyConnection.setDisconnected(
    405                                     DisconnectCauseUtil.toTelecomDisconnectCause(
    406                                             android.telephony.DisconnectCause.OUTGOING_CANCELED,
    407                                             "Reconnecting outgoing Emergency Call."));
    408                             emergencyConnection.destroy();
    409                         } else {
    410                             placeOutgoingConnection((TelephonyConnection) emergencyConnection,
    411                                     phone, request);
    412                         }
    413                     } else {
    414                         Log.w(this, "onCreateOutgoingConnection, failed to turn on radio");
    415                         emergencyConnection.setDisconnected(
    416                                 DisconnectCauseUtil.toTelecomDisconnectCause(
    417                                         android.telephony.DisconnectCause.POWER_OFF,
    418                                         "Failed to turn on radio."));
    419                         emergencyConnection.destroy();
    420                     }
    421                 }
    422             });
    423             // Return the still unconnected GsmConnection and wait for the Radios to boot before
    424             // connecting it to the underlying Phone.
    425             return emergencyConnection;
    426         } else {
    427             if (!canAddCall() && !isEmergencyNumber) {
    428                 Log.d(this, "onCreateOutgoingConnection, cannot add call .");
    429                 return Connection.createFailedConnection(
    430                         new DisconnectCause(DisconnectCause.ERROR,
    431                                 getApplicationContext().getText(
    432                                         R.string.incall_error_cannot_add_call),
    433                                 getApplicationContext().getText(
    434                                         R.string.incall_error_cannot_add_call),
    435                                 "Add call restricted due to ongoing video call"));
    436             }
    437 
    438             // Get the right phone object from the account data passed in.
    439             final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
    440             Connection resultConnection = getTelephonyConnection(request, numberToDial,
    441                     isEmergencyNumber, handle, phone);
    442             // If there was a failure, the resulting connection will not be a TelephonyConnection,
    443             // so don't place the call!
    444             if(resultConnection instanceof TelephonyConnection) {
    445                 placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
    446             }
    447             return resultConnection;
    448         }
    449     }
    450 
    451     /**
    452      * @return {@code true} if any other call is disabling the ability to add calls, {@code false}
    453      *      otherwise.
    454      */
    455     private boolean canAddCall() {
    456         Collection<Connection> connections = getAllConnections();
    457         for (Connection connection : connections) {
    458             if (connection.getExtras() != null &&
    459                     connection.getExtras().getBoolean(Connection.EXTRA_DISABLE_ADD_CALL, false)) {
    460                 return false;
    461             }
    462         }
    463         return true;
    464     }
    465 
    466     private Connection getTelephonyConnection(final ConnectionRequest request, final String number,
    467             boolean isEmergencyNumber, final Uri handle, Phone phone) {
    468 
    469         if (phone == null) {
    470             final Context context = getApplicationContext();
    471             if (context.getResources().getBoolean(R.bool.config_checkSimStateBeforeOutgoingCall)) {
    472                 // Check SIM card state before the outgoing call.
    473                 // Start the SIM unlock activity if PIN_REQUIRED.
    474                 final Phone defaultPhone = mPhoneFactoryProxy.getDefaultPhone();
    475                 final IccCard icc = defaultPhone.getIccCard();
    476                 IccCardConstants.State simState = IccCardConstants.State.UNKNOWN;
    477                 if (icc != null) {
    478                     simState = icc.getState();
    479                 }
    480                 if (simState == IccCardConstants.State.PIN_REQUIRED) {
    481                     final String simUnlockUiPackage = context.getResources().getString(
    482                             R.string.config_simUnlockUiPackage);
    483                     final String simUnlockUiClass = context.getResources().getString(
    484                             R.string.config_simUnlockUiClass);
    485                     if (simUnlockUiPackage != null && simUnlockUiClass != null) {
    486                         Intent simUnlockIntent = new Intent().setComponent(new ComponentName(
    487                                 simUnlockUiPackage, simUnlockUiClass));
    488                         simUnlockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    489                         try {
    490                             context.startActivity(simUnlockIntent);
    491                         } catch (ActivityNotFoundException exception) {
    492                             Log.e(this, exception, "Unable to find SIM unlock UI activity.");
    493                         }
    494                     }
    495                     return Connection.createFailedConnection(
    496                             DisconnectCauseUtil.toTelecomDisconnectCause(
    497                                     android.telephony.DisconnectCause.OUT_OF_SERVICE,
    498                                     "SIM_STATE_PIN_REQUIRED"));
    499                 }
    500             }
    501 
    502             Log.d(this, "onCreateOutgoingConnection, phone is null");
    503             return Connection.createFailedConnection(
    504                     DisconnectCauseUtil.toTelecomDisconnectCause(
    505                             android.telephony.DisconnectCause.OUT_OF_SERVICE, "Phone is null"));
    506         }
    507 
    508         // Check both voice & data RAT to enable normal CS call,
    509         // when voice RAT is OOS but Data RAT is present.
    510         int state = phone.getServiceState().getState();
    511         if (state == ServiceState.STATE_OUT_OF_SERVICE) {
    512             int dataNetType = phone.getServiceState().getDataNetworkType();
    513             if (dataNetType == TelephonyManager.NETWORK_TYPE_LTE ||
    514                     dataNetType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
    515                 state = phone.getServiceState().getDataRegState();
    516             }
    517         }
    518 
    519         // If we're dialing a non-emergency number and the phone is in ECM mode, reject the call if
    520         // carrier configuration specifies that we cannot make non-emergency calls in ECM mode.
    521         if (!isEmergencyNumber && phone.isInEcm()) {
    522             boolean allowNonEmergencyCalls = true;
    523             CarrierConfigManager cfgManager = (CarrierConfigManager)
    524                     phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
    525             if (cfgManager != null) {
    526                 allowNonEmergencyCalls = cfgManager.getConfigForSubId(phone.getSubId())
    527                         .getBoolean(CarrierConfigManager.KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL);
    528             }
    529 
    530             if (!allowNonEmergencyCalls) {
    531                 return Connection.createFailedConnection(
    532                         DisconnectCauseUtil.toTelecomDisconnectCause(
    533                                 android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY,
    534                                 "Cannot make non-emergency call in ECM mode."
    535                         ));
    536             }
    537         }
    538 
    539         if (!isEmergencyNumber) {
    540             switch (state) {
    541                 case ServiceState.STATE_IN_SERVICE:
    542                 case ServiceState.STATE_EMERGENCY_ONLY:
    543                     break;
    544                 case ServiceState.STATE_OUT_OF_SERVICE:
    545                     if (phone.isUtEnabled() && number.endsWith("#")) {
    546                         Log.d(this, "onCreateOutgoingConnection dial for UT");
    547                         break;
    548                     } else {
    549                         return Connection.createFailedConnection(
    550                                 DisconnectCauseUtil.toTelecomDisconnectCause(
    551                                         android.telephony.DisconnectCause.OUT_OF_SERVICE,
    552                                         "ServiceState.STATE_OUT_OF_SERVICE"));
    553                     }
    554                 case ServiceState.STATE_POWER_OFF:
    555                     return Connection.createFailedConnection(
    556                             DisconnectCauseUtil.toTelecomDisconnectCause(
    557                                     android.telephony.DisconnectCause.POWER_OFF,
    558                                     "ServiceState.STATE_POWER_OFF"));
    559                 default:
    560                     Log.d(this, "onCreateOutgoingConnection, unknown service state: %d", state);
    561                     return Connection.createFailedConnection(
    562                             DisconnectCauseUtil.toTelecomDisconnectCause(
    563                                     android.telephony.DisconnectCause.OUTGOING_FAILURE,
    564                                     "Unknown service state " + state));
    565             }
    566         }
    567 
    568         final Context context = getApplicationContext();
    569         if (VideoProfile.isVideo(request.getVideoState()) && isTtyModeEnabled(context) &&
    570                 !isEmergencyNumber) {
    571             return Connection.createFailedConnection(DisconnectCauseUtil.toTelecomDisconnectCause(
    572                     android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED));
    573         }
    574 
    575         // Check for additional limits on CDMA phones.
    576         final Connection failedConnection = checkAdditionalOutgoingCallLimits(phone);
    577         if (failedConnection != null) {
    578             return failedConnection;
    579         }
    580 
    581         // Check roaming status to see if we should block custom call forwarding codes
    582         if (blockCallForwardingNumberWhileRoaming(phone, number)) {
    583             return Connection.createFailedConnection(
    584                     DisconnectCauseUtil.toTelecomDisconnectCause(
    585                             android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING,
    586                             "Call forwarding while roaming"));
    587         }
    588 
    589 
    590         final TelephonyConnection connection =
    591                 createConnectionFor(phone, null, true /* isOutgoing */, request.getAccountHandle(),
    592                         request.getTelecomCallId(), request.getAddress(), request.getVideoState());
    593         if (connection == null) {
    594             return Connection.createFailedConnection(
    595                     DisconnectCauseUtil.toTelecomDisconnectCause(
    596                             android.telephony.DisconnectCause.OUTGOING_FAILURE,
    597                             "Invalid phone type"));
    598         }
    599         connection.setAddress(handle, PhoneConstants.PRESENTATION_ALLOWED);
    600         connection.setInitializing();
    601         connection.setVideoState(request.getVideoState());
    602 
    603         return connection;
    604     }
    605 
    606     @Override
    607     public Connection onCreateIncomingConnection(
    608             PhoneAccountHandle connectionManagerPhoneAccount,
    609             ConnectionRequest request) {
    610         Log.i(this, "onCreateIncomingConnection, request: " + request);
    611         // If there is an incoming emergency CDMA Call (while the phone is in ECBM w/ No SIM),
    612         // make sure the PhoneAccount lookup retrieves the default Emergency Phone.
    613         PhoneAccountHandle accountHandle = request.getAccountHandle();
    614         boolean isEmergency = false;
    615         if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
    616                 accountHandle.getId())) {
    617             Log.i(this, "Emergency PhoneAccountHandle is being used for incoming call... " +
    618                     "Treat as an Emergency Call.");
    619             isEmergency = true;
    620         }
    621         Phone phone = getPhoneForAccount(accountHandle, isEmergency);
    622         if (phone == null) {
    623             return Connection.createFailedConnection(
    624                     DisconnectCauseUtil.toTelecomDisconnectCause(
    625                             android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
    626                             "Phone is null"));
    627         }
    628 
    629         Call call = phone.getRingingCall();
    630         if (!call.getState().isRinging()) {
    631             Log.i(this, "onCreateIncomingConnection, no ringing call");
    632             return Connection.createFailedConnection(
    633                     DisconnectCauseUtil.toTelecomDisconnectCause(
    634                             android.telephony.DisconnectCause.INCOMING_MISSED,
    635                             "Found no ringing call"));
    636         }
    637 
    638         com.android.internal.telephony.Connection originalConnection =
    639                 call.getState() == Call.State.WAITING ?
    640                     call.getLatestConnection() : call.getEarliestConnection();
    641         if (isOriginalConnectionKnown(originalConnection)) {
    642             Log.i(this, "onCreateIncomingConnection, original connection already registered");
    643             return Connection.createCanceledConnection();
    644         }
    645 
    646         // We should rely on the originalConnection to get the video state.  The request coming
    647         // from Telecom does not know the video state of the incoming call.
    648         int videoState = originalConnection != null ? originalConnection.getVideoState() :
    649                 VideoProfile.STATE_AUDIO_ONLY;
    650 
    651         Connection connection =
    652                 createConnectionFor(phone, originalConnection, false /* isOutgoing */,
    653                         request.getAccountHandle(), request.getTelecomCallId(),
    654                         request.getAddress(), videoState);
    655         if (connection == null) {
    656             return Connection.createCanceledConnection();
    657         } else {
    658             return connection;
    659         }
    660     }
    661 
    662     /**
    663      * Called by the {@link ConnectionService} when a newly created {@link Connection} has been
    664      * added to the {@link ConnectionService} and sent to Telecom.  Here it is safe to send
    665      * connection events.
    666      *
    667      * @param connection the {@link Connection}.
    668      */
    669     @Override
    670     public void onCreateConnectionComplete(Connection connection) {
    671         if (connection instanceof TelephonyConnection) {
    672             TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
    673             maybeSendInternationalCallEvent(telephonyConnection);
    674         }
    675     }
    676 
    677     @Override
    678     public void triggerConferenceRecalculate() {
    679         if (mTelephonyConferenceController.shouldRecalculate()) {
    680             mTelephonyConferenceController.recalculate();
    681         }
    682     }
    683 
    684     @Override
    685     public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
    686             ConnectionRequest request) {
    687         Log.i(this, "onCreateUnknownConnection, request: " + request);
    688         // Use the registered emergency Phone if the PhoneAccountHandle is set to Telephony's
    689         // Emergency PhoneAccount
    690         PhoneAccountHandle accountHandle = request.getAccountHandle();
    691         boolean isEmergency = false;
    692         if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
    693                 accountHandle.getId())) {
    694             Log.i(this, "Emergency PhoneAccountHandle is being used for unknown call... " +
    695                     "Treat as an Emergency Call.");
    696             isEmergency = true;
    697         }
    698         Phone phone = getPhoneForAccount(accountHandle, isEmergency);
    699         if (phone == null) {
    700             return Connection.createFailedConnection(
    701                     DisconnectCauseUtil.toTelecomDisconnectCause(
    702                             android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
    703                             "Phone is null"));
    704         }
    705         Bundle extras = request.getExtras();
    706 
    707         final List<com.android.internal.telephony.Connection> allConnections = new ArrayList<>();
    708 
    709         // Handle the case where an unknown connection has an IMS external call ID specified; we can
    710         // skip the rest of the guesswork and just grad that unknown call now.
    711         if (phone.getImsPhone() != null && extras != null &&
    712                 extras.containsKey(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID)) {
    713 
    714             ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
    715             ImsExternalCallTracker externalCallTracker = imsPhone.getExternalCallTracker();
    716             int externalCallId = extras.getInt(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID,
    717                     -1);
    718 
    719             if (externalCallTracker != null) {
    720                 com.android.internal.telephony.Connection connection =
    721                         externalCallTracker.getConnectionById(externalCallId);
    722 
    723                 if (connection != null) {
    724                     allConnections.add(connection);
    725                 }
    726             }
    727         }
    728 
    729         if (allConnections.isEmpty()) {
    730             final Call ringingCall = phone.getRingingCall();
    731             if (ringingCall.hasConnections()) {
    732                 allConnections.addAll(ringingCall.getConnections());
    733             }
    734             final Call foregroundCall = phone.getForegroundCall();
    735             if ((foregroundCall.getState() != Call.State.DISCONNECTED)
    736                     && (foregroundCall.hasConnections())) {
    737                 allConnections.addAll(foregroundCall.getConnections());
    738             }
    739             if (phone.getImsPhone() != null) {
    740                 final Call imsFgCall = phone.getImsPhone().getForegroundCall();
    741                 if ((imsFgCall.getState() != Call.State.DISCONNECTED) && imsFgCall
    742                         .hasConnections()) {
    743                     allConnections.addAll(imsFgCall.getConnections());
    744                 }
    745             }
    746             final Call backgroundCall = phone.getBackgroundCall();
    747             if (backgroundCall.hasConnections()) {
    748                 allConnections.addAll(phone.getBackgroundCall().getConnections());
    749             }
    750         }
    751 
    752         com.android.internal.telephony.Connection unknownConnection = null;
    753         for (com.android.internal.telephony.Connection telephonyConnection : allConnections) {
    754             if (!isOriginalConnectionKnown(telephonyConnection)) {
    755                 unknownConnection = telephonyConnection;
    756                 Log.d(this, "onCreateUnknownConnection: conn = " + unknownConnection);
    757                 break;
    758             }
    759         }
    760 
    761         if (unknownConnection == null) {
    762             Log.i(this, "onCreateUnknownConnection, did not find previously unknown connection.");
    763             return Connection.createCanceledConnection();
    764         }
    765 
    766         // We should rely on the originalConnection to get the video state.  The request coming
    767         // from Telecom does not know the video state of the unknown call.
    768         int videoState = unknownConnection != null ? unknownConnection.getVideoState() :
    769                 VideoProfile.STATE_AUDIO_ONLY;
    770 
    771         TelephonyConnection connection =
    772                 createConnectionFor(phone, unknownConnection,
    773                         !unknownConnection.isIncoming() /* isOutgoing */,
    774                         request.getAccountHandle(), request.getTelecomCallId(),
    775                         request.getAddress(), videoState);
    776 
    777         if (connection == null) {
    778             return Connection.createCanceledConnection();
    779         } else {
    780             connection.updateState();
    781             return connection;
    782         }
    783     }
    784 
    785     /**
    786      * Conferences two connections.
    787      *
    788      * Note: The {@link android.telecom.RemoteConnection#setConferenceableConnections(List)} API has
    789      * a limitation in that it can only specify conferenceables which are instances of
    790      * {@link android.telecom.RemoteConnection}.  In the case of an {@link ImsConference}, the
    791      * regular {@link Connection#setConferenceables(List)} API properly handles being able to merge
    792      * a {@link Conference} and a {@link Connection}.  As a result when, merging a
    793      * {@link android.telecom.RemoteConnection} into a {@link android.telecom.RemoteConference}
    794      * require merging a {@link ConferenceParticipantConnection} which is a child of the
    795      * {@link Conference} with a {@link TelephonyConnection}.  The
    796      * {@link ConferenceParticipantConnection} class does not have the capability to initiate a
    797      * conference merge, so we need to call
    798      * {@link TelephonyConnection#performConference(Connection)} on either {@code connection1} or
    799      * {@code connection2}, one of which is an instance of {@link TelephonyConnection}.
    800      *
    801      * @param connection1 A connection to merge into a conference call.
    802      * @param connection2 A connection to merge into a conference call.
    803      */
    804     @Override
    805     public void onConference(Connection connection1, Connection connection2) {
    806         if (connection1 instanceof TelephonyConnection) {
    807             ((TelephonyConnection) connection1).performConference(connection2);
    808         } else if (connection2 instanceof TelephonyConnection) {
    809             ((TelephonyConnection) connection2).performConference(connection1);
    810         } else {
    811             Log.w(this, "onConference - cannot merge connections " +
    812                     "Connection1: %s, Connection2: %2", connection1, connection2);
    813         }
    814     }
    815 
    816     private boolean blockCallForwardingNumberWhileRoaming(Phone phone, String number) {
    817         if (phone == null || TextUtils.isEmpty(number) || !phone.getServiceState().getRoaming()) {
    818             return false;
    819         }
    820         String[] blockPrefixes = null;
    821         CarrierConfigManager cfgManager = (CarrierConfigManager)
    822                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
    823         if (cfgManager != null) {
    824             blockPrefixes = cfgManager.getConfigForSubId(phone.getSubId()).getStringArray(
    825                     CarrierConfigManager.KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY);
    826         }
    827 
    828         if (blockPrefixes != null) {
    829             for (String prefix : blockPrefixes) {
    830                 if (number.startsWith(prefix)) {
    831                     return true;
    832                 }
    833             }
    834         }
    835         return false;
    836     }
    837 
    838     private boolean isRadioOn() {
    839         boolean result = false;
    840         for (Phone phone : mPhoneFactoryProxy.getPhones()) {
    841             result |= phone.isRadioOn();
    842         }
    843         return result;
    844     }
    845 
    846     private Pair<WeakReference<TelephonyConnection>, List<Phone>> makeCachedConnectionPhonePair(
    847             TelephonyConnection c) {
    848         List<Phone> phones = new ArrayList<>(Arrays.asList(mPhoneFactoryProxy.getPhones()));
    849         return new Pair<>(new WeakReference<>(c), phones);
    850     }
    851 
    852     // Check the mEmergencyRetryCache to see if it contains the TelephonyConnection. If it doesn't,
    853     // then it is stale. Create a new one!
    854     private void updateCachedConnectionPhonePair(TelephonyConnection c) {
    855         if (mEmergencyRetryCache == null) {
    856             Log.i(this, "updateCachedConnectionPhonePair, cache is null. Generating new cache");
    857             mEmergencyRetryCache = makeCachedConnectionPhonePair(c);
    858         } else {
    859             // Check to see if old cache is stale. If it is, replace it
    860             WeakReference<TelephonyConnection> cachedConnection = mEmergencyRetryCache.first;
    861             if (cachedConnection.get() != c) {
    862                 Log.i(this, "updateCachedConnectionPhonePair, cache is stale. Regenerating.");
    863                 mEmergencyRetryCache = makeCachedConnectionPhonePair(c);
    864             }
    865         }
    866     }
    867 
    868     /**
    869      * Returns the first Phone that has not been used yet to place the call. Any Phones that have
    870      * been used to place a call will have already been removed from mEmergencyRetryCache.second.
    871      * The phone that it excluded will be removed from mEmergencyRetryCache.second in this method.
    872      * @param phoneToExclude The Phone object that will be removed from our cache of available
    873      * phones.
    874      * @return the first Phone that is available to be used to retry the call.
    875      */
    876     private Phone getPhoneForRedial(Phone phoneToExclude) {
    877         List<Phone> cachedPhones = mEmergencyRetryCache.second;
    878         if (cachedPhones.contains(phoneToExclude)) {
    879             Log.i(this, "getPhoneForRedial, removing Phone[" + phoneToExclude.getPhoneId() +
    880                     "] from the available Phone cache.");
    881             cachedPhones.remove(phoneToExclude);
    882         }
    883         return cachedPhones.isEmpty() ? null : cachedPhones.get(0);
    884     }
    885 
    886     private void retryOutgoingOriginalConnection(TelephonyConnection c) {
    887         updateCachedConnectionPhonePair(c);
    888         Phone newPhoneToUse = getPhoneForRedial(c.getPhone());
    889         if (newPhoneToUse != null) {
    890             int videoState = c.getVideoState();
    891             Bundle connExtras = c.getExtras();
    892             Log.i(this, "retryOutgoingOriginalConnection, redialing on Phone Id: " + newPhoneToUse);
    893             c.clearOriginalConnection();
    894             placeOutgoingConnection(c, newPhoneToUse, videoState, connExtras);
    895         } else {
    896             // We have run out of Phones to use. Disconnect the call and destroy the connection.
    897             Log.i(this, "retryOutgoingOriginalConnection, no more Phones to use. Disconnecting.");
    898             c.setDisconnected(new DisconnectCause(DisconnectCause.ERROR));
    899             c.clearOriginalConnection();
    900             c.destroy();
    901         }
    902     }
    903 
    904     private void placeOutgoingConnection(
    905             TelephonyConnection connection, Phone phone, ConnectionRequest request) {
    906         placeOutgoingConnection(connection, phone, request.getVideoState(), request.getExtras());
    907     }
    908 
    909     private void placeOutgoingConnection(
    910             TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {
    911         String number = connection.getAddress().getSchemeSpecificPart();
    912 
    913         com.android.internal.telephony.Connection originalConnection = null;
    914         try {
    915             if (phone != null) {
    916                 originalConnection = phone.dial(number, null, videoState, extras);
    917             }
    918         } catch (CallStateException e) {
    919             Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
    920             int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
    921             if (e.getError() == CallStateException.ERROR_DISCONNECTED) {
    922                 cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
    923             } else if (e.getError() == CallStateException.ERROR_POWER_OFF) {
    924                 cause = android.telephony.DisconnectCause.POWER_OFF;
    925             }
    926             connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
    927                     cause, e.getMessage()));
    928             return;
    929         }
    930 
    931         if (originalConnection == null) {
    932             int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
    933             // On GSM phones, null connection means that we dialed an MMI code
    934             if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
    935                 Log.d(this, "dialed MMI code");
    936                 int subId = phone.getSubId();
    937                 Log.d(this, "subId: "+subId);
    938                 telephonyDisconnectCause = android.telephony.DisconnectCause.DIALED_MMI;
    939                 final Intent intent = new Intent(this, MMIDialogActivity.class);
    940                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
    941                         Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
    942                 if (SubscriptionManager.isValidSubscriptionId(subId)) {
    943                     intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
    944                 }
    945                 startActivity(intent);
    946             }
    947             Log.d(this, "placeOutgoingConnection, phone.dial returned null");
    948             connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
    949                     telephonyDisconnectCause, "Connection is null"));
    950         } else {
    951             connection.setOriginalConnection(originalConnection);
    952         }
    953     }
    954 
    955     private TelephonyConnection createConnectionFor(
    956             Phone phone,
    957             com.android.internal.telephony.Connection originalConnection,
    958             boolean isOutgoing,
    959             PhoneAccountHandle phoneAccountHandle,
    960             String telecomCallId,
    961             Uri address,
    962             int videoState) {
    963         TelephonyConnection returnConnection = null;
    964         int phoneType = phone.getPhoneType();
    965         if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
    966             returnConnection = new GsmConnection(originalConnection, telecomCallId, isOutgoing);
    967         } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
    968             boolean allowsMute = allowsMute(phone);
    969             returnConnection = new CdmaConnection(originalConnection, mEmergencyTonePlayer,
    970                     allowsMute, isOutgoing, telecomCallId);
    971         }
    972         if (returnConnection != null) {
    973             // Listen to Telephony specific callbacks from the connection
    974             returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
    975             returnConnection.setVideoPauseSupported(
    976                     TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(
    977                             phoneAccountHandle));
    978         }
    979         return returnConnection;
    980     }
    981 
    982     private boolean isOriginalConnectionKnown(
    983             com.android.internal.telephony.Connection originalConnection) {
    984         for (Connection connection : getAllConnections()) {
    985             if (connection instanceof TelephonyConnection) {
    986                 TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
    987                 if (telephonyConnection.getOriginalConnection() == originalConnection) {
    988                     return true;
    989                 }
    990             }
    991         }
    992         return false;
    993     }
    994 
    995     private Phone getPhoneForAccount(PhoneAccountHandle accountHandle, boolean isEmergency) {
    996         Phone chosenPhone = null;
    997         int subId = PhoneUtils.getSubIdForPhoneAccountHandle(accountHandle);
    998         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
    999             int phoneId = mSubscriptionManagerProxy.getPhoneId(subId);
   1000             chosenPhone = mPhoneFactoryProxy.getPhone(phoneId);
   1001         }
   1002         // If this is an emergency call and the phone we originally planned to make this call
   1003         // with is not in service or was invalid, try to find one that is in service, using the
   1004         // default as a last chance backup.
   1005         if (isEmergency && (chosenPhone == null || ServiceState.STATE_IN_SERVICE != chosenPhone
   1006                 .getServiceState().getState())) {
   1007             Log.d(this, "getPhoneForAccount: phone for phone acct handle %s is out of service "
   1008                     + "or invalid for emergency call.", accountHandle);
   1009             chosenPhone = getFirstPhoneForEmergencyCall();
   1010             Log.d(this, "getPhoneForAccount: using subId: " +
   1011                     (chosenPhone == null ? "null" : chosenPhone.getSubId()));
   1012         }
   1013         return chosenPhone;
   1014     }
   1015 
   1016     /**
   1017      * Retrieves the most sensible Phone to use for an emergency call using the following Priority
   1018      *  list (for multi-SIM devices):
   1019      *  1) The User's SIM preference for Voice calling
   1020      *  2) The First Phone that is currently IN_SERVICE or is available for emergency calling
   1021      *  3) If there is a PUK locked SIM, compare the SIMs that are not PUK locked. If all the SIMs
   1022      *     are locked, skip to condition 4).
   1023      *  4) The Phone with more Capabilities.
   1024      *  5) The First Phone that has a SIM card in it (Starting from Slot 0...N)
   1025      *  6) The Default Phone (Currently set as Slot 0)
   1026      */
   1027     @VisibleForTesting
   1028     public Phone getFirstPhoneForEmergencyCall() {
   1029         // 1)
   1030         int phoneId = mSubscriptionManagerProxy.getDefaultVoicePhoneId();
   1031         if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
   1032             Phone defaultPhone = mPhoneFactoryProxy.getPhone(phoneId);
   1033             if (defaultPhone != null && isAvailableForEmergencyCalls(defaultPhone)) {
   1034                 return defaultPhone;
   1035             }
   1036         }
   1037 
   1038         Phone firstPhoneWithSim = null;
   1039         int phoneCount = mTelephonyManagerProxy.getPhoneCount();
   1040         List<SlotStatus> phoneSlotStatus = new ArrayList<>(phoneCount);
   1041         for (int i = 0; i < phoneCount; i++) {
   1042             Phone phone = mPhoneFactoryProxy.getPhone(i);
   1043             if (phone == null) {
   1044                 continue;
   1045             }
   1046             // 2)
   1047             if (isAvailableForEmergencyCalls(phone)) {
   1048                 // the slot has the radio on & state is in service.
   1049                 Log.i(this, "getFirstPhoneForEmergencyCall, radio on & in service, Phone Id:" + i);
   1050                 return phone;
   1051             }
   1052             // 4)
   1053             // Store the RAF Capabilities for sorting later.
   1054             int radioAccessFamily = phone.getRadioAccessFamily();
   1055             SlotStatus status = new SlotStatus(i, radioAccessFamily);
   1056             phoneSlotStatus.add(status);
   1057             Log.i(this, "getFirstPhoneForEmergencyCall, RAF:" +
   1058                     Integer.toHexString(radioAccessFamily) + " saved for Phone Id:" + i);
   1059             // 3)
   1060             // Report Slot's PIN/PUK lock status for sorting later.
   1061             int simState = mSubscriptionManagerProxy.getSimStateForSlotIdx(i);
   1062             if (simState == TelephonyManager.SIM_STATE_PIN_REQUIRED ||
   1063                     simState == TelephonyManager.SIM_STATE_PUK_REQUIRED) {
   1064                 status.isLocked = true;
   1065             }
   1066             // 5)
   1067             if (firstPhoneWithSim == null && mTelephonyManagerProxy.hasIccCard(i)) {
   1068                 // The slot has a SIM card inserted, but is not in service, so keep track of this
   1069                 // Phone. Do not return because we want to make sure that none of the other Phones
   1070                 // are in service (because that is always faster).
   1071                 firstPhoneWithSim = phone;
   1072                 Log.i(this, "getFirstPhoneForEmergencyCall, SIM card inserted, Phone Id:" +
   1073                         firstPhoneWithSim.getPhoneId());
   1074             }
   1075         }
   1076         // 6)
   1077         if (firstPhoneWithSim == null && phoneSlotStatus.isEmpty()) {
   1078             // No Phones available, get the default.
   1079             Log.i(this, "getFirstPhoneForEmergencyCall, return default phone");
   1080             return mPhoneFactoryProxy.getDefaultPhone();
   1081         } else {
   1082             // 4)
   1083             final int defaultPhoneId = mPhoneFactoryProxy.getDefaultPhone().getPhoneId();
   1084             final Phone firstOccupiedSlot = firstPhoneWithSim;
   1085             if (!phoneSlotStatus.isEmpty()) {
   1086                 // Only sort if there are enough elements to do so.
   1087                 if (phoneSlotStatus.size() > 1) {
   1088                     Collections.sort(phoneSlotStatus, (o1, o2) -> {
   1089                         // First start by seeing if either of the phone slots are locked. If they
   1090                         // are, then sort by non-locked SIM first. If they are both locked, sort
   1091                         // by capability instead.
   1092                         if (o1.isLocked && !o2.isLocked) {
   1093                             return -1;
   1094                         }
   1095                         if (o2.isLocked && !o1.isLocked) {
   1096                             return 1;
   1097                         }
   1098                         // sort by number of RadioAccessFamily Capabilities.
   1099                         int compare = Integer.bitCount(o1.capabilities) -
   1100                                 Integer.bitCount(o2.capabilities);
   1101                         if (compare == 0) {
   1102                             // Sort by highest RAF Capability if the number is the same.
   1103                             compare = RadioAccessFamily.getHighestRafCapability(o1.capabilities) -
   1104                                     RadioAccessFamily.getHighestRafCapability(o2.capabilities);
   1105                             if (compare == 0) {
   1106                                 if (firstOccupiedSlot != null) {
   1107                                     // If the RAF capability is the same, choose based on whether or
   1108                                     // not any of the slots are occupied with a SIM card (if both
   1109                                     // are, always choose the first).
   1110                                     if (o1.slotId == firstOccupiedSlot.getPhoneId()) {
   1111                                         return 1;
   1112                                     } else if (o2.slotId == firstOccupiedSlot.getPhoneId()) {
   1113                                         return -1;
   1114                                     }
   1115                                 } else {
   1116                                     // No slots have SIMs detected in them, so weight the default
   1117                                     // Phone Id greater than the others.
   1118                                     if (o1.slotId == defaultPhoneId) {
   1119                                         return 1;
   1120                                     } else if (o2.slotId == defaultPhoneId) {
   1121                                         return -1;
   1122                                     }
   1123                                 }
   1124                             }
   1125                         }
   1126                         return compare;
   1127                     });
   1128                 }
   1129                 int mostCapablePhoneId = phoneSlotStatus.get(phoneSlotStatus.size() - 1).slotId;
   1130                 Log.i(this, "getFirstPhoneForEmergencyCall, Using Phone Id: " + mostCapablePhoneId +
   1131                         "with highest capability");
   1132                 return mPhoneFactoryProxy.getPhone(mostCapablePhoneId);
   1133             } else {
   1134                 // 5)
   1135                 return firstPhoneWithSim;
   1136             }
   1137         }
   1138     }
   1139 
   1140     /**
   1141      * Returns true if the state of the Phone is IN_SERVICE or available for emergency calling only.
   1142      */
   1143     private boolean isAvailableForEmergencyCalls(Phone phone) {
   1144         return ServiceState.STATE_IN_SERVICE == phone.getServiceState().getState() ||
   1145                 phone.getServiceState().isEmergencyOnly();
   1146     }
   1147 
   1148     /**
   1149      * Determines if the connection should allow mute.
   1150      *
   1151      * @param phone The current phone.
   1152      * @return {@code True} if the connection should allow mute.
   1153      */
   1154     private boolean allowsMute(Phone phone) {
   1155         // For CDMA phones, check if we are in Emergency Callback Mode (ECM).  Mute is disallowed
   1156         // in ECM mode.
   1157         if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
   1158             if (phone.isInEcm()) {
   1159                 return false;
   1160             }
   1161         }
   1162 
   1163         return true;
   1164     }
   1165 
   1166     @Override
   1167     public void removeConnection(Connection connection) {
   1168         super.removeConnection(connection);
   1169         if (connection instanceof TelephonyConnection) {
   1170             TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
   1171             telephonyConnection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
   1172         }
   1173     }
   1174 
   1175     /**
   1176      * When a {@link TelephonyConnection} has its underlying original connection configured,
   1177      * we need to add it to the correct conference controller.
   1178      *
   1179      * @param connection The connection to be added to the controller
   1180      */
   1181     public void addConnectionToConferenceController(TelephonyConnection connection) {
   1182         // TODO: Need to revisit what happens when the original connection for the
   1183         // TelephonyConnection changes.  If going from CDMA --> GSM (for example), the
   1184         // instance of TelephonyConnection will still be a CdmaConnection, not a GsmConnection.
   1185         // The CDMA conference controller makes the assumption that it will only have CDMA
   1186         // connections in it, while the other conference controllers aren't as restrictive.  Really,
   1187         // when we go between CDMA and GSM we should replace the TelephonyConnection.
   1188         if (connection.isImsConnection()) {
   1189             Log.d(this, "Adding IMS connection to conference controller: " + connection);
   1190             mImsConferenceController.add(connection);
   1191             mTelephonyConferenceController.remove(connection);
   1192             if (connection instanceof CdmaConnection) {
   1193                 mCdmaConferenceController.remove((CdmaConnection) connection);
   1194             }
   1195         } else {
   1196             int phoneType = connection.getCall().getPhone().getPhoneType();
   1197             if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
   1198                 Log.d(this, "Adding GSM connection to conference controller: " + connection);
   1199                 mTelephonyConferenceController.add(connection);
   1200                 if (connection instanceof CdmaConnection) {
   1201                     mCdmaConferenceController.remove((CdmaConnection) connection);
   1202                 }
   1203             } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA &&
   1204                     connection instanceof CdmaConnection) {
   1205                 Log.d(this, "Adding CDMA connection to conference controller: " + connection);
   1206                 mCdmaConferenceController.add((CdmaConnection) connection);
   1207                 mTelephonyConferenceController.remove(connection);
   1208             }
   1209             Log.d(this, "Removing connection from IMS conference controller: " + connection);
   1210             mImsConferenceController.remove(connection);
   1211         }
   1212     }
   1213 
   1214     /**
   1215      * Create a new CDMA connection. CDMA connections have additional limitations when creating
   1216      * additional calls which are handled in this method.  Specifically, CDMA has a "FLASH" command
   1217      * that can be used for three purposes: merging a call, swapping unmerged calls, and adding
   1218      * a new outgoing call. The function of the flash command depends on the context of the current
   1219      * set of calls. This method will prevent an outgoing call from being made if it is not within
   1220      * the right circumstances to support adding a call.
   1221      */
   1222     private Connection checkAdditionalOutgoingCallLimits(Phone phone) {
   1223         if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
   1224             // Check to see if any CDMA conference calls exist, and if they do, check them for
   1225             // limitations.
   1226             for (Conference conference : getAllConferences()) {
   1227                 if (conference instanceof CdmaConference) {
   1228                     CdmaConference cdmaConf = (CdmaConference) conference;
   1229 
   1230                     // If the CDMA conference has not been merged, add-call will not work, so fail
   1231                     // this request to add a call.
   1232                     if (cdmaConf.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
   1233                         return Connection.createFailedConnection(new DisconnectCause(
   1234                                     DisconnectCause.RESTRICTED,
   1235                                     null,
   1236                                     getResources().getString(R.string.callFailed_cdma_call_limit),
   1237                                     "merge-capable call exists, prevent flash command."));
   1238                     }
   1239                 }
   1240             }
   1241         }
   1242 
   1243         return null; // null means nothing went wrong, and call should continue.
   1244     }
   1245 
   1246     private boolean isTtyModeEnabled(Context context) {
   1247         return (android.provider.Settings.Secure.getInt(
   1248                 context.getContentResolver(),
   1249                 android.provider.Settings.Secure.PREFERRED_TTY_MODE,
   1250                 TelecomManager.TTY_MODE_OFF) != TelecomManager.TTY_MODE_OFF);
   1251     }
   1252 
   1253     /**
   1254      * For outgoing dialed calls, potentially send a ConnectionEvent if the user is on WFC and is
   1255      * dialing an international number.
   1256      * @param telephonyConnection The connection.
   1257      */
   1258     private void maybeSendInternationalCallEvent(TelephonyConnection telephonyConnection) {
   1259         if (telephonyConnection == null || telephonyConnection.getPhone() == null ||
   1260                 telephonyConnection.getPhone().getDefaultPhone() == null) {
   1261             return;
   1262         }
   1263         Phone phone = telephonyConnection.getPhone().getDefaultPhone();
   1264         if (phone instanceof GsmCdmaPhone) {
   1265             GsmCdmaPhone gsmCdmaPhone = (GsmCdmaPhone) phone;
   1266             if (telephonyConnection.isOutgoingCall() &&
   1267                     gsmCdmaPhone.isNotificationOfWfcCallRequired(
   1268                             telephonyConnection.getOriginalConnection().getOrigDialString())) {
   1269                 // Send connection event to InCall UI to inform the user of the fact they
   1270                 // are potentially placing an international call on WFC.
   1271                 Log.i(this, "placeOutgoingConnection - sending international call on WFC " +
   1272                         "confirmation event");
   1273                 telephonyConnection.sendConnectionEvent(
   1274                         TelephonyManager.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC, null);
   1275             }
   1276         }
   1277     }
   1278 }
   1279