Add emergency SUPL DDS switch for DP only roaming partners

Although an operator may support control plane fallback for
emergency SUPL requests, a roaming operator may not. Define
a new CarrierConfig for known roaming partners of an operator
that do not support CP fallback and in those cases, perform
a DDS switch before placing the emergency call if possible.

Bug: 144383368
Test: manual; atest TeleServiceTests
Merged-In: I7bd81ddb9730cd2cff7c246f0321af090eb6b2c6
Change-Id: I7bd81ddb9730cd2cff7c246f0321af090eb6b2c6
diff --git a/src/com/android/services/telephony/DeviceState.java b/src/com/android/services/telephony/DeviceState.java
new file mode 100644
index 0000000..7cd2d7e
--- /dev/null
+++ b/src/com/android/services/telephony/DeviceState.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
+
+import com.android.internal.telephony.PhoneConstants;
+import com.android.phone.R;
+
+/**
+ * Abstracts device state and settings for better testability.
+ */
+public class DeviceState {
+
+    public boolean shouldCheckSimStateBeforeOutgoingCall(Context context) {
+        return context.getResources().getBoolean(
+                R.bool.config_checkSimStateBeforeOutgoingCall);
+    }
+
+    public boolean isSuplDdsSwitchRequiredForEmergencyCall(Context context) {
+        return context.getResources().getBoolean(
+                R.bool.config_gnss_supl_requires_default_data_for_emergency);
+    }
+
+    public boolean isRadioPowerDownAllowedOnBluetooth(Context context) {
+        return context.getResources().getBoolean(R.bool.config_allowRadioPowerDownOnBluetooth);
+    }
+
+    public boolean isAirplaneModeOn(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
+    }
+
+    public int getCellOnStatus(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(), Settings.Global.CELL_ON,
+                PhoneConstants.CELL_ON_FLAG);
+    }
+
+    public boolean isTtyModeEnabled(Context context) {
+        return android.provider.Settings.Secure.getInt(context.getContentResolver(),
+                android.provider.Settings.Secure.PREFERRED_TTY_MODE,
+                TelecomManager.TTY_MODE_OFF) != TelecomManager.TTY_MODE_OFF;
+    }
+}
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 996c8ea..38672a8 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -1340,7 +1340,7 @@
                 b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
     }
 
-    private PersistableBundle getCarrierConfig() {
+    public PersistableBundle getCarrierConfig() {
         Phone phone = getPhone();
         if (phone == null) {
             return null;
@@ -2188,7 +2188,7 @@
      * 3. If call is a video call, carrier supports video conference calls.
      * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls.
      */
-    private void refreshConferenceSupported() {
+    void refreshConferenceSupported() {
         boolean isVideoCall = VideoProfile.isVideo(getVideoState());
         Phone phone = getPhone();
         if (phone == null) {
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 6637e19..c517cd8 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -156,12 +156,7 @@
             new TelephonyConferenceController(mTelephonyConnectionServiceProxy);
     private final CdmaConferenceController mCdmaConferenceController =
             new CdmaConferenceController(this);
-    private final ImsConferenceController mImsConferenceController =
-            new ImsConferenceController(TelecomAccountRegistry.getInstance(this),
-                    mTelephonyConnectionServiceProxy,
-                    // FeatureFlagProxy; used to determine if standalone call emulation is enabled.
-                    // TODO: Move to carrier config
-                    () -> true);
+    private ImsConferenceController mImsConferenceController;
 
     private ComponentName mExpectedComponentName = null;
     private RadioOnHelper mRadioOnHelper;
@@ -176,6 +171,7 @@
     // destroyed.
     @VisibleForTesting
     public Pair<WeakReference<TelephonyConnection>, Queue<Phone>> mEmergencyRetryCache;
+    private DeviceState mDeviceState = new DeviceState();
 
     /**
      * Keeps track of the status of a SIM slot.
@@ -195,7 +191,10 @@
         }
     }
 
-    // SubscriptionManager Proxy interface for testing
+    /**
+     * SubscriptionManager dependencies for testing.
+     */
+    @VisibleForTesting
     public interface SubscriptionManagerProxy {
         int getDefaultVoicePhoneId();
         int getSimStateForSlotIdx(int slotId);
@@ -219,7 +218,9 @@
         }
     };
 
-    // TelephonyManager Proxy interface for testing
+    /**
+     * TelephonyManager dependencies for testing.
+     */
     @VisibleForTesting
     public interface TelephonyManagerProxy {
         int getPhoneCount();
@@ -259,7 +260,9 @@
         }
     }
 
-    //PhoneFactory proxy interface for testing
+    /**
+     * PhoneFactory Dependencies for testing.
+     */
     @VisibleForTesting
     public interface PhoneFactoryProxy {
         Phone getPhone(int index);
@@ -284,22 +287,156 @@
         }
     };
 
+    /**
+     * PhoneUtils dependencies for testing.
+     */
+    @VisibleForTesting
+    public interface PhoneUtilsProxy {
+        int getSubIdForPhoneAccountHandle(PhoneAccountHandle accountHandle);
+        PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone);
+        PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(Phone phone, String prefix,
+                boolean isEmergency);
+    }
+
+    private PhoneUtilsProxy mPhoneUtilsProxy = new PhoneUtilsProxy() {
+        @Override
+        public int getSubIdForPhoneAccountHandle(PhoneAccountHandle accountHandle) {
+            return PhoneUtils.getSubIdForPhoneAccountHandle(accountHandle);
+        }
+
+        @Override
+        public PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) {
+            return PhoneUtils.makePstnPhoneAccountHandle(phone);
+        }
+
+        @Override
+        public PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(Phone phone, String prefix,
+                boolean isEmergency) {
+            return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(phone, prefix, isEmergency);
+        }
+    };
+
+    /**
+     * PhoneNumberUtils dependencies for testing.
+     */
+    @VisibleForTesting
+    public interface PhoneNumberUtilsProxy {
+        String convertToEmergencyNumber(Context context, String number);
+    }
+
+    private PhoneNumberUtilsProxy mPhoneNumberUtilsProxy = new PhoneNumberUtilsProxy() {
+        @Override
+        public String convertToEmergencyNumber(Context context, String number) {
+            return PhoneNumberUtils.convertToEmergencyNumber(context, number);
+        }
+    };
+
+    /**
+     * PhoneSwitcher dependencies for testing.
+     */
+    @VisibleForTesting
+    public interface PhoneSwitcherProxy {
+        PhoneSwitcher getPhoneSwitcher();
+    }
+
+    private PhoneSwitcherProxy mPhoneSwitcherProxy = new PhoneSwitcherProxy() {
+        @Override
+        public PhoneSwitcher getPhoneSwitcher() {
+            return PhoneSwitcher.getInstance();
+        }
+    };
+
+    /**
+     * DisconnectCause depends on PhoneGlobals in order to get a system context. Mock out
+     * dependency for testing.
+     */
+    @VisibleForTesting
+    public interface DisconnectCauseFactory {
+        DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause, String reason);
+        DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
+                String reason, int phoneId);
+    }
+
+    private DisconnectCauseFactory mDisconnectCauseFactory = new DisconnectCauseFactory() {
+        @Override
+        public DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
+                String reason) {
+            return DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause, reason);
+        }
+
+        @Override
+        public DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause, String reason,
+                int phoneId) {
+            return DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause, reason,
+                    phoneId);
+        }
+    };
+
+    /**
+     * Overrides SubscriptionManager dependencies for testing.
+     */
     @VisibleForTesting
     public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) {
         mSubscriptionManagerProxy = proxy;
     }
 
+    /**
+     * Overrides TelephonyManager dependencies for testing.
+     */
     @VisibleForTesting
     public void setTelephonyManagerProxy(TelephonyManagerProxy proxy) {
         mTelephonyManagerProxy = proxy;
     }
 
+    /**
+     * Overrides PhoneFactory dependencies for testing.
+     */
     @VisibleForTesting
     public void setPhoneFactoryProxy(PhoneFactoryProxy proxy) {
         mPhoneFactoryProxy = proxy;
     }
 
     /**
+     * Overrides configuration and settings dependencies for testing.
+     */
+    @VisibleForTesting
+    public void setDeviceState(DeviceState state) {
+        mDeviceState = state;
+    }
+
+    /**
+     * Overrides PhoneSwitcher dependencies for testing.
+     */
+    @VisibleForTesting
+    public void setPhoneSwitcherProxy(PhoneSwitcherProxy proxy) {
+        mPhoneSwitcherProxy = proxy;
+    }
+
+    /**
+     * Overrides PhoneNumberUtils dependencies for testing.
+     */
+    @VisibleForTesting
+    public void setPhoneNumberUtilsProxy(PhoneNumberUtilsProxy proxy) {
+        mPhoneNumberUtilsProxy = proxy;
+    }
+
+    /**
+     * Overrides PhoneUtils dependencies for testing.
+     */
+    @VisibleForTesting
+    public void setPhoneUtilsProxy(PhoneUtilsProxy proxy) {
+        mPhoneUtilsProxy = proxy;
+    }
+
+    /**
+     * Override DisconnectCause creation for testing.
+     */
+    @VisibleForTesting
+    public void setDisconnectCauseFactory(DisconnectCauseFactory factory) {
+        mDisconnectCauseFactory = factory;
+    }
+
+    /**
      * A listener to actionable events specific to the TelephonyConnection.
      */
     private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
@@ -319,12 +456,18 @@
     public void onCreate() {
         super.onCreate();
         Log.initLogging(this);
+        mImsConferenceController = new ImsConferenceController(
+                TelecomAccountRegistry.getInstance(this),
+                mTelephonyConnectionServiceProxy,
+                // FeatureFlagProxy; used to determine if standalone call emulation is enabled.
+                // TODO: Move to carrier config
+                () -> true);
         setTelephonyManagerProxy(new TelephonyManagerProxyImpl(getApplicationContext()));
         mExpectedComponentName = new ComponentName(this, this.getClass());
         mEmergencyTonePlayer = new EmergencyTonePlayer(this);
         TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);
         mHoldTracker = new HoldTracker();
-        mIsTtyEnabled = isTtyModeEnabled(getApplicationContext());
+        mIsTtyEnabled = mDeviceState.isTtyModeEnabled(this);
 
         IntentFilter intentFilter = new IntentFilter(
                 TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
@@ -347,7 +490,7 @@
         if (handle == null) {
             Log.d(this, "onCreateOutgoingConnection, handle is null");
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED,
                             "No phone number supplied"));
         }
@@ -362,7 +505,7 @@
             if (phone == null) {
                 Log.d(this, "onCreateOutgoingConnection, phone is null");
                 return Connection.createFailedConnection(
-                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                        mDisconnectCauseFactory.toTelecomDisconnectCause(
                                 android.telephony.DisconnectCause.OUT_OF_SERVICE,
                                 "Phone is null"));
             }
@@ -370,7 +513,7 @@
             if (TextUtils.isEmpty(number)) {
                 Log.d(this, "onCreateOutgoingConnection, no voicemail number set.");
                 return Connection.createFailedConnection(
-                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                        mDisconnectCauseFactory.toTelecomDisconnectCause(
                                 android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING,
                                 "Voicemail scheme provided but no voicemail number set.",
                                 phone.getPhoneId()));
@@ -382,7 +525,7 @@
             if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
                 Log.d(this, "onCreateOutgoingConnection, Handle %s is not type tel", scheme);
                 return Connection.createFailedConnection(
-                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                        mDisconnectCauseFactory.toTelecomDisconnectCause(
                                 android.telephony.DisconnectCause.INVALID_NUMBER,
                                 "Handle scheme is not type tel"));
             }
@@ -391,7 +534,7 @@
             if (TextUtils.isEmpty(number)) {
                 Log.d(this, "onCreateOutgoingConnection, unable to parse number");
                 return Connection.createFailedConnection(
-                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                        mDisconnectCauseFactory.toTelecomDisconnectCause(
                                 android.telephony.DisconnectCause.INVALID_NUMBER,
                                 "Unable to parse number"));
             }
@@ -412,7 +555,7 @@
 
                 if (disableActivation) {
                     return Connection.createFailedConnection(
-                            DisconnectCauseUtil.toTelecomDisconnectCause(
+                            mDisconnectCauseFactory.toTelecomDisconnectCause(
                                     android.telephony.DisconnectCause
                                             .CDMA_ALREADY_ACTIVATED,
                                     "Tried to dial *228",
@@ -436,7 +579,8 @@
             // service.
             if (phone == null || phone.getServiceState().getState()
                     != ServiceState.STATE_IN_SERVICE) {
-                String convertedNumber = PhoneNumberUtils.convertToEmergencyNumber(this, number);
+                String convertedNumber = mPhoneNumberUtilsProxy.convertToEmergencyNumber(this,
+                        number);
                 if (!TextUtils.equals(convertedNumber, number)) {
                     Log.i(this, "onCreateOutgoingConnection, converted to emergency number");
                     number = convertedNumber;
@@ -447,8 +591,7 @@
         final String numberToDial = number;
 
 
-        final boolean isAirplaneModeOn = Settings.Global.getInt(getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
+        final boolean isAirplaneModeOn = mDeviceState.isAirplaneModeOn(this);
 
         boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
                 || isRadioPowerDownOnBluetooth();
@@ -457,7 +600,7 @@
             final Uri resultHandle = handle;
             // By default, Connection based on the default Phone, since we need to return to Telecom
             // now.
-            final int originalPhoneType = PhoneFactory.getDefaultPhone().getPhoneType();
+            final int originalPhoneType = mPhoneFactoryProxy.getDefaultPhone().getPhoneType();
             final Connection resultConnection = getTelephonyConnection(request, numberToDial,
                     isEmergencyNumber, resultHandle, PhoneFactory.getDefaultPhone());
             if (mRadioOnHelper == null) {
@@ -571,12 +714,8 @@
      * Whether the cellular radio is power off because the device is on Bluetooth.
      */
     private boolean isRadioPowerDownOnBluetooth() {
-        final Context context = getApplicationContext();
-        final boolean allowed = context.getResources().getBoolean(
-                R.bool.config_allowRadioPowerDownOnBluetooth);
-        final int cellOn = Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.CELL_ON,
-                PhoneConstants.CELL_ON_FLAG);
+        final boolean allowed = mDeviceState.isRadioPowerDownAllowedOnBluetooth(this);
+        final int cellOn = mDeviceState.getCellOnStatus(this);
         return (allowed && cellOn == PhoneConstants.CELL_ON_FLAG && !isRadioOn());
     }
 
@@ -615,7 +754,7 @@
         } else {
             Log.w(this, "onCreateOutgoingConnection, failed to turn on radio");
             originalConnection.setDisconnected(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                     mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.POWER_OFF,
                             "Failed to turn on radio."));
             originalConnection.destroy();
@@ -641,11 +780,11 @@
             boolean noActiveSimCard = SubscriptionController.getInstance()
                     .getActiveSubInfoCount(phone.getContext().getOpPackageName()) == 0;
             // If there's no active sim card and the device is in emergency mode, use E account.
-            addExistingConnection(PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
+            addExistingConnection(mPhoneUtilsProxy.makePstnPhoneAccountHandleWithPrefix(
                     phone, "", isEmergencyNumber && noActiveSimCard), repConnection);
             // Remove the old connection from Telecom after.
             connectionToEvaluate.setDisconnected(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.OUTGOING_CANCELED,
                             "Reconnecting outgoing Emergency Call.",
                             phone.getPhoneId()));
@@ -675,7 +814,7 @@
 
         if (phone == null) {
             final Context context = getApplicationContext();
-            if (context.getResources().getBoolean(R.bool.config_checkSimStateBeforeOutgoingCall)) {
+            if (mDeviceState.shouldCheckSimStateBeforeOutgoingCall(this)) {
                 // Check SIM card state before the outgoing call.
                 // Start the SIM unlock activity if PIN_REQUIRED.
                 final Phone defaultPhone = mPhoneFactoryProxy.getDefaultPhone();
@@ -700,7 +839,7 @@
                         }
                     }
                     return Connection.createFailedConnection(
-                            DisconnectCauseUtil.toTelecomDisconnectCause(
+                            mDisconnectCauseFactory.toTelecomDisconnectCause(
                                     android.telephony.DisconnectCause.OUT_OF_SERVICE,
                                     "SIM_STATE_PIN_REQUIRED"));
                 }
@@ -708,7 +847,7 @@
 
             Log.d(this, "onCreateOutgoingConnection, phone is null");
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.OUT_OF_SERVICE, "Phone is null"));
         }
 
@@ -736,7 +875,7 @@
 
             if (!allowNonEmergencyCalls) {
                 return Connection.createFailedConnection(
-                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                        mDisconnectCauseFactory.toTelecomDisconnectCause(
                                 android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY,
                                 "Cannot make non-emergency call in ECM mode.",
                                 phone.getPhoneId()));
@@ -754,7 +893,7 @@
                         break;
                     } else {
                         return Connection.createFailedConnection(
-                                DisconnectCauseUtil.toTelecomDisconnectCause(
+                                mDisconnectCauseFactory.toTelecomDisconnectCause(
                                         android.telephony.DisconnectCause.OUT_OF_SERVICE,
                                         "ServiceState.STATE_OUT_OF_SERVICE",
                                         phone.getPhoneId()));
@@ -765,25 +904,25 @@
                         break;
                     }
                     return Connection.createFailedConnection(
-                            DisconnectCauseUtil.toTelecomDisconnectCause(
+                            mDisconnectCauseFactory.toTelecomDisconnectCause(
                                     android.telephony.DisconnectCause.POWER_OFF,
                                     "ServiceState.STATE_POWER_OFF",
                                     phone.getPhoneId()));
                 default:
                     Log.d(this, "onCreateOutgoingConnection, unknown service state: %d", state);
                     return Connection.createFailedConnection(
-                            DisconnectCauseUtil.toTelecomDisconnectCause(
+                            mDisconnectCauseFactory.toTelecomDisconnectCause(
                                     android.telephony.DisconnectCause.OUTGOING_FAILURE,
                                     "Unknown service state " + state,
                                     phone.getPhoneId()));
             }
         }
 
-        final Context context = getApplicationContext();
-        final boolean isTtyModeEnabled = isTtyModeEnabled(context);
+        final boolean isTtyModeEnabled = mDeviceState.isTtyModeEnabled(this);
         if (VideoProfile.isVideo(request.getVideoState()) && isTtyModeEnabled
                 && !isEmergencyNumber) {
-            return Connection.createFailedConnection(DisconnectCauseUtil.toTelecomDisconnectCause(
+            return Connection.createFailedConnection(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                     android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED,
                     null, phone.getPhoneId()));
         }
@@ -797,7 +936,7 @@
         // Check roaming status to see if we should block custom call forwarding codes
         if (blockCallForwardingNumberWhileRoaming(phone, number)) {
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING,
                             "Call forwarding while roaming",
                             phone.getPhoneId()));
@@ -809,7 +948,7 @@
                         request.getTelecomCallId(), request.getAddress(), request.getVideoState());
         if (connection == null) {
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.OUTGOING_FAILURE,
                             "Invalid phone type",
                             phone.getPhoneId()));
@@ -842,7 +981,7 @@
                 request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
         if (phone == null) {
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
                             "Phone is null"));
         }
@@ -851,7 +990,7 @@
         if (!call.getState().isRinging()) {
             Log.i(this, "onCreateIncomingConnection, no ringing call");
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.INCOMING_MISSED,
                             "Found no ringing call",
                             phone.getPhoneId()));
@@ -955,7 +1094,7 @@
                 request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
         if (phone == null) {
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
                             "Phone is null"));
         }
@@ -1211,7 +1350,7 @@
     }
 
     private void updatePhoneAccount(TelephonyConnection connection, Phone phone) {
-        PhoneAccountHandle pHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
+        PhoneAccountHandle pHandle = mPhoneUtilsProxy.makePstnPhoneAccountHandle(phone);
         // For ECall handling on MSIM, until the request reaches here (i.e PhoneApp), we don't know
         // on which phone account ECall can be placed. After deciding, we should notify Telecom of
         // the change so that the proper PhoneAccount can be displayed.
@@ -1263,7 +1402,7 @@
                     cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
                     break;
             }
-            connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+            connection.setDisconnected(mDisconnectCauseFactory.toTelecomDisconnectCause(
                     cause, e.getMessage(), phone.getPhoneId()));
             connection.clearOriginalConnection();
             connection.destroy();
@@ -1287,7 +1426,7 @@
                 startActivity(intent);
             }
             Log.d(this, "placeOutgoingConnection, phone.dial returned null");
-            connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+            connection.setDisconnected(mDisconnectCauseFactory.toTelecomDisconnectCause(
                     telephonyDisconnectCause, "Connection is null", phone.getPhoneId()));
             connection.clearOriginalConnection();
             connection.destroy();
@@ -1354,7 +1493,7 @@
     private Phone getPhoneForAccount(PhoneAccountHandle accountHandle, boolean isEmergency,
                                      @Nullable String emergencyNumberAddress) {
         Phone chosenPhone = null;
-        int subId = PhoneUtils.getSubIdForPhoneAccountHandle(accountHandle);
+        int subId = mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(accountHandle);
         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
             int phoneId = mSubscriptionManagerProxy.getPhoneId(subId);
             chosenPhone = mPhoneFactoryProxy.getPhone(phoneId);
@@ -1395,13 +1534,21 @@
      */
     private CompletableFuture<Boolean> possiblyOverrideDefaultDataForEmergencyCall(
             @NonNull Phone phone) {
-        TelephonyManager telephony = TelephonyManager.from(phone.getContext());
-        int phoneCount = telephony.getPhoneCount();
+        int phoneCount = mTelephonyManagerProxy.getPhoneCount();
         // Do not override DDS if this is a single SIM device.
         if (phoneCount <= PhoneConstants.MAX_PHONE_COUNT_SINGLE_SIM) {
             return CompletableFuture.completedFuture(Boolean.TRUE);
         }
 
+        // Do not switch Default data if this device supports emergency SUPL on non-DDS.
+        final boolean gnssSuplRequiresDefaultData =
+                mDeviceState.isSuplDdsSwitchRequiredForEmergencyCall(this);
+        if (!gnssSuplRequiresDefaultData) {
+            Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, does not "
+                    + "require DDS switch.");
+            return CompletableFuture.completedFuture(Boolean.TRUE);
+        }
+
         CarrierConfigManager cfgManager = (CarrierConfigManager)
                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
         if (cfgManager == null) {
@@ -1410,34 +1557,41 @@
                     + "CarrierConfigManager");
             return CompletableFuture.completedFuture(Boolean.TRUE);
         }
-        // Only override default data if we are IN_SERVICE and on a home network. We don't want to
-        // perform a DDS switch of we are on a roaming network, where SUPL may not be available.
-        boolean isPhoneAvailableForEmergency = isAvailableForEmergencyCalls(phone);
+
+        // Only override default data if we are IN_SERVICE already.
+        if (!isAvailableForEmergencyCalls(phone)) {
+            Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS");
+            return CompletableFuture.completedFuture(Boolean.TRUE);
+        }
+
+        // Only override default data if we are not roaming, we do not want to switch onto a network
+        // that only supports data plane only (if we do not know).
         boolean isRoaming = phone.getServiceState().getVoiceRoaming();
-        if (!isPhoneAvailableForEmergency || isRoaming) {
-            Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, avail = "
-                    + isPhoneAvailableForEmergency + ", roaming = " + isRoaming);
+        // In some roaming conditions, we know the roaming network doesn't support control plane
+        // fallback even though the home operator does. For these operators we will need to do a DDS
+        // switch anyway to make sure the SUPL request doesn't fail.
+        boolean roamingNetworkSupportsControlPlaneFallback = true;
+        String[] dataPlaneRoamPlmns = cfgManager.getConfigForSubId(phone.getSubId()).getStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY);
+        if (dataPlaneRoamPlmns != null && Arrays.asList(dataPlaneRoamPlmns).contains(
+                phone.getServiceState().getOperatorNumeric())) {
+            roamingNetworkSupportsControlPlaneFallback = false;
+        }
+        if (isRoaming && roamingNetworkSupportsControlPlaneFallback) {
+            Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: roaming network is assumed "
+                    + "to support CP fallback, not switching DDS.");
             return CompletableFuture.completedFuture(Boolean.TRUE);
         }
-
-        // Do not switch Default data if this device supports emergency SUPL on non-DDS.
-        final boolean gnssSuplRequiresDefaultData = phone.getContext().getResources().getBoolean(
-                R.bool.config_gnss_supl_requires_default_data_for_emergency);
-        if (!gnssSuplRequiresDefaultData) {
-            Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, does not "
-                    + "require DDS switch.");
-            return CompletableFuture.completedFuture(Boolean.TRUE);
-        }
-
+        // Do not try to swap default data if we support CS fallback or it is assumed that the
+        // roaming network supports control plane fallback, we do not want to introduce
+        // a lag in emergency call setup time if possible.
         final boolean supportsCpFallback = cfgManager.getConfigForSubId(phone.getSubId())
                 .getInt(CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                         CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_ONLY)
                 != CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY;
-        if (supportsCpFallback) {
+        if (supportsCpFallback && roamingNetworkSupportsControlPlaneFallback) {
             Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, carrier "
                     + "supports CP fallback.");
-            // Do not try to swap default data if we support CS fallback, do not want to introduce
-            // a lag in emergency call setup time if possible.
             return CompletableFuture.completedFuture(Boolean.TRUE);
         }
 
@@ -1445,7 +1599,7 @@
         // CarrierConfig default if format fails.
         int extensionTime = 0;
         try {
-            extensionTime = Integer.valueOf(cfgManager.getConfigForSubId(phone.getSubId())
+            extensionTime = Integer.parseInt(cfgManager.getConfigForSubId(phone.getSubId())
                     .getString(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0"));
         } catch (NumberFormatException e) {
             // Just use default.
@@ -1454,12 +1608,13 @@
         try {
             Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: overriding DDS for "
                     + extensionTime + "seconds");
-            PhoneSwitcher.getInstance().overrideDefaultDataForEmergency(phone.getPhoneId(),
-                    extensionTime, modemResultFuture);
+            mPhoneSwitcherProxy.getPhoneSwitcher().overrideDefaultDataForEmergency(
+                    phone.getPhoneId(), extensionTime, modemResultFuture);
             // Catch all exceptions, we want to continue with emergency call if possible.
         } catch (Exception e) {
             Log.w(this, "possiblyOverrideDefaultDataForEmergencyCall: exception = "
                     + e.getMessage());
+            modemResultFuture = CompletableFuture.completedFuture(Boolean.FALSE);
         }
         return modemResultFuture;
     }
@@ -1768,13 +1923,6 @@
         return null; // null means nothing went wrong, and call should continue.
     }
 
-    private boolean isTtyModeEnabled(Context context) {
-        return (android.provider.Settings.Secure.getInt(
-                context.getContentResolver(),
-                android.provider.Settings.Secure.PREFERRED_TTY_MODE,
-                TelecomManager.TTY_MODE_OFF) != TelecomManager.TTY_MODE_OFF);
-    }
-
     /**
      * For outgoing dialed calls, potentially send a ConnectionEvent if the user is on WFC and is
      * dialing an international number.
diff --git a/tests/Android.mk b/tests/Android.mk
index 44bf176..76140ac 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -31,6 +31,7 @@
 LOCAL_INSTRUMENTATION_FOR := TeleService
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
+        android.test.mock \
         androidx.test.rules \
         mockito-target-minus-junit4 \
         androidx.test.espresso.core \
diff --git a/tests/src/com/android/TelephonyTestBase.java b/tests/src/com/android/TelephonyTestBase.java
index d30ae6b..01267d8 100644
--- a/tests/src/com/android/TelephonyTestBase.java
+++ b/tests/src/com/android/TelephonyTestBase.java
@@ -16,13 +16,10 @@
 
 package com.android;
 
-import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
 
-import androidx.test.InstrumentationRegistry;
-
 import org.mockito.MockitoAnnotations;
 
 import java.util.concurrent.CountDownLatch;
@@ -33,11 +30,11 @@
  */
 public class TelephonyTestBase {
 
-    protected Context mContext;
+    protected TestContext mContext;
 
     public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
         MockitoAnnotations.initMocks(this);
+        mContext = new TestContext();
         // Set up the looper if it does not exist on the test thread.
         if (Looper.myLooper() == null) {
             Looper.prepare();
@@ -86,4 +83,8 @@
             Log.e("TelephonyTestBase", "InterruptedException while waiting: " + e);
         }
     }
+
+    protected TestContext getTestContext() {
+        return mContext;
+    }
 }
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
new file mode 100644
index 0000000..4868647
--- /dev/null
+++ b/tests/src/com/android/TestContext.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContext;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class TestContext extends MockContext {
+
+    @Mock CarrierConfigManager mMockCarrierConfigManager;
+    @Mock TelecomManager mMockTelecomManager;
+    @Mock TelephonyManager mMockTelephonyManager;
+    @Mock SubscriptionManager mMockSubscriptionManager;
+    @Mock Resources mMockResources;
+
+    private PersistableBundle mCarrierConfig = new PersistableBundle();
+
+    public TestContext() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mCarrierConfig).when(mMockCarrierConfigManager).getConfigForSubId(anyInt());
+        doReturn("test").when(mMockResources).getText(anyInt());
+    }
+
+    @Override
+    public Context getApplicationContext() {
+        return this;
+    }
+
+    @Override
+    public String getPackageName() {
+        return "com.android.phone.tests";
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        return null;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+        return null;
+    }
+
+    @Override
+    public Resources getResources() {
+        return mMockResources;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler) {
+        return null;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler, int flags) {
+        return null;
+    }
+
+    @Override
+    public Object getSystemService(String name) {
+        switch (name) {
+            case (Context.CARRIER_CONFIG_SERVICE) : {
+                return mMockCarrierConfigManager;
+            }
+            case (Context.TELECOM_SERVICE) : {
+                return mMockTelecomManager;
+            }
+            case (Context.TELEPHONY_SERVICE) : {
+                return mMockTelephonyManager;
+            }
+            case (Context.TELEPHONY_SUBSCRIPTION_SERVICE) : {
+                return mMockSubscriptionManager;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String getSystemServiceName(Class<?> serviceClass) {
+        if (serviceClass == CarrierConfigManager.class) {
+            return Context.CARRIER_CONFIG_SERVICE;
+        }
+        if (serviceClass == TelecomManager.class) {
+            return Context.TELECOM_SERVICE;
+        }
+        if (serviceClass == TelephonyManager.class) {
+            return Context.TELEPHONY_SERVICE;
+        }
+        if (serviceClass == SubscriptionManager.class) {
+            return Context.TELEPHONY_SUBSCRIPTION_SERVICE;
+        }
+        return null;
+    }
+
+    public PersistableBundle getCarrierConfig() {
+        return mCarrierConfig;
+    }
+}
diff --git a/tests/src/com/android/phone/CallFeaturesSettingTest.java b/tests/src/com/android/phone/CallFeaturesSettingTest.java
index 78d68e3..2468de4 100644
--- a/tests/src/com/android/phone/CallFeaturesSettingTest.java
+++ b/tests/src/com/android/phone/CallFeaturesSettingTest.java
@@ -31,6 +31,7 @@
 import com.android.internal.telephony.PhoneConstants;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -54,6 +55,7 @@
         mActivity = mRule.getActivity();
     }
 
+    @Ignore
     @FlakyTest
     @Test
     public void onResume_fdnIsAvailable_shouldShowFdnMenu() throws NoSuchFieldException,
@@ -69,6 +71,7 @@
         onView(withText(R.string.fdn)).check(matches(isDisplayed()));
     }
 
+    @Ignore
     @FlakyTest
     @Test
     public void onResume_iccCardIsNull_shouldNotShowFdnMenu() throws NoSuchFieldException,
@@ -83,6 +86,7 @@
         onView(withText(R.string.fdn)).check(doesNotExist());
     }
 
+    @Ignore
     @FlakyTest
     @Test
     public void onResume_fdnIsNotAvailable_shouldNotShowFdnMenu() throws NoSuchFieldException,
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 1329a77..583b498 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -17,35 +17,45 @@
 package com.android.services.telephony;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
+import android.content.Context;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
+import android.telecom.ConnectionRequest;
 import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.TelephonyTestBase;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneSwitcher;
+import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 
 import org.junit.After;
@@ -56,6 +66,8 @@
 import org.mockito.Mock;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -69,19 +81,65 @@
     private static final int SLOT_0_PHONE_ID = 0;
     private static final int SLOT_1_PHONE_ID = 1;
 
+    private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
+            "com.android.phone.tests", TelephonyConnectionServiceTest.class.getName());
+    private static final String TEST_ACCOUNT_ID1 = "id1";
+    private static final String TEST_ACCOUNT_ID2 = "id2";
+    private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_1 = new PhoneAccountHandle(
+            TEST_COMPONENT_NAME, TEST_ACCOUNT_ID1);
+    private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_2 = new PhoneAccountHandle(
+            TEST_COMPONENT_NAME, TEST_ACCOUNT_ID2);
+    private static final Uri TEST_ADDRESS = Uri.parse("tel:+16505551212");
+
     @Mock TelephonyConnectionService.TelephonyManagerProxy mTelephonyManagerProxy;
     @Mock TelephonyConnectionService.SubscriptionManagerProxy mSubscriptionManagerProxy;
     @Mock TelephonyConnectionService.PhoneFactoryProxy mPhoneFactoryProxy;
+    @Mock DeviceState mDeviceState;
+    @Mock TelephonyConnectionService.PhoneSwitcherProxy mPhoneSwitcherProxy;
+    @Mock TelephonyConnectionService.PhoneNumberUtilsProxy mPhoneNumberUtilsProxy;
+    @Mock TelephonyConnectionService.PhoneUtilsProxy mPhoneUtilsProxy;
+    @Mock TelephonyConnectionService.DisconnectCauseFactory mDisconnectCauseFactory;
+    @Mock EmergencyNumberTracker mEmergencyNumberTracker;
+    @Mock PhoneSwitcher mPhoneSwitcher;
 
-    TelephonyConnectionService mTestConnectionService;
+    private static class TestTelephonyConnectionService extends TelephonyConnectionService {
+
+        private final Context mContext;
+
+        TestTelephonyConnectionService(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public void onCreate() {
+            // attach test context.
+            attachBaseContext(mContext);
+            super.onCreate();
+        }
+    }
+
+    private TelephonyConnectionService mTestConnectionService;
 
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        mTestConnectionService = new TelephonyConnectionService();
+        mTestConnectionService = new TestTelephonyConnectionService(mContext);
         mTestConnectionService.setPhoneFactoryProxy(mPhoneFactoryProxy);
-        mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy);
         mTestConnectionService.setSubscriptionManagerProxy(mSubscriptionManagerProxy);
+        // Set configurations statically
+        doReturn(false).when(mDeviceState).shouldCheckSimStateBeforeOutgoingCall(any());
+        mTestConnectionService.setPhoneSwitcherProxy(mPhoneSwitcherProxy);
+        doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
+        mTestConnectionService.setPhoneNumberUtilsProxy(mPhoneNumberUtilsProxy);
+        mTestConnectionService.setPhoneUtilsProxy(mPhoneUtilsProxy);
+        mTestConnectionService.setDeviceState(mDeviceState);
+        doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
+                .toTelecomDisconnectCause(anyInt(), any());
+        doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
+                .toTelecomDisconnectCause(anyInt(), any(), anyInt());
+        mTestConnectionService.setDisconnectCauseFactory(mDisconnectCauseFactory);
+        mTestConnectionService.onCreate();
+        mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy);
     }
 
     @After
@@ -522,7 +580,6 @@
      * called.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialTempFailOneSlot() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -531,7 +588,7 @@
         List<Phone> phones = new ArrayList<>(1);
         phones.add(slot0Phone);
         setPhones(phones);
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
 
         mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
 
@@ -554,7 +611,6 @@
      * not called.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialPermFailOneSlot() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -563,7 +619,7 @@
         List<Phone> phones = new ArrayList<>(1);
         phones.add(slot0Phone);
         setPhones(phones);
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
 
         mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
 
@@ -588,7 +644,6 @@
      * PhoneAccount.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialTempFailTwoSlot() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -597,11 +652,15 @@
         Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
                 false /*isEmergencyOnly*/);
         setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
         List<Phone> phones = new ArrayList<>(2);
         phones.add(slot0Phone);
         phones.add(slot1Phone);
         setPhones(phones);
+        doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot0Phone);
+        doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot1Phone);
 
         mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
 
@@ -626,7 +685,6 @@
      * PhoneAccount.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialPermFailTwoSlot() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -635,11 +693,15 @@
         Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
                 false /*isEmergencyOnly*/);
         setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
         List<Phone> phones = new ArrayList<>(2);
         phones.add(slot0Phone);
         phones.add(slot1Phone);
         setPhones(phones);
+        doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot0Phone);
+        doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot1Phone);
 
         mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
 
@@ -664,7 +726,6 @@
      * notified of this twice.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialTempFailTwoSlot_twoFailure() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -673,11 +734,15 @@
         Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
                 false /*isEmergencyOnly*/);
         setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
         List<Phone> phones = new ArrayList<>(2);
         phones.add(slot0Phone);
         phones.add(slot1Phone);
         setPhones(phones);
+        doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot0Phone);
+        doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot1Phone);
 
         // First Temporary failure
         mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
@@ -716,7 +781,6 @@
      * notified of the change to slot 1.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialPermFailTwoSlot_twoFailure() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -725,11 +789,15 @@
         Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
                 false /*isEmergencyOnly*/);
         setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
         List<Phone> phones = new ArrayList<>(2);
         phones.add(slot0Phone);
         phones.add(slot1Phone);
         setPhones(phones);
+        doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot0Phone);
+        doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot1Phone);
 
         // First Permanent failure
         mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
@@ -767,6 +835,7 @@
         // registration to occur.
         Phone phone = c.getPhone();
         c.setOriginalConnection(c.getOriginalConnection());
+        doReturn(mContext).when(phone).getContext();
 
         // When the registration occurs, we'll capture the handler and message so we can post our
         // own messages to it.
@@ -803,6 +872,252 @@
                 extras.getInt(TelephonyManager.EXTRA_NOTIFICATION_CODE));
     }
 
+    /**
+     * Test that the TelephonyConnectionService successfully performs a DDS switch before a call
+     * when we are not roaming and the carrier only supports SUPL over the data plane.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial_carrierconfig_dds() {
+        ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+                .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+                .setAddress(TEST_ADDRESS)
+                .build();
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        // Setup test to not support SUPL on the non-DDS subscription
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                null);
+        testPhone.getServiceState().setRoaming(false);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "150");
+
+        android.telecom.Connection testConnection = mTestConnectionService
+                .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+        assertNotNull("test connection was not set up correctly.", testConnection);
+
+        verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
+                eq(150) /*extensionTime*/, any());
+    }
+
+    /**
+     * Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
+     * supports control-plane fallback.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial_nocarrierconfig() {
+        ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+                .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+                .setAddress(TEST_ADDRESS)
+                .build();
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        // Setup test to not support SUPL on the non-DDS subscription
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                null);
+        testPhone.getServiceState().setRoaming(false);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+
+        android.telecom.Connection testConnection = mTestConnectionService
+                .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+        assertNotNull("test connection was not set up correctly.", testConnection);
+
+        verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+    }
+
+    /**
+     * Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
+     * supports control-plane fallback.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial_supportsuplondds() {
+        ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+                .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+                .setAddress(TEST_ADDRESS)
+                .build();
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        // If the non-DDS supports SUPL, dont switch data
+        doReturn(false).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                null);
+        testPhone.getServiceState().setRoaming(false);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+
+        android.telecom.Connection testConnection = mTestConnectionService
+                .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+        assertNotNull("test connection was not set up correctly.", testConnection);
+
+        verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+    }
+
+    /**
+     * Test that the TelephonyConnectionService does not perform a DDS switch when the carrier does
+     * not support control-plane fallback CarrierConfig while roaming.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial_roaming_nocarrierconfig() {
+        ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+                .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+                .setAddress(TEST_ADDRESS)
+                .build();
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        // Setup test to not support SUPL on the non-DDS subscription
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                null);
+        testPhone.getServiceState().setRoaming(true);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+
+        android.telecom.Connection testConnection = mTestConnectionService
+                .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+        assertNotNull("test connection was not set up correctly.", testConnection);
+
+        verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+    }
+
+    /**
+     * Test that the TelephonyConnectionService does perform a DDS switch even though the carrier
+     * supports control-plane fallback CarrierConfig and the roaming partner is configured to look
+     * like a home network.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial_roamingcarrierconfig() {
+        ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+                .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+                .setAddress(TEST_ADDRESS)
+                .build();
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        // Setup voice roaming scenario
+        String testRoamingOperator = "001001";
+        // In some roaming conditions, we are not technically "roaming"
+        testPhone.getServiceState().setRoaming(false);
+        testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
+        // Setup test to not support SUPL on the non-DDS subscription
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        String[] roamingPlmns = new String[1];
+        roamingPlmns[0] = testRoamingOperator;
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                roamingPlmns);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+
+        android.telecom.Connection testConnection = mTestConnectionService
+                .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+        assertNotNull("test connection was not set up correctly.", testConnection);
+
+        verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
+                eq(0) /*extensionTime*/, any());
+    }
+
+    /**
+     * Test that the TelephonyConnectionService does perform a DDS switch even though the carrier
+     * supports control-plane fallback CarrierConfig if we are roaming and the roaming partner is
+     * configured to use data plane only SUPL.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial__roaming_roamingcarrierconfig() {
+        ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+                .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+                .setAddress(TEST_ADDRESS)
+                .build();
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        // Setup voice roaming scenario
+        String testRoamingOperator = "001001";
+        testPhone.getServiceState().setRoaming(true);
+        testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
+        // Setup test to not support SUPL on the non-DDS subscription
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        String[] roamingPlmns = new String[1];
+        roamingPlmns[0] = testRoamingOperator;
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                roamingPlmns);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+
+        android.telecom.Connection testConnection = mTestConnectionService
+                .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+        assertNotNull("test connection was not set up correctly.", testConnection);
+
+        verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
+                eq(0) /*extensionTime*/, any());
+    }
+
+    /**
+     * Set up a mock MSIM device with TEST_ADDRESS set as an emergency number.
+     * @return the Phone associated with slot 0.
+     */
+    private Phone setupConnectionServiceForDelayDial() {
+        Phone testPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_IN_SERVICE,
+                false /*isEmergencyOnly*/);
+        Phone testPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_OUT_OF_SERVICE,
+                false /*isEmergencyOnly*/);
+        List<Phone> phones = new ArrayList<>(2);
+        doReturn(true).when(testPhone0).isRadioOn();
+        doReturn(true).when(testPhone1).isRadioOn();
+        phones.add(testPhone0);
+        phones.add(testPhone1);
+        setPhones(phones);
+        setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_1, testPhone0);
+        setupDeviceConfig(testPhone0, testPhone1, 1);
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(
+                TEST_ADDRESS.getSchemeSpecificPart());
+        HashMap<Integer, List<EmergencyNumber>> emergencyNumbers = new HashMap<>(1);
+        List<EmergencyNumber> numbers = new ArrayList<>();
+        numbers.add(setupEmergencyNumber(TEST_ADDRESS));
+        emergencyNumbers.put(0 /*subId*/, numbers);
+        doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
+        doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
+
+        return testPhone0;
+    }
+
+    private EmergencyNumber setupEmergencyNumber(Uri address) {
+        return new EmergencyNumber(address.getSchemeSpecificPart(), "", "",
+        EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+        Collections.emptyList(),
+        EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
+        EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+    }
+
+    private void setupHandleToPhoneMap(PhoneAccountHandle handle,  Phone phone) {
+        // use subId 0
+        when(mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(handle)).thenReturn(0);
+        when(mSubscriptionManagerProxy.getPhoneId(0)).thenReturn(0);
+        when(mPhoneFactoryProxy.getPhone(0)).thenReturn(phone);
+    }
+
     private AsyncResult getSuppServiceNotification(int notificationType, int code) {
         SuppServiceNotification notification = new SuppServiceNotification();
         notification.notificationType = notificationType;
@@ -815,6 +1130,7 @@
         ServiceState testServiceState = new ServiceState();
         testServiceState.setState(serviceState);
         testServiceState.setEmergencyOnly(isEmergencyOnly);
+        when(phone.getContext()).thenReturn(mContext);
         when(phone.getServiceState()).thenReturn(testServiceState);
         when(phone.getPhoneId()).thenReturn(phoneId);
         when(phone.getDefaultPhone()).thenReturn(phone);
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index b6e4bf3..c4d6568 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.telecom.PhoneAccountHandle;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -125,6 +126,18 @@
         // Do nothing since the original connection is mock object
     }
 
+    @Override
+    public PersistableBundle getCarrierConfig() {
+        // Depends on PhoneGlobals for context in TelephonyConnection, do not implement during
+        // testing.
+        return new PersistableBundle();
+    }
+
+    @Override
+    void refreshConferenceSupported() {
+        // Requires ImsManager dependencies, do not implement during testing.
+    }
+
     public int getNotifyPhoneAccountChangedCount() {
         return mNotifyPhoneAccountChangedCount;
     }