Add the core assisted dialing logic to telephony.

This module contains all of the transformation logic,
constraints, settings, tests, and other functionality for the feature.

Test: Unit tests that do not require a ShadowPhoneGlobals or AndroidManifest
Bug: 63995261
Change-Id: Iec93497042e6288965054bba359ac984b8db694e
diff --git a/Android.mk b/Android.mk
index 6680191..f70277d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -7,7 +7,11 @@
 phone_common_dir := ../../apps/PhoneCommon
 
 src_dirs := src $(phone_common_dir)/src sip/src
-res_dirs := res $(phone_common_dir)/res sip/res
+res_dirs := res \
+    $(phone_common_dir)/res \
+    sip/res \
+    src/com/android/phone/assisteddialing/res \
+    src/com/android/phone/settings/assisteddialing/res
 
 LOCAL_JAVA_LIBRARIES := \
         telephony-common \
@@ -35,7 +39,9 @@
 
 LOCAL_AAPT_FLAGS := \
     --extra-packages com.android.phone.common \
-    --extra-packages com.android.services.telephony.sip
+    --extra-packages com.android.services.telephony.sip \
+    --extra-packages com.android.phone.assisteddialing \
+    --extra-packages com.android.phone.settings.assisteddialing
 
 LOCAL_PACKAGE_NAME := TeleService
 
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 53384d9..3cb4491 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -583,6 +583,13 @@
                 android:theme="@style/Empty">
         </activity>
 
+        <activity
+            android:name="com.android.phone.settings.assisteddialing.AssistedDialingSettingActivity"
+            android:label="@string/assisted_dialing_setting_title"
+            android:theme="@style/DialerSettingsLight"
+            android:exported="false">
+        </activity>
+
         <activity android:name="com.android.phone.settings.PhoneAccountSettingsActivity"
             android:label="@string/phone_accounts"
             android:theme="@style/DialerSettingsLight">
diff --git a/res/xml/call_feature_setting.xml b/res/xml/call_feature_setting.xml
index 5eb28fb..b3e570b 100644
--- a/res/xml/call_feature_setting.xml
+++ b/res/xml/call_feature_setting.xml
@@ -28,6 +28,14 @@
     </PreferenceScreen>
 
     <PreferenceScreen
+        android:key="assisted_dialing_settings_preference_key"
+        android:title="@string/assisted_dialing_setting_title">
+        <intent
+            android:targetPackage="com.android.phone"
+            android:targetClass="com.android.phone.settings.AssistedDialingSettingActivity"/>
+    </PreferenceScreen>
+
+    <PreferenceScreen
         android:key="button_voicemail_category_key"
         android:title="@string/voicemail" />
 
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index a3b0892..bf465a4 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -50,6 +50,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.phone.settings.PhoneAccountSettingsFragment;
 import com.android.phone.settings.VoicemailSettingsActivity;
+import com.android.phone.settings.assisteddialing.AssistedDialingSettingActivity;
 import com.android.phone.settings.fdn.FdnSetting;
 
 import java.util.List;
@@ -83,10 +84,12 @@
     // persistent, they are used as the key to store shared preferences and the name should not be
     // changed unless the settings are also migrated.
     private static final String VOICEMAIL_SETTING_SCREEN_PREF_KEY = "button_voicemail_category_key";
-    private static final String BUTTON_FDN_KEY   = "button_fdn_key";
-    private static final String BUTTON_RETRY_KEY       = "button_auto_retry_key";
+    private static final String BUTTON_FDN_KEY = "button_fdn_key";
+    private static final String BUTTON_RETRY_KEY = "button_auto_retry_key";
     private static final String BUTTON_GSM_UMTS_OPTIONS = "button_gsm_more_expand_key";
     private static final String BUTTON_CDMA_OPTIONS = "button_cdma_more_expand_key";
+    private static final String BUTTON_ASSISTED_DIALING_KEY =
+            "assisted_dialing_settings_preference_key";
 
     private static final String PHONE_ACCOUNT_SETTINGS_KEY =
             "phone_account_settings_preference_screen";
@@ -144,7 +147,7 @@
                             }
                         };
                 builder.setMessage(getResources().getString(
-                                R.string.enable_video_calling_dialog_msg))
+                        R.string.enable_video_calling_dialog_msg))
                         .setNeutralButton(getResources().getString(
                                 R.string.enable_video_calling_dialog_settings),
                                 networkSettingsClickListener)
@@ -285,11 +288,11 @@
 
         if (mImsMgr.isVtEnabledByPlatform() && mImsMgr.isVtProvisionedOnDevice()
                 && (carrierConfig.getBoolean(
-                        CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS)
+                CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS)
                 || mPhone.mDcTracker.isDataEnabled())) {
             boolean currentValue =
                     mImsMgr.isEnhanced4gLteModeSettingEnabledByUser()
-                    ? PhoneGlobals.getInstance().phoneMgr.isVideoCallingEnabled(
+                            ? PhoneGlobals.getInstance().phoneMgr.isVideoCallingEnabled(
                             getOpPackageName()) : false;
             mEnableVideoCalling.setChecked(currentValue);
             mEnableVideoCalling.setOnPreferenceChangeListener(this);
@@ -299,7 +302,7 @@
 
         if (mImsMgr.isVolteEnabledByPlatform()
                 && !carrierConfig.getBoolean(
-                        CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
+                CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
             TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
             /* tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); */
         }
@@ -360,6 +363,8 @@
             prefSet.removePreference(wifiCallingSettings);
             prefSet.removePreference(mEnableVideoCalling);
         }
+
+        updateAssistedDialingButton();
     }
 
     /**
@@ -407,6 +412,31 @@
         mPhone = mSubscriptionInfoHelper.getPhone();
     }
 
+    private void updateAssistedDialingButton() {
+        Log.i(LOG_TAG, "updateAssistedDialingButton, subId: " + mPhone.getSubId());
+        PreferenceScreen preferenceScreen = getPreferenceScreen();
+        Preference preference = preferenceScreen.findPreference(BUTTON_ASSISTED_DIALING_KEY);
+        Intent intent = mSubscriptionInfoHelper.getIntent(AssistedDialingSettingActivity.class);
+        intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, mPhone.getSubId());
+        preference.setIntent(intent);
+
+        if (!shouldShowAssistedDialingButton()) {
+            preferenceScreen.removePreference(preference);
+        }
+    }
+
+    private boolean shouldShowAssistedDialingButton() {
+        // Allow carriers to disable this feature.
+        PersistableBundle carrierConfig =
+                PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+        if (!carrierConfig.getBoolean(CarrierConfigManager.KEY_ASSISTED_DIALING_ENABLED_BOOL)) {
+            Log.i(LOG_TAG, "shouldShowAssistedDialingButton, disabled by carrier config");
+            return false;
+        }
+
+        return true;
+    }
+
     private static void log(String msg) {
         Log.d(LOG_TAG, msg);
     }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 3fc3c7c..16c6bf7 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -49,6 +49,7 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallManager;
 import com.android.internal.telephony.IccCardConstants;
@@ -183,6 +184,8 @@
 
     private final SettingsObserver mSettingsObserver;
 
+    private final PhoneNumberUtil mPhoneNumberUtil = PhoneNumberUtil.getInstance();
+
     Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -196,7 +199,7 @@
                             CarrierConfigManager.KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL)) {
                         // Some products don't have the concept of a "SIM network lock"
                         Log.i(LOG_TAG, "Ignoring EVENT_SIM_NETWORK_LOCKED event; "
-                              + "not showing 'SIM network unlock' PIN entry screen");
+                                + "not showing 'SIM network unlock' PIN entry screen");
                     } else {
                         // Normal case: show the "SIM network unlock" PIN entry screen.
                         // The user won't be able to do anything else until
@@ -439,6 +442,10 @@
         return mCM;
     }
 
+    public PhoneNumberUtil getPhoneNumberUtil() {
+        return mPhoneNumberUtil;
+    }
+
     public PersistableBundle getCarrierConfig() {
         return getCarrierConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
     }
@@ -652,7 +659,7 @@
     private void setRadioPowerOff(Context context) {
         Log.i(LOG_TAG, "Turning radio off - airplane");
         Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
-                 PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
+                PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
         SystemProperties.set("persist.radio.airplane_mode_on", "1");
         Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT, 0);
         PhoneUtils.setRadioPower(false);
diff --git a/src/com/android/phone/assisteddialing/AssistedDialingMediator.java b/src/com/android/phone/assisteddialing/AssistedDialingMediator.java
new file mode 100755
index 0000000..f02dae3
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/AssistedDialingMediator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.NonNull;
+import android.telecom.TransformationInfo;
+
+import java.util.Optional;
+
+/**
+ * The core interface for the AssistedDialingMediator.
+ */
+public interface AssistedDialingMediator {
+
+    /**
+     * Returns {@code true} if the current client platform supports Assisted Dialing.
+     */
+    boolean isPlatformEligible();
+
+    /**
+     * Returns the country code in which the library thinks the user typically resides.
+     */
+    Optional<String> userHomeCountryCode();
+
+    /**
+     * Attempts to perform the assisted dialing action on the supplied number.
+     */
+    Optional<TransformationInfo> attemptAssistedDial(@NonNull String numberToTransform);
+}
diff --git a/src/com/android/phone/assisteddialing/AssistedDialingMediatorImpl.java b/src/com/android/phone/assisteddialing/AssistedDialingMediatorImpl.java
new file mode 100755
index 0000000..37ae15d
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/AssistedDialingMediatorImpl.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.NonNull;
+import android.telecom.TransformationInfo;
+import android.util.Log;
+
+import java.util.Optional;
+
+/**
+ * The Mediator for Assisted Dialing.
+ * <p>
+ * <p>This class is responsible for mediating location discovery of the user, determining if the
+ * call is eligible for assisted dialing, and performing the transformation of numbers eligible for
+ * assisted dialing.
+ */
+final class AssistedDialingMediatorImpl implements AssistedDialingMediator {
+
+    private final LocationDetector mLocationDetector;
+    private final NumberTransformer mNumberTransformer;
+    private static final String LOG_TAG = "AssistedDialingMediatorImpl";
+
+    AssistedDialingMediatorImpl(
+            @NonNull LocationDetector locationDetector,
+            @NonNull NumberTransformer numberTransformer) {
+        if (locationDetector == null) {
+            throw new NullPointerException("locationDetector was null");
+        }
+
+        if (numberTransformer == null) {
+            throw new NullPointerException("numberTransformer was null");
+        }
+        this.mLocationDetector = locationDetector;
+        this.mNumberTransformer = numberTransformer;
+    }
+
+    @Override
+    public boolean isPlatformEligible() {
+        // This impl is only instantiated if it passes platform checks in ConcreteCreator,
+        // so we return true here.
+        return true;
+    }
+
+    /**
+     * Returns the country code in which the library thinks the user typically resides.
+     */
+    @Override
+    public Optional<String> userHomeCountryCode() {
+        return mLocationDetector.getUpperCaseUserHomeCountry();
+    }
+
+    /**
+     * Returns an Optional of type String containing the transformed number that was provided. The
+     * transformed number should be capable of dialing out of the User's current country and
+     * successfully connecting with a contact in the User's home country.
+     */
+    @Override
+    public Optional<TransformationInfo> attemptAssistedDial(@NonNull String numberToTransform) {
+        Optional<String> userHomeCountryCode = mLocationDetector.getUpperCaseUserHomeCountry();
+        Optional<String> userRoamingCountryCode = mLocationDetector
+                .getUpperCaseUserRoamingCountry();
+
+        if (!userHomeCountryCode.isPresent() || !userRoamingCountryCode.isPresent()) {
+            Log.i(LOG_TAG,
+                    "Unable to determine country codes");
+            return Optional.empty();
+        }
+
+        return mNumberTransformer.doAssistedDialingTransformation(
+                numberToTransform, userHomeCountryCode.get(), userRoamingCountryCode.get());
+    }
+}
diff --git a/src/com/android/phone/assisteddialing/AssistedDialingMediatorStub.java b/src/com/android/phone/assisteddialing/AssistedDialingMediatorStub.java
new file mode 100755
index 0000000..6c282e6
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/AssistedDialingMediatorStub.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.NonNull;
+import android.telecom.TransformationInfo;
+
+import java.util.Optional;
+
+/**
+ * A stub assisted dialing implementation.
+ */
+public final class AssistedDialingMediatorStub implements AssistedDialingMediator {
+
+    /**
+     * Always returns an empty Optional.
+     */
+    @Override
+    public Optional<TransformationInfo> attemptAssistedDial(@NonNull String numberToTransform) {
+        return Optional.empty();
+    }
+
+    /**
+     * Always returns an empty Optional.
+     */
+    @Override
+    public Optional<String> userHomeCountryCode() {
+        return Optional.empty();
+    }
+
+    @Override
+    public boolean isPlatformEligible() {
+        return false;
+    }
+}
diff --git a/src/com/android/phone/assisteddialing/ConcreteCreator.java b/src/com/android/phone/assisteddialing/ConcreteCreator.java
new file mode 100755
index 0000000..a9ea0ab
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/ConcreteCreator.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.content.Context;
+import android.os.UserManager;
+import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+/**
+ * A Creator for AssistedDialingMediators.
+ * <p>
+ * <p>This helps keep the dependencies required by AssistedDialingMediator for assisted dialing
+ * explicit.
+ */
+public final class ConcreteCreator {
+
+    private static final String LOG_TAG = "ConcreteCreator";
+
+    /**
+     * Creates a new AssistedDialingMediator
+     *
+     * @param telephonyManager The telephony manager used to determine user location.
+     * @param context          The context used to determine whether or not a provided number is an
+     *                        emergency
+     *                         number.
+     * @return An AssistedDialingMediator
+     */
+    public static AssistedDialingMediator createNewAssistedDialingMediator(
+            @NonNull TelephonyManager telephonyManager, @NonNull Context context) {
+
+        if (telephonyManager == null) {
+            Log.i(
+                    LOG_TAG,
+                    "provided TelephonyManager was null");
+            return new AssistedDialingMediatorStub();
+        }
+        if (context == null) {
+            Log.i(LOG_TAG, "provided context was null");
+            return new AssistedDialingMediatorStub();
+        }
+
+        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        if (!userManager.isUserUnlocked()) {
+            // To avoid any issues reading preferences, we disable the feature when the user is in a
+            // locked state.
+            Log.i(LOG_TAG, "user is locked");
+            return new AssistedDialingMediatorStub();
+        }
+
+        if (!PreferenceManager.getDefaultSharedPreferences(context)
+                .getBoolean(context.getString(
+                        R.string.assisted_dialing_setting_toggle_key), true)) {
+            Log.i(LOG_TAG, "disabled by local setting");
+
+            return new AssistedDialingMediatorStub();
+        }
+
+        Constraints constraints = new Constraints(context, getCountryCodeProvider());
+        return new AssistedDialingMediatorImpl(
+                new LocationDetector(
+                        telephonyManager,
+                        PreferenceManager.getDefaultSharedPreferences(context)
+                                .getString(context.getString(
+                                        R.string.assisted_dialing_setting_cc_key), null)),
+                new NumberTransformer(constraints));
+    }
+
+    /**
+     * Returns a CountryCodeProvider responsible for providing countries eligible for
+     * assisted Dialing
+     */
+    public static CountryCodeProvider getCountryCodeProvider() {
+        return new CountryCodeProvider();
+    }
+}
diff --git a/src/com/android/phone/assisteddialing/Constraints.java b/src/com/android/phone/assisteddialing/Constraints.java
new file mode 100755
index 0000000..f5f0933
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/Constraints.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.i18n.phonenumbers.NumberParseException;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber.CountryCodeSource;
+import com.android.phone.PhoneGlobals;
+
+import java.util.Locale;
+import java.util.Optional;
+
+/**
+ * Ensures that a number is eligible for Assisted Dialing
+ */
+final class Constraints {
+    private final PhoneNumberUtil mPhoneNumberUtil = PhoneGlobals.getInstance()
+            .getPhoneNumberUtil();
+    private final Context mContext;
+    private final CountryCodeProvider mCountryCodeProvider;
+    private static final String LOG_TAG = "Constraints";
+
+    /**
+     * Create a new instance of Constraints.
+     *
+     * @param mContext                   The mContext used to determine whether or not a number
+     *                                   is an emergency number.
+     * @param configProviderCountryCodes A csv of supported country codes, e.g. "US,CA"
+     */
+    Constraints(@NonNull Context context, @NonNull CountryCodeProvider countryCodeProvider) {
+        this.mContext = context;
+        this.mCountryCodeProvider = countryCodeProvider;
+    }
+
+    /**
+     * Determines whether or not we think Assisted Dialing is possible.
+     *
+     * @param numberToCheck          A string containing the phone number.
+     * @param userHomeCountryCode    A string containing an ISO 3166-1 alpha-2 country code
+     *                               representing the user's home country.
+     * @param userRoamingCountryCode A string containing an ISO 3166-1 alpha-2 country code
+     *                               representing the user's roaming country.
+     * @return A boolean indicating whether or not the provided values are eligible for assisted
+     * dialing.
+     */
+    public boolean meetsPreconditions(
+            @NonNull String numberToCheck,
+            @NonNull String userHomeCountryCode,
+            @NonNull String userRoamingCountryCode) {
+        if (this.mContext == null) {
+            Log.i(LOG_TAG, "Provided context cannot be null");
+            return false;
+        }
+
+        if (this.mCountryCodeProvider == null) {
+            Log.i(LOG_TAG, "Provided configProviderCountryCodes cannot be null");
+            return false;
+        }
+
+        if (TextUtils.isEmpty(numberToCheck)) {
+            Log.i(LOG_TAG, "numberToCheck was empty");
+            return false;
+        }
+
+        if (TextUtils.isEmpty(userHomeCountryCode)) {
+            Log.i(LOG_TAG, "userHomeCountryCode was empty");
+            return false;
+        }
+
+        if (TextUtils.isEmpty(userRoamingCountryCode)) {
+            Log.i(LOG_TAG, "userRoamingCountryCode was empty");
+            return false;
+        }
+
+        userHomeCountryCode = userHomeCountryCode.toUpperCase(Locale.US);
+        userRoamingCountryCode = userRoamingCountryCode.toUpperCase(Locale.US);
+
+        Optional<PhoneNumber> parsedPhoneNumber = parsePhoneNumber(numberToCheck,
+                userHomeCountryCode);
+
+        if (!parsedPhoneNumber.isPresent()) {
+            Log.i(LOG_TAG, "parsedPhoneNumber was empty");
+            return false;
+        }
+
+        return areSupportedCountryCodes(userHomeCountryCode, userRoamingCountryCode)
+                && isUserRoaming(userHomeCountryCode, userRoamingCountryCode)
+                && !isInternationalNumber(parsedPhoneNumber)
+                && !isEmergencyNumber(numberToCheck, mContext)
+                && isValidNumber(parsedPhoneNumber)
+                && !hasExtension(parsedPhoneNumber);
+    }
+
+    /**
+     * Returns a boolean indicating the value equivalence of the provided country codes.
+     */
+    private boolean isUserRoaming(
+            @NonNull String userHomeCountryCode, @NonNull String userRoamingCountryCode) {
+        boolean result = !userHomeCountryCode.equals(userRoamingCountryCode);
+        Log.i(LOG_TAG, "isUserRoaming = " + String.valueOf(result));
+        return result;
+    }
+
+    /**
+     * Returns a boolean indicating the support of both provided country codes for assisted dialing.
+     * Both country codes must be allowed for the return value to be true.
+     */
+    private boolean areSupportedCountryCodes(
+            @NonNull String userHomeCountryCode, @NonNull String userRoamingCountryCode) {
+        if (TextUtils.isEmpty(userHomeCountryCode)) {
+            Log.i(LOG_TAG, "userHomeCountryCode was empty");
+            return false;
+        }
+
+        if (TextUtils.isEmpty(userRoamingCountryCode)) {
+            Log.i(LOG_TAG, "userRoamingCountryCode was empty");
+            return false;
+        }
+
+        boolean result =
+                mCountryCodeProvider.isSupportedCountryCode(userHomeCountryCode)
+                        && mCountryCodeProvider.isSupportedCountryCode(userRoamingCountryCode);
+        Log.i("Constraints.areSupportedCountryCodes", String.valueOf(result));
+        return result;
+    }
+
+    /**
+     * A convenience method to take a number as a String and a specified country code, and return a
+     * PhoneNumber object.
+     */
+    private Optional<PhoneNumber> parsePhoneNumber(
+            @NonNull String numberToParse, @NonNull String userHomeCountryCode) {
+        try {
+            return Optional.of(mPhoneNumberUtil.parseAndKeepRawInput(numberToParse,
+                    userHomeCountryCode));
+        } catch (NumberParseException e) {
+            Log.i(LOG_TAG, "could not parse the number");
+            return Optional.empty();
+        }
+    }
+
+    /**
+     * Returns a boolean indicating if the provided number is already internationally formatted.
+     */
+    private boolean isInternationalNumber(@NonNull Optional<PhoneNumber> parsedPhoneNumber) {
+
+        if (parsedPhoneNumber.get().hasCountryCode()
+                && parsedPhoneNumber.get().getCountryCodeSource()
+                != CountryCodeSource.FROM_DEFAULT_COUNTRY) {
+            Log.i(
+                    LOG_TAG,
+                    "phone number already provided the country code");
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns a boolean indicating if the provided number has an extension.
+     * <p>
+     * <p>Extensions are currently stripped when formatting a number for mobile dialing, so we don't
+     * want to purposefully truncate a number.
+     */
+    private boolean hasExtension(@NonNull Optional<PhoneNumber> parsedPhoneNumber) {
+
+        if (parsedPhoneNumber.get().hasExtension()
+                && !TextUtils.isEmpty(parsedPhoneNumber.get().getExtension())) {
+            Log.i(LOG_TAG, "phone number has an extension");
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns a boolean indicating if the provided number is considered to be a valid number.
+     */
+    private boolean isValidNumber(@NonNull Optional<PhoneNumber> parsedPhoneNumber) {
+        boolean result = PhoneNumberUtil.getInstance().isValidNumber(parsedPhoneNumber.get());
+        Log.i(LOG_TAG, "isValidNumber = " + String.valueOf(result));
+
+        return result;
+    }
+
+    /**
+     * Returns a boolean indicating if the provided number is an emergency number.
+     */
+    private boolean isEmergencyNumber(@NonNull String numberToCheck, @NonNull Context mContext) {
+        // isEmergencyNumber may depend on network state, so also use isLocalEmergencyNumber when
+        // roaming and out of service.
+        boolean result =
+                PhoneNumberUtils.isEmergencyNumber(numberToCheck)
+                        || PhoneNumberUtils.isLocalEmergencyNumber(mContext, numberToCheck);
+        Log.i("Constraints.isEmergencyNumber", String.valueOf(result));
+        return result;
+    }
+}
diff --git a/src/com/android/phone/assisteddialing/CountryCodeProvider.java b/src/com/android/phone/assisteddialing/CountryCodeProvider.java
new file mode 100755
index 0000000..5dac283
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/CountryCodeProvider.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.VisibleForTesting;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A class to provide the appropriate country codes related to assisted dialing.
+ */
+public final class CountryCodeProvider {
+
+    // ISO 3166-1 alpha-2 Country Codes that are eligible for assisted dialing.
+    @VisibleForTesting
+    static final List<String> DEFAULT_COUNTRY_CODES =
+            Arrays.asList(
+                    "CA" /* Canada */,
+                    "GB" /* United Kingdom */,
+                    "JP" /* Japan */,
+                    "MX" /* Mexico */,
+                    "US" /* United States */);
+
+    /**
+     * Checks whether a supplied country code is supported.
+     */
+    public boolean isSupportedCountryCode(String countryCode) {
+        return DEFAULT_COUNTRY_CODES.contains(countryCode);
+    }
+}
diff --git a/src/com/android/phone/assisteddialing/LocationDetector.java b/src/com/android/phone/assisteddialing/LocationDetector.java
new file mode 100755
index 0000000..a601167
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/LocationDetector.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Locale;
+import java.util.Optional;
+
+/**
+ * LocationDetector is responsible for determining the Roaming location of the User, in addition to
+ * User's home country.
+ */
+final class LocationDetector {
+
+    private final TelephonyManager mTelephonyManager;
+    private final String mUserProvidedHomeCountry;
+    private static final String LOG_TAG = "LocationDetector";
+
+    LocationDetector(
+            @NonNull TelephonyManager telephonyManager, @Nullable String userProvidedHomeCountry) {
+        this.mTelephonyManager = telephonyManager;
+        this.mUserProvidedHomeCountry = userProvidedHomeCountry;
+    }
+
+    /**
+     * Returns what we believe to be the User's home country. This should resolve to
+     * PROPERTY_ICC_OPERATOR_ISO_COUNTRY
+     */
+    public Optional<String> getUpperCaseUserHomeCountry() {
+        if (mTelephonyManager == null) {
+            Log.i(LOG_TAG, "Provided TelephonyManager was null");
+            return Optional.empty();
+        }
+
+
+        if (!TextUtils.isEmpty(mUserProvidedHomeCountry)) {
+            Log.i(
+                    LOG_TAG,
+                    "user provided home country code");
+            return Optional.of(mUserProvidedHomeCountry.toUpperCase(Locale.US));
+        }
+
+        String simCountryIso = mTelephonyManager.getSimCountryIso();
+        if (simCountryIso != null) {
+            Log.i(LOG_TAG, "using sim country iso");
+            return Optional.of(mTelephonyManager.getSimCountryIso().toUpperCase(Locale.US));
+        }
+        Log.i(LOG_TAG, "user home country was null");
+        return Optional.empty();
+    }
+
+    /**
+     * Returns what we believe to be the User's current (roaming) country
+     */
+    public Optional<String> getUpperCaseUserRoamingCountry() {
+        if (mTelephonyManager == null) {
+            Log.i(LOG_TAG, "Provided TelephonyManager was null");
+            return Optional.empty();
+        }
+
+        String networkCountryIso = mTelephonyManager.getNetworkCountryIso();
+        if (networkCountryIso != null) {
+            return Optional.of(mTelephonyManager.getNetworkCountryIso().toUpperCase(Locale.US));
+        }
+        Log.i(LOG_TAG, "user roaming country was null");
+        return Optional.empty();
+    }
+}
diff --git a/src/com/android/phone/assisteddialing/NumberTransformer.java b/src/com/android/phone/assisteddialing/NumberTransformer.java
new file mode 100755
index 0000000..ea0bd8f
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/NumberTransformer.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.NonNull;
+import android.telecom.TransformationInfo;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.i18n.phonenumbers.NumberParseException;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.android.phone.PhoneGlobals;
+
+import java.util.Optional;
+
+/**
+ * Responsible for transforming numbers to make them dialable and valid when roaming.
+ */
+final class NumberTransformer {
+
+    private final PhoneNumberUtil mPhoneNumberUtil = PhoneGlobals.getInstance()
+            .getPhoneNumberUtil();
+    private final Constraints mConstraints;
+    private static final String LOG_TAG = "NumberTransformer";
+
+    NumberTransformer(Constraints constraints) {
+        this.mConstraints = constraints;
+    }
+
+    /**
+     * Returns a boolean for callers to quickly determine whether or not the AssistedDialingMediator
+     * thinks an attempt at assisted dialing is likely to succeed.
+     */
+    public boolean canDoAssistedDialingTransformation(
+            @NonNull String numberToCheck,
+            @NonNull String userHomeCountryCode,
+            @NonNull String userRoamingCountryCode) {
+        return mConstraints.meetsPreconditions(
+                numberToCheck, userHomeCountryCode, userRoamingCountryCode);
+    }
+
+    /**
+     * A method to do assisted dialing transformations.
+     * <p>
+     * <p>The library will do its best to attempt a transformation, but, if for any reason the
+     * transformation fails, we return an empty optional. The operation can be considered a success
+     * when the Optional we return has a value set.
+     */
+    public Optional<TransformationInfo> doAssistedDialingTransformation(
+            String numbertoTransform, String userHomeCountryCode, String userRoamingCountryCode) {
+
+        if (!mConstraints.meetsPreconditions(
+                numbertoTransform, userHomeCountryCode, userRoamingCountryCode)) {
+            Log.i(
+                    LOG_TAG,
+                    "assisted dialing failed preconditions");
+            return Optional.empty();
+        }
+
+        PhoneNumber phoneNumber;
+        try {
+            phoneNumber = mPhoneNumberUtil.parse(numbertoTransform, userHomeCountryCode);
+        } catch (NumberParseException e) {
+            Log.i(LOG_TAG, "number failed to parse");
+            return Optional.empty();
+        }
+
+        String transformedNumber =
+                mPhoneNumberUtil.formatNumberForMobileDialing(phoneNumber, userRoamingCountryCode,
+                        true);
+
+        // formatNumberForMobileDialing may return an empty String.
+        if (TextUtils.isEmpty(transformedNumber)) {
+            Log.i(
+                    LOG_TAG,
+                    "formatNumberForMobileDialing returned an empty string");
+            return Optional.empty();
+        }
+
+        return Optional.of(
+                new TransformationInfo(
+                        numbertoTransform,
+                        transformedNumber,
+                        userHomeCountryCode,
+                        userRoamingCountryCode,
+                        phoneNumber.getCountryCode()));
+    }
+}
diff --git a/src/com/android/phone/assisteddialing/res/values/strings.xml b/src/com/android/phone/assisteddialing/res/values/strings.xml
new file mode 100755
index 0000000..f9cb123
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<resources>
+  <!-- Key for the assisted dialing setting toggle-->
+  <string name="assisted_dialing_setting_toggle_key" translatable="false">assisted_dialing_setting_toggle_key</string>
+
+  <!-- Key for the assisted dialing home country setting-->
+  <string name="assisted_dialing_setting_cc_key" translatable="false">assisted_dialing_setting_cc_key</string>
+</resources>
\ No newline at end of file
diff --git a/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingActivity.java b/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingActivity.java
new file mode 100755
index 0000000..7386e45
--- /dev/null
+++ b/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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.phone.settings.assisteddialing;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.view.MenuItem;
+
+/**
+ * The Settings Activity for Assisted Dialing.
+ */
+public class AssistedDialingSettingActivity extends PreferenceActivity {
+
+    @Override
+    protected void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+
+        getFragmentManager()
+                .beginTransaction()
+                .replace(android.R.id.content, new AssistedDialingSettingFragment())
+                .commit();
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            onBackPressed();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingFragment.java b/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingFragment.java
new file mode 100755
index 0000000..ec255ca
--- /dev/null
+++ b/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingFragment.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2017 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.phone.settings.assisteddialing;
+
+import android.icu.util.ULocale;
+import android.icu.util.ULocale.Builder;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.SwitchPreference;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.phone.assisteddialing.AssistedDialingMediator;
+import com.android.phone.assisteddialing.ConcreteCreator;
+import com.android.phone.assisteddialing.CountryCodeProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * The setting for Assisted Dialing
+ */
+public class AssistedDialingSettingFragment extends PreferenceFragment {
+
+    private CountryCodeProvider mCountryCodeProvider;
+    private AssistedDialingMediator mAssistedDialingMediator;
+    private static final String LOG_TAG = "AssistedDialingSettingFragment";
+
+    final class DisplayNameAndCountryCodeTuple {
+
+        private final CharSequence mCountryDisplayname;
+        private final CharSequence mCountryCode;
+
+        DisplayNameAndCountryCodeTuple(
+                CharSequence mCountryDisplayname,
+                CharSequence mCountryCode) {
+            if (mCountryDisplayname == null) {
+                throw new NullPointerException("Null mCountryDisplayname");
+            }
+            this.mCountryDisplayname = mCountryDisplayname;
+            if (mCountryCode == null) {
+                throw new NullPointerException("Null mCountryCode");
+            }
+            this.mCountryCode = mCountryCode;
+        }
+
+        CharSequence countryDisplayname() {
+            return mCountryDisplayname;
+        }
+
+        CharSequence countryCode() {
+            return mCountryCode;
+        }
+
+        public String toString() {
+            return "DisplayNameAndCountryCodeTuple{"
+                    + "mCountryDisplayname=" + mCountryDisplayname + ", "
+                    + "mCountryCode=" + mCountryCode
+                    + "}";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (o instanceof AssistedDialingSettingFragment.DisplayNameAndCountryCodeTuple) {
+                AssistedDialingSettingFragment.DisplayNameAndCountryCodeTuple that = (
+                        AssistedDialingSettingFragment.DisplayNameAndCountryCodeTuple) o;
+                return (this.mCountryDisplayname.equals(that.countryDisplayname()))
+                        && (this.mCountryCode.equals(that.countryCode()));
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            int h = 1;
+            h *= 1000003;
+            h ^= this.mCountryDisplayname.hashCode();
+            h *= 1000003;
+            h ^= this.mCountryCode.hashCode();
+            return h;
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mAssistedDialingMediator =
+                ConcreteCreator.createNewAssistedDialingMediator(
+                        getContext().getSystemService(TelephonyManager.class), getContext());
+
+        mCountryCodeProvider =
+                ConcreteCreator.getCountryCodeProvider();
+
+        // Load the preferences from an XML resource
+        addPreferencesFromResource(R.xml.assisted_dialing_setting);
+        SwitchPreference switchPref =
+                (SwitchPreference)
+                        findPreference(getContext().getString(
+                                R.string.assisted_dialing_setting_toggle_key));
+
+        ListPreference countryChooserPref =
+                (ListPreference)
+                        findPreference(getContext().getString(
+                                R.string.assisted_dialing_setting_cc_key));
+
+        updateCountryChoices(countryChooserPref);
+        updateCountryChooserSummary(countryChooserPref);
+
+        countryChooserPref.setOnPreferenceChangeListener(this::updateListSummary);
+    }
+
+    private void updateCountryChooserSummary(ListPreference countryChooserPref) {
+        String defaultSummaryText = countryChooserPref.getEntries()[0].toString();
+
+        if (countryChooserPref.getEntry().equals(defaultSummaryText)) {
+            Optional<String> userHomeCountryCode = mAssistedDialingMediator.userHomeCountryCode();
+            if (userHomeCountryCode.isPresent()) {
+                CharSequence[] entries = countryChooserPref.getEntries();
+                try {
+                    CharSequence regionalDisplayName =
+                            entries[countryChooserPref.findIndexOfValue(userHomeCountryCode.get())];
+                    countryChooserPref.setSummary(
+                            getContext()
+                                    .getString(
+                                            R.string.assisted_dialing_setting_cc_default_summary,
+                                            regionalDisplayName));
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    // This might happen if there is a mismatch between the automatically
+                    // detected home country, and the countries currently eligible to select in
+                    // the settings.
+                    Log.i(
+                            LOG_TAG,
+                            "Failed to find human readable mapping for country, using default.");
+                }
+            }
+        } else {
+            countryChooserPref.setSummary(countryChooserPref.getEntry());
+        }
+    }
+
+    /**
+     * Filters the default entries in the country chooser by only showing those countries in which
+     * the feature in enabled.
+     */
+    private void updateCountryChoices(ListPreference countryChooserPref) {
+
+        List<DisplayNameAndCountryCodeTuple> defaultCountryChoices =
+                buildDefaultCountryChooserKeysAndValues(countryChooserPref);
+
+        // Always include the default preference.
+        List<CharSequence> newKeys = new ArrayList<>();
+        List<CharSequence> newValues = new ArrayList<>();
+        newKeys.add(countryChooserPref.getEntries()[0]);
+        newValues.add(countryChooserPref.getEntryValues()[0]);
+
+        for (DisplayNameAndCountryCodeTuple tuple : defaultCountryChoices) {
+            if (mCountryCodeProvider.isSupportedCountryCode(tuple.countryCode().toString())) {
+                newKeys.add(tuple.countryDisplayname());
+                newValues.add(tuple.countryCode());
+            }
+        }
+
+        countryChooserPref.setEntries(newKeys.toArray(new CharSequence[newKeys.size()]));
+        countryChooserPref.setEntryValues(newValues.toArray(new CharSequence[newValues.size()]));
+    }
+
+    private List<DisplayNameAndCountryCodeTuple> buildDefaultCountryChooserKeysAndValues(
+            ListPreference countryChooserPref) {
+        CharSequence[] keys = countryChooserPref.getEntries();
+        CharSequence[] values = countryChooserPref.getEntryValues();
+
+        if (keys.length != values.length) {
+            throw new IllegalStateException(
+                    "Unexpected mismatch in country chooser key/value size");
+        }
+
+        List<DisplayNameAndCountryCodeTuple> displayNamesandCountryCodes = new ArrayList<>();
+        // getCountry() is actually getRegion() and conforms to the iso standards of input for the
+        // builder.
+        ULocale userLocale =
+                new ULocale.Builder()
+                        .setRegion(getResources().getConfiguration().getLocales().get(0)
+                                .getCountry())
+                        .setLanguage(getResources().getConfiguration().getLocales().get(0)
+                                .getLanguage())
+                        .build();
+        for (int i = 0; i < keys.length; i++) {
+            ULocale settingRowDisplayCountry = new Builder().setRegion(values[i].toString())
+                    .build();
+            String localizedDisplayCountry = settingRowDisplayCountry.getDisplayCountry(userLocale);
+            String settingDisplayName = localizedDisplayCountry + " " + keys[i];
+            displayNamesandCountryCodes.add(
+                    new DisplayNameAndCountryCodeTuple(settingDisplayName, values[i]));
+        }
+
+        return displayNamesandCountryCodes;
+    }
+
+    boolean updateListSummary(Preference pref, Object newValue) {
+        ListPreference listPref = (ListPreference) pref;
+        CharSequence[] entries = listPref.getEntries();
+        listPref.setSummary(entries[listPref.findIndexOfValue(newValue.toString())]);
+        return true;
+    }
+
+}
diff --git a/src/com/android/phone/settings/assisteddialing/res/values/strings.xml b/src/com/android/phone/settings/assisteddialing/res/values/strings.xml
new file mode 100755
index 0000000..417191e
--- /dev/null
+++ b/src/com/android/phone/settings/assisteddialing/res/values/strings.xml
@@ -0,0 +1,531 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <!-- Label for a setting enabling assisted dialing [CHAR LIMIT=40]-->
+  <string name="assisted_dialing_setting_title">Assisted dialing</string>
+
+  <!-- Label for a setting enabling assisted dialing switch preference-->
+  <string name="assisted_dialing_setting_summary">Predict and add a country code when you call while traveling abroad</string>
+
+  <!-- Indicates the default state for the home country selector-->
+  <string name="assisted_dialing_setting_cc_default_summary">Automatically detected • <xliff:g example="United Kingdom (+44)" id="ad_country_code_info">%1$s</xliff:g></string>
+
+  <!-- Indicates the default failure state for the home country selector-->
+  <string name="assisted_dialing_setting_cc_default_summary_fallback">Automatically detected</string>
+
+  <!-- Category title for the country code picker in assisted dialing [CHAR LIMIT=40]-->
+  <string name="assisted_dialing_setting_cc_category_title">Home country</string>
+
+  <!-- Label for the country code picker in assisted dialing [CHAR LIMIT=40]-->
+  <string name="assisted_dialing_setting_cc_title">Default home country</string>
+
+  <!-- Composed from https://www.iso.org/obp/ui/#search and
+       https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/metadata.js#L34 -->
+  <!-- Excluding ['Antarctica', 'Bouvet Island', 'French Southern Territories (the)', 'Heard Island and McDonald Islands', 'Pitcairn', 'South Georgia and the South Sandwich Islands', 'United States Minor Outlying Islands (the)'] -->
+  <!-- DO NOT TRANSLATE. Country codes used in assisted dialing. [CHAR LIMIT=40] -->
+  <string-array name="assisted_dialing_cc_entries" translatable="false">
+    <item>@string/assisted_dialing_setting_cc_default_summary_fallback</item>
+    <item>(+93)</item>
+    <item>(+358)</item>
+    <item>(+355)</item>
+    <item>(+213)</item>
+    <item>(+1)</item>
+    <item>(+376)</item>
+    <item>(+244)</item>
+    <item>(+1)</item>
+    <item>(+1)</item>
+    <item>(+54)</item>
+    <item>(+374)</item>
+    <item>(+297)</item>
+    <item>(+61)</item>
+    <item>(+43)</item>
+    <item>(+994)</item>
+    <item>(+1)</item>
+    <item>(+973)</item>
+    <item>(+880)</item>
+    <item>(+1)</item>
+    <item>(+375)</item>
+    <item>(+32)</item>
+    <item>(+501)</item>
+    <item>(+229)</item>
+    <item>(+1)</item>
+    <item>(+975)</item>
+    <item>(+591)</item>
+    <item>(+599)</item>
+    <item>(+387)</item>
+    <item>(+267)</item>
+    <item>(+55)</item>
+    <item>(+246)</item>
+    <item>(+673)</item>
+    <item>(+359)</item>
+    <item>(+226)</item>
+    <item>(+257)</item>
+    <item>(+238)</item>
+    <item>(+855)</item>
+    <item>(+237)</item>
+    <item>(+1)</item>
+    <item>(+1)</item>
+    <item>(+236)</item>
+    <item>(+235)</item>
+    <item>(+56)</item>
+    <item>(+86)</item>
+    <item>(+61)</item>
+    <item>(+61)</item>
+    <item>(+57)</item>
+    <item>(+269)</item>
+    <item>(+243)</item>
+    <item>(+242)</item>
+    <item>(+682)</item>
+    <item>(+506)</item>
+    <item>(+225)</item>
+    <item>(+385)</item>
+    <item>(+53)</item>
+    <item>(+599)</item>
+    <item>(+357)</item>
+    <item>(+420)</item>
+    <item>(+45)</item>
+    <item>(+253)</item>
+    <item>(+1)</item>
+    <item>(+1)</item>
+    <item>(+593)</item>
+    <item>(+20)</item>
+    <item>(+503)</item>
+    <item>(+240)</item>
+    <item>(+291)</item>
+    <item>(+372)</item>
+    <item>(+251)</item>
+    <item>(+500)</item>
+    <item>(+298)</item>
+    <item>(+679)</item>
+    <item>(+358)</item>
+    <item>(+33)</item>
+    <item>(+594)</item>
+    <item>(+689)</item>
+    <item>(+241)</item>
+    <item>(+220)</item>
+    <item>(+995)</item>
+    <item>(+49)</item>
+    <item>(+233)</item>
+    <item>(+350)</item>
+    <item>(+30)</item>
+    <item>(+299)</item>
+    <item>(+1)</item>
+    <item>(+590)</item>
+    <item>(+1)</item>
+    <item>(+502)</item>
+    <item>(+44)</item>
+    <item>(+224)</item>
+    <item>(+245)</item>
+    <item>(+592)</item>
+    <item>(+509)</item>
+    <item>(+39)</item>
+    <item>(+504)</item>
+    <item>(+852)</item>
+    <item>(+36)</item>
+    <item>(+354)</item>
+    <item>(+91)</item>
+    <item>(+62)</item>
+    <item>(+98)</item>
+    <item>(+964)</item>
+    <item>(+353)</item>
+    <item>(+44)</item>
+    <item>(+972)</item>
+    <item>(+39)</item>
+    <item>(+1)</item>
+    <item>(+81)</item>
+    <item>(+44)</item>
+    <item>(+962)</item>
+    <item>(+7)</item>
+    <item>(+254)</item>
+    <item>(+686)</item>
+    <item>(+850)</item>
+    <item>(+82)</item>
+    <item>(+965)</item>
+    <item>(+996)</item>
+    <item>(+856)</item>
+    <item>(+371)</item>
+    <item>(+961)</item>
+    <item>(+266)</item>
+    <item>(+231)</item>
+    <item>(+218)</item>
+    <item>(+423)</item>
+    <item>(+370)</item>
+    <item>(+352)</item>
+    <item>(+853)</item>
+    <item>(+389)</item>
+    <item>(+261)</item>
+    <item>(+265)</item>
+    <item>(+60)</item>
+    <item>(+960)</item>
+    <item>(+223)</item>
+    <item>(+356)</item>
+    <item>(+692)</item>
+    <item>(+596)</item>
+    <item>(+222)</item>
+    <item>(+230)</item>
+    <item>(+262)</item>
+    <item>(+52)</item>
+    <item>(+691)</item>
+    <item>(+373)</item>
+    <item>(+377)</item>
+    <item>(+976)</item>
+    <item>(+382)</item>
+    <item>(+1)</item>
+    <item>(+212)</item>
+    <item>(+258)</item>
+    <item>(+95)</item>
+    <item>(+264)</item>
+    <item>(+674)</item>
+    <item>(+977)</item>
+    <item>(+31)</item>
+    <item>(+687)</item>
+    <item>(+64)</item>
+    <item>(+505)</item>
+    <item>(+227)</item>
+    <item>(+234)</item>
+    <item>(+683)</item>
+    <item>(+672)</item>
+    <item>(+1)</item>
+    <item>(+47)</item>
+    <item>(+968)</item>
+    <item>(+92)</item>
+    <item>(+680)</item>
+    <item>(+970)</item>
+    <item>(+507)</item>
+    <item>(+675)</item>
+    <item>(+595)</item>
+    <item>(+51)</item>
+    <item>(+63)</item>
+    <item>(+48)</item>
+    <item>(+351)</item>
+    <item>(+1)</item>
+    <item>(+974)</item>
+    <item>(+262)</item>
+    <item>(+40)</item>
+    <item>(+7)</item>
+    <item>(+250)</item>
+    <item>(+590)</item>
+    <item>(+290)</item>
+    <item>(+1)</item>
+    <item>(+1)</item>
+    <item>(+590)</item>
+    <item>(+508)</item>
+    <item>(+1)</item>
+    <item>(+685)</item>
+    <item>(+378)</item>
+    <item>(+239)</item>
+    <item>(+966)</item>
+    <item>(+221)</item>
+    <item>(+381)</item>
+    <item>(+248)</item>
+    <item>(+232)</item>
+    <item>(+65)</item>
+    <item>(+1)</item>
+    <item>(+421)</item>
+    <item>(+386)</item>
+    <item>(+677)</item>
+    <item>(+252)</item>
+    <item>(+27)</item>
+    <item>(+211)</item>
+    <item>(+34)</item>
+    <item>(+94)</item>
+    <item>(+249)</item>
+    <item>(+597)</item>
+    <item>(+47)</item>
+    <item>(+268)</item>
+    <item>(+46)</item>
+    <item>(+41)</item>
+    <item>(+963)</item>
+    <item>(+886)</item>
+    <item>(+992)</item>
+    <item>(+255)</item>
+    <item>(+66)</item>
+    <item>(+670)</item>
+    <item>(+228)</item>
+    <item>(+690)</item>
+    <item>(+676)</item>
+    <item>(+1)</item>
+    <item>(+216)</item>
+    <item>(+90)</item>
+    <item>(+993)</item>
+    <item>(+1)</item>
+    <item>(+688)</item>
+    <item>(+256)</item>
+    <item>(+380)</item>
+    <item>(+971)</item>
+    <item>(+44)</item>
+    <item>(+1)</item>
+    <item>(+598)</item>
+    <item>(+998)</item>
+    <item>(+678)</item>
+    <item>(+58)</item>
+    <item>(+84)</item>
+    <item>(+1)</item>
+    <item>(+1)</item>
+    <item>(+681)</item>
+    <item>(+212)</item>
+    <item>(+967)</item>
+    <item>(+260)</item>
+    <item>(+263)</item>
+  </string-array>
+  <string-array name="assisted_dialing_cc_values" translatable="false">
+    <item></item>
+    <item>AF</item>
+    <item>AX</item>
+    <item>AL</item>
+    <item>DZ</item>
+    <item>AS</item>
+    <item>AD</item>
+    <item>AO</item>
+    <item>AI</item>
+    <item>AG</item>
+    <item>AR</item>
+    <item>AM</item>
+    <item>AW</item>
+    <item>AU</item>
+    <item>AT</item>
+    <item>AZ</item>
+    <item>BS</item>
+    <item>BH</item>
+    <item>BD</item>
+    <item>BB</item>
+    <item>BY</item>
+    <item>BE</item>
+    <item>BZ</item>
+    <item>BJ</item>
+    <item>BM</item>
+    <item>BT</item>
+    <item>BO</item>
+    <item>BQ</item>
+    <item>BA</item>
+    <item>BW</item>
+    <item>BR</item>
+    <item>IO</item>
+    <item>BN</item>
+    <item>BG</item>
+    <item>BF</item>
+    <item>BI</item>
+    <item>CV</item>
+    <item>KH</item>
+    <item>CM</item>
+    <item>CA</item>
+    <item>KY</item>
+    <item>CF</item>
+    <item>TD</item>
+    <item>CL</item>
+    <item>CN</item>
+    <item>CX</item>
+    <item>CC</item>
+    <item>CO</item>
+    <item>KM</item>
+    <item>CD</item>
+    <item>CG</item>
+    <item>CK</item>
+    <item>CR</item>
+    <item>CI</item>
+    <item>HR</item>
+    <item>CU</item>
+    <item>CW</item>
+    <item>CY</item>
+    <item>CZ</item>
+    <item>DK</item>
+    <item>DJ</item>
+    <item>DM</item>
+    <item>DO</item>
+    <item>EC</item>
+    <item>EG</item>
+    <item>SV</item>
+    <item>GQ</item>
+    <item>ER</item>
+    <item>EE</item>
+    <item>ET</item>
+    <item>FK</item>
+    <item>FO</item>
+    <item>FJ</item>
+    <item>FI</item>
+    <item>FR</item>
+    <item>GF</item>
+    <item>PF</item>
+    <item>GA</item>
+    <item>GM</item>
+    <item>GE</item>
+    <item>DE</item>
+    <item>GH</item>
+    <item>GI</item>
+    <item>GR</item>
+    <item>GL</item>
+    <item>GD</item>
+    <item>GP</item>
+    <item>GU</item>
+    <item>GT</item>
+    <item>GG</item>
+    <item>GN</item>
+    <item>GW</item>
+    <item>GY</item>
+    <item>HT</item>
+    <item>VA</item>
+    <item>HN</item>
+    <item>HK</item>
+    <item>HU</item>
+    <item>IS</item>
+    <item>IN</item>
+    <item>ID</item>
+    <item>IR</item>
+    <item>IQ</item>
+    <item>IE</item>
+    <item>IM</item>
+    <item>IL</item>
+    <item>IT</item>
+    <item>JM</item>
+    <item>JP</item>
+    <item>JE</item>
+    <item>JO</item>
+    <item>KZ</item>
+    <item>KE</item>
+    <item>KI</item>
+    <item>KP</item>
+    <item>KR</item>
+    <item>KW</item>
+    <item>KG</item>
+    <item>LA</item>
+    <item>LV</item>
+    <item>LB</item>
+    <item>LS</item>
+    <item>LR</item>
+    <item>LY</item>
+    <item>LI</item>
+    <item>LT</item>
+    <item>LU</item>
+    <item>MO</item>
+    <item>MK</item>
+    <item>MG</item>
+    <item>MW</item>
+    <item>MY</item>
+    <item>MV</item>
+    <item>ML</item>
+    <item>MT</item>
+    <item>MH</item>
+    <item>MQ</item>
+    <item>MR</item>
+    <item>MU</item>
+    <item>YT</item>
+    <item>MX</item>
+    <item>FM</item>
+    <item>MD</item>
+    <item>MC</item>
+    <item>MN</item>
+    <item>ME</item>
+    <item>MS</item>
+    <item>MA</item>
+    <item>MZ</item>
+    <item>MM</item>
+    <item>NA</item>
+    <item>NR</item>
+    <item>NP</item>
+    <item>NL</item>
+    <item>NC</item>
+    <item>NZ</item>
+    <item>NI</item>
+    <item>NE</item>
+    <item>NG</item>
+    <item>NU</item>
+    <item>NF</item>
+    <item>MP</item>
+    <item>NO</item>
+    <item>OM</item>
+    <item>PK</item>
+    <item>PW</item>
+    <item>PS</item>
+    <item>PA</item>
+    <item>PG</item>
+    <item>PY</item>
+    <item>PE</item>
+    <item>PH</item>
+    <item>PL</item>
+    <item>PT</item>
+    <item>PR</item>
+    <item>QA</item>
+    <item>RE</item>
+    <item>RO</item>
+    <item>RU</item>
+    <item>RW</item>
+    <item>BL</item>
+    <item>SH</item>
+    <item>KN</item>
+    <item>LC</item>
+    <item>MF</item>
+    <item>PM</item>
+    <item>VC</item>
+    <item>WS</item>
+    <item>SM</item>
+    <item>ST</item>
+    <item>SA</item>
+    <item>SN</item>
+    <item>RS</item>
+    <item>SC</item>
+    <item>SL</item>
+    <item>SG</item>
+    <item>SX</item>
+    <item>SK</item>
+    <item>SI</item>
+    <item>SB</item>
+    <item>SO</item>
+    <item>ZA</item>
+    <item>SS</item>
+    <item>ES</item>
+    <item>LK</item>
+    <item>SD</item>
+    <item>SR</item>
+    <item>SJ</item>
+    <item>SZ</item>
+    <item>SE</item>
+    <item>CH</item>
+    <item>SY</item>
+    <item>TW</item>
+    <item>TJ</item>
+    <item>TZ</item>
+    <item>TH</item>
+    <item>TL</item>
+    <item>TG</item>
+    <item>TK</item>
+    <item>TO</item>
+    <item>TT</item>
+    <item>TN</item>
+    <item>TR</item>
+    <item>TM</item>
+    <item>TC</item>
+    <item>TV</item>
+    <item>UG</item>
+    <item>UA</item>
+    <item>AE</item>
+    <item>GB</item>
+    <item>US</item>
+    <item>UY</item>
+    <item>UZ</item>
+    <item>VU</item>
+    <item>VE</item>
+    <item>VN</item>
+    <item>VG</item>
+    <item>VI</item>
+    <item>WF</item>
+    <item>EH</item>
+    <item>YE</item>
+    <item>ZM</item>
+    <item>ZW</item>
+  </string-array>
+</resources>
diff --git a/src/com/android/phone/settings/assisteddialing/res/xml/assisted_dialing_setting.xml b/src/com/android/phone/settings/assisteddialing/res/xml/assisted_dialing_setting.xml
new file mode 100755
index 0000000..9fb61a1
--- /dev/null
+++ b/src/com/android/phone/settings/assisteddialing/res/xml/assisted_dialing_setting.xml
@@ -0,0 +1,38 @@
+<!--
+  ~ Copyright (C) 2017 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.
+  -->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+  <SwitchPreference
+      android:defaultValue="true"
+      android:key="@string/assisted_dialing_setting_toggle_key"
+      android:summary="@string/assisted_dialing_setting_summary"
+      android:title="@string/assisted_dialing_setting_title"/>
+
+  <PreferenceCategory
+      android:title="@string/assisted_dialing_setting_cc_category_title">
+    <ListPreference
+        android:dependency="@string/assisted_dialing_setting_toggle_key"
+        android:defaultValue=""
+        android:entries="@array/assisted_dialing_cc_entries"
+        android:entryValues="@array/assisted_dialing_cc_values"
+        android:key="@string/assisted_dialing_setting_cc_key"
+        android:summary="@string/assisted_dialing_setting_cc_default_summary_fallback"
+        android:title="@string/assisted_dialing_setting_cc_title"/>
+  </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 3614148..fe75006 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -31,6 +31,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telecom.TransformationInfo;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
@@ -55,6 +56,8 @@
 import com.android.phone.MMIDialogActivity;
 import com.android.phone.PhoneUtils;
 import com.android.phone.R;
+import com.android.phone.assisteddialing.AssistedDialingMediator;
+import com.android.phone.assisteddialing.ConcreteCreator;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -63,6 +66,7 @@
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Optional;
 import java.util.Queue;
 import java.util.regex.Pattern;
 
@@ -77,39 +81,39 @@
 
     private final TelephonyConnectionServiceProxy mTelephonyConnectionServiceProxy =
             new TelephonyConnectionServiceProxy() {
-        @Override
-        public Collection<Connection> getAllConnections() {
-            return TelephonyConnectionService.this.getAllConnections();
-        }
-        @Override
-        public void addConference(TelephonyConference mTelephonyConference) {
-            TelephonyConnectionService.this.addConference(mTelephonyConference);
-        }
-        @Override
-        public void addConference(ImsConference mImsConference) {
-            TelephonyConnectionService.this.addConference(mImsConference);
-        }
-        @Override
-        public void removeConnection(Connection connection) {
-            TelephonyConnectionService.this.removeConnection(connection);
-        }
-        @Override
-        public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
-                                          Connection connection) {
-            TelephonyConnectionService.this
-                    .addExistingConnection(phoneAccountHandle, connection);
-        }
-        @Override
-        public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
-                Connection connection, Conference conference) {
-            TelephonyConnectionService.this
-                    .addExistingConnection(phoneAccountHandle, connection, conference);
-        }
-        @Override
-        public void addConnectionToConferenceController(TelephonyConnection connection) {
-            TelephonyConnectionService.this.addConnectionToConferenceController(connection);
-        }
-    };
+                @Override
+                public Collection<Connection> getAllConnections() {
+                    return TelephonyConnectionService.this.getAllConnections();
+                }
+                @Override
+                public void addConference(TelephonyConference mTelephonyConference) {
+                    TelephonyConnectionService.this.addConference(mTelephonyConference);
+                }
+                @Override
+                public void addConference(ImsConference mImsConference) {
+                    TelephonyConnectionService.this.addConference(mImsConference);
+                }
+                @Override
+                public void removeConnection(Connection connection) {
+                    TelephonyConnectionService.this.removeConnection(connection);
+                }
+                @Override
+                public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
+                                                  Connection connection) {
+                    TelephonyConnectionService.this
+                            .addExistingConnection(phoneAccountHandle, connection);
+                }
+                @Override
+                public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
+                                                  Connection connection, Conference conference) {
+                    TelephonyConnectionService.this
+                            .addExistingConnection(phoneAccountHandle, connection, conference);
+                }
+                @Override
+                public void addConnectionToConferenceController(TelephonyConnection connection) {
+                    TelephonyConnectionService.this.addConnectionToConferenceController(connection);
+                }
+            };
 
     private final Connection.Listener mConnectionListener = new Connection.Listener() {
         @Override
@@ -243,16 +247,17 @@
      */
     private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
             new TelephonyConnection.TelephonyConnectionListener() {
-        @Override
-        public void onOriginalConnectionConfigured(TelephonyConnection c) {
-            addConnectionToConferenceController(c);
-        }
+                @Override
+                public void onOriginalConnectionConfigured(TelephonyConnection c) {
+                    addConnectionToConferenceController(c);
+                }
 
-        @Override
-        public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {
-            retryOutgoingOriginalConnection(c, isPermanentFailure);
-        }
-    };
+                @Override
+                public void onOriginalConnectionRetry(TelephonyConnection c,
+                                                      boolean isPermanentFailure) {
+                    retryOutgoingOriginalConnection(c, isPermanentFailure);
+                }
+            };
 
     @Override
     public void onCreate() {
@@ -399,13 +404,13 @@
                         // been powered on and isn't in the UNAVAILABLE state, even if it is
                         // reporting the OUT_OF_SERVICE state.
                         return (phone.getState() == PhoneConstants.State.OFFHOOK)
-                            || phone.getServiceStateTracker().isRadioOn();
+                                || phone.getServiceStateTracker().isRadioOn();
                     } else {
                         // It is not an emergency number, so wait until we are in service and ready
                         // to make calls. This can happen when we power down the radio on bluetooth
                         // to save power on watches.
                         return (phone.getState() == PhoneConstants.State.OFFHOOK)
-                            || serviceState == ServiceState.STATE_IN_SERVICE;
+                                || serviceState == ServiceState.STATE_IN_SERVICE;
                     }
                 }
             });
@@ -431,9 +436,28 @@
             // If there was a failure, the resulting connection will not be a TelephonyConnection,
             // so don't place the call!
             if (resultConnection instanceof TelephonyConnection) {
-                if (request.getExtras() != null && request.getExtras().getBoolean(
-                        TelecomManager.EXTRA_USE_ASSISTED_DIALING, false)) {
-                    ((TelephonyConnection) resultConnection).setIsUsingAssistedDialing(true);
+                CarrierConfigManager cfgManager = (CarrierConfigManager)
+                        phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+
+                boolean hasAssistedDialingExtra = request.getExtras() != null && request.getExtras()
+                        .getBoolean(TelecomManager.EXTRA_USE_ASSISTED_DIALING, false);
+                boolean isConfigManagerEnabled = cfgManager != null
+                        && cfgManager.getConfigForSubId(phone.getSubId()).getBoolean(
+                        CarrierConfigManager.KEY_ASSISTED_DIALING_ENABLED_BOOL);
+
+                if (hasAssistedDialingExtra && isConfigManagerEnabled) {
+                    Optional<TransformationInfo> transformedNumber = doAssistedDialing(request,
+                            phone);
+                    if (transformedNumber.isPresent()) {
+                        resultConnection.setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL,
+                                transformedNumber.get().getTransformedNumber(), null),
+                                resultConnection.getAddressPresentation());
+                        resultConnection.putExtra(TelecomManager
+                                .EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO,
+                                transformedNumber.get());
+                        ((TelephonyConnection) resultConnection).setIsUsingAssistedDialing(true);
+                    }
+
                 }
                 placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
             }
@@ -442,6 +466,37 @@
     }
 
     /**
+     * Performs Assisted Dialing and updates the state of relevant call objects.
+     */
+    private Optional<TransformationInfo> doAssistedDialing(ConnectionRequest request, Phone phone) {
+        TelephonyManager telephonyManager = phone.getContext().getSystemService(TelephonyManager
+                .class);
+
+        if (telephonyManager == null) {
+            return Optional.empty();
+        }
+
+        TelephonyManager simSpecificTelephonyManager = telephonyManager
+                .createForPhoneAccountHandle(request.getAccountHandle());
+
+        if (simSpecificTelephonyManager == null) {
+            return Optional.empty();
+        }
+
+        AssistedDialingMediator assistedDialingMediator =
+                ConcreteCreator.createNewAssistedDialingMediator(
+                        simSpecificTelephonyManager, phone.getContext());
+        if (!assistedDialingMediator.isPlatformEligible()) {
+            return Optional.empty();
+        }
+        String phoneNumber =
+                request.getAddress().getScheme().equals(PhoneAccount.SCHEME_TEL)
+                        ? request.getAddress().getSchemeSpecificPart()
+                        : "";
+        return assistedDialingMediator.attemptAssistedDial(phoneNumber);
+    }
+
+    /**
      * Whether the cellular radio is power off because the device is on Bluetooth.
      */
     private boolean isRadioPowerDownOnBluetooth() {
@@ -458,8 +513,8 @@
      * Handle the onComplete callback of RadioOnStateListener.
      */
     private void handleOnComplete(boolean isRadioReady, boolean isEmergencyNumber,
-            Connection originalConnection, ConnectionRequest request, String numberToDial,
-            Uri handle, int originalPhoneType) {
+                                  Connection originalConnection, ConnectionRequest request,
+                                  String numberToDial, Uri handle, int originalPhoneType) {
         // Make sure the Call has not already been canceled by the user.
         if (originalConnection.getState() == Connection.STATE_DISCONNECTED) {
             Log.i(this, "Call disconnected before the outgoing call was placed. Skipping call "
@@ -520,7 +575,8 @@
     }
 
     private Connection getTelephonyConnection(final ConnectionRequest request, final String number,
-            boolean isEmergencyNumber, final Uri handle, Phone phone) {
+                                              boolean isEmergencyNumber, final Uri handle,
+                                              Phone phone) {
 
         if (phone == null) {
             final Context context = getApplicationContext();
@@ -697,7 +753,7 @@
 
         com.android.internal.telephony.Connection originalConnection =
                 call.getState() == Call.State.WAITING ?
-                    call.getLatestConnection() : call.getEarliestConnection();
+                        call.getLatestConnection() : call.getEarliestConnection();
         if (isOriginalConnectionKnown(originalConnection)) {
             Log.i(this, "onCreateIncomingConnection, original connection already registered");
             return Connection.createCanceledConnection();
@@ -743,7 +799,7 @@
 
     @Override
     public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
-            ConnectionRequest request) {
+                                                ConnectionRequest request) {
         Log.i(this, "onCreateUnknownConnection, request: " + request);
         // Use the registered emergency Phone if the PhoneAccountHandle is set to Telephony's
         // Emergency PhoneAccount
@@ -948,12 +1004,12 @@
     // number and then moving it to the back of the queue if it is not a permanent failure cause
     // from the modem.
     private void updateCachedConnectionPhonePair(TelephonyConnection c,
-            boolean isPermanentFailure) {
+                                                 boolean isPermanentFailure) {
         // No cache exists, create a new one.
         if (mEmergencyRetryCache == null) {
             Log.i(this, "updateCachedConnectionPhonePair, cache is null. Generating new cache");
             mEmergencyRetryCache = makeCachedConnectionPhonePair(c);
-        // Cache is stale, create a new one with the new TelephonyConnection.
+            // Cache is stale, create a new one with the new TelephonyConnection.
         } else if (mEmergencyRetryCache.first.get() != c) {
             Log.i(this, "updateCachedConnectionPhonePair, cache is stale. Regenerating.");
             mEmergencyRetryCache = makeCachedConnectionPhonePair(c);
@@ -1356,10 +1412,10 @@
                     // this request to add a call.
                     if (cdmaConf.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
                         return Connection.createFailedConnection(new DisconnectCause(
-                                    DisconnectCause.RESTRICTED,
-                                    null,
-                                    getResources().getString(R.string.callFailed_cdma_call_limit),
-                                    "merge-capable call exists, prevent flash command."));
+                                DisconnectCause.RESTRICTED,
+                                null,
+                                getResources().getString(R.string.callFailed_cdma_call_limit),
+                                "merge-capable call exists, prevent flash command."));
                     }
                 }
             }
diff --git a/tests/Android.mk b/tests/Android.mk
index 6c915a9..414a52b 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -38,3 +38,6 @@
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
 include $(BUILD_PACKAGE)
+
+# Include all makefiles in subdirectories
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/robotests/Android.mk b/tests/robotests/Android.mk
new file mode 100644
index 0000000..d686e42
--- /dev/null
+++ b/tests/robotests/Android.mk
@@ -0,0 +1,46 @@
+#############################################
+# Telephony Robolectric test target. #
+#############################################
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PRIVILEGED_MODULE := true
+
+# Include the testing libraries (JUnit4 + Robolectric libs).
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    platform-robolectric-android-all-stubs \
+    android-support-test \
+    mockito-robolectric-prebuilt \
+    platform-test-annotations \
+    truth-prebuilt \
+    testng
+
+LOCAL_JAVA_LIBRARIES := \
+    junit \
+    platform-robolectric-3.6.1-prebuilt \
+    telephony-common \
+    sdk_vcurrent
+
+LOCAL_INSTRUMENTATION_FOR := TeleService
+LOCAL_MODULE := TeleRobo
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+#############################################################
+# Telephony runner target to run the previous target. #
+#############################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := TelephonyRoboTests
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    TeleRobo \
+
+LOCAL_TEST_PACKAGE := TeleService
+
+include prebuilts/misc/common/robolectric/3.6.1/run_robotests.mk
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/phone/assisteddialing/AssistedDialingMediatorStubTest.java b/tests/robotests/src/com/android/phone/assisteddialing/AssistedDialingMediatorStubTest.java
new file mode 100755
index 0000000..57bf127
--- /dev/null
+++ b/tests/robotests/src/com/android/phone/assisteddialing/AssistedDialingMediatorStubTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.content.Context;
+import android.telephony.TelephonyManager;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowTelephonyManager;
+
+import java.util.Optional;
+/**
+ * Unit Tests for AssistedDialingMediator.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(
+        manifest = Config.NONE,
+        shadows = {ShadowTelephonyManager.class}
+)
+public class AssistedDialingMediatorStubTest {
+
+    private final TelephonyManager mTelephonyManager =
+            (TelephonyManager) RuntimeEnvironment.application.getSystemService(
+                    Context.TELEPHONY_SERVICE);
+    private final ShadowTelephonyManager mShadowTelephonyManager =
+            (ShadowTelephonyManager) Shadow.extract(mTelephonyManager);
+    private final AssistedDialingMediatorStub mAssistedDialingMediatorStub =
+            new AssistedDialingMediatorStub();
+
+    @Test
+    public void testAttemptAssistedDial_conditionsEligibleButStubInert() {
+        // User home country
+        mShadowTelephonyManager.setSimCountryIso(
+                AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_STATES);
+        // User roaming country
+        mShadowTelephonyManager.setNetworkCountryIso(
+                AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM);
+        assertThat(
+                mAssistedDialingMediatorStub.attemptAssistedDial(
+                        AssistedDialingTestHelper.TEST_NUMBER_UNITED_STATES))
+                .isEqualTo(Optional.empty());
+    }
+
+    @Test
+    public void testIsPlatformEligible() {
+        assertThat(mAssistedDialingMediatorStub.isPlatformEligible()).isFalse();
+    }
+
+    @Test
+    public void testUserHomeCountryCode() {
+        assertThat(mAssistedDialingMediatorStub.userHomeCountryCode())
+                .isInstanceOf(Optional.empty().getClass());
+    }
+}
diff --git a/tests/robotests/src/com/android/phone/assisteddialing/AssistedDialingTestHelper.java b/tests/robotests/src/com/android/phone/assisteddialing/AssistedDialingTestHelper.java
new file mode 100755
index 0000000..f028c66
--- /dev/null
+++ b/tests/robotests/src/com/android/phone/assisteddialing/AssistedDialingTestHelper.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+/**
+ * Common variables for Assisted Dialing Unit Tests.
+ */
+class AssistedDialingTestHelper {
+
+    public static final String INVALID_NUMBER_ENCODING = "\u2026";
+
+    public static final String INTERNATIONAL_TEST_NUMBER_UNITED_STATES = "+1 123-456-7890";
+
+    public static final String INVALID_TEST_NUMBER_UNITED_STATES = "3-456-7890";
+
+    public static final String TEST_NUMBER_UNITED_STATES = "650-456-7890";
+    public static final String TEST_NUMBER_UNITED_STATES_WITH_POST_DIAL_EXTENSION =
+            "650-456-7890;123";
+    public static final String TEST_NUMBER_UNITED_KINGDOM = "0113-169-0455";
+    public static final String TEST_NUMBER_JAPAN = "0570-055777";
+    public static final String TEST_NUMBER_MEXICO = "55 4155 0000";
+    public static final String TEST_NUMBER_CANADA = "604-659-3400";
+
+    public static final String TEST_NUMBER_EMERGENCY_UNITED_STATES = "911";
+    public static final String TEST_NUMBER_EMERGENCY_UNITED_KINGDOM = "999";
+    public static final String TEST_NUMBER_EMERGENCY_JAPAN = "110";
+    public static final String TEST_NUMBER_EMERGENCY_MEXICO = "911";
+    public static final String TEST_NUMBER_EMERGENCY_CANADA = "911";
+
+    public static final String UNSUPPORTED_COUNTRY_CODE_NORTH_KOREA = "KP";
+
+    public static final String SUPPORTED_COUNTRY_CODE_CANADA = "CA";
+    public static final String SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM = "GB";
+    public static final String SUPPORTED_COUNTRY_CODE_JAPAN = "JP";
+    public static final String SUPPORTED_COUNTRY_CODE_MEXICO = "MX";
+    public static final String SUPPORTED_COUNTRY_CODE_UNITED_STATES = "US";
+
+    public static final String EXPECTED_FROM_UK_TO_US_DIALABLE_NUMBER = "+1 650-456-7890";
+    public static final String EXPECTED_FROM_US_TO_UK_DIALABLE_NUMBER = "+44 113 169 0455";
+    public static final String EXPECTED_FROM_CA_TO_JP_DIALABLE_NUMBER = "+81 570-055-777";
+    public static final String EXPECTED_FROM_JP_TO_MX_DIALABLE_NUMBER = "+52 55 4155 0000";
+    public static final String EXPECTED_FROM_US_TO_JP_DIALABLE_NUMBER = "+81 570-055-777";
+
+    public static final int UNITED_STATES_COUNTRY_CALLING_CODE = 1;
+}
diff --git a/tests/robotests/src/com/android/phone/assisteddialing/CountryCodeProviderTest.java b/tests/robotests/src/com/android/phone/assisteddialing/CountryCodeProviderTest.java
new file mode 100755
index 0000000..3afbc76
--- /dev/null
+++ b/tests/robotests/src/com/android/phone/assisteddialing/CountryCodeProviderTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+/**
+ * Unit Tests for CountryCodeProvider.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class CountryCodeProviderTest {
+
+    private final CountryCodeProvider mCountryCodeProvider = new CountryCodeProvider();
+
+    @Test
+    public void testnumberMeetsPreconditionsForAssistedDiailng_defaults() {
+        for (String country : CountryCodeProvider.DEFAULT_COUNTRY_CODES) {
+            assertThat(mCountryCodeProvider.isSupportedCountryCode(country)).isTrue();
+        }
+    }
+
+}
diff --git a/tests/robotests/src/com/android/phone/assisteddialing/LocationDetectorTest.java b/tests/robotests/src/com/android/phone/assisteddialing/LocationDetectorTest.java
new file mode 100755
index 0000000..4b19f71
--- /dev/null
+++ b/tests/robotests/src/com/android/phone/assisteddialing/LocationDetectorTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.content.Context;
+import android.telephony.TelephonyManager;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowTelephonyManager;
+
+import java.util.Locale;
+import java.util.Optional;
+
+/**
+ * Unit Tests for LocationDetector.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(
+        manifest = Config.NONE,
+        shadows = {ShadowTelephonyManager.class}
+)
+public class LocationDetectorTest {
+
+    private final TelephonyManager mTelephonyManager =
+            (TelephonyManager) RuntimeEnvironment.application.getSystemService(
+                    Context.TELEPHONY_SERVICE);
+    private final ShadowTelephonyManager mShadowTelephonyManager =
+            (ShadowTelephonyManager) Shadow.extract(mTelephonyManager);
+    private final LocationDetector mLocationDetector = new LocationDetector(
+            mTelephonyManager, null);
+
+    @Test
+    public void testGetUserHomeCountry() {
+        // User home country
+        mShadowTelephonyManager.setSimCountryIso(
+                AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM);
+        assertThat(mLocationDetector.getUpperCaseUserHomeCountry().get())
+                .isEqualTo(AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM);
+    }
+
+    @Test
+    public void testGetUserHomeCountry_userProvidedHomeCountry() {
+        // User home country
+        mShadowTelephonyManager.setSimCountryIso(
+                AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM);
+
+        LocationDetector localLocationDetector = new LocationDetector(mTelephonyManager, "ZZ");
+        assertThat(localLocationDetector.getUpperCaseUserHomeCountry().get()).isEqualTo("ZZ");
+    }
+
+    @Test
+    public void testGetUserRoamingCountry() {
+        // User roaming country
+        mShadowTelephonyManager.setNetworkCountryIso(
+                AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM);
+        assertThat(mLocationDetector.getUpperCaseUserRoamingCountry().get())
+                .isEqualTo(AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM);
+    }
+
+    @Test
+    public void testGetLocationValues_returnValuesAreAlwaysUpperCase() {
+        mShadowTelephonyManager.setNetworkCountryIso(
+                AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM.toLowerCase());
+        mShadowTelephonyManager.setSimCountryIso(
+                AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM.toLowerCase());
+
+        assertThat(mLocationDetector.getUpperCaseUserHomeCountry().get())
+                .isEqualTo(
+                        AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM.toUpperCase(
+                                Locale.US));
+        assertThat(mLocationDetector.getUpperCaseUserRoamingCountry().get())
+                .isEqualTo(
+                        AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM.toUpperCase(
+                                Locale.US));
+    }
+
+    @Test
+    public void testGetLocationValues_returnValuesMayBeEmpty() {
+        mShadowTelephonyManager.setNetworkCountryIso(null);
+        mShadowTelephonyManager.setSimCountryIso(null);
+
+        assertThat(mLocationDetector.getUpperCaseUserHomeCountry()).isEqualTo(Optional.empty());
+        assertThat(mLocationDetector.getUpperCaseUserRoamingCountry()).isEqualTo(Optional.empty());
+    }
+
+    @Test
+    public void testConstructorWithNullYieldsEmptyOptional() {
+        mShadowTelephonyManager.setNetworkCountryIso(
+                AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM.toLowerCase());
+        mShadowTelephonyManager.setSimCountryIso(
+                AssistedDialingTestHelper.SUPPORTED_COUNTRY_CODE_UNITED_KINGDOM.toLowerCase());
+
+        LocationDetector locationDetector = new LocationDetector(null, null);
+        assertThat(locationDetector.getUpperCaseUserHomeCountry().isPresent())
+                .isFalse();
+        assertThat(locationDetector.getUpperCaseUserRoamingCountry().isPresent())
+                .isFalse();
+    }
+}