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