Merge "Hide the empty buckets when selected network is empty."
diff --git a/res/layout/emergency_dialer.xml b/res/layout/emergency_dialer.xml
index cdb9530..7f99664 100644
--- a/res/layout/emergency_dialer.xml
+++ b/res/layout/emergency_dialer.xml
@@ -22,6 +22,7 @@
     <!-- Emergency dialer shortcuts layout-->
     <FrameLayout
         android:id="@+id/emergency_dialer_shortcuts"
+        android:accessibilityPaneTitle="@string/emergencyDialerIconLabel"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:visibility="gone">
@@ -52,6 +53,7 @@
     <!--Emergency Dialer Layout-->
     <FrameLayout
         android:id="@+id/emergency_dialer"
+        android:accessibilityPaneTitle="@string/pane_title_emergency_dialpad"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:paddingLeft="36dp"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 834f384..de14389 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1399,6 +1399,13 @@
     -->
     <string name="description_dialpad_button">show dialpad</string>
 
+    <!-- Pane title of the Emergency Dialpad
+
+     Used by AccessibilityService to announce the purpose of the pane of emergency dialpad.
+     [CHAR LIMIT=NONE]
+    -->
+    <string name="pane_title_emergency_dialpad">Emergency Dialpad</string>
+
     <!-- Visual voicemail on/off title [CHAR LIMIT=40] -->
     <string name="voicemail_visual_voicemail_switch_title">Visual Voicemail</string>
 
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index bfeee7a..bdffb26 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -76,6 +76,11 @@
         mReplaceInvalidCFNumber = replaceInvalidCFNumber;
     }
 
+    void restoreCallForwardInfo(CallForwardInfo cf) {
+        handleCallForwardResult(cf);
+        updateSummaryText();
+    }
+
     @Override
     protected void onBindDialogView(View view) {
         // default the button clicked to be the cancel button.
@@ -100,7 +105,15 @@
             int action = (isToggled() || (mButtonClicked == DialogInterface.BUTTON_POSITIVE)) ?
                     CommandsInterface.CF_ACTION_REGISTRATION :
                     CommandsInterface.CF_ACTION_DISABLE;
-            int time = (reason != CommandsInterface.CF_REASON_NO_REPLY) ? 0 : 20;
+            int time = 0;
+            if (reason == CommandsInterface.CF_REASON_NO_REPLY) {
+                PersistableBundle carrierConfig = PhoneGlobals.getInstance()
+                        .getCarrierConfigForSubId(mPhone.getSubId());
+                if (carrierConfig.getBoolean(
+                        CarrierConfigManager.KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true)) {
+                    time = 20;
+                }
+            }
             final String number = getPhoneNumber();
 
             Log.d(LOG_TAG, "callForwardInfo=" + callForwardInfo);
@@ -137,7 +150,7 @@
         }
     }
 
-    void handleCallForwardResult(CallForwardInfo cf) {
+    private void handleCallForwardResult(CallForwardInfo cf) {
         callForwardInfo = cf;
         Log.d(LOG_TAG, "handleGetCFResponse done, callForwardInfo=" + callForwardInfo);
         // In some cases, the network can send call forwarding URIs for voicemail that violate the
diff --git a/src/com/android/phone/CdmaOptions.java b/src/com/android/phone/CdmaOptions.java
index 7f7d58c..9c713ef 100644
--- a/src/com/android/phone/CdmaOptions.java
+++ b/src/com/android/phone/CdmaOptions.java
@@ -24,12 +24,12 @@
 import android.preference.PreferenceScreen;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.settingslib.RestrictedLockUtils;
 
@@ -39,6 +39,7 @@
 public class CdmaOptions {
     private static final String LOG_TAG = "CdmaOptions";
 
+    private CarrierConfigManager mCarrierConfigManager;
     private CdmaSystemSelectListPreference mButtonCdmaSystemSelect;
     private CdmaSubscriptionListPreference mButtonCdmaSubscription;
     private RestrictedPreference mButtonAPNExpand;
@@ -53,18 +54,13 @@
 
     private PreferenceFragment mPrefFragment;
     private PreferenceScreen mPrefScreen;
-    private Phone mPhone;
+    private int mSubId;
 
-    // Constructor for CdmaOptionsTest, since PreferenceScreen is final and cannot be mocked
-    @VisibleForTesting
-    public CdmaOptions(Phone phone) {
-        mPhone = phone;
-    }
-
-    public CdmaOptions(PreferenceFragment prefFragment, PreferenceScreen prefScreen, Phone phone) {
+    public CdmaOptions(PreferenceFragment prefFragment, PreferenceScreen prefScreen, int subId) {
         mPrefFragment = prefFragment;
         mPrefScreen = prefScreen;
         mPrefFragment.addPreferencesFromResource(R.xml.cdma_options);
+        mCarrierConfigManager = new CarrierConfigManager(prefFragment.getContext());
 
         // Initialize preferences.
         mButtonCdmaSystemSelect = (CdmaSystemSelectListPreference) mPrefScreen
@@ -75,19 +71,17 @@
         mButtonAPNExpand = (RestrictedPreference) mPrefScreen.findPreference(BUTTON_APN_EXPAND_KEY);
         mCategoryAPNExpand = mPrefScreen.findPreference(CATEGORY_APN_EXPAND_KEY);
 
-        update(phone);
+        updateSubscriptionId(subId);
     }
 
-    // Unlike mPrefFragment or mPrefScreen, mPhone may change during lifecycle of CdmaOptions.
-    // For example, a new sim card is inserted. When that happens, we update CdmaOptions with new
-    // phone.
-    protected void update(Phone phone) {
-        mPhone = phone;
+    protected void updateSubscriptionId(int subId) {
+        mSubId = subId;
+        int phoneType = TelephonyManager.from(mPrefFragment.getContext())
+                .createForSubscriptionId(mSubId).getPhoneType();
 
-        PersistableBundle carrierConfig =
-                PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+        PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
         // Some CDMA carriers want the APN settings.
-        boolean addAPNExpand = shouldAddApnExpandPreference(carrierConfig);
+        boolean addAPNExpand = shouldAddApnExpandPreference(phoneType, carrierConfig);
         boolean addCdmaSubscription =
                 deviceSupportsNvAndRuim();
         // Read platform settings for carrier settings
@@ -118,7 +112,7 @@
                             final Intent intent = new Intent(Settings.ACTION_APN_SETTINGS);
                             // This will setup the Home and Search affordance
                             intent.putExtra(":settings:show_fragment_as_subsetting", true);
-                            intent.putExtra("sub_id", mPhone.getSubId());
+                            intent.putExtra("sub_id", mSubId);
                             mPrefFragment.startActivity(intent);
                             return true;
                         }
@@ -149,12 +143,9 @@
      * carrier config
      */
     @VisibleForTesting
-    public boolean shouldAddApnExpandPreference(PersistableBundle config) {
-        if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA
-                && config.getBoolean(CarrierConfigManager.KEY_SHOW_APN_SETTING_CDMA_BOOL)) {
-            return true;
-        }
-        return false;
+    public static boolean shouldAddApnExpandPreference(int phoneType, PersistableBundle config) {
+        return phoneType == PhoneConstants.PHONE_TYPE_CDMA
+                && config.getBoolean(CarrierConfigManager.KEY_SHOW_APN_SETTING_CDMA_BOOL);
     }
 
     private boolean deviceSupportsNvAndRuim() {
diff --git a/src/com/android/phone/CdmaSubscriptionListPreference.java b/src/com/android/phone/CdmaSubscriptionListPreference.java
index 4a32dc4..cd0c681 100644
--- a/src/com/android/phone/CdmaSubscriptionListPreference.java
+++ b/src/com/android/phone/CdmaSubscriptionListPreference.java
@@ -17,17 +17,15 @@
 package com.android.phone;
 
 import android.content.Context;
-import android.os.AsyncResult;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
 import android.preference.ListPreference;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
 
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
+import com.android.settingslib.utils.ThreadUtils;
 
 public class CdmaSubscriptionListPreference extends ListPreference {
 
@@ -41,19 +39,17 @@
     //                           1 - NV
     static final int preferredSubscriptionMode = Phone.PREFERRED_CDMA_SUBSCRIPTION;
 
-    private Phone mPhone;
-    private CdmaSubscriptionButtonHandler mHandler;
+    private TelephonyManager mTelephonyManager;
 
     public CdmaSubscriptionListPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mPhone = PhoneFactory.getDefaultPhone();
-        mHandler = new CdmaSubscriptionButtonHandler();
+        mTelephonyManager = TelephonyManager.from(context);
         setCurrentCdmaSubscriptionModeValue();
     }
 
     private void setCurrentCdmaSubscriptionModeValue() {
-        int cdmaSubscriptionMode = Settings.Global.getInt(mPhone.getContext().getContentResolver(),
+        int cdmaSubscriptionMode = Settings.Global.getInt(getContext().getContentResolver(),
                 Settings.Global.CDMA_SUBSCRIPTION_MODE, preferredSubscriptionMode);
         setValue(Integer.toString(cdmaSubscriptionMode));
     }
@@ -62,6 +58,15 @@
         this(context, null);
     }
 
+    /**
+     * Sets the subscription id associated with this preference.
+     *
+     * @param subId the subscription id.
+     */
+    public void setSubscriptionId(int subId) {
+        mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(subId);
+    }
+
     @Override
     protected void showDialog(Bundle state) {
         setCurrentCdmaSubscriptionModeValue();
@@ -92,40 +97,23 @@
                 statusCdmaSubscriptionMode = Phone.PREFERRED_CDMA_SUBSCRIPTION;
         }
 
-        // Set the CDMA subscription mode, when mode has been successfully changed
-        // handleSetCdmaSubscriptionMode will be invoked and the value saved.
-        mPhone.setCdmaSubscription(statusCdmaSubscriptionMode, mHandler
-                .obtainMessage(CdmaSubscriptionButtonHandler.MESSAGE_SET_CDMA_SUBSCRIPTION,
-                        getValue()));
+        // Set the CDMA subscription mode, when mode has been successfully changed, update the
+        // mode to the global setting.
+        ThreadUtils.postOnBackgroundThread(() -> {
+            // The subscription mode selected by user.
+            int cdmaSubscriptionMode = Integer.parseInt(getValue());
 
-    }
+            boolean isSuccessed = mTelephonyManager.setCdmaSubscriptionMode(
+                    statusCdmaSubscriptionMode);
 
-    private class CdmaSubscriptionButtonHandler extends Handler {
-
-        static final int MESSAGE_SET_CDMA_SUBSCRIPTION = 0;
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_SET_CDMA_SUBSCRIPTION:
-                    handleSetCdmaSubscriptionMode(msg);
-                    break;
-            }
-        }
-
-        private void handleSetCdmaSubscriptionMode(Message msg) {
-            mPhone = PhoneFactory.getDefaultPhone();
-            AsyncResult ar = (AsyncResult) msg.obj;
-
-            if (ar.exception == null) {
-                // Get the original string entered by the user
-                int cdmaSubscriptionMode = Integer.parseInt((String) ar.userObj);
-                Settings.Global.putInt(mPhone.getContext().getContentResolver(),
+            // Update the global settings if successed.
+            if (isSuccessed) {
+                Settings.Global.putInt(getContext().getContentResolver(),
                         Settings.Global.CDMA_SUBSCRIPTION_MODE,
-                        cdmaSubscriptionMode );
+                        cdmaSubscriptionMode);
             } else {
                 Log.e(LOG_TAG, "Setting Cdma subscription source failed");
             }
-        }
+        });
     }
 }
diff --git a/src/com/android/phone/CdmaSystemSelectListPreference.java b/src/com/android/phone/CdmaSystemSelectListPreference.java
index 9c24aa7..0044a8b 100644
--- a/src/com/android/phone/CdmaSystemSelectListPreference.java
+++ b/src/com/android/phone/CdmaSystemSelectListPreference.java
@@ -17,43 +17,49 @@
 package com.android.phone;
 
 import android.content.Context;
-import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.preference.ListPreference;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
 
-import com.android.internal.telephony.Phone;
+import com.android.settingslib.utils.ThreadUtils;
 
 public class CdmaSystemSelectListPreference extends ListPreference {
 
     private static final String LOG_TAG = "CdmaRoamingListPreference";
     private static final boolean DBG = false;
 
-    private Phone mPhone;
+    private TelephonyManager mTelephonyManager;
     private MyHandler mHandler = new MyHandler();
 
     public CdmaSystemSelectListPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mPhone = PhoneGlobals.getPhone();
         mHandler = new MyHandler();
-        mPhone.queryCdmaRoamingPreference(
-                mHandler.obtainMessage(MyHandler.MESSAGE_GET_ROAMING_PREFERENCE));
+        mTelephonyManager = TelephonyManager.from(context);
     }
 
     public CdmaSystemSelectListPreference(Context context) {
         this(context, null);
     }
 
+    /**
+     * Sets the subscription id associated with this preference.
+     *
+     * @param subId the subscription id.
+     */
+    public void setSubscriptionId(int subId) {
+        mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(subId);
+        queryCdmaRoamingMode();
+    }
+
     @Override
     protected void showDialog(Bundle state) {
-        if (mPhone.isInEcm()) {
-            // In ECM mode do not show selection options
-        } else {
+        if (!mTelephonyManager.getEmergencyCallbackMode()) {
             super.showDialog(state);
         }
     }
@@ -64,26 +70,22 @@
 
         if (positiveResult && (getValue() != null)) {
             int buttonCdmaRoamingMode = Integer.parseInt(getValue());
-            int settingsCdmaRoamingMode =
-                    Settings.Global.getInt(mPhone.getContext().getContentResolver(),
-                    Settings.Global.CDMA_ROAMING_MODE, Phone.CDMA_RM_HOME);
+            int settingsCdmaRoamingMode = Settings.Global.getInt(
+                    getContext().getContentResolver(),
+                    Settings.Global.CDMA_ROAMING_MODE,
+                    TelephonyManager.CDMA_ROAMING_MODE_HOME);
             if (buttonCdmaRoamingMode != settingsCdmaRoamingMode) {
-                int statusCdmaRoamingMode;
-                switch(buttonCdmaRoamingMode) {
-                    case Phone.CDMA_RM_ANY:
-                        statusCdmaRoamingMode = Phone.CDMA_RM_ANY;
-                        break;
-                    case Phone.CDMA_RM_HOME:
-                    default:
-                        statusCdmaRoamingMode = Phone.CDMA_RM_HOME;
+                int cdmaRoamingMode = TelephonyManager.CDMA_ROAMING_MODE_ANY;
+                if (buttonCdmaRoamingMode != TelephonyManager.CDMA_ROAMING_MODE_ANY) {
+                    cdmaRoamingMode = TelephonyManager.CDMA_ROAMING_MODE_HOME;
                 }
                 //Set the Settings.Secure network mode
-                Settings.Global.putInt(mPhone.getContext().getContentResolver(),
+                Settings.Global.putInt(
+                        getContext().getContentResolver(),
                         Settings.Global.CDMA_ROAMING_MODE,
-                        buttonCdmaRoamingMode );
+                        buttonCdmaRoamingMode);
                 //Set the roaming preference mode
-                mPhone.setCdmaRoamingPreference(statusCdmaRoamingMode, mHandler
-                        .obtainMessage(MyHandler.MESSAGE_SET_ROAMING_PREFERENCE));
+                setCdmaRoamingMode(cdmaRoamingMode);
             }
         } else {
             Log.d(LOG_TAG, String.format("onDialogClosed: positiveResult=%b value=%s -- do nothing",
@@ -110,27 +112,28 @@
         }
 
         private void handleQueryCdmaRoamingPreference(Message msg) {
-            AsyncResult ar = (AsyncResult) msg.obj;
+            int cdmaRoamingMode = msg.arg1;
 
-            if (ar.exception == null) {
-                int statusCdmaRoamingMode = ((int[])ar.result)[0];
+            if (cdmaRoamingMode != TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT) {
                 int settingsRoamingMode = Settings.Global.getInt(
-                        mPhone.getContext().getContentResolver(),
-                        Settings.Global.CDMA_ROAMING_MODE, Phone.CDMA_RM_HOME);
+                        getContext().getContentResolver(),
+                        Settings.Global.CDMA_ROAMING_MODE,
+                        TelephonyManager.CDMA_ROAMING_MODE_HOME);
+
                 //check that statusCdmaRoamingMode is from an accepted value
-                if (statusCdmaRoamingMode == Phone.CDMA_RM_HOME ||
-                        statusCdmaRoamingMode == Phone.CDMA_RM_ANY ) {
+                if (cdmaRoamingMode == TelephonyManager.CDMA_ROAMING_MODE_HOME
+                        || cdmaRoamingMode == TelephonyManager.CDMA_ROAMING_MODE_ANY) {
                     //check changes in statusCdmaRoamingMode and updates settingsRoamingMode
-                    if (statusCdmaRoamingMode != settingsRoamingMode) {
-                        settingsRoamingMode = statusCdmaRoamingMode;
+                    if (cdmaRoamingMode != settingsRoamingMode) {
+                        settingsRoamingMode = cdmaRoamingMode;
                         //changes the Settings.Secure accordingly to statusCdmaRoamingMode
                         Settings.Global.putInt(
-                                mPhone.getContext().getContentResolver(),
+                                getContext().getContentResolver(),
                                 Settings.Global.CDMA_ROAMING_MODE,
-                                settingsRoamingMode );
+                                settingsRoamingMode);
                     }
                     //changes the mButtonPreferredNetworkMode accordingly to modemNetworkMode
-                    setValue(Integer.toString(statusCdmaRoamingMode));
+                    setValue(Integer.toString(cdmaRoamingMode));
                 }
                 else {
                     if(DBG) Log.i(LOG_TAG, "reset cdma roaming mode to default" );
@@ -140,29 +143,45 @@
         }
 
         private void handleSetCdmaRoamingPreference(Message msg) {
-            AsyncResult ar = (AsyncResult) msg.obj;
+            boolean isSuccessed = (boolean) msg.obj;
 
-            if ((ar.exception == null) && (getValue() != null)) {
+            if (isSuccessed && (getValue() != null)) {
                 int cdmaRoamingMode = Integer.parseInt(getValue());
-                Settings.Global.putInt(mPhone.getContext().getContentResolver(),
+                Settings.Global.putInt(
+                        getContext().getContentResolver(),
                         Settings.Global.CDMA_ROAMING_MODE,
                         cdmaRoamingMode );
             } else {
-                mPhone.queryCdmaRoamingPreference(obtainMessage(MESSAGE_GET_ROAMING_PREFERENCE));
+                queryCdmaRoamingMode();
             }
         }
 
         private void resetCdmaRoamingModeToDefault() {
             //set the mButtonCdmaRoam
-            setValue(Integer.toString(Phone.CDMA_RM_ANY));
+            setValue(Integer.toString(TelephonyManager.CDMA_ROAMING_MODE_ANY));
             //set the Settings.System
-            Settings.Global.putInt(mPhone.getContext().getContentResolver(),
-                        Settings.Global.CDMA_ROAMING_MODE,
-                        Phone.CDMA_RM_ANY );
+            Settings.Global.putInt(
+                    getContext().getContentResolver(),
+                    Settings.Global.CDMA_ROAMING_MODE,
+                    TelephonyManager.CDMA_ROAMING_MODE_ANY);
             //Set the Status
-            mPhone.setCdmaRoamingPreference(Phone.CDMA_RM_ANY,
-                    obtainMessage(MyHandler.MESSAGE_SET_ROAMING_PREFERENCE));
+            setCdmaRoamingMode(TelephonyManager.CDMA_ROAMING_MODE_ANY);
         }
     }
 
+    private void queryCdmaRoamingMode() {
+        ThreadUtils.postOnBackgroundThread(() -> {
+            Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_GET_ROAMING_PREFERENCE);
+            msg.arg1 = mTelephonyManager.getCdmaRoamingMode();
+            msg.sendToTarget();
+        });
+    }
+
+    private void setCdmaRoamingMode(int mode) {
+        ThreadUtils.postOnBackgroundThread(() -> {
+            Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_SET_ROAMING_PREFERENCE);
+            msg.obj = mTelephonyManager.setCdmaRoamingMode(mode);
+            msg.sendToTarget();
+        });
+    }
 }
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index a89b52d..66701d5 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -23,6 +23,7 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
+import android.app.KeyguardManager;
 import android.app.WallpaperManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -31,13 +32,20 @@
 import android.database.DataSetObserver;
 import android.graphics.Color;
 import android.graphics.Point;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.media.AudioManager;
 import android.media.ToneGenerator;
+import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.PersistableBundle;
+import android.os.SystemClock;
 import android.provider.Settings;
+import android.telecom.ParcelableCallAnalytics;
 import android.telecom.PhoneAccount;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
@@ -54,6 +62,7 @@
 import android.text.TextWatcher;
 import android.text.method.DialerKeyListener;
 import android.text.style.TtsSpan;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.HapticFeedbackConstants;
@@ -61,18 +70,23 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.AccessibilityDelegate;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.TextView;
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.phone.common.dialpad.DialpadKeyButton;
 import com.android.phone.common.util.ViewUtil;
 import com.android.phone.common.widget.ResizingTextEditText;
 import com.android.phone.ecc.CountryEccInfo;
 import com.android.phone.ecc.EccInfoHelper;
+import com.android.phone.ecc.IsoToEccProtobufRepository;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -104,13 +118,81 @@
 public class EmergencyDialer extends Activity implements View.OnClickListener,
         View.OnLongClickListener, View.OnKeyListener, TextWatcher,
         DialpadKeyButton.OnPressedListener, ColorExtractor.OnColorsChangedListener,
-        EmergencyShortcutButton.OnConfirmClickListener {
+        EmergencyShortcutButton.OnConfirmClickListener, SensorEventListener {
+
+    private class MetricsWriter {
+        // Metrics constants indicating the entry type that user opened emergency dialer.
+        // This info is sent from system UI with EXTRA_ENTRY_TYPE. Please make them being
+        // in sync with those in com.android.systemui.util.EmergencyDialerConstants.
+        public static final int ENTRY_TYPE_UNKNOWN = 0;
+        public static final int ENTRY_TYPE_LOCKSCREEN_BUTTON = 1;
+        public static final int ENTRY_TYPE_POWER_MENU = 2;
+
+        // Metrics constants indicating the UI that user made phone call.
+        public static final int CALL_SOURCE_DIALPAD = 0;
+        public static final int CALL_SOURCE_SHORTCUT = 1;
+
+        // Metrics constants indicating the phone number type of a call user made.
+        public static final int PHONE_NUMBER_TYPE_GENERAL = 0;
+        public static final int PHONE_NUMBER_TYPE_EMERGENCY = 1;
+
+        // Metrics constants indicating the actions performed by user.
+        public static final int USER_ACTION_NONE = 0x0;
+        public static final int USER_ACTION_OPEN_DIALPAD = 0x1;
+        public static final int USER_ACTION_OPEN_EMERGENCY_INFO = 0x2;
+        public static final int USER_ACTION_MAKE_CALL_VIA_DIALPAD = 0x4;
+        public static final int USER_ACTION_MAKE_CALL_VIA_SHORTCUT = 0x8;
+
+        private MetricsLogger mMetricsLogger = new MetricsLogger();
+
+        public void writeMetricsForEnter() {
+            int entryType = getIntent().getIntExtra(EXTRA_ENTRY_TYPE, ENTRY_TYPE_UNKNOWN);
+            KeyguardManager keyguard = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+            mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER)
+                    .setType(MetricsEvent.TYPE_OPEN)
+                    .setSubtype(entryType)
+                    .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_IS_SCREEN_LOCKED,
+                            keyguard.isKeyguardLocked() ? 1 : 0));
+        }
+
+        public void writeMetricsForExit() {
+            int entryType = getIntent().getIntExtra(EXTRA_ENTRY_TYPE, ENTRY_TYPE_UNKNOWN);
+            long userStayDuration = SystemClock.elapsedRealtime() - mUserEnterTimeMillis;
+            mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER)
+                    .setType(MetricsEvent.TYPE_CLOSE)
+                    .setSubtype(entryType)
+                    .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_USER_ACTIONS, mUserActions)
+                    .addTaggedData(
+                            MetricsEvent.FIELD_EMERGENCY_DIALER_DURATION_MS, userStayDuration));
+        }
+
+        public void writeMetricsForMakingCall(int callSource, int phoneNumberType,
+                boolean hasShortcut) {
+            mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER_MAKE_CALL)
+                    .setType(MetricsEvent.TYPE_ACTION)
+                    .setSubtype(callSource)
+                    .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_PHONE_NUMBER_TYPE,
+                            phoneNumberType)
+                    .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_PHONE_NUMBER_HAS_SHORTCUT,
+                            hasShortcut ? 1 : 0)
+                    .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_IN_POCKET,
+                            mIsProximityNear ? 1 : 0));
+        }
+    }
+
     // Keys used with onSaveInstanceState().
     private static final String LAST_NUMBER = "lastNumber";
 
     // Intent action for this activity.
     public static final String ACTION_DIAL = "com.android.phone.EmergencyDialer.DIAL";
 
+    /**
+     * Extra included in {@link #ACTION_DIAL} to indicate the entry type that user starts
+     * the emergency dialer.
+     */
+    public static final String EXTRA_ENTRY_TYPE =
+            "com.android.phone.EmergencyDialer.extra.ENTRY_TYPE";
+
     // List of dialer button IDs.
     private static final int[] DIALER_KEYS = new int[] {
             R.id.one, R.id.two, R.id.three,
@@ -172,6 +254,30 @@
         }
     };
 
+    /**
+     * Customize accessibility methods in View.
+     */
+    private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
+
+        /**
+         * Stop AccessiblityService from reading the title of a hidden View.
+         *
+         * <p>The crossfade animation will set the visibility of fade out view to {@link View.GONE}
+         * in the animation end. The view with an accessibility pane title would call the
+         * {@link AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED} event, which would trigger the
+         * accessibility service to read the pane title of fade out view instead of pane title of
+         * fade in view. So it need to filter out the event called by vanished pane.
+         */
+        @Override
+        public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+                    && host.getVisibility() == View.GONE) {
+                return;
+            }
+            super.onPopulateAccessibilityEvent(host, event);
+        }
+    };
+
     private String mLastNumber; // last number we tried to dial. Used to restore error dialog.
 
     // Background gradient
@@ -184,6 +290,22 @@
 
     private boolean mAreEmergencyDialerShortcutsEnabled;
 
+    private MetricsWriter mMetricsWriter;
+    private SensorManager mSensorManager;
+    private Sensor mProximitySensor;
+    private boolean mIsProximityNear = false;
+
+    /**
+     * The time, in millis, since boot when user opened emergency dialer.
+     * This is used when calculating the user stay duration for metrics data.
+     */
+    private long mUserEnterTimeMillis = 0;
+
+    /**
+     * Bit flag indicating the actions performed by user. This is used for metrics data.
+     */
+    private int mUserActions = MetricsWriter.USER_ACTION_NONE;
+
     @Override
     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
         // Do nothing
@@ -218,13 +340,19 @@
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
+        mMetricsWriter = new MetricsWriter();
+        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
+        if (mSensorManager != null) {
+            mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+        }
+
         // Allow this activity to be displayed in front of the keyguard / lockscreen.
         setShowWhenLocked(true);
         // Allow turning screen on
         setTurnScreenOn(true);
 
-        mAreEmergencyDialerShortcutsEnabled = Settings.Global.getInt(getContentResolver(),
-                Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, 0) != 0;
+        mAreEmergencyDialerShortcutsEnabled = FeatureFlagUtils
+                .isEnabled(this, FeatureFlagUtils.EMERGENCY_DIAL_SHORTCUTS);
         Log.d(LOG_TAG, "Enable emergency dialer shortcut: "
                 + mAreEmergencyDialerShortcutsEnabled);
 
@@ -324,6 +452,7 @@
         mEmergencyActionGroup = (EmergencyActionGroup) findViewById(R.id.emergency_action_group);
 
         if (mAreEmergencyDialerShortcutsEnabled) {
+            mEccInfoHelper = new EccInfoHelper(new IsoToEccProtobufRepository());
             setupEmergencyShortcutsView();
         }
     }
@@ -455,12 +584,22 @@
     public void onConfirmClick(EmergencyShortcutButton button) {
         if (button == null) return;
 
+        mUserActions |= MetricsWriter.USER_ACTION_MAKE_CALL_VIA_SHORTCUT;
+
+        // We interest on the context when user has intention to make phone call,
+        // so write metrics here for shortcut number even the call may not be created.
+        mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_SHORTCUT,
+                MetricsWriter.PHONE_NUMBER_TYPE_EMERGENCY, true);
+
         String phoneNumber = button.getPhoneNumber();
 
         if (!TextUtils.isEmpty(phoneNumber)) {
             if (DBG) Log.d(LOG_TAG, "dial emergency number: " + Rlog.pii(LOG_TAG, phoneNumber));
+            Bundle extras = new Bundle();
+            extras.putInt(TelecomManager.EXTRA_CALL_SOURCE,
+                    ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT);
             TelecomManager tm = (TelecomManager) getSystemService(TELECOM_SERVICE);
-            tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null), null);
+            tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null), extras);
         } else {
             Log.d(LOG_TAG, "emergency number is empty");
         }
@@ -485,11 +624,13 @@
                 return;
             }
             case R.id.floating_action_button_dialpad: {
+                mUserActions |= MetricsWriter.USER_ACTION_OPEN_DIALPAD;
                 mDigits.getText().clear();
                 switchView(mDialpadView, mEmergencyShortcutView, true);
                 return;
             }
             case R.id.emergency_info_button: {
+                mUserActions |= MetricsWriter.USER_ACTION_OPEN_EMERGENCY_INFO;
                 Intent intent = (Intent) view.getTag(R.id.tag_intent);
                 if (intent != null) {
                     startActivity(intent);
@@ -591,6 +732,11 @@
     @Override
     protected void onStart() {
         super.onStart();
+
+        mUserEnterTimeMillis = SystemClock.elapsedRealtime();
+        mUserActions = MetricsWriter.USER_ACTION_NONE;
+        mMetricsWriter.writeMetricsForEnter();
+
         // It does not support dark text theme, when emergency dialer shortcuts are enabled.
         // And set background color to black.
         if (mAreEmergencyDialerShortcutsEnabled) {
@@ -636,6 +782,11 @@
     protected void onResume() {
         super.onResume();
 
+        if (mProximitySensor != null) {
+            mSensorManager.registerListener(
+                    this, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
+        }
+
         // retrieve the DTMF tone play back setting.
         mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
                 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
@@ -660,11 +811,15 @@
     @Override
     public void onPause() {
         super.onPause();
+        if (mProximitySensor != null) {
+            mSensorManager.unregisterListener(this, mProximitySensor);
+        }
     }
 
     @Override
     protected void onStop() {
         super.onStop();
+        mMetricsWriter.writeMetricsForExit();
         mColorExtractor.removeOnColorsChangedListener(this);
     }
 
@@ -702,6 +857,7 @@
      * place the call, but check to make sure it is a viable number.
      */
     private void placeCall() {
+        mUserActions |= MetricsWriter.USER_ACTION_MAKE_CALL_VIA_DIALPAD;
         mLastNumber = mDigits.getText().toString();
 
         // Convert into emergency number according to emergency conversion map.
@@ -718,11 +874,23 @@
                 playTone(ToneGenerator.TONE_PROP_NACK);
                 return;
             }
+
+            mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_DIALPAD,
+                    MetricsWriter.PHONE_NUMBER_TYPE_EMERGENCY, isShortcutNumber(mLastNumber));
+
+            Bundle extras = new Bundle();
+            extras.putInt(TelecomManager.EXTRA_CALL_SOURCE,
+                    ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD);
             TelecomManager tm = (TelecomManager) getSystemService(TELECOM_SERVICE);
-            tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, mLastNumber, null), null);
+            tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, mLastNumber, null), extras);
         } else {
             if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
 
+            // We interest on the context when user has intention to make phone call,
+            // so write metrics here for non-emergency numbers even these numbers are rejected.
+            mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_DIALPAD,
+                    MetricsWriter.PHONE_NUMBER_TYPE_GENERAL, false);
+
             showDialog(BAD_EMERGENCY_NUMBER_DIALOG);
         }
         mDigits.getText().delete(0, mDigits.getText().length());
@@ -925,6 +1093,9 @@
         mEmergencyShortcutView = findViewById(R.id.emergency_dialer_shortcuts);
         mDialpadView = findViewById(R.id.emergency_dialer);
 
+        mEmergencyShortcutView.setAccessibilityDelegate(mAccessibilityDelegate);
+        mDialpadView.setAccessibilityDelegate(mAccessibilityDelegate);
+
         final View dialpadButton = findViewById(R.id.floating_action_button_dialpad);
         dialpadButton.setOnClickListener(this);
 
@@ -1107,4 +1278,30 @@
                     }
                 });
     }
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        float distance = event.values[0];
+        mIsProximityNear = (distance < mProximitySensor.getMaximumRange());
+    }
+
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        // Not used.
+    }
+
+    private boolean isShortcutNumber(String number) {
+        if (TextUtils.isEmpty(number) || mEmergencyShortcutButtonList == null) {
+            return false;
+        }
+
+        boolean isShortcut = false;
+        for (EmergencyShortcutButton button : mEmergencyShortcutButtonList) {
+            if (button != null && number.equals(button.getPhoneNumber())) {
+                isShortcut = true;
+                break;
+            }
+        }
+        return isShortcut;
+    }
 }
diff --git a/src/com/android/phone/EmergencyShortcutButton.java b/src/com/android/phone/EmergencyShortcutButton.java
index 275dac0..59b3794 100644
--- a/src/com/android/phone/EmergencyShortcutButton.java
+++ b/src/com/android/phone/EmergencyShortcutButton.java
@@ -19,6 +19,8 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
+import android.metrics.LogMaker;
+import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -30,6 +32,9 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
 /**
  * Emergency shortcut button displays a local emergency phone number information(including phone
  * number, and phone type). To decrease false clicking, it need to click twice to confirm to place
@@ -61,6 +66,12 @@
 
     private boolean mConfirmViewHiding;
 
+    /**
+     * The time, in millis, since boot when user taps on shortcut button to reveal confirm view.
+     * This is used for metrics when calculating the interval between reveal tap and confirm tap.
+     */
+    private long mTimeOfRevealTapInMillis = 0;
+
     public EmergencyShortcutButton(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -185,6 +196,8 @@
         switch (view.getId()) {
             case R.id.emergency_call_number_info_view:
                 if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
+                    // TalkBack itself includes a prompt to confirm click action implicitly,
+                    // so we don't need an additional confirmation with second tap on button.
                     if (mOnConfirmClickListener != null) {
                         mOnConfirmClickListener.onConfirmClick(this);
                     }
@@ -193,6 +206,15 @@
                 }
                 break;
             case R.id.emergency_call_confirm_view:
+                if (mTimeOfRevealTapInMillis != 0) {
+                    long timeBetweenTwoTaps =
+                            SystemClock.elapsedRealtime() - mTimeOfRevealTapInMillis;
+                    // Reset reveal time to zero for next reveal-confirm taps pair.
+                    mTimeOfRevealTapInMillis = 0;
+
+                    writeMetricsForConfirmTap(timeBetweenTwoTaps);
+                }
+
                 if (mOnConfirmClickListener != null) {
                     mOnConfirmClickListener.onConfirmClick(this);
                 }
@@ -204,6 +226,7 @@
         mConfirmViewHiding = false;
 
         mConfirmView.setVisibility(View.VISIBLE);
+        mTimeOfRevealTapInMillis = SystemClock.elapsedRealtime();
         int centerX = mCallNumberInfoView.getLeft() + mCallNumberInfoView.getWidth() / 2;
         int centerY = mCallNumberInfoView.getTop() + mCallNumberInfoView.getHeight() / 2;
         Animator reveal = ViewAnimationUtils.createCircularReveal(
@@ -240,6 +263,8 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mConfirmView.setVisibility(INVISIBLE);
+                // Reset reveal time to zero for next reveal-confirm taps pair.
+                mTimeOfRevealTapInMillis = 0;
             }
         });
         reveal.start();
@@ -254,4 +279,12 @@
             hideSelectedButton();
         }
     };
+
+    private void writeMetricsForConfirmTap(long timeBetweenTwoTaps) {
+        LogMaker logContent = new LogMaker(MetricsEvent.EMERGENCY_DIALER_SHORTCUT_CONFIRM_TAP)
+                .setType(MetricsEvent.TYPE_ACTION)
+                .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_SHORTCUT_TAPS_INTERVAL,
+                        timeBetweenTwoTaps);
+        MetricsLogger.action(logContent);
+    }
 }
diff --git a/src/com/android/phone/GsmUmtsCallForwardOptions.java b/src/com/android/phone/GsmUmtsCallForwardOptions.java
index e562e46..6d80621 100644
--- a/src/com/android/phone/GsmUmtsCallForwardOptions.java
+++ b/src/com/android/phone/GsmUmtsCallForwardOptions.java
@@ -31,6 +31,7 @@
     private static final String KEY_TOGGLE = "toggle";
     private static final String KEY_STATUS = "status";
     private static final String KEY_NUMBER = "number";
+    private static final String KEY_ENABLE = "enable";
 
     private CallForwardEditPreference mButtonCFU;
     private CallForwardEditPreference mButtonCFB;
@@ -112,11 +113,12 @@
                 for (CallForwardEditPreference pref : mPreferences) {
                     Bundle bundle = mIcicle.getParcelable(pref.getKey());
                     pref.setToggled(bundle.getBoolean(KEY_TOGGLE));
+                    pref.setEnabled(bundle.getBoolean(KEY_ENABLE));
                     CallForwardInfo cf = new CallForwardInfo();
                     cf.number = bundle.getString(KEY_NUMBER);
                     cf.status = bundle.getInt(KEY_STATUS);
                     pref.init(this, mPhone, mReplaceInvalidCFNumbers);
-                    pref.handleCallForwardResult(cf);
+                    pref.restoreCallForwardInfo(cf);
                 }
             }
             mFirstResume = false;
@@ -131,6 +133,7 @@
         for (CallForwardEditPreference pref : mPreferences) {
             Bundle bundle = new Bundle();
             bundle.putBoolean(KEY_TOGGLE, pref.isToggled());
+            bundle.putBoolean(KEY_ENABLE, pref.isEnabled());
             if (pref.callForwardInfo != null) {
                 bundle.putString(KEY_NUMBER, pref.callForwardInfo.number);
                 bundle.putInt(KEY_STATUS, pref.callForwardInfo.status);
diff --git a/src/com/android/phone/GsmUmtsOptions.java b/src/com/android/phone/GsmUmtsOptions.java
index 19cd3ef..a3f5cfb 100644
--- a/src/com/android/phone/GsmUmtsOptions.java
+++ b/src/com/android/phone/GsmUmtsOptions.java
@@ -16,6 +16,7 @@
 
 package com.android.phone;
 
+import android.content.Context;
 import android.content.Intent;
 import android.os.PersistableBundle;
 import android.preference.Preference;
@@ -23,6 +24,7 @@
 import android.preference.PreferenceScreen;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -36,6 +38,7 @@
 public class GsmUmtsOptions {
     private static final String LOG_TAG = "GsmUmtsOptions";
 
+    private CarrierConfigManager mCarrierConfigManager;
     private RestrictedPreference mButtonAPNExpand;
     private Preference mCategoryAPNExpand;
     Preference mCarrierSettingPref;
@@ -52,8 +55,10 @@
 
     public GsmUmtsOptions(PreferenceFragment prefFragment, PreferenceScreen prefScreen,
             final int subId, INetworkQueryService queryService) {
+        final Context context = prefFragment.getContext();
         mPrefFragment = prefFragment;
         mPrefScreen = prefScreen;
+        mCarrierConfigManager = new CarrierConfigManager(context);
         mPrefFragment.addPreferencesFromResource(R.xml.gsm_umts_options);
         mButtonAPNExpand = (RestrictedPreference) mPrefScreen.findPreference(BUTTON_APN_EXPAND_KEY);
         mCategoryAPNExpand = mPrefScreen.findPreference(CATEGORY_APN_EXPAND_KEY);
@@ -72,16 +77,17 @@
         boolean addAPNExpand = true;
         boolean addNetworkOperatorsCategory = true;
         boolean addCarrierSettings = true;
+        final TelephonyManager telephonyManager = TelephonyManager.from(mPrefFragment.getContext())
+                .createForSubscriptionId(subId);
         Phone phone = PhoneGlobals.getPhone(subId);
         if (phone == null) return;
-        if (phone.getPhoneType() != PhoneConstants.PHONE_TYPE_GSM) {
+        if (telephonyManager.getPhoneType() != PhoneConstants.PHONE_TYPE_GSM) {
             log("Not a GSM phone");
             addAPNExpand = false;
             mNetworkOperator.setEnabled(false);
         } else {
             log("Not a CDMA phone");
-            PersistableBundle carrierConfig =
-                    PhoneGlobals.getInstance().getCarrierConfigForSubId(subId);
+            PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
 
             // Determine which options to display. For GSM these are defaulted to true in
             // CarrierConfigManager, but they maybe overriden by DefaultCarrierConfigService or a
diff --git a/src/com/android/phone/MobileDataPreference.java b/src/com/android/phone/MobileDataPreference.java
index 38d700e..4e82f20 100644
--- a/src/com/android/phone/MobileDataPreference.java
+++ b/src/com/android/phone/MobileDataPreference.java
@@ -52,6 +52,9 @@
 
     public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     public boolean mChecked;
+    // Whether to show the dialog to ask switching default data subscription.
+    // Should be true only when a multi-sim phone only supports data connection on a single phone,
+    // and user is enabling data on the non-default phone.
     public boolean mMultiSimDialog;
     private TelephonyManager mTelephonyManager;
     private SubscriptionManager mSubscriptionManager;
@@ -123,47 +126,46 @@
         final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(
                 mSubId);
         final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
-        boolean isMultiSim = (mTelephonyManager.getSimCount() > 1);
+        final boolean isMultiSim = (mTelephonyManager.getSimCount() > 1);
+        final boolean isMultipleDataOnCapable =
+                (mTelephonyManager.getNumberOfModemsWithSimultaneousDataConnections() > 1);
+        final boolean isDefaultDataSubscription = (nextSir != null && currentSir != null
+                && currentSir.getSubscriptionId() == nextSir.getSubscriptionId());
         if (mChecked) {
-            // If the device is single SIM or is enabling data on the active data SIM then forgo
-            // the pop-up.
-            if (isMultiSim || (nextSir != null && currentSir != null
-                    && currentSir.getSubscriptionId() == nextSir.getSubscriptionId())) {
-                setMobileDataEnabled(false);
-                if (nextSir != null && currentSir != null
-                        && currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
-                    disableDataForOtherSubscriptions(mSubId);
-                }
-                return;
-            }
-            // disabling data; show confirmation dialog which eventually
-            // calls setMobileDataEnabled() once user confirms.
-            mMultiSimDialog = false;
-            super.performClick(preferenceScreen);
-        } else {
-            // If we are showing the Sim Card tile then we are a Multi-Sim device.
-            if (isMultiSim) {
-                mMultiSimDialog = true;
-                if (nextSir != null && currentSir != null
-                        && currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
-                    setMobileDataEnabled(true);
-                    disableDataForOtherSubscriptions(mSubId);
-                    return;
-                }
+            if (!isMultiSim) {
+                // disabling data; show confirmation dialog which eventually
+                // calls setMobileDataEnabled() once user confirms.
+                mMultiSimDialog = false;
                 super.performClick(preferenceScreen);
             } else {
-                setMobileDataEnabled(true);
+                // Don't show any dialog.
+                setMobileDataEnabled(false /* enabled */, false /* disableOtherSubscriptions */);
+            }
+        } else {
+            if (isMultiSim && !isMultipleDataOnCapable && !isDefaultDataSubscription) {
+                // enabling data and setting to default; show confirmation dialog which eventually
+                // calls setMobileDataEnabled() once user confirms.
+                mMultiSimDialog = true;
+                super.performClick(preferenceScreen);
+            } else {
+                // Don't show any dialog.
+                setMobileDataEnabled(true /* enabled */, false /* disableOtherSubscriptions */);
             }
         }
     }
 
-    private void setMobileDataEnabled(boolean enabled) {
+    private void setMobileDataEnabled(boolean enabled, boolean disableOtherSubscriptions) {
         if (DBG) Log.d(TAG, "setMobileDataEnabled(" + enabled + "," + mSubId + ")");
 
         MetricsLogger.action(getContext(), MetricsEvent.ACTION_MOBILE_NETWORK_MOBILE_DATA_TOGGLE,
                 enabled);
 
         mTelephonyManager.setDataEnabled(mSubId, enabled);
+
+        if (disableOtherSubscriptions) {
+            disableDataForOtherSubscriptions(mSubId);
+        }
+
         setChecked(enabled);
     }
 
@@ -232,11 +234,10 @@
         }
         if (mMultiSimDialog) {
             mSubscriptionManager.setDefaultDataSubId(mSubId);
-            setMobileDataEnabled(true);
-            disableDataForOtherSubscriptions(mSubId);
+            setMobileDataEnabled(true /* enabled */, true /* disableOtherSubscriptions */);
         } else {
             // TODO: extend to modify policy enabled flag.
-            setMobileDataEnabled(false);
+            setMobileDataEnabled(false /* enabled */, false /* disableOtherSubscriptions */);
         }
     }
 
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index 0c4cffc..c4f91ca 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -35,7 +35,7 @@
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
-import android.os.AsyncResult;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -76,7 +76,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.phone.settings.PhoneAccountSettingsFragment;
 import com.android.settingslib.RestrictedLockUtils;
@@ -149,6 +148,45 @@
         return super.onOptionsItemSelected(item);
     }
 
+
+    /**
+     * Returns true if Wifi calling is enabled for at least one phone.
+     */
+    public static boolean isWifiCallingEnabled(Context context) {
+        int phoneCount = TelephonyManager.from(context).getPhoneCount();
+        for (int i = 0; i < phoneCount; i++) {
+            if (isWifiCallingEnabled(context, i)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if Wifi calling is enabled for the specific phone with id {@code phoneId}.
+     */
+    public static boolean isWifiCallingEnabled(Context context, int phoneId) {
+        final PhoneAccountHandle simCallManager =
+                TelecomManager.from(context).getSimCallManager();
+
+        boolean isWifiCallingEnabled;
+        if (simCallManager != null) {
+            Intent intent = PhoneAccountSettingsFragment
+                    .buildPhoneAccountConfigureIntent(context, simCallManager);
+            PackageManager pm = context.getPackageManager();
+            isWifiCallingEnabled = intent != null
+                    && !pm.queryIntentActivities(intent, 0 /* flags */).isEmpty();
+        } else {
+            ImsManager imsMgr = ImsManager.getInstance(context, phoneId);
+            isWifiCallingEnabled = imsMgr != null
+                    && imsMgr.isWfcEnabledByPlatform()
+                    && imsMgr.isWfcProvisionedOnDevice()
+                    && isImsServiceStateReady(imsMgr);
+        }
+
+        return isWifiCallingEnabled;
+    }
+
     /**
      * Whether to show the entry point to eUICC settings.
      *
@@ -201,15 +239,16 @@
      * doesn't set {@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL} to false.
      */
     public static boolean hideEnhanced4gLteSettings(Context context) {
-        List<SubscriptionInfo> sil =
+        final CarrierConfigManager carrierConfigManager = new CarrierConfigManager(context);
+        final List<SubscriptionInfo> sil =
                 SubscriptionManager.from(context).getActiveSubscriptionInfoList();
         // Check all active subscriptions. We only hide the button if it's disabled for all
         // active subscriptions.
         if (sil != null) {
             for (SubscriptionInfo subInfo : sil) {
                 ImsManager imsManager = ImsManager.getInstance(context, subInfo.getSimSlotIndex());
-                PersistableBundle carrierConfig = PhoneGlobals.getInstance()
-                        .getCarrierConfigForSubId(subInfo.getSubscriptionId());
+                PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(
+                        subInfo.getSubscriptionId());
                 if ((imsManager.isVolteEnabledByPlatform()
                         && imsManager.isVolteProvisionedOnDevice())
                         || carrierConfig.getBoolean(
@@ -236,6 +275,22 @@
         }
     }
 
+    private static boolean isImsServiceStateReady(ImsManager imsMgr) {
+        boolean isImsServiceStateReady = false;
+
+        try {
+            if (imsMgr != null && imsMgr.getImsServiceState() == ImsFeature.STATE_READY) {
+                isImsServiceStateReady = true;
+            }
+        } catch (ImsException ex) {
+            Log.e(MobileNetworkFragment.LOG_TAG,
+                    "Exception when trying to get ImsServiceStatus: " + ex);
+        }
+
+        Log.d(MobileNetworkFragment.LOG_TAG, "isImsServiceStateReady=" + isImsServiceStateReady);
+        return isImsServiceStateReady;
+    }
+
     public static class MobileNetworkFragment extends PreferenceFragment implements
             Preference.OnPreferenceChangeListener, RoamingDialogFragment.RoamingDialogListener {
 
@@ -293,6 +348,8 @@
 
         private SubscriptionManager mSubscriptionManager;
         private TelephonyManager mTelephonyManager;
+        private CarrierConfigManager mCarrierConfigManager;
+        private int mSubId;
 
         //UI objects
         private AdvancedOptionsPreference mAdvancedOptions;
@@ -313,7 +370,6 @@
         private List<SubscriptionInfo> mActiveSubInfos;
 
         private UserManager mUm;
-        private Phone mPhone;
         private ImsManager mImsMgr;
         private MyHandler mHandler;
         private boolean mOkClicked;
@@ -347,20 +403,20 @@
                 updateVideoCallState();
             }
 
-            /*
-             * Listen to different subId if mPhone is updated.
+            /**
+             * Listen to different subId if it's changed.
              */
-            protected void updatePhone() {
-                int newSubId = (mPhone != null
-                        && SubscriptionManager.isValidSubscriptionId(mPhone.getSubId()))
-                        ? mPhone.getSubId()
-                        : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+            protected void updateSubscriptionId(Integer subId) {
+                if (subId.equals(PhoneCallStateListener.this.mSubId)) {
+                    return;
+                }
 
-                // Now, listen to new subId if it's valid.
+                PhoneCallStateListener.this.mSubId = subId;
+
                 mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE);
 
-                mSubId = newSubId;
-                if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
+                // Now, listen to new subId if it's valid.
+                if (SubscriptionManager.isValidSubscriptionId(subId)) {
                     mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
                 }
             }
@@ -417,7 +473,7 @@
 
         @Override
         public void onPositiveButtonClick(DialogFragment dialog) {
-            mPhone.setDataRoamingEnabled(true);
+            mTelephonyManager.setDataRoamingEnabled(true);
             mButtonDataRoam.setChecked(true);
             MetricsLogger.action(getContext(),
                     getMetricsEventCategory(getPreferenceScreen(), mButtonDataRoam),
@@ -448,7 +504,6 @@
             sendMetricsEventPreferenceClicked(preferenceScreen, preference);
 
             /** TODO: Refactor and get rid of the if's using subclasses */
-            final int phoneSubId = mPhone.getSubId();
             if (preference.getKey().equals(BUTTON_4G_LTE_KEY)) {
                 return true;
             } else if (mGsmUmtsOptions != null &&
@@ -456,7 +511,7 @@
                 return true;
             } else if (mCdmaOptions != null &&
                     mCdmaOptions.preferenceTreeClick(preference) == true) {
-                if (mPhone.isInEcm()) {
+                if (mTelephonyManager.getEmergencyCallbackMode()) {
 
                     mClickedPreference = preference;
 
@@ -469,8 +524,8 @@
             } else if (preference == mButtonPreferredNetworkMode) {
                 //displays the value taken from the Settings.System
                 int settingsNetworkMode = android.provider.Settings.Global.getInt(
-                        mPhone.getContext().getContentResolver(),
-                        android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
+                        getContext().getContentResolver(),
+                        android.provider.Settings.Global.PREFERRED_NETWORK_MODE + mSubId,
                         preferredNetworkMode);
                 mButtonPreferredNetworkMode.setValue(Integer.toString(settingsNetworkMode));
                 return true;
@@ -493,8 +548,8 @@
                 return true;
             }  else if (preference == mButtonEnabledNetworks) {
                 int settingsNetworkMode = android.provider.Settings.Global.getInt(
-                        mPhone.getContext().getContentResolver(),
-                        android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
+                        getContext().getContentResolver(),
+                        android.provider.Settings.Global.PREFERRED_NETWORK_MODE + mSubId,
                         preferredNetworkMode);
                 mButtonEnabledNetworks.setValue(Integer.toString(settingsNetworkMode));
                 return true;
@@ -669,27 +724,26 @@
         private void updatePhone(int slotId) {
             final SubscriptionInfo sir = mSubscriptionManager
                     .getActiveSubscriptionInfoForSimSlotIndex(slotId);
+
             if (sir != null) {
-                int phoneId = SubscriptionManager.getPhoneId(sir.getSubscriptionId());
-                if (SubscriptionManager.isValidPhoneId(phoneId)) {
-                    mPhone = PhoneFactory.getPhone(phoneId);
+                mSubId = sir.getSubscriptionId();
+
+                Log.i(LOG_TAG, "updatePhone:- slotId=" + slotId + " sir=" + sir);
+
+                mImsMgr = ImsManager.getInstance(getContext(),
+                        SubscriptionManager.getPhoneId(mSubId));
+                mTelephonyManager = new TelephonyManager(getContext(), mSubId);
+                if (mImsMgr == null) {
+                    log("updatePhone :: Could not get ImsManager instance!");
+                } else if (DBG) {
+                    log("updatePhone :: mImsMgr=" + mImsMgr);
                 }
-            }
-            if (mPhone == null) {
-                // Do the best we can
-                mPhone = PhoneGlobals.getPhone();
-            }
-            Log.i(LOG_TAG, "updatePhone:- slotId=" + slotId + " sir=" + sir);
-
-            mImsMgr = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
-            mTelephonyManager = new TelephonyManager(mPhone.getContext(), mPhone.getSubId());
-            if (mImsMgr == null) {
-                log("updatePhone :: Could not get ImsManager instance!");
-            } else if (DBG) {
-                log("updatePhone :: mImsMgr=" + mImsMgr);
+            } else {
+                // There is no active subscription in the given slot.
+                mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
             }
 
-            mPhoneStateListener.updatePhone();
+            mPhoneStateListener.updateSubscriptionId(mSubId);
         }
 
         private TabHost.TabContentFactory mEmptyTabContent = new TabHost.TabContentFactory() {
@@ -736,6 +790,7 @@
             mSubscriptionManager = SubscriptionManager.from(activity);
             mTelephonyManager = (TelephonyManager) activity.getSystemService(
                             Context.TELEPHONY_SERVICE);
+            mCarrierConfigManager = new CarrierConfigManager(getContext());
 
             if (icicle != null) {
                 mExpandAdvancedFields = icicle.getBoolean(EXPAND_ADVANCED_FIELDS, false);
@@ -790,7 +845,9 @@
 
             int currentTab = mTabHost != null ? mTabHost.getCurrentTab() : 0;
             updatePhone(currentTab);
-            updateEnabledNetworksEntries();
+            if (hasActiveSubscriptions()) {
+                updateEnabledNetworksEntries();
+            }
             Log.i(LOG_TAG, "onCreate:-");
         }
 
@@ -866,7 +923,7 @@
             // Set UI state in onResume because a user could go home, launch some
             // app to change this setting's backend, and re-launch this settings app
             // and the UI state would be inconsistent with actual state
-            mButtonDataRoam.setChecked(mPhone.getDataRoamingEnabled());
+            mButtonDataRoam.setChecked(mTelephonyManager.isDataRoamingEnabled());
 
             if (getPreferenceScreen().findPreference(BUTTON_PREFERED_NETWORK_MODE) != null
                     || getPreferenceScreen().findPreference(BUTTON_ENABLED_NETWORKS_KEY) != null)  {
@@ -912,24 +969,26 @@
             prefSet.addPreference(mButtonDataRoam);
             prefSet.addPreference(mDataUsagePref);
 
-            // Customized preferences needs to be initialized with subId.
-            mMobileDataPref.initialize(phoneSubId);
-            mDataUsagePref.initialize(phoneSubId);
-
             mMobileDataPref.setEnabled(hasActiveSubscriptions);
             mButtonDataRoam.setEnabled(hasActiveSubscriptions);
             mDataUsagePref.setEnabled(hasActiveSubscriptions);
 
-            // Initialize states of mButtonDataRoam.
-            mButtonDataRoam.setChecked(mPhone.getDataRoamingEnabled());
-            mButtonDataRoam.setDisabledByAdmin(false);
-            if (mButtonDataRoam.isEnabled()) {
-                if (RestrictedLockUtils.hasBaseUserRestriction(context,
-                        UserManager.DISALLOW_DATA_ROAMING, UserHandle.myUserId())) {
-                    mButtonDataRoam.setEnabled(false);
-                } else {
-                    mButtonDataRoam.checkRestrictionAndSetDisabled(
-                            UserManager.DISALLOW_DATA_ROAMING);
+            if (hasActiveSubscriptions) {
+                // Customized preferences needs to be initialized with subId.
+                mMobileDataPref.initialize(phoneSubId);
+                mDataUsagePref.initialize(phoneSubId);
+
+                // Initialize states of mButtonDataRoam.
+                mButtonDataRoam.setChecked(mTelephonyManager.isDataRoamingEnabled());
+                mButtonDataRoam.setDisabledByAdmin(false);
+                if (mButtonDataRoam.isEnabled()) {
+                    if (RestrictedLockUtils.hasBaseUserRestriction(context,
+                            UserManager.DISALLOW_DATA_ROAMING, UserHandle.myUserId())) {
+                        mButtonDataRoam.setEnabled(false);
+                    } else {
+                        mButtonDataRoam.checkRestrictionAndSetDisabled(
+                                UserManager.DISALLOW_DATA_ROAMING);
+                    }
                 }
             }
         }
@@ -937,7 +996,6 @@
         private void updateBody() {
             final Activity activity = getActivity();
             final PreferenceScreen prefSet = getPreferenceScreen();
-            final int phoneSubId = mPhone.getSubId();
             final boolean hasActiveSubscriptions = hasActiveSubscriptions();
 
             if (activity == null || activity.isDestroyed()) {
@@ -952,18 +1010,27 @@
 
             prefSet.removeAll();
 
-            updateBodyBasicFields(activity, prefSet, phoneSubId, hasActiveSubscriptions);
+            updateBodyBasicFields(activity, prefSet, mSubId, hasActiveSubscriptions);
 
-            if (mExpandAdvancedFields) {
-                updateBodyAdvancedFields(activity, prefSet, phoneSubId, hasActiveSubscriptions);
+            if (hasActiveSubscriptions) {
+                if (mExpandAdvancedFields) {
+                    updateBodyAdvancedFields(activity, prefSet, mSubId, hasActiveSubscriptions);
+                } else {
+                    prefSet.addPreference(mAdvancedOptions);
+                }
             } else {
-                prefSet.addPreference(mAdvancedOptions);
+                // Shows the "Carrier" preference that allows user to add a e-sim profile.
+                if (showEuiccSettings(getContext())) {
+                    mEuiccSettingsPref.setSummary(null /* summary */);
+                    prefSet.addPreference(mEuiccSettingsPref);
+                }
             }
         }
 
         private void updateBodyAdvancedFields(Activity activity, PreferenceScreen prefSet,
                 int phoneSubId, boolean hasActiveSubscriptions) {
-            boolean isLteOnCdma = mPhone.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE;
+            boolean isLteOnCdma = mTelephonyManager.getLteOnCdmaMode()
+                    == PhoneConstants.LTE_ON_CDMA_TRUE;
 
             if (DBG) {
                 log("updateBody: isLteOnCdma=" + isLteOnCdma + " phoneSubId=" + phoneSubId);
@@ -984,12 +1051,11 @@
             }
 
             int settingsNetworkMode = android.provider.Settings.Global.getInt(
-                    mPhone.getContext().getContentResolver(),
+                    getContext().getContentResolver(),
                     android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
                     preferredNetworkMode);
 
-            PersistableBundle carrierConfig =
-                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
             mIsGlobalCdma = isLteOnCdma
                     && carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_CDMA_CHOICES_BOOL);
             if (carrierConfig.getBoolean(
@@ -999,15 +1065,15 @@
                 prefSet.removePreference(mLteDataServicePref);
             } else if (carrierConfig.getBoolean(CarrierConfigManager
                     .KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL)
-                    && !mPhone.getServiceState().getRoaming()
-                    && mPhone.getServiceState().getDataRegState()
+                    && !mTelephonyManager.getServiceState().getRoaming()
+                    && mTelephonyManager.getServiceState().getDataRegState()
                     == ServiceState.STATE_IN_SERVICE) {
                 prefSet.removePreference(mButtonPreferredNetworkMode);
                 prefSet.removePreference(mButtonEnabledNetworks);
 
-                final int phoneType = mPhone.getPhoneType();
+                final int phoneType = mTelephonyManager.getPhoneType();
                 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
-                    updateCdmaOptions(this, prefSet, mPhone);
+                    updateCdmaOptions(this, prefSet, mSubId);
                 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                     updateGsmUmtsOptions(this, prefSet, phoneSubId, mNetworkQueryService);
                 } else {
@@ -1024,7 +1090,7 @@
                 // change Preferred Network Mode.
                 mButtonPreferredNetworkMode.setOnPreferenceChangeListener(this);
 
-                updateCdmaOptions(this, prefSet, mPhone);
+                updateCdmaOptions(this, prefSet, mSubId);
                 updateGsmUmtsOptions(this, prefSet, phoneSubId, mNetworkQueryService);
             } else {
                 prefSet.removePreference(mButtonPreferredNetworkMode);
@@ -1076,8 +1142,12 @@
             UpdatePreferredNetworkModeSummary(settingsNetworkMode);
             UpdateEnabledNetworksValueAndSummary(settingsNetworkMode);
             // Display preferred network type based on what modem returns b/18676277
-            mPhone.setPreferredNetworkType(settingsNetworkMode, mHandler
-                    .obtainMessage(MyHandler.MESSAGE_SET_PREFERRED_NETWORK_TYPE));
+            new SetPreferredNetworkAsyncTask(
+                    mTelephonyManager,
+                    mSubId,
+                    settingsNetworkMode,
+                    mHandler.obtainMessage(MyHandler.MESSAGE_SET_PREFERRED_NETWORK_TYPE))
+                    .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 
             /**
              * Enable/disable depending upon if there are any active subscriptions.
@@ -1129,21 +1199,20 @@
             }
         }
 
-        // Requires that mPhone is up to date
+        // Requires that mSubId is up to date
         void updateEnabledNetworksEntries() {
-            final int phoneType = mPhone.getPhoneType();
-            final PersistableBundle carrierConfig =
-                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            final int phoneType = mTelephonyManager.getPhoneType();
+            final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
             if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                 final int lteForced = android.provider.Settings.Global.getInt(
-                        mPhone.getContext().getContentResolver(),
-                        android.provider.Settings.Global.LTE_SERVICE_FORCED + mPhone.getSubId(),
+                        getContext().getContentResolver(),
+                        android.provider.Settings.Global.LTE_SERVICE_FORCED + mSubId,
                         0);
-                final boolean isLteOnCdma = mPhone.getLteOnCdmaMode()
+                final boolean isLteOnCdma = mTelephonyManager.getLteOnCdmaMode()
                         == PhoneConstants.LTE_ON_CDMA_TRUE;
                 final int settingsNetworkMode = android.provider.Settings.Global.getInt(
-                        mPhone.getContext().getContentResolver(),
-                        android.provider.Settings.Global.PREFERRED_NETWORK_MODE + mPhone.getSubId(),
+                        getContext().getContentResolver(),
+                        android.provider.Settings.Global.PREFERRED_NETWORK_MODE + mSubId,
                         preferredNetworkMode);
                 if (isLteOnCdma) {
                     if (lteForced == 0) {
@@ -1179,7 +1248,7 @@
                         }
                     }
                 }
-                updateCdmaOptions(this, getPreferenceScreen(), mPhone);
+                updateCdmaOptions(this, getPreferenceScreen(), mSubId);
 
             } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                 if (isSupportTdscdma()) {
@@ -1217,7 +1286,7 @@
                     mButtonEnabledNetworks.setEntryValues(
                             R.array.enabled_networks_values);
                 }
-                updateGsmUmtsOptions(this, getPreferenceScreen(), mPhone.getSubId(),
+                updateGsmUmtsOptions(this, getPreferenceScreen(), mSubId,
                         mNetworkQueryService);
             } else {
                 throw new IllegalStateException("Unexpected phone type: " + phoneType);
@@ -1257,7 +1326,7 @@
         public boolean onPreferenceChange(Preference preference, Object objValue) {
             sendMetricsEventPreferenceChanged(getPreferenceScreen(), preference, objValue);
 
-            final int phoneSubId = mPhone.getSubId();
+            final int phoneSubId = mSubId;
             if (preference == mButtonPreferredNetworkMode) {
                 //NOTE onPreferenceChange seems to be called even if there is no change
                 //Check if the button value is changed from the System.Setting
@@ -1265,7 +1334,7 @@
                 int buttonNetworkMode;
                 buttonNetworkMode = Integer.parseInt((String) objValue);
                 int settingsNetworkMode = android.provider.Settings.Global.getInt(
-                        mPhone.getContext().getContentResolver(),
+                        getContext().getContentResolver(),
                         android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
                         preferredNetworkMode);
                 if (buttonNetworkMode != settingsNetworkMode) {
@@ -1304,12 +1373,16 @@
                     }
 
                     android.provider.Settings.Global.putInt(
-                            mPhone.getContext().getContentResolver(),
+                            getContext().getContentResolver(),
                             android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
                             buttonNetworkMode );
                     //Set the modem network mode
-                    mPhone.setPreferredNetworkType(modemNetworkMode, mHandler
-                            .obtainMessage(MyHandler.MESSAGE_SET_PREFERRED_NETWORK_TYPE));
+                    new SetPreferredNetworkAsyncTask(
+                            mTelephonyManager,
+                            mSubId,
+                            modemNetworkMode,
+                            mHandler.obtainMessage(MyHandler.MESSAGE_SET_PREFERRED_NETWORK_TYPE))
+                            .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                 }
             } else if (preference == mButtonEnabledNetworks) {
                 mButtonEnabledNetworks.setValue((String) objValue);
@@ -1317,7 +1390,7 @@
                 buttonNetworkMode = Integer.parseInt((String) objValue);
                 if (DBG) log("buttonNetworkMode: " + buttonNetworkMode);
                 int settingsNetworkMode = android.provider.Settings.Global.getInt(
-                        mPhone.getContext().getContentResolver(),
+                        getContext().getContentResolver(),
                         android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
                         preferredNetworkMode);
                 if (buttonNetworkMode != settingsNetworkMode) {
@@ -1352,12 +1425,16 @@
                     UpdateEnabledNetworksValueAndSummary(buttonNetworkMode);
 
                     android.provider.Settings.Global.putInt(
-                            mPhone.getContext().getContentResolver(),
+                            getContext().getContentResolver(),
                             android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
                             buttonNetworkMode );
                     //Set the modem network mode
-                    mPhone.setPreferredNetworkType(modemNetworkMode, mHandler
-                            .obtainMessage(MyHandler.MESSAGE_SET_PREFERRED_NETWORK_TYPE));
+                    new SetPreferredNetworkAsyncTask(
+                            mTelephonyManager,
+                            mSubId,
+                            modemNetworkMode,
+                            mHandler.obtainMessage(MyHandler.MESSAGE_SET_PREFERRED_NETWORK_TYPE))
+                            .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                 }
             } else if (preference == mButton4glte) {
                 boolean enhanced4gMode = !mButton4glte.isChecked();
@@ -1368,11 +1445,11 @@
 
                 //normally called on the toggle click
                 if (!mButtonDataRoam.isChecked()) {
-                    PersistableBundle carrierConfig =
-                            PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+                    PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(
+                            mSubId);
                     if (carrierConfig != null && carrierConfig.getBoolean(
                             CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL)) {
-                        mPhone.setDataRoamingEnabled(true);
+                        mTelephonyManager.setDataRoamingEnabled(true);
                         MetricsLogger.action(getContext(),
                                 getMetricsEventCategory(getPreferenceScreen(), mButtonDataRoam),
                                 true);
@@ -1383,13 +1460,15 @@
                         // First confirm with a warning dialog about charges
                         mOkClicked = false;
                         RoamingDialogFragment fragment = new RoamingDialogFragment();
-                        fragment.setPhone(mPhone);
+                        Bundle b = new Bundle();
+                        b.putInt(RoamingDialogFragment.SUB_ID_KEY, mSubId);
+                        fragment.setArguments(b);
                         fragment.show(getFragmentManager(), ROAMING_TAG);
                         // Don't update the toggle unless the confirm button is actually pressed.
                         return false;
                     }
                 } else {
-                    mPhone.setDataRoamingEnabled(false);
+                    mTelephonyManager.setDataRoamingEnabled(false);
                     MetricsLogger.action(getContext(),
                             getMetricsEventCategory(getPreferenceScreen(), mButtonDataRoam),
                             false);
@@ -1419,7 +1498,7 @@
         }
 
         private boolean is4gLtePrefEnabled(PersistableBundle carrierConfig) {
-            return (mTelephonyManager.getCallState(mPhone.getSubId())
+            return (mTelephonyManager.getCallState(mSubId)
                     == TelephonyManager.CALL_STATE_IDLE)
                     && mImsMgr != null
                     && mImsMgr.isNonTtyOrTtyOnVolteEnabled()
@@ -1448,26 +1527,25 @@
                     return;
                 }
 
-                AsyncResult ar = (AsyncResult) msg.obj;
-                final int phoneSubId = mPhone.getSubId();
+                boolean success = (boolean) msg.obj;
 
-                if (ar.exception == null) {
+                if (success) {
                     int networkMode;
                     if (getPreferenceScreen().findPreference(
                             BUTTON_PREFERED_NETWORK_MODE) != null)  {
                         networkMode =  Integer.parseInt(mButtonPreferredNetworkMode.getValue());
                         android.provider.Settings.Global.putInt(
-                                mPhone.getContext().getContentResolver(),
+                                getContext().getContentResolver(),
                                 android.provider.Settings.Global.PREFERRED_NETWORK_MODE
-                                        + phoneSubId,
+                                        + mSubId,
                                 networkMode );
                     }
                     if (getPreferenceScreen().findPreference(BUTTON_ENABLED_NETWORKS_KEY) != null) {
                         networkMode = Integer.parseInt(mButtonEnabledNetworks.getValue());
                         android.provider.Settings.Global.putInt(
-                                mPhone.getContext().getContentResolver(),
+                                getContext().getContentResolver(),
                                 android.provider.Settings.Global.PREFERRED_NETWORK_MODE
-                                        + phoneSubId,
+                                        + mSubId,
                                 networkMode );
                     }
                 } else {
@@ -1479,11 +1557,9 @@
         }
 
         private void updatePreferredNetworkUIFromDb() {
-            final int phoneSubId = mPhone.getSubId();
-
             int settingsNetworkMode = android.provider.Settings.Global.getInt(
-                    mPhone.getContext().getContentResolver(),
-                    android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
+                    getContext().getContentResolver(),
+                    android.provider.Settings.Global.PREFERRED_NETWORK_MODE + mSubId,
                     preferredNetworkMode);
 
             if (DBG) {
@@ -1528,7 +1604,7 @@
                             R.string.preferred_network_mode_gsm_wcdma_summary);
                     break;
                 case Phone.NT_MODE_CDMA:
-                    switch (mPhone.getLteOnCdmaMode()) {
+                    switch (mTelephonyManager.getLteOnCdmaMode()) {
                         case PhoneConstants.LTE_ON_CDMA_TRUE:
                             mButtonPreferredNetworkMode.setSummary(
                                     R.string.preferred_network_mode_cdma_summary);
@@ -1581,9 +1657,9 @@
                             R.string.preferred_network_mode_lte_tdscdma_cdma_evdo_gsm_wcdma_summary);
                     break;
                 case Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
-                    if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA ||
-                            mIsGlobalCdma ||
-                            isWorldMode()) {
+                    if (mTelephonyManager.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA
+                            || mIsGlobalCdma
+                            || isWorldMode()) {
                         mButtonPreferredNetworkMode.setSummary(
                                 R.string.preferred_network_mode_global_summary);
                     } else {
@@ -1718,9 +1794,9 @@
                         }
                         mButtonEnabledNetworks.setValue(
                                 Integer.toString(Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA));
-                        if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA ||
-                                mIsGlobalCdma ||
-                                isWorldMode()) {
+                        if (mTelephonyManager.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA
+                                || mIsGlobalCdma
+                                || isWorldMode()) {
                             mButtonEnabledNetworks.setSummary(R.string.network_global);
                         } else {
                             mButtonEnabledNetworks.setSummary((mShow4GForLTE == true)
@@ -1759,31 +1835,23 @@
                 return;
             }
 
-            boolean removePref = false;
+            // Removes the preference if the wifi calling is disabled.
+            if (!isWifiCallingEnabled(getContext(), SubscriptionManager.getPhoneId(mSubId))) {
+                mCallingCategory.removePreference(mWiFiCallingPref);
+                return;
+            }
+
             final PhoneAccountHandle simCallManager =
                     TelecomManager.from(getContext()).getSimCallManager();
 
             if (simCallManager != null) {
                 Intent intent = PhoneAccountSettingsFragment.buildPhoneAccountConfigureIntent(
                         getContext(), simCallManager);
-                if (intent != null) {
-                    PackageManager pm = mPhone.getContext().getPackageManager();
-                    List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
-                    if (!resolutions.isEmpty()) {
-                        mWiFiCallingPref.setTitle(resolutions.get(0).loadLabel(pm));
-                        mWiFiCallingPref.setSummary(null);
-                        mWiFiCallingPref.setIntent(intent);
-                    } else {
-                        removePref = true;
-                    }
-                } else {
-                    removePref = true;
-                }
-            } else if (mImsMgr == null
-                    || !mImsMgr.isWfcEnabledByPlatform()
-                    || !mImsMgr.isWfcProvisionedOnDevice()
-                    || !isImsServiceStateReady()) {
-                removePref = true;
+                PackageManager pm = getContext().getPackageManager();
+                List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
+                mWiFiCallingPref.setTitle(resolutions.get(0).loadLabel(pm));
+                mWiFiCallingPref.setSummary(null);
+                mWiFiCallingPref.setIntent(intent);
             } else {
                 int resId = com.android.internal.R.string.wifi_calling_off_summary;
                 if (mImsMgr.isWfcEnabledByUser()) {
@@ -1808,13 +1876,9 @@
                 mWiFiCallingPref.setSummary(resId);
             }
 
-            if (removePref) {
-                mCallingCategory.removePreference(mWiFiCallingPref);
-            } else {
-                mCallingCategory.addPreference(mWiFiCallingPref);
-                mWiFiCallingPref.setEnabled(mTelephonyManager.getCallState(mPhone.getSubId())
-                        == TelephonyManager.CALL_STATE_IDLE && hasActiveSubscriptions());
-            }
+            mCallingCategory.addPreference(mWiFiCallingPref);
+            mWiFiCallingPref.setEnabled(mTelephonyManager.getCallState(mSubId)
+                    == TelephonyManager.CALL_STATE_IDLE && hasActiveSubscriptions());
         }
 
         private void updateEnhanced4gLteState() {
@@ -1822,13 +1886,12 @@
                 return;
             }
 
-            PersistableBundle carrierConfig = PhoneGlobals.getInstance()
-                    .getCarrierConfigForSubId(mPhone.getSubId());
+            PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
 
             if ((mImsMgr == null
                     || !mImsMgr.isVolteEnabledByPlatform()
                     || !mImsMgr.isVolteProvisionedOnDevice()
-                    || !isImsServiceStateReady()
+                    || !isImsServiceStateReady(mImsMgr)
                     || carrierConfig.getBoolean(
                     CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL))) {
                 getPreferenceScreen().removePreference(mButton4glte);
@@ -1846,22 +1909,21 @@
                 return;
             }
 
-            PersistableBundle carrierConfig = PhoneGlobals.getInstance()
-                    .getCarrierConfigForSubId(mPhone.getSubId());
+            PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
 
             if (mImsMgr != null
                     && mImsMgr.isVtEnabledByPlatform()
                     && mImsMgr.isVtProvisionedOnDevice()
-                    && isImsServiceStateReady()
+                    && isImsServiceStateReady(mImsMgr)
                     && (carrierConfig.getBoolean(
                         CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS)
-                        || mPhone.mDcTracker.isDataEnabled())) {
+                        || mTelephonyManager.isDataEnabled())) {
                 mCallingCategory.addPreference(mVideoCallingPref);
                 if (!mButton4glte.isChecked()) {
                     mVideoCallingPref.setEnabled(false);
                     mVideoCallingPref.setChecked(false);
                 } else {
-                    mVideoCallingPref.setEnabled(mTelephonyManager.getCallState(mPhone.getSubId())
+                    mVideoCallingPref.setEnabled(mTelephonyManager.getCallState(mSubId)
                             == TelephonyManager.CALL_STATE_IDLE && hasActiveSubscriptions());
                     mVideoCallingPref.setChecked(mImsMgr.isVtEnabledByUser());
                     mVideoCallingPref.setOnPreferenceChangeListener(this);
@@ -1947,7 +2009,7 @@
                 return;
             }
 
-            updateGsmUmtsOptions(this, prefSet, mPhone.getSubId(), mNetworkQueryService);
+            updateGsmUmtsOptions(this, prefSet, mSubId, mNetworkQueryService);
 
             PreferenceCategory networkOperatorCategory =
                     (PreferenceCategory) prefSet.findPreference(
@@ -1970,10 +2032,11 @@
             if (prefSet == null) {
                 return;
             }
-            updateCdmaOptions(this, prefSet, mPhone);
+            updateCdmaOptions(this, prefSet, mSubId);
             CdmaSystemSelectListPreference systemSelect =
                     (CdmaSystemSelectListPreference)prefSet.findPreference
                             (BUTTON_CDMA_SYSTEM_SELECT_KEY);
+            systemSelect.setSubscriptionId(mSubId);
             if (systemSelect != null) {
                 systemSelect.setEnabled(enable);
             }
@@ -1984,7 +2047,7 @@
                 return true;
             }
 
-            String operatorNumeric = mPhone.getServiceState().getOperatorNumeric();
+            String operatorNumeric = mTelephonyManager.getServiceState().getOperatorNumeric();
             String[] numericArray = getResources().getStringArray(
                     R.array.config_support_tdscdma_roaming_on_networks);
             if (numericArray.length == 0 || operatorNumeric == null) {
@@ -2111,31 +2174,42 @@
         }
 
         private void updateCdmaOptions(PreferenceFragment prefFragment, PreferenceScreen prefScreen,
-                Phone phone) {
+                int subId) {
             // We don't want to re-create CdmaOptions if already exists. Otherwise, the preferences
             // inside it will also be re-created which causes unexpected behavior. For example,
             // the open dialog gets dismissed or detached after pause / resume.
             if (mCdmaOptions == null) {
-                mCdmaOptions = new CdmaOptions(prefFragment, prefScreen, phone);
+                mCdmaOptions = new CdmaOptions(prefFragment, prefScreen, subId);
             } else {
-                mCdmaOptions.update(phone);
+                mCdmaOptions.updateSubscriptionId(subId);
             }
         }
+    }
 
-        private boolean isImsServiceStateReady() {
-            boolean isImsServiceStateReady = false;
+    private static final class SetPreferredNetworkAsyncTask extends AsyncTask<Void, Void, Boolean> {
 
-            try {
-                if (mImsMgr != null && mImsMgr.getImsServiceState() == ImsFeature.STATE_READY) {
-                    isImsServiceStateReady = true;
-                }
-            } catch (ImsException ex) {
-                loge("Exception when trying to get ImsServiceStatus: " + ex);
-            }
+        private final TelephonyManager mTelephonyManager;
+        private final int mSubId;
+        private final int mNetworkType;
+        private final Message mCallback;
 
-            log("isImsServiceStateReady=" + isImsServiceStateReady);
-            return isImsServiceStateReady;
+        SetPreferredNetworkAsyncTask(
+                TelephonyManager tm, int subId, int networkType, Message callback) {
+            mTelephonyManager = tm;
+            mSubId = subId;
+            mNetworkType = networkType;
+            mCallback = callback;
+        }
+
+        @Override
+        protected Boolean doInBackground(Void... voids) {
+            return mTelephonyManager.setPreferredNetworkType(mSubId, mNetworkType);
+        }
+
+        @Override
+        protected void onPostExecute(Boolean isSuccessed) {
+            mCallback.obj = isSuccessed;
+            mCallback.sendToTarget();
         }
     }
 }
-
diff --git a/src/com/android/phone/NetworkOperators.java b/src/com/android/phone/NetworkOperators.java
index 316d11a..7e54a1b 100644
--- a/src/com/android/phone/NetworkOperators.java
+++ b/src/com/android/phone/NetworkOperators.java
@@ -19,7 +19,6 @@
 import android.app.ProgressDialog;
 import android.content.Context;
 import android.content.Intent;
-import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
 import android.preference.Preference;
@@ -30,12 +29,11 @@
 import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.widget.Toast;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.telephony.CommandException;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
+import com.android.settingslib.utils.ThreadUtils;
 
 /**
  * "Networks" settings UI for the Phone app.
@@ -55,16 +53,14 @@
     public static final String BUTTON_CHOOSE_NETWORK_KEY = "button_choose_network_key";
     public static final String CATEGORY_NETWORK_OPERATORS_KEY = "network_operators_category_key";
 
-    int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
-    private static final int ALREADY_IN_AUTO_SELECTION = 1;
-
     //preference objects
     private NetworkSelectListPreference mNetworkSelect;
     private TwoStatePreference mAutoSelect;
     private Preference mChooseNetwork;
+    private ProgressDialog mProgressDialog;
 
     private int mSubId;
-    private ProgressDialog mProgressDialog;
+    private TelephonyManager mTelephonyManager;
 
     // There's two sets of Auto-Select UI in this class.
     // If {@code com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI} set as true
@@ -94,6 +90,7 @@
             removePreference(mChooseNetwork);
         }
         mProgressDialog = new ProgressDialog(getContext());
+        mTelephonyManager = TelephonyManager.from(getContext());
     }
 
     /**
@@ -104,7 +101,7 @@
      */
     protected void update(final int subId, INetworkQueryService queryService) {
         mSubId = subId;
-        mPhoneId = SubscriptionManager.getPhoneId(mSubId);
+        mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(mSubId);
 
         if (mAutoSelect != null) {
             mAutoSelect.setOnPreferenceChangeListener(this);
@@ -112,11 +109,9 @@
 
         if (mEnableNewManualSelectNetworkUI) {
             if (mChooseNetwork != null) {
-                TelephonyManager telephonyManager = (TelephonyManager)
-                        getContext().getSystemService(Context.TELEPHONY_SERVICE);
-                if (DBG) logd("data connection status " + telephonyManager.getDataState());
-                if (telephonyManager.getDataState() == telephonyManager.DATA_CONNECTED) {
-                    mChooseNetwork.setSummary(telephonyManager.getNetworkOperatorName());
+                ServiceState ss = mTelephonyManager.getServiceState();
+                if (ss != null && ss.getState() == ServiceState.STATE_IN_SERVICE) {
+                    mChooseNetwork.setSummary(mTelephonyManager.getNetworkOperatorName());
                 } else {
                     mChooseNetwork.setSummary(R.string.network_disconnected);
                 }
@@ -152,48 +147,44 @@
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
-            AsyncResult ar;
             switch (msg.what) {
                 case EVENT_AUTO_SELECT_DONE:
                     mAutoSelect.setEnabled(true);
                     dismissProgressBar();
 
-                    ar = (AsyncResult) msg.obj;
-                    if (ar.exception != null) {
-                        if (DBG) logd("automatic network selection: failed!");
-                        displayNetworkSelectionFailed(ar.exception);
-                    } else {
+                    boolean isSuccessed = (boolean) msg.obj;
+
+                    if (isSuccessed) {
                         if (DBG) logd("automatic network selection: succeeded!");
-                        displayNetworkSelectionSucceeded(msg.arg1);
+                        displayNetworkSelectionSucceeded();
+                    } else {
+                        if (DBG) logd("automatic network selection: failed!");
+                        displayNetworkSelectionFailed();
                     }
 
                     break;
                 case EVENT_GET_NETWORK_SELECTION_MODE_DONE:
-                    ar = (AsyncResult) msg.obj;
-                    if (ar.exception != null) {
+                    int networkSelectionMode = msg.arg1;
+                    if (networkSelectionMode == TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN) {
                         if (DBG) logd("get network selection mode: failed!");
-                    } else if (ar.result != null) {
-                        try {
-                            int[] modes = (int[]) ar.result;
-                            boolean autoSelect = (modes[0] == 0);
-                            if (DBG) {
-                                logd("get network selection mode: "
-                                        + (autoSelect ? "auto" : "manual") + " selection");
+                    } else {
+                        boolean autoSelect = networkSelectionMode
+                                == TelephonyManager.NETWORK_SELECTION_MODE_AUTO;
+                        if (DBG) {
+                            logd("get network selection mode: "
+                                    + (autoSelect ? "auto" : "manual") + " selection");
+                        }
+                        if (mAutoSelect != null) {
+                            mAutoSelect.setChecked(autoSelect);
+                        }
+                        if (mEnableNewManualSelectNetworkUI) {
+                            if (mChooseNetwork != null) {
+                                mChooseNetwork.setEnabled(!autoSelect);
                             }
-                            if (mAutoSelect != null) {
-                                mAutoSelect.setChecked(autoSelect);
+                        } else {
+                            if (mNetworkSelect != null) {
+                                mNetworkSelect.setEnabled(!autoSelect);
                             }
-                            if (mEnableNewManualSelectNetworkUI) {
-                                if (mChooseNetwork != null) {
-                                    mChooseNetwork.setEnabled(!autoSelect);
-                                }
-                            } else {
-                                if (mNetworkSelect != null) {
-                                    mNetworkSelect.setEnabled(!autoSelect);
-                                }
-                            }
-                        } catch (Exception e) {
-                            if (DBG) loge("get network selection mode: unable to parse result.");
                         }
                     }
             }
@@ -202,42 +193,19 @@
     };
 
     // Used by both mAutoSelect and mNetworkSelect buttons.
-    protected void displayNetworkSelectionFailed(Throwable ex) {
-        String status;
-        if ((ex != null && ex instanceof CommandException)
-                && ((CommandException) ex).getCommandError()
-                == CommandException.Error.ILLEGAL_SIM_OR_ME) {
-            status = getContext().getResources().getString(R.string.not_allowed);
-        } else {
-            status = getContext().getResources().getString(R.string.connect_later);
-        }
+    protected void displayNetworkSelectionFailed() {
+        Toast.makeText(getContext(), R.string.connect_later, Toast.LENGTH_LONG).show();
 
         final PhoneGlobals app = PhoneGlobals.getInstance();
-        app.notificationMgr.postTransientNotification(
-                NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status);
-
-        TelephonyManager tm = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
-        Phone phone = PhoneFactory.getPhone(mPhoneId);
-        if (phone != null) {
-            ServiceState ss = tm.getServiceStateForSubscriber(phone.getSubId());
-            if (ss != null) {
-                app.notificationMgr.updateNetworkSelection(ss.getState(), phone.getSubId());
-            }
+        ServiceState ss = mTelephonyManager.getServiceStateForSubscriber(mSubId);
+        if (ss != null) {
+            app.notificationMgr.updateNetworkSelection(ss.getState(), mSubId);
         }
     }
 
     // Used by both mAutoSelect and mNetworkSelect buttons.
-    protected void displayNetworkSelectionSucceeded(int msgArg1) {
-        String status = null;
-        if (msgArg1 == ALREADY_IN_AUTO_SELECTION) {
-            status = getContext().getResources().getString(R.string.already_auto);
-        } else {
-            status = getContext().getResources().getString(R.string.registration_done);
-        }
-
-        final PhoneGlobals app = PhoneGlobals.getInstance();
-        app.notificationMgr.postTransientNotification(
-                NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status);
+    protected void displayNetworkSelectionSucceeded() {
+        Toast.makeText(getContext(), R.string.registration_done, Toast.LENGTH_LONG).show();
     }
 
     private void selectNetworkAutomatic(boolean autoSelect) {
@@ -256,10 +224,17 @@
             if (DBG) logd("select network automatically...");
             showAutoSelectProgressBar();
             mAutoSelect.setEnabled(false);
-            Message msg = mHandler.obtainMessage(EVENT_AUTO_SELECT_DONE);
-            Phone phone = PhoneFactory.getPhone(mPhoneId);
-            if (phone != null) {
-                phone.setNetworkSelectionModeAutomatic(msg);
+            if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
+                ThreadUtils.postOnBackgroundThread(() -> {
+                    mTelephonyManager.setNetworkSelectionModeAutomatic();
+                    // Because TelephonyManager#setNetworkSelectionModeAutomatic doesn't have a
+                    // return value, we query the current network selection mode to tell if the
+                    // TelephonyManager#setNetworkSelectionModeAutomatic is successed.
+                    int networkSelectionMode = mTelephonyManager.getNetworkSelectionMode();
+                    Message msg = mHandler.obtainMessage(EVENT_AUTO_SELECT_DONE);
+                    msg.obj = networkSelectionMode == TelephonyManager.NETWORK_SELECTION_MODE_AUTO;
+                    msg.sendToTarget();
+                });
             }
         } else {
             if (mEnableNewManualSelectNetworkUI) {
@@ -277,11 +252,12 @@
 
     protected void getNetworkSelectionMode() {
         if (DBG) logd("getting network selection mode...");
-        Message msg = mHandler.obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE);
-        Phone phone = PhoneFactory.getPhone(mPhoneId);
-        if (phone != null) {
-            phone.getNetworkSelectionMode(msg);
-        }
+        ThreadUtils.postOnBackgroundThread(() -> {
+            int networkSelectionMode = mTelephonyManager.getNetworkSelectionMode();
+            Message msg = mHandler.obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE);
+            msg.arg1 = networkSelectionMode;
+            msg.sendToTarget();
+        });
     }
 
     private void dismissProgressBar() {
@@ -300,10 +276,10 @@
     }
 
     /**
-     * Open the Choose netwotk page via {@alink NetworkSelectSettingActivity}
+     * Open the Choose network page via {@alink NetworkSelectSettingActivity}
      */
     public void openChooseNetworkPage() {
-        Intent intent = NetworkSelectSettingActivity.getIntent(getContext(), mPhoneId);
+        Intent intent = NetworkSelectSettingActivity.getIntent(getContext(), mSubId);
         getContext().startActivity(intent);
     }
 
diff --git a/src/com/android/phone/NetworkQueryService.java b/src/com/android/phone/NetworkQueryService.java
index 22b5509..e67582f 100644
--- a/src/com/android/phone/NetworkQueryService.java
+++ b/src/com/android/phone/NetworkQueryService.java
@@ -19,7 +19,6 @@
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
-import android.os.AsyncResult;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -33,13 +32,14 @@
 import android.telephony.NetworkScan;
 import android.telephony.NetworkScanRequest;
 import android.telephony.RadioAccessSpecifier;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyScanManager;
 import android.util.Log;
 
+import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.OperatorInfo;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -185,7 +185,7 @@
          * completion.
          */
         public void startNetworkQuery(
-                INetworkQueryServiceCallback cb, int phoneId, boolean isIncrementalResult) {
+                INetworkQueryServiceCallback cb, int subId, boolean isIncrementalResult) {
             if (cb != null) {
                 // register the callback to the list of callbacks.
                 synchronized (mCallbacks) {
@@ -233,16 +233,23 @@
                                         networkScanCallback);
                                 mState = QUERY_IS_RUNNING;
                             } else {
-                                Phone phone = PhoneFactory.getPhone(phoneId);
-                                if (phone != null) {
-                                    phone.getAvailableNetworks(
-                                            mHandler.obtainMessage(
-                                                    EVENT_NETWORK_SCAN_VIA_PHONE_COMPLETED));
+                                if (SubscriptionManager.isValidSubscriptionId(subId)) {
                                     mState = QUERY_IS_RUNNING;
-                                    if (DBG) log("start network scan via Phone");
+                                    ThreadUtils.postOnBackgroundThread(() -> {
+                                        if (DBG) log("start network scan via Phone xxx");
+                                        TelephonyManager telephonyManager =
+                                                TelephonyManager.from(getApplicationContext())
+                                                        .createForSubscriptionId(subId);
+                                        CellNetworkScanResult result =
+                                                telephonyManager.getAvailableNetworks();
+                                        Message msg = mHandler.obtainMessage(
+                                                EVENT_NETWORK_SCAN_VIA_PHONE_COMPLETED);
+                                        msg.obj = result;
+                                        msg.sendToTarget();
+                                    });
                                 } else {
                                     if (DBG) {
-                                        log("phone is null");
+                                        log("SubscriptionId is invalid");
                                     }
                                 }
                             }
@@ -316,8 +323,7 @@
     }
 
     /**
-     * Broadcast the results from the query to all registered callback
-     * objects. 
+     * Broadcast the results from the query to all registered callback objects.
      */
     private void broadcastQueryResults(Message msg) {
         // reset the state.
@@ -331,11 +337,11 @@
                 try {
                     switch (msg.what) {
                         case EVENT_NETWORK_SCAN_VIA_PHONE_COMPLETED:
-                            AsyncResult ar = (AsyncResult) msg.obj;
-                            if (ar != null) {
-                                cb.onResults(getCellInfoList((List<OperatorInfo>) ar.result));
+                            CellNetworkScanResult result = (CellNetworkScanResult) msg.obj;
+                            if (result.getOperators() != null) {
+                                cb.onResults(getCellInfoList(result.getOperators()));
                             } else {
-                                if (DBG) log("AsyncResult is null.");
+                                if (DBG) log("Operators list is null.");
                             }
                             // Send the onComplete() callback to indicate the one-time network
                             // scan has completed.
diff --git a/src/com/android/phone/NetworkSelectListPreference.java b/src/com/android/phone/NetworkSelectListPreference.java
index 51a678d..cec914a 100644
--- a/src/com/android/phone/NetworkSelectListPreference.java
+++ b/src/com/android/phone/NetworkSelectListPreference.java
@@ -19,7 +19,6 @@
 import android.app.ProgressDialog;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.os.AsyncResult;
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Message;
@@ -44,8 +43,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.OperatorInfo;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -64,7 +62,7 @@
     private static final String LOG_TAG = "networkSelect";
     private static final boolean DBG = true;
 
-    private static final int EVENT_NETWORK_SELECTION_DONE = 1;
+    private static final int EVENT_MANUALLY_NETWORK_SELECTION_DONE = 1;
     private static final int EVENT_NETWORK_SCAN_RESULTS = 2;
     private static final int EVENT_NETWORK_SCAN_COMPLETED = 3;
 
@@ -72,13 +70,12 @@
     private static final int DIALOG_NETWORK_SELECTION = 100;
     private static final int DIALOG_NETWORK_LIST_LOAD = 200;
 
-    private int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
     private List<CellInfo> mCellInfoList;
     private CellInfo mCellInfo;
 
     private int mSubId;
+    private TelephonyManager mTelephonyManager;
     private NetworkOperators mNetworkOperators;
-    private boolean mNeedScanAgain;
     private List<String> mForbiddenPlmns;
 
     private ProgressDialog mProgressDialog;
@@ -113,9 +110,8 @@
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
-            AsyncResult ar;
             switch (msg.what) {
-                case EVENT_NETWORK_SELECTION_DONE:
+                case EVENT_MANUALLY_NETWORK_SELECTION_DONE:
                     if (DBG) logd("hideProgressPanel");
                     try {
                         dismissProgressBar();
@@ -123,16 +119,16 @@
                     }
                     setEnabled(true);
 
-                    ar = (AsyncResult) msg.obj;
-                    if (ar.exception != null) {
-                        if (DBG) logd("manual network selection: failed!");
-                        mNetworkOperators.displayNetworkSelectionFailed(ar.exception);
-                    } else {
+                    boolean isSuccessed = (boolean) msg.obj;
+                    if (isSuccessed) {
                         if (DBG) {
                             logd("manual network selection: succeeded! "
                                     + getNetworkTitle(mCellInfo));
                         }
-                        mNetworkOperators.displayNetworkSelectionSucceeded(msg.arg1);
+                        mNetworkOperators.displayNetworkSelectionSucceeded();
+                    } else {
+                        if (DBG) logd("manual network selection: failed!");
+                        mNetworkOperators.displayNetworkSelectionFailed();
                     }
                     mNetworkOperators.getNetworkSelectionMode();
                     break;
@@ -235,14 +231,9 @@
         // This preference should share the same progressDialog with networkOperators category.
         mProgressDialog = progressDialog;
 
-        if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
-            mPhoneId = SubscriptionManager.getPhoneId(mSubId);
-        }
+        mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(mSubId);
 
-        TelephonyManager telephonyManager = (TelephonyManager)
-                getContext().getSystemService(Context.TELEPHONY_SERVICE);
-
-        setSummary(telephonyManager.getNetworkOperatorName(mSubId));
+        setSummary(mTelephonyManager.getNetworkOperatorName());
 
         setOnPreferenceChangeListener(this);
     }
@@ -300,7 +291,8 @@
         if (DBG) logd("load networks list...");
         try {
             if (mNetworkQueryService != null) {
-                mNetworkQueryService.startNetworkQuery(mCallback, mPhoneId, false);
+                mNetworkQueryService.startNetworkQuery(
+                        mCallback, mSubId, false /* isIncrementalResult */);
             } else {
                 displayNetworkQueryFailed(NetworkQueryService.QUERY_EXCEPTION);
             }
@@ -406,16 +398,21 @@
         MetricsLogger.action(getContext(),
                 MetricsEvent.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK);
 
-        Message msg = mHandler.obtainMessage(EVENT_NETWORK_SELECTION_DONE);
-        Phone phone = PhoneFactory.getPhone(mPhoneId);
-        if (phone != null) {
-            OperatorInfo operatorInfo = getOperatorInfoFromCellInfo(mCellInfo);
-            if (DBG) logd("manually selected network: " + operatorInfo.toString());
-            phone.selectNetworkManually(operatorInfo, true, msg);
-            displayNetworkSelectionInProgress();
+        if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            ThreadUtils.postOnBackgroundThread(() -> {
+                final OperatorInfo operatorInfo = getOperatorInfoFromCellInfo(mCellInfo);
+                if (DBG) logd("manually selected network: " + operatorInfo.toString());
+                boolean isSuccessed = mTelephonyManager.setNetworkSelectionModeManual(
+                        operatorInfo.getOperatorNumeric(), true /* persistSelection */);
+                int mode = mTelephonyManager.getNetworkSelectionMode();
+                Message msg = mHandler.obtainMessage(EVENT_MANUALLY_NETWORK_SELECTION_DONE);
+                msg.obj = isSuccessed;
+                msg.sendToTarget();
+            });
         } else {
-            loge("Error selecting network. phone is null.");
+            loge("Error selecting network, subscription Id is invalid " + mSubId);
         }
+
         return true;
     }
 
diff --git a/src/com/android/phone/NetworkSelectSetting.java b/src/com/android/phone/NetworkSelectSetting.java
index 6bb42c8..56f0187 100644
--- a/src/com/android/phone/NetworkSelectSetting.java
+++ b/src/com/android/phone/NetworkSelectSetting.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.os.AsyncResult;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
@@ -47,8 +46,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.OperatorInfo;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -64,7 +62,7 @@
     private static final String TAG = "NetworkSelectSetting";
     private static final boolean DBG = true;
 
-    private static final int EVENT_NETWORK_SELECTION_DONE = 1;
+    private static final int EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE = 1;
     private static final int EVENT_NETWORK_SCAN_RESULTS = 2;
     private static final int EVENT_NETWORK_SCAN_ERROR = 3;
     private static final int EVENT_NETWORK_SCAN_COMPLETED = 4;
@@ -81,11 +79,10 @@
     private View mProgressHeader;
     private Preference mStatusMessagePreference;
     private List<CellInfo> mCellInfoList;
-    private int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
+    private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private ViewGroup mFrameLayout;
     private NetworkOperatorPreference mSelectedNetworkOperatorPreference;
     private TelephonyManager mTelephonyManager;
-    private NetworkOperators mNetworkOperators;
     private List<String> mForbiddenPlmns;
 
     private final Runnable mUpdateNetworkOperatorsRunnable = () -> {
@@ -95,9 +92,9 @@
     /**
      * Create a new instance of this fragment.
      */
-    public static NetworkSelectSetting newInstance(int phoneId) {
+    public static NetworkSelectSetting newInstance(int subId) {
         Bundle args = new Bundle();
-        args.putInt(NetworkSelectSettingActivity.KEY_PHONE_ID, phoneId);
+        args.putInt(NetworkSelectSettingActivity.KEY_SUBSCRIPTION_ID, subId);
         NetworkSelectSetting fragment = new NetworkSelectSetting();
         fragment.setArguments(args);
 
@@ -109,7 +106,7 @@
         if (DBG) logd("onCreate");
         super.onCreate(icicle);
 
-        mPhoneId = getArguments().getInt(NetworkSelectSettingActivity.KEY_PHONE_ID);
+        mSubId = getArguments().getInt(NetworkSelectSettingActivity.KEY_SUBSCRIPTION_ID);
 
         addPreferencesFromResource(R.xml.choose_network);
         mConnectedNetworkOperatorsPreference =
@@ -118,9 +115,7 @@
                 (PreferenceCategory) findPreference(PREF_KEY_NETWORK_OPERATORS);
         mStatusMessagePreference = new Preference(getContext());
         mSelectedNetworkOperatorPreference = null;
-        mTelephonyManager = (TelephonyManager)
-                getContext().getSystemService(Context.TELEPHONY_SERVICE);
-        mNetworkOperators = new NetworkOperators(getContext());
+        mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(mSubId);
         setRetainInstance(true);
     }
 
@@ -199,9 +194,7 @@
                     MetricsEvent.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK);
 
             // Connect to the network
-            Message msg = mHandler.obtainMessage(EVENT_NETWORK_SELECTION_DONE);
-            Phone phone = PhoneFactory.getPhone(mPhoneId);
-            if (phone != null) {
+            if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
                 if (DBG) {
                     logd("Connect to the network: " + CellInfoUtil.getNetworkTitle(cellInfo));
                 }
@@ -218,14 +211,20 @@
                     }
                 }
 
-                // Select network manually via Phone
                 OperatorInfo operatorInfo = CellInfoUtil.getOperatorInfoFromCellInfo(cellInfo);
                 if (DBG) logd("manually selected network operator: " + operatorInfo.toString());
-                phone.selectNetworkManually(operatorInfo, true, msg);
+
+                ThreadUtils.postOnBackgroundThread(() -> {
+                    Message msg = mHandler.obtainMessage(EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE);
+                    msg.obj = mTelephonyManager.setNetworkSelectionModeManual(
+                            operatorInfo.getOperatorNumeric(), true /* persistSelection */);
+                    msg.sendToTarget();
+                });
+
                 setProgressBarVisible(true);
                 return true;
             } else {
-                loge("Error selecting network. phone is null.");
+                loge("Error selecting network. Subscription Id is invalid.");
                 mSelectedNetworkOperatorPreference = null;
                 return false;
             }
@@ -257,23 +256,22 @@
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
-            AsyncResult ar;
             switch (msg.what) {
-                case EVENT_NETWORK_SELECTION_DONE:
+                case EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE:
                     if (DBG) logd("network selection done: hide the progress header");
                     setProgressBarVisible(false);
 
-                    ar = (AsyncResult) msg.obj;
-                    if (ar.exception != null) {
+                    boolean isSuccessed = (boolean) msg.obj;
+                    if (isSuccessed) {
+                        if (DBG) logd("manual network selection: succeeded! ");
+                        // Set summary as "Connected" to the selected network.
+                        mSelectedNetworkOperatorPreference.setSummary(R.string.network_connected);
+                    } else {
                         if (DBG) logd("manual network selection: failed! ");
                         updateNetworkSelection();
                         // Set summary as "Couldn't connect" to the selected network.
                         mSelectedNetworkOperatorPreference.setSummary(
                                 R.string.network_could_not_connect);
-                    } else {
-                        if (DBG) logd("manual network selection: succeeded! ");
-                        // Set summary as "Connected" to the selected network.
-                        mSelectedNetworkOperatorPreference.setSummary(R.string.network_connected);
                     }
                     break;
 
@@ -316,7 +314,7 @@
             if (mNetworkQueryService != null) {
                 if (DBG) logd("start network query");
                 mNetworkQueryService
-                        .startNetworkQuery(mCallback, mPhoneId, true /* is incremental result */);
+                        .startNetworkQuery(mCallback, mSubId, true /* is incremental result */);
             } else {
                 if (DBG) logd("unable to start network query, mNetworkQueryService is null");
                 addMessagePreference(R.string.network_query_error);
@@ -420,7 +418,7 @@
         if (DBG) logd("Force config ConnectedNetworkOperatorsPreferenceCategory");
         if (mTelephonyManager.getDataState() == mTelephonyManager.DATA_CONNECTED) {
             // Try to get the network registration states
-            ServiceState ss = mTelephonyManager.getServiceStateForSubscriber(mPhoneId);
+            ServiceState ss = mTelephonyManager.getServiceState();
             List<NetworkRegistrationState> networkList =
                     ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN);
             if (networkList == null || networkList.size() == 0) {
@@ -626,11 +624,10 @@
     private void updateNetworkSelection() {
         if (DBG) logd("Update notification about no service of user selected operator");
         final PhoneGlobals app = PhoneGlobals.getInstance();
-        Phone phone = PhoneFactory.getPhone(mPhoneId);
-        if (phone != null) {
-            ServiceState ss = mTelephonyManager.getServiceStateForSubscriber(phone.getSubId());
+        if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            ServiceState ss = mTelephonyManager.getServiceState();
             if (ss != null) {
-                app.notificationMgr.updateNetworkSelection(ss.getState(), phone.getSubId());
+                app.notificationMgr.updateNetworkSelection(ss.getState(), mSubId);
             }
         }
     }
diff --git a/src/com/android/phone/NetworkSelectSettingActivity.java b/src/com/android/phone/NetworkSelectSettingActivity.java
index a151c81..5b29b281 100644
--- a/src/com/android/phone/NetworkSelectSettingActivity.java
+++ b/src/com/android/phone/NetworkSelectSettingActivity.java
@@ -31,14 +31,14 @@
  */
 public class NetworkSelectSettingActivity extends Activity {
     private static final String TAG = "NetworkSelectSettingActivity";
-    public static final String KEY_PHONE_ID = "phone_id";
+    public static final String KEY_SUBSCRIPTION_ID = "subscription_id";
 
     /**
      * Returns the Android Intent that led to this Activity being created.
      */
-    public static Intent getIntent(Context context, int phoneId) {
+    public static Intent getIntent(Context context, int subId) {
         Intent intent = new Intent(context, NetworkSelectSettingActivity.class);
-        intent.putExtra(KEY_PHONE_ID, phoneId);
+        intent.putExtra(KEY_SUBSCRIPTION_ID, subId);
         return intent;
     }
 
@@ -46,7 +46,7 @@
     public void onCreate(Bundle savedState) {
         Log.d(TAG, "onCreate()");
         super.onCreate(savedState);
-        int phoneId = getIntent().getExtras().getInt(KEY_PHONE_ID);
+        int subId = getIntent().getExtras().getInt(KEY_SUBSCRIPTION_ID);
         setContentView(R.layout.choose_network);
 
         FragmentManager fragmentManager = getFragmentManager();
@@ -54,7 +54,7 @@
         if (fragment == null) {
             fragmentManager.beginTransaction()
                     .add(R.id.choose_network_content,
-                            NetworkSelectSetting.newInstance(phoneId), TAG)
+                            NetworkSelectSetting.newInstance(subId), TAG)
                     .commit();
         }
     }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index b1e061f..d0b236f 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -41,6 +41,7 @@
 import android.os.UserManager;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
+import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
@@ -665,7 +666,9 @@
         if (isAirplaneNewlyOn) {
             // If we are trying to turn off the radio, make sure there are no active
             // emergency calls.  If there are, switch airplane mode back to off.
-            if (PhoneUtils.isInEmergencyCall(mCM)) {
+            TelecomManager tm = (TelecomManager) context.getSystemService(TELECOM_SERVICE);
+
+            if (tm != null && tm.isInEmergencyCall()) {
                 // Switch airplane mode back to off.
                 ConnectivityManager.from(this).setAirplaneMode(false);
                 Toast.makeText(this, R.string.radio_off_during_emergency_call, Toast.LENGTH_LONG)
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 51ffbd8..2e4684e 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -57,6 +57,7 @@
 import android.telephony.CellInfo;
 import android.telephony.CellInfoGsm;
 import android.telephony.CellInfoWcdma;
+import android.telephony.CellLocation;
 import android.telephony.ClientRequestStats;
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.telephony.LocationAccessPolicy;
@@ -75,6 +76,8 @@
 import android.telephony.UiccSlotInfo;
 import android.telephony.UssdResponse;
 import android.telephony.VisualVoicemailSmsFilterSettings;
+import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.gsm.GsmCellLocation;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsRcsFeature;
@@ -82,7 +85,6 @@
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.ArraySet;
-import android.util.EventLog;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
@@ -148,8 +150,6 @@
 
     // Message codes used with mMainThreadHandler
     private static final int CMD_HANDLE_PIN_MMI = 1;
-    private static final int CMD_ANSWER_RINGING_CALL = 4;
-    private static final int CMD_END_CALL = 5;  // not used yet
     private static final int CMD_TRANSMIT_APDU_LOGICAL_CHANNEL = 7;
     private static final int EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE = 8;
     private static final int CMD_OPEN_CHANNEL = 9;
@@ -195,6 +195,18 @@
     private static final int EVENT_GET_FORBIDDEN_PLMNS_DONE = 49;
     private static final int CMD_SWITCH_SLOTS = 50;
     private static final int EVENT_SWITCH_SLOTS_DONE = 51;
+    private static final int CMD_GET_NETWORK_SELECTION_MODE = 52;
+    private static final int EVENT_GET_NETWORK_SELECTION_MODE_DONE = 53;
+    private static final int CMD_GET_CDMA_ROAMING_MODE = 54;
+    private static final int EVENT_GET_CDMA_ROAMING_MODE_DONE = 55;
+    private static final int CMD_SET_CDMA_ROAMING_MODE = 56;
+    private static final int EVENT_SET_CDMA_ROAMING_MODE_DONE = 57;
+    private static final int CMD_SET_CDMA_SUBSCRIPTION_MODE = 58;
+    private static final int EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE = 59;
+    private static final int CMD_GET_ALL_CELL_INFO = 60;
+    private static final int EVENT_GET_ALL_CELL_INFO_DONE = 61;
+    private static final int CMD_GET_CELL_LOCATION = 62;
+    private static final int EVENT_GET_CELL_LOCATION_DONE = 63;
 
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
@@ -276,15 +288,18 @@
         // SubscriptionManager.INVALID_SUBSCRIPTION_ID
         public Integer subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
+        public WorkSource workSource;
+
         public MainThreadRequest(Object argument) {
             this.argument = argument;
         }
 
-        public MainThreadRequest(Object argument, Integer subId) {
+        MainThreadRequest(Object argument, Integer subId, WorkSource workSource) {
             this.argument = argument;
             if (subId != null) {
                 this.subId = subId;
             }
+            this.workSource = workSource;
         }
     }
 
@@ -323,40 +338,35 @@
             IccAPDUArgument iccArgument;
 
             switch (msg.what) {
-                 case CMD_HANDLE_USSD_REQUEST: {
-                     request = (MainThreadRequest) msg.obj;
-                     final Phone phone = getPhoneFromRequest(request);
-                     Pair<String, ResultReceiver> ussdObject = (Pair) request.argument;
-                     String ussdRequest =  ussdObject.first;
-                     ResultReceiver wrappedCallback = ussdObject.second;
+                case CMD_HANDLE_USSD_REQUEST: {
+                    request = (MainThreadRequest) msg.obj;
+                    final Phone phone = getPhoneFromRequest(request);
+                    Pair<String, ResultReceiver> ussdObject = (Pair) request.argument;
+                    String ussdRequest =  ussdObject.first;
+                    ResultReceiver wrappedCallback = ussdObject.second;
 
-                     if (!isUssdApiAllowed(request.subId)) {
-                         // Carrier does not support use of this API, return failure.
-                         Rlog.w(LOG_TAG, "handleUssdRequest: carrier does not support USSD apis.");
-                         UssdResponse response = new UssdResponse(ussdRequest, null);
-                         Bundle returnData = new Bundle();
-                         returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response);
-                         wrappedCallback.send(TelephonyManager.USSD_RETURN_FAILURE, returnData);
+                    if (!isUssdApiAllowed(request.subId)) {
+                        // Carrier does not support use of this API, return failure.
+                        Rlog.w(LOG_TAG, "handleUssdRequest: carrier does not support USSD apis.");
+                        UssdResponse response = new UssdResponse(ussdRequest, null);
+                        Bundle returnData = new Bundle();
+                        returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response);
+                        wrappedCallback.send(TelephonyManager.USSD_RETURN_FAILURE, returnData);
 
-                         request.result = true;
-                         synchronized (request) {
-                             request.notifyAll();
-                         }
-                         return;
-                     }
+                        request.result = true;
+                        notifyRequester(request);
+                        return;
+                    }
 
-                     try {
-                         request.result = phone != null ?
-                                 phone.handleUssdRequest(ussdRequest, wrappedCallback)
-                                 : false;
-                     } catch (CallStateException cse) {
-                         request.result = false;
-                     }
-                     // Wake up the requesting thread
-                     synchronized (request) {
-                         request.notifyAll();
-                     }
-                     break;
+                    try {
+                        request.result = phone != null
+                                ? phone.handleUssdRequest(ussdRequest, wrappedCallback) : false;
+                    } catch (CallStateException cse) {
+                        request.result = false;
+                    }
+                    // Wake up the requesting thread
+                    notifyRequester(request);
+                    break;
                 }
 
                 case CMD_HANDLE_PIN_MMI: {
@@ -366,51 +376,10 @@
                             getPhoneFromRequest(request).handlePinMmi((String) request.argument)
                             : false;
                     // Wake up the requesting thread
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
                 }
 
-                case CMD_ANSWER_RINGING_CALL:
-                    request = (MainThreadRequest) msg.obj;
-                    int answer_subId = request.subId;
-                    answerRingingCallInternal(answer_subId);
-                    request.result = ""; // dummy result for notifying the waiting thread
-                    // Wake up the requesting thread
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
-                    break;
-
-                case CMD_END_CALL:
-                    request = (MainThreadRequest) msg.obj;
-                    int end_subId = request.subId;
-                    final boolean hungUp;
-                    Phone phone = getPhone(end_subId);
-                    if (phone == null) {
-                        if (DBG) log("CMD_END_CALL: no phone for id: " + end_subId);
-                        break;
-                    }
-                    int phoneType = phone.getPhoneType();
-                    if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
-                        // CDMA: If the user presses the Power button we treat it as
-                        // ending the complete call session
-                        hungUp = PhoneUtils.hangupRingingAndActive(getPhone(end_subId));
-                    } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
-                        // GSM: End the call as per the Phone state
-                        hungUp = PhoneUtils.hangup(mCM);
-                    } else {
-                        throw new IllegalStateException("Unexpected phone type: " + phoneType);
-                    }
-                    if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
-                    request.result = hungUp;
-                    // Wake up the requesting thread
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
-                    break;
-
                 case CMD_TRANSMIT_APDU_LOGICAL_CHANNEL:
                     request = (MainThreadRequest) msg.obj;
                     iccArgument = (IccAPDUArgument) request.argument;
@@ -418,9 +387,7 @@
                     if (uiccCard == null) {
                         loge("iccTransmitApduLogicalChannel: No UICC");
                         request.result = new IccIoResult(0x6F, 0, (byte[])null);
-                        synchronized (request) {
-                            request.notifyAll();
-                        }
+                        notifyRequester(request);
                     } else {
                         onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE,
                             request);
@@ -447,9 +414,7 @@
                             loge("iccTransmitApduLogicalChannel: Unknown exception");
                         }
                     }
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_TRANSMIT_APDU_BASIC_CHANNEL:
@@ -459,9 +424,7 @@
                     if (uiccCard == null) {
                         loge("iccTransmitApduBasicChannel: No UICC");
                         request.result = new IccIoResult(0x6F, 0, (byte[])null);
-                        synchronized (request) {
-                            request.notifyAll();
-                        }
+                        notifyRequester(request);
                     } else {
                         onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE,
                             request);
@@ -487,9 +450,7 @@
                             loge("iccTransmitApduBasicChannel: Unknown exception");
                         }
                     }
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_EXCHANGE_SIM_IO:
@@ -499,9 +460,7 @@
                     if (uiccCard == null) {
                         loge("iccExchangeSimIO: No UICC");
                         request.result = new IccIoResult(0x6F, 0, (byte[])null);
-                        synchronized (request) {
-                            request.notifyAll();
-                        }
+                        notifyRequester(request);
                     } else {
                         onCompleted = obtainMessage(EVENT_EXCHANGE_SIM_IO_DONE,
                                 request);
@@ -519,9 +478,7 @@
                     } else {
                         request.result = new IccIoResult(0x6f, 0, (byte[])null);
                     }
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_SEND_ENVELOPE:
@@ -530,9 +487,7 @@
                     if (uiccCard == null) {
                         loge("sendEnvelopeWithStatus: No UICC");
                         request.result = new IccIoResult(0x6F, 0, (byte[])null);
-                        synchronized (request) {
-                            request.notifyAll();
-                        }
+                        notifyRequester(request);
                     } else {
                         onCompleted = obtainMessage(EVENT_SEND_ENVELOPE_DONE, request);
                         uiccCard.sendEnvelopeWithStatus((String)request.argument, onCompleted);
@@ -555,9 +510,7 @@
                             loge("sendEnvelopeWithStatus: exception:" + ar.exception);
                         }
                     }
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_OPEN_CHANNEL:
@@ -568,9 +521,7 @@
                         loge("iccOpenLogicalChannel: No UICC");
                         request.result = new IccOpenLogicalChannelResponse(-1,
                             IccOpenLogicalChannelResponse.STATUS_MISSING_RESOURCE, null);
-                        synchronized (request) {
-                            request.notifyAll();
-                        }
+                        notifyRequester(request);
                     } else {
                         onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
                         uiccCard.iccOpenLogicalChannel(openChannelArgs.first,
@@ -616,9 +567,7 @@
                             IccOpenLogicalChannelResponse.INVALID_CHANNEL, errorCode, null);
                     }
                     request.result = openChannelResp;
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_CLOSE_CHANNEL:
@@ -627,9 +576,7 @@
                     if (uiccCard == null) {
                         loge("iccCloseLogicalChannel: No UICC");
                         request.result = false;
-                        synchronized (request) {
-                            request.notifyAll();
-                        }
+                        notifyRequester(request);
                     } else {
                         onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE, request);
                         uiccCard.iccCloseLogicalChannel((Integer) request.argument, onCompleted);
@@ -643,7 +590,7 @@
                 case CMD_NV_READ_ITEM:
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_NV_READ_ITEM_DONE, request);
-                    mPhone.nvReadItem((Integer) request.argument, onCompleted);
+                    mPhone.nvReadItem((Integer) request.argument, onCompleted, request.workSource);
                     break;
 
                 case EVENT_NV_READ_ITEM_DONE:
@@ -662,16 +609,15 @@
                             loge("nvReadItem: Unknown exception");
                         }
                     }
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_NV_WRITE_ITEM:
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_NV_WRITE_ITEM_DONE, request);
                     Pair<Integer, String> idValue = (Pair<Integer, String>) request.argument;
-                    mPhone.nvWriteItem(idValue.first, idValue.second, onCompleted);
+                    mPhone.nvWriteItem(idValue.first, idValue.second, onCompleted,
+                            request.workSource);
                     break;
 
                 case EVENT_NV_WRITE_ITEM_DONE:
@@ -720,9 +666,7 @@
                             loge("getPreferredNetworkType: Unknown exception");
                         }
                     }
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_SET_PREFERRED_NETWORK_TYPE:
@@ -739,16 +683,14 @@
                 case CMD_INVOKE_OEM_RIL_REQUEST_RAW:
                     request = (MainThreadRequest)msg.obj;
                     onCompleted = obtainMessage(EVENT_INVOKE_OEM_RIL_REQUEST_RAW_DONE, request);
-                    mPhone.invokeOemRilRequestRaw((byte[])request.argument, onCompleted);
+                    mPhone.invokeOemRilRequestRaw((byte[]) request.argument, onCompleted);
                     break;
 
                 case EVENT_INVOKE_OEM_RIL_REQUEST_RAW_DONE:
                     ar = (AsyncResult)msg.obj;
                     request = (MainThreadRequest)ar.userObj;
                     request.result = ar;
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_SET_VOICEMAIL_NUMBER:
@@ -808,9 +750,7 @@
                         cellScanResult = new CellNetworkScanResult(errorCode, null);
                     }
                     request.result = cellScanResult;
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_SET_NETWORK_SELECTION_MODE_MANUAL:
@@ -830,7 +770,7 @@
                 case CMD_GET_MODEM_ACTIVITY_INFO:
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_GET_MODEM_ACTIVITY_INFO_DONE, request);
-                    mPhone.getModemActivityInfo(onCompleted);
+                    mPhone.getModemActivityInfo(onCompleted, request.workSource);
                     break;
 
                 case EVENT_GET_MODEM_ACTIVITY_INFO_DONE:
@@ -852,9 +792,7 @@
                     if (request.result == null) {
                         request.result = new ModemActivityInfo(0, 0, 0, null, 0, 0);
                     }
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_SET_ALLOWED_CARRIERS:
@@ -862,7 +800,7 @@
                     onCompleted = obtainMessage(EVENT_SET_ALLOWED_CARRIERS_DONE, request);
                     mPhone.setAllowedCarriers(
                             (List<CarrierIdentifier>) request.argument,
-                            onCompleted);
+                            onCompleted, request.workSource);
                     break;
 
                 case EVENT_SET_ALLOWED_CARRIERS_DONE:
@@ -884,15 +822,13 @@
                     if (request.result == null) {
                         request.result = new int[]{-1};
                     }
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_GET_ALLOWED_CARRIERS:
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_GET_ALLOWED_CARRIERS_DONE, request);
-                    mPhone.getAllowedCarriers(onCompleted);
+                    mPhone.getAllowedCarriers(onCompleted, request.workSource);
                     break;
 
                 case EVENT_GET_ALLOWED_CARRIERS_DONE:
@@ -914,9 +850,7 @@
                     if (request.result == null) {
                         request.result = new ArrayList<CarrierIdentifier>(0);
                     }
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case EVENT_GET_FORBIDDEN_PLMNS_DONE:
@@ -933,9 +867,7 @@
                             loge("getForbiddenPlmns: Unknown exception");
                         }
                     }
-                    synchronized (request) {
-                        request.notifyAll();
-                    }
+                    notifyRequester(request);
                     break;
 
                 case CMD_GET_FORBIDDEN_PLMNS:
@@ -945,9 +877,7 @@
                         loge("getForbiddenPlmns() UiccCard is null");
                         request.result = new IllegalArgumentException(
                                 "getForbiddenPlmns() UiccCard is null");
-                        synchronized (request) {
-                            request.notifyAll();
-                        }
+                        notifyRequester(request);
                         break;
                     }
                     Integer appType = (Integer) request.argument;
@@ -956,9 +886,7 @@
                         loge("getForbiddenPlmns() no app with specified type -- "
                                 + appType);
                         request.result = new IllegalArgumentException("Failed to get UICC App");
-                        synchronized (request) {
-                            request.notifyAll();
-                        }
+                        notifyRequester(request);
                         break;
                     } else {
                         if (DBG) logv("getForbiddenPlmns() found app " + uiccApp.getAid()
@@ -980,17 +908,122 @@
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
                     request.result = (ar.exception == null);
+                    notifyRequester(request);
+                    break;
+                case CMD_GET_NETWORK_SELECTION_MODE:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE, request);
+                    getPhoneFromRequest(request).getNetworkSelectionMode(onCompleted);
+                    break;
+
+                case EVENT_GET_NETWORK_SELECTION_MODE_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception != null) {
+                        request.result = TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN;
+                    } else {
+                        int mode = ((int[]) ar.result)[0];
+                        if (mode == 0) {
+                            request.result = TelephonyManager.NETWORK_SELECTION_MODE_AUTO;
+                        } else {
+                            request.result = TelephonyManager.NETWORK_SELECTION_MODE_MANUAL;
+                        }
+                    }
+                    notifyRequester(request);
+                    break;
+                case CMD_GET_CDMA_ROAMING_MODE:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_GET_CDMA_ROAMING_MODE_DONE, request);
+                    getPhoneFromRequest(request).queryCdmaRoamingPreference(onCompleted);
+                    break;
+                case EVENT_GET_CDMA_ROAMING_MODE_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception != null) {
+                        request.result = TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT;
+                    } else {
+                        request.result = ((int[]) ar.result)[0];
+                    }
+                    notifyRequester(request);
+                    break;
+                case CMD_SET_CDMA_ROAMING_MODE:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_SET_CDMA_ROAMING_MODE_DONE, request);
+                    int mode = (int) request.argument;
+                    getPhoneFromRequest(request).setCdmaRoamingPreference(mode, onCompleted);
+                    break;
+                case EVENT_SET_CDMA_ROAMING_MODE_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    request.result = ar.exception == null;
+                    notifyRequester(request);
+                    break;
+                case CMD_SET_CDMA_SUBSCRIPTION_MODE:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE, request);
+                    int subscriptionMode = (int) request.argument;
+                    getPhoneFromRequest(request).setCdmaSubscription(subscriptionMode, onCompleted);
+                    break;
+                case EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    request.result = ar.exception == null;
+                    notifyRequester(request);
+                    break;
+
+                case CMD_GET_ALL_CELL_INFO:
+                    request = (MainThreadRequest) msg.obj;
+                    Pair<Phone, WorkSource> args = (Pair<Phone, WorkSource>) request.argument;
+                    onCompleted = obtainMessage(EVENT_GET_ALL_CELL_INFO_DONE, request);
+                    ((Phone) args.first).getAllCellInfo(args.second, onCompleted);
+                    break;
+
+                case EVENT_GET_ALL_CELL_INFO_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    request.result = (ar.exception == null) ? ar.result : new ArrayList<CellInfo>();
                     synchronized (request) {
                         request.notifyAll();
                     }
                     break;
 
+                case CMD_GET_CELL_LOCATION: {
+                    request = (MainThreadRequest) msg.obj;
+                    WorkSource ws = (WorkSource) request.argument;
+                    Phone phone = getPhoneFromRequest(request);
+                    phone.getCellLocation(ws, obtainMessage(EVENT_GET_CELL_LOCATION_DONE, request));
+                    break;
+                }
+
+                case EVENT_GET_CELL_LOCATION_DONE: {
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null) {
+                        request.result = ar.result;
+                    } else {
+                        Phone phone = getPhoneFromRequest(request);
+                        request.result = (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)
+                                ? new CdmaCellLocation() : new GsmCellLocation();
+                    }
+
+                    synchronized (request) {
+                        request.notifyAll();
+                    }
+                    break;
+                }
+
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
             }
         }
 
+        private void notifyRequester(MainThreadRequest request) {
+            synchronized (request) {
+                request.notifyAll();
+            }
+        }
+
         private void handleNullReturnEvent(Message msg, String command) {
             AsyncResult ar = (AsyncResult) msg.obj;
             MainThreadRequest request = (MainThreadRequest) ar.userObj;
@@ -1004,9 +1037,7 @@
                     loge(command + ": Unknown exception");
                 }
             }
-            synchronized (request) {
-                request.notifyAll();
-            }
+            notifyRequester(request);
         }
     }
 
@@ -1016,7 +1047,17 @@
      * @see #sendRequestAsync
      */
     private Object sendRequest(int command, Object argument) {
-        return sendRequest(command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        return sendRequest(command, argument,  SubscriptionManager.INVALID_SUBSCRIPTION_ID, null);
+    }
+
+    /**
+     * Posts the specified command to be executed on the main thread,
+     * waits for the request to complete, and returns the result.
+     * @see #sendRequestAsync
+     */
+    private Object sendRequest(int command, Object argument, WorkSource workSource) {
+        return sendRequest(command, argument,  SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                workSource);
     }
 
     /**
@@ -1025,11 +1066,20 @@
      * @see #sendRequestAsync
      */
     private Object sendRequest(int command, Object argument, Integer subId) {
+        return sendRequest(command, argument, subId, null);
+    }
+
+    /**
+     * Posts the specified command to be executed on the main thread,
+     * waits for the request to complete, and returns the result.
+     * @see #sendRequestAsync
+     */
+    private Object sendRequest(int command, Object argument, Integer subId, WorkSource workSource) {
         if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
             throw new RuntimeException("This method will deadlock if called from the main thread.");
         }
 
-        MainThreadRequest request = new MainThreadRequest(argument, subId);
+        MainThreadRequest request = new MainThreadRequest(argument, subId, workSource);
         Message msg = mMainThreadHandler.obtainMessage(command, request);
         msg.sendToTarget();
 
@@ -1204,7 +1254,7 @@
      * @return true is a call was ended
      */
     public boolean endCall() {
-        return endCallForSubscriber(getDefaultSubscription());
+        return false;
     }
 
     /**
@@ -1212,75 +1262,15 @@
      * @return true is a call was ended
      */
     public boolean endCallForSubscriber(int subId) {
-        if (mApp.checkCallingOrSelfPermission(permission.MODIFY_PHONE_STATE)
-                != PackageManager.PERMISSION_GRANTED) {
-            Log.i(LOG_TAG, "endCall: called without modify phone state.");
-            EventLog.writeEvent(0x534e4554, "67862398", -1, "");
-            throw new SecurityException("MODIFY_PHONE_STATE permission required.");
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return (Boolean) sendRequest(CMD_END_CALL, null, new Integer(subId));
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+        return false;
     }
 
     public void answerRingingCall() {
-        answerRingingCallForSubscriber(getDefaultSubscription());
+        // Deprecated.
     }
 
     public void answerRingingCallForSubscriber(int subId) {
-        if (DBG) log("answerRingingCall...");
-        // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
-        // but that can probably wait till the big TelephonyManager API overhaul.
-        // For now, protect this call with the MODIFY_PHONE_STATE permission.
-        enforceModifyPermission();
-
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            sendRequest(CMD_ANSWER_RINGING_CALL, null, new Integer(subId));
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
-     * Make the actual telephony calls to implement answerRingingCall().
-     * This should only be called from the main thread of the Phone app.
-     * @see #answerRingingCall
-     *
-     * TODO: it would be nice to return true if we answered the call, or
-     * false if there wasn't actually a ringing incoming call, or some
-     * other error occurred.  (In other words, pass back the return value
-     * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
-     * But that would require calling this method via sendRequest() rather
-     * than sendRequestAsync(), and right now we don't actually *need* that
-     * return value, so let's just return void for now.
-     */
-    private void answerRingingCallInternal(int subId) {
-        final boolean hasRingingCall = !getPhone(subId).getRingingCall().isIdle();
-        if (hasRingingCall) {
-            final boolean hasActiveCall = !getPhone(subId).getForegroundCall().isIdle();
-            final boolean hasHoldingCall = !getPhone(subId).getBackgroundCall().isIdle();
-            if (hasActiveCall && hasHoldingCall) {
-                // Both lines are in use!
-                // TODO: provide a flag to let the caller specify what
-                // policy to use if both lines are in use.  (The current
-                // behavior is hardwired to "answer incoming, end ongoing",
-                // which is how the CALL button is specced to behave.)
-                PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
-                return;
-            } else {
-                // answerCall() will automatically hold the current active
-                // call, if there is one.
-                PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
-                return;
-            }
-        } else {
-            // No call was ringing.
-            return;
-        }
+        // Deprecated
     }
 
     /**
@@ -1835,12 +1825,9 @@
         try {
             if (DBG_LOC) log("getCellLocation: is active user");
             Bundle data = new Bundle();
-            Phone phone = getPhone(mSubscriptionController.getDefaultDataSubId());
-            if (phone == null) {
-                return null;
-            }
-
-            phone.getCellLocation(workSource).fillInNotifierBundle(data);
+            int subId = mSubscriptionController.getDefaultDataSubId();
+            CellLocation cl = (CellLocation) sendRequest(CMD_GET_CELL_LOCATION, workSource, subId);
+            cl.fillInNotifierBundle(data);
             return data;
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -1964,7 +1951,9 @@
         try {
             List<CellInfo> cellInfos = new ArrayList<CellInfo>();
             for (Phone phone : PhoneFactory.getPhones()) {
-                final List<CellInfo> info = phone.getAllCellInfo(workSource);
+                final List<CellInfo> info = (List<CellInfo>) sendRequest(
+                        CMD_GET_ALL_CELL_INFO,
+                        new Pair<Phone, WorkSource>(phone, workSource));
                 if (info != null) cellInfos.addAll(info);
             }
             return cellInfos;
@@ -2593,6 +2582,11 @@
         }
     }
 
+    @Override
+    public int getNetworkSelectionMode(int subId) {
+        return (int) sendRequest(CMD_GET_NETWORK_SELECTION_MODE, null /* argument */, subId);
+    }
+
     /**
      * Returns the network type for a subId
      */
@@ -3012,13 +3006,14 @@
      */
     @Override
     public String nvReadItem(int itemID) {
+        WorkSource workSource = getWorkSource(Binder.getCallingUid());
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, getDefaultSubscription(), "nvReadItem");
 
         final long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) log("nvReadItem: item " + itemID);
-            String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
+            String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID, workSource);
             if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
             return value;
         } finally {
@@ -3036,6 +3031,7 @@
      */
     @Override
     public boolean nvWriteItem(int itemID, String itemValue) {
+        WorkSource workSource = getWorkSource(Binder.getCallingUid());
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, getDefaultSubscription(), "nvWriteItem");
 
@@ -3043,7 +3039,7 @@
         try {
             if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
             Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
-                    new Pair<Integer, String>(itemID, itemValue));
+                    new Pair<Integer, String>(itemID, itemValue), workSource);
             if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail"));
             return success;
         } finally {
@@ -3298,24 +3294,30 @@
         }
     }
 
-    /**
-     * Set the network selection mode to manual with the selected carrier.
+   /**
+     * Ask the radio to connect to the input network and change selection mode to manual.
+     *
+     * @param subId the id of the subscription.
+     * @param operatorInfo the operator information, included the PLMN, long name and short name of
+     * the operator to attach to.
+     * @param persistSelection whether the selection will persist until reboot. If true, only allows
+     * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+     * normal network selection next time.
+     * @return {@code true} on success; {@code true} on any failure.
      */
     @Override
-    public boolean setNetworkSelectionModeManual(int subId, String operatorNumeric,
-            boolean persistSelection) {
+    public boolean setNetworkSelectionModeManual(
+            int subId, OperatorInfo operatorInfo, boolean persistSelection) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setNetworkSelectionModeManual");
-
         final long identity = Binder.clearCallingIdentity();
         try {
-            OperatorInfo operator = new OperatorInfo(
-                /* operatorAlphaLong */ "",
-                /* operatorAlphaShort */ "",
-                    operatorNumeric);
-            if (DBG) log("setNetworkSelectionModeManual: subId:" + subId + " operator:" + operator);
-            ManualNetworkSelectionArgument arg = new ManualNetworkSelectionArgument(operator,
+            ManualNetworkSelectionArgument arg = new ManualNetworkSelectionArgument(operatorInfo,
                     persistSelection);
+            if (DBG) {
+                log("setNetworkSelectionModeManual: subId: " + subId
+                        + " operator: " + operatorInfo);
+            }
             return (Boolean) sendRequest(CMD_SET_NETWORK_SELECTION_MODE_MANUAL, arg, subId);
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -3330,12 +3332,11 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "getCellNetworkScanResults");
 
-        final long identity = Binder.clearCallingIdentity();
+        long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) log("getCellNetworkScanResults: subId " + subId);
-            CellNetworkScanResult result = (CellNetworkScanResult) sendRequest(
+            return (CellNetworkScanResult) sendRequest(
                     CMD_PERFORM_NETWORK_SCAN, null, subId);
-            return result;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -3951,7 +3952,7 @@
 
         int returnValue = 0;
         try {
-            AsyncResult result = (AsyncResult)sendRequest(CMD_INVOKE_OEM_RIL_REQUEST_RAW, oemReq);
+            AsyncResult result = (AsyncResult) sendRequest(CMD_INVOKE_OEM_RIL_REQUEST_RAW, oemReq);
             if(result.exception == null) {
                 if (result.result != null) {
                     byte[] responseData = (byte[])(result.result);
@@ -4356,12 +4357,14 @@
     public void requestModemActivityInfo(ResultReceiver result) {
         enforceModifyPermission();
         ModemActivityInfo ret = null;
+        WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         final long identity = Binder.clearCallingIdentity();
         try {
             synchronized (mLastModemActivityInfo) {
-                ModemActivityInfo info = (ModemActivityInfo) sendRequest(CMD_GET_MODEM_ACTIVITY_INFO,
-                        null);
+                ModemActivityInfo info = (ModemActivityInfo) sendRequest(
+                        CMD_GET_MODEM_ACTIVITY_INFO,
+                        null, workSource);
                 if (isModemActivityInfoValid(info)) {
                     int[] mergedTxTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
                     for (int i = 0; i < mergedTxTimeMs.length; i++) {
@@ -4708,6 +4711,7 @@
     @Override
     public int setAllowedCarriers(int slotIndex, List<CarrierIdentifier> carriers) {
         enforceModifyPermission();
+        WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         if (carriers == null) {
             throw new NullPointerException("carriers cannot be null");
@@ -4716,7 +4720,8 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             int subId = SubscriptionManager.getSubId(slotIndex)[0];
-            int[] retVal = (int[]) sendRequest(CMD_SET_ALLOWED_CARRIERS, carriers, subId);
+            int[] retVal = (int[]) sendRequest(CMD_SET_ALLOWED_CARRIERS, carriers, subId,
+                    workSource);
             return retVal[0];
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -4734,11 +4739,13 @@
     @Override
     public List<CarrierIdentifier> getAllowedCarriers(int slotIndex) {
         enforceReadPrivilegedPermission();
+        WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         final long identity = Binder.clearCallingIdentity();
         try {
             int subId = SubscriptionManager.getSubId(slotIndex)[0];
-            return (List<CarrierIdentifier>) sendRequest(CMD_GET_ALLOWED_CARRIERS, null, subId);
+            return (List<CarrierIdentifier>) sendRequest(CMD_GET_ALLOWED_CARRIERS, null, subId,
+                    workSource);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -4940,10 +4947,12 @@
         enforceModifyPermission();
         Phone phone = PhoneFactory.getPhone(slotIndex);
 
+        WorkSource workSource = getWorkSource(Binder.getCallingUid());
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (phone != null) {
-                phone.setSimPowerState(state);
+                phone.setSimPowerState(state, workSource);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -5023,16 +5032,20 @@
      */
     @Override
     public boolean isDataRoamingEnabled(int subId) {
+        boolean isEnabled = false;
+        final long identity = Binder.clearCallingIdentity();
         try {
             mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
-                    null);
+                    null /* message */);
+            Phone phone = getPhone(subId);
+            isEnabled =  phone != null ? phone.getDataRoamingEnabled() : false;
         } catch (Exception e) {
             TelephonyPermissions.enforeceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
                     mApp, subId, "isDataRoamingEnabled");
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-
-        Phone phone = getPhone(subId);
-        return phone != null ? phone.getDataRoamingEnabled() : false;
+        return isEnabled;
     }
 
 
@@ -5048,12 +5061,17 @@
      */
     @Override
     public void setDataRoamingEnabled(int subId, boolean isEnabled) {
-        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
-                mApp, subId, "setDataRoamingEbaled");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                    mApp, subId, "setDataRoamingEnabled");
 
-        Phone phone = getPhone(subId);
-        if (phone != null) {
-            phone.setDataRoamingEnabled(isEnabled);
+            Phone phone = getPhone(subId);
+            if (phone != null) {
+                phone.setDataRoamingEnabled(isEnabled);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -5261,4 +5279,43 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    @Override
+    public int getCdmaRoamingMode(int subId) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                mApp, subId, "getCdmaRoamingMode");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return (int) sendRequest(CMD_GET_CDMA_ROAMING_MODE, null /* argument */, subId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public boolean setCdmaRoamingMode(int subId, int mode) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                mApp, subId, "setCdmaRoamingMode");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return (boolean) sendRequest(CMD_SET_CDMA_ROAMING_MODE, mode, subId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public boolean setCdmaSubscriptionMode(int subId, int mode) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                mApp, subId, "setCdmaSubscriptionMode");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return (boolean) sendRequest(CMD_SET_CDMA_SUBSCRIPTION_MODE, mode, subId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
 }
diff --git a/src/com/android/phone/PhoneSearchIndexablesProvider.java b/src/com/android/phone/PhoneSearchIndexablesProvider.java
index 57a5b8a..339602d 100644
--- a/src/com/android/phone/PhoneSearchIndexablesProvider.java
+++ b/src/com/android/phone/PhoneSearchIndexablesProvider.java
@@ -42,6 +42,11 @@
 
 public class PhoneSearchIndexablesProvider extends SearchIndexablesProvider {
     private static final String TAG = "PhoneSearchIndexablesProvider";
+
+    private static final String ESIM_LIST_PROFILE_KEY = "esim_list_profile";
+    private static final String ENHANCED_4G_LTE_KEY = "enhanced_4g_lte";
+    private static final String WIFI_CALLING_KEY = "wifi_calling_key";
+
     private UserManager mUserManager;
 
     private static SearchIndexableResource[] INDEXABLE_RES = new SearchIndexableResource[] {
@@ -113,10 +118,15 @@
                 }
             } else {
                 if (isEuiccSettingsHidden()) {
-                    cursor.addRow(createNonIndexableRow("esim_list_profile" /* key */));
+                    cursor.addRow(createNonIndexableRow(ESIM_LIST_PROFILE_KEY));
                 }
+
                 if (isEnhanced4gLteHidden()) {
-                    cursor.addRow(createNonIndexableRow("enhanced_4g_lte" /* key */));
+                    cursor.addRow(createNonIndexableRow(ENHANCED_4G_LTE_KEY));
+                }
+
+                if (isWifiCallingHidden()) {
+                    cursor.addRow(createNonIndexableRow(WIFI_CALLING_KEY));
                 }
             }
 
@@ -141,6 +151,10 @@
         return MobileNetworkSettings.hideEnhanced4gLteSettings(getContext());
     }
 
+    boolean isWifiCallingHidden() {
+        return !MobileNetworkSettings.isWifiCallingEnabled(getContext());
+    }
+
     private Object[] createNonIndexableRow(String key) {
         final Object[] ref = new Object[NON_INDEXABLES_KEYS_COLUMNS.length];
         ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = key;
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 712b8cb..9ede914 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -19,7 +19,6 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.ProgressDialog;
-import android.bluetooth.IBluetoothHeadsetPhone;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
@@ -30,7 +29,6 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
-import android.os.RemoteException;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.VideoProfile;
@@ -106,9 +104,6 @@
     /** Phone state changed event*/
     private static final int PHONE_STATE_CHANGED = -1;
 
-    /** check status then decide whether answerCall */
-    private static final int MSG_CHECK_STATUS_ANSWERCALL = 100;
-
     /** poll phone DISCONNECTING status interval */
     private static final int DISCONNECTING_POLLING_INTERVAL_MS = 200;
 
@@ -125,15 +120,6 @@
      */
     private static final int THEME = com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert;
 
-    private static class FgRingCalls {
-        private Call fgCall;
-        private Call ringing;
-        public FgRingCalls(Call fg, Call ring) {
-            fgCall = fg;
-            ringing = ring;
-        }
-    }
-
     /** USSD information used to aggregate all USSD messages */
     private static AlertDialog sUssdDialog = null;
     private static StringBuilder sUssdMsg = new StringBuilder();
@@ -147,34 +133,6 @@
      * Mute settings for each connection as needed.
      */
     private static class ConnectionHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_CHECK_STATUS_ANSWERCALL:
-                    FgRingCalls frC = (FgRingCalls) msg.obj;
-                    // wait for finishing disconnecting
-                    // before check the ringing call state
-                    if ((frC.fgCall != null) &&
-                        (frC.fgCall.getState() == Call.State.DISCONNECTING) &&
-                        (msg.arg1 < DISCONNECTING_POLLING_TIMES_LIMIT)) {
-                        Message retryMsg =
-                            mConnectionHandler.obtainMessage(MSG_CHECK_STATUS_ANSWERCALL);
-                        retryMsg.arg1 = 1 + msg.arg1;
-                        retryMsg.obj = msg.obj;
-                        mConnectionHandler.sendMessageDelayed(retryMsg,
-                            DISCONNECTING_POLLING_INTERVAL_MS);
-                    // since hangupActiveCall() also accepts the ringing call
-                    // check if the ringing call was already answered or not
-                    // only answer it when the call still is ringing
-                    } else if (frC.ringing.isRinging()) {
-                        if (msg.arg1 == DISCONNECTING_POLLING_TIMES_LIMIT) {
-                            Log.e(LOG_TAG, "DISCONNECTING time out");
-                        }
-                        answerCall(frC.ringing);
-                    }
-                    break;
-            }
-        }
     }
 
     /**
@@ -195,294 +153,6 @@
     }
 
     /**
-     * Answer the currently-ringing call.
-     *
-     * @return true if we answered the call, or false if there wasn't
-     *         actually a ringing incoming call, or some other error occurred.
-     *
-     * @see #answerAndEndHolding(CallManager, Call)
-     * @see #answerAndEndActive(CallManager, Call)
-     */
-    /* package */ static boolean answerCall(Call ringingCall) {
-        log("answerCall(" + ringingCall + ")...");
-        final PhoneGlobals app = PhoneGlobals.getInstance();
-        final CallNotifier notifier = app.notifier;
-
-        final Phone phone = ringingCall.getPhone();
-        final boolean phoneIsCdma = (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA);
-        boolean answered = false;
-        IBluetoothHeadsetPhone btPhone = null;
-
-        if (phoneIsCdma) {
-            // Stop any signalInfo tone being played when a Call waiting gets answered
-            if (ringingCall.getState() == Call.State.WAITING) {
-                notifier.stopSignalInfoTone();
-            }
-        }
-
-        if (ringingCall != null && ringingCall.isRinging()) {
-            if (DBG) log("answerCall: call state = " + ringingCall.getState());
-            try {
-                if (phoneIsCdma) {
-                    if (app.cdmaPhoneCallState.getCurrentCallState()
-                            == CdmaPhoneCallState.PhoneCallState.IDLE) {
-                        // This is the FIRST incoming call being answered.
-                        // Set the Phone Call State to SINGLE_ACTIVE
-                        app.cdmaPhoneCallState.setCurrentCallState(
-                                CdmaPhoneCallState.PhoneCallState.SINGLE_ACTIVE);
-                    } else {
-                        // This is the CALL WAITING call being answered.
-                        // Set the Phone Call State to CONF_CALL
-                        app.cdmaPhoneCallState.setCurrentCallState(
-                                CdmaPhoneCallState.PhoneCallState.CONF_CALL);
-                        // Enable "Add Call" option after answering a Call Waiting as the user
-                        // should be allowed to add another call in case one of the parties
-                        // drops off
-                        app.cdmaPhoneCallState.setAddCallMenuStateAfterCallWaiting(true);
-                    }
-                }
-
-                final boolean isRealIncomingCall = isRealIncomingCall(ringingCall.getState());
-
-                //if (DBG) log("sPhone.acceptCall");
-                app.mCM.acceptCall(ringingCall);
-                answered = true;
-
-                setAudioMode();
-            } catch (CallStateException ex) {
-                Log.w(LOG_TAG, "answerCall: caught " + ex, ex);
-
-                if (phoneIsCdma) {
-                    // restore the cdmaPhoneCallState and btPhone.cdmaSetSecondCallState:
-                    app.cdmaPhoneCallState.setCurrentCallState(
-                            app.cdmaPhoneCallState.getPreviousCallState());
-                    if (btPhone != null) {
-                        try {
-                            btPhone.cdmaSetSecondCallState(false);
-                        } catch (RemoteException e) {
-                            Log.e(LOG_TAG, Log.getStackTraceString(new Throwable()));
-                        }
-                    }
-                }
-            }
-        }
-        return answered;
-    }
-
-    /**
-     * Hangs up all active calls.
-     */
-    static void hangupAllCalls(CallManager cm) {
-        final Call ringing = cm.getFirstActiveRingingCall();
-        final Call fg = cm.getActiveFgCall();
-        final Call bg = cm.getFirstActiveBgCall();
-
-        // We go in reverse order, BG->FG->RINGING because hanging up a ringing call or an active
-        // call can move a bg call to a fg call which would force us to loop over each call
-        // several times.  This ordering works best to ensure we dont have any more calls.
-        if (bg != null && !bg.isIdle()) {
-            hangup(bg);
-        }
-        if (fg != null && !fg.isIdle()) {
-            hangup(fg);
-        }
-        if (ringing != null && !ringing.isIdle()) {
-            hangupRingingCall(fg);
-        }
-    }
-
-    /**
-     * Smart "hang up" helper method which hangs up exactly one connection,
-     * based on the current Phone state, as follows:
-     * <ul>
-     * <li>If there's a ringing call, hang that up.
-     * <li>Else if there's a foreground call, hang that up.
-     * <li>Else if there's a background call, hang that up.
-     * <li>Otherwise do nothing.
-     * </ul>
-     * @return true if we successfully hung up, or false
-     *              if there were no active calls at all.
-     */
-    static boolean hangup(CallManager cm) {
-        boolean hungup = false;
-        Call ringing = cm.getFirstActiveRingingCall();
-        Call fg = cm.getActiveFgCall();
-        Call bg = cm.getFirstActiveBgCall();
-
-        if (!ringing.isIdle()) {
-            log("hangup(): hanging up ringing call");
-            hungup = hangupRingingCall(ringing);
-        } else if (!fg.isIdle()) {
-            log("hangup(): hanging up foreground call");
-            hungup = hangup(fg);
-        } else if (!bg.isIdle()) {
-            log("hangup(): hanging up background call");
-            hungup = hangup(bg);
-        } else {
-            // No call to hang up!  This is unlikely in normal usage,
-            // since the UI shouldn't be providing an "End call" button in
-            // the first place.  (But it *can* happen, rarely, if an
-            // active call happens to disconnect on its own right when the
-            // user is trying to hang up..)
-            log("hangup(): no active call to hang up");
-        }
-        if (DBG) log("==> hungup = " + hungup);
-
-        return hungup;
-    }
-
-    static boolean hangupRingingCall(Call ringing) {
-        if (DBG) log("hangup ringing call");
-        int phoneType = ringing.getPhone().getPhoneType();
-        Call.State state = ringing.getState();
-
-        if (state == Call.State.INCOMING) {
-            // Regular incoming call (with no other active calls)
-            log("hangupRingingCall(): regular incoming call: hangup()");
-            return hangup(ringing);
-        } else {
-            // Unexpected state: the ringing call isn't INCOMING or
-            // WAITING, so there's no reason to have called
-            // hangupRingingCall() in the first place.
-            // (Presumably the incoming call went away at the exact moment
-            // we got here, so just do nothing.)
-            Log.w(LOG_TAG, "hangupRingingCall: no INCOMING or WAITING call");
-            return false;
-        }
-    }
-
-    static boolean hangupActiveCall(Call foreground) {
-        if (DBG) log("hangup active call");
-        return hangup(foreground);
-    }
-
-    static boolean hangupHoldingCall(Call background) {
-        if (DBG) log("hangup holding call");
-        return hangup(background);
-    }
-
-    /**
-     * Used in CDMA phones to end the complete Call session
-     * @param phone the Phone object.
-     * @return true if *any* call was successfully hung up
-     */
-    static boolean hangupRingingAndActive(Phone phone) {
-        boolean hungUpRingingCall = false;
-        boolean hungUpFgCall = false;
-        Call ringingCall = phone.getRingingCall();
-        Call fgCall = phone.getForegroundCall();
-
-        // Hang up any Ringing Call
-        if (!ringingCall.isIdle()) {
-            log("hangupRingingAndActive: Hang up Ringing Call");
-            hungUpRingingCall = hangupRingingCall(ringingCall);
-        }
-
-        // Hang up any Active Call
-        if (!fgCall.isIdle()) {
-            log("hangupRingingAndActive: Hang up Foreground Call");
-            hungUpFgCall = hangupActiveCall(fgCall);
-        }
-
-        return hungUpRingingCall || hungUpFgCall;
-    }
-
-    /**
-     * Trivial wrapper around Call.hangup(), except that we return a
-     * boolean success code rather than throwing CallStateException on
-     * failure.
-     *
-     * @return true if the call was successfully hung up, or false
-     *         if the call wasn't actually active.
-     */
-    static boolean hangup(Call call) {
-        try {
-            CallManager cm = PhoneGlobals.getInstance().mCM;
-
-            if (call.getState() == Call.State.ACTIVE && cm.hasActiveBgCall()) {
-                // handle foreground call hangup while there is background call
-                log("- hangup(Call): hangupForegroundResumeBackground...");
-                cm.hangupForegroundResumeBackground(cm.getFirstActiveBgCall());
-            } else {
-                log("- hangup(Call): regular hangup()...");
-                call.hangup();
-            }
-            return true;
-        } catch (CallStateException ex) {
-            Log.e(LOG_TAG, "Call hangup: caught " + ex, ex);
-        }
-
-        return false;
-    }
-
-    /**
-     * Trivial wrapper around Connection.hangup(), except that we silently
-     * do nothing (rather than throwing CallStateException) if the
-     * connection wasn't actually active.
-     */
-    static void hangup(Connection c) {
-        try {
-            if (c != null) {
-                c.hangup();
-            }
-        } catch (CallStateException ex) {
-            Log.w(LOG_TAG, "Connection hangup: caught " + ex, ex);
-        }
-    }
-
-    static boolean answerAndEndHolding(CallManager cm, Call ringing) {
-        if (DBG) log("end holding & answer waiting: 1");
-        if (!hangupHoldingCall(cm.getFirstActiveBgCall())) {
-            Log.e(LOG_TAG, "end holding failed!");
-            return false;
-        }
-
-        if (DBG) log("end holding & answer waiting: 2");
-        return answerCall(ringing);
-
-    }
-
-    /**
-     * Answers the incoming call specified by "ringing", and ends the currently active phone call.
-     *
-     * This method is useful when's there's an incoming call which we cannot manage with the
-     * current call. e.g. when you are having a phone call with CDMA network and has received
-     * a SIP call, then we won't expect our telephony can manage those phone calls simultaneously.
-     * Note that some types of network may allow multiple phone calls at once; GSM allows to hold
-     * an ongoing phone call, so we don't need to end the active call. The caller of this method
-     * needs to check if the network allows multiple phone calls or not.
-     *
-     * @see #answerCall(Call)
-     * @see InCallScreen#internalAnswerCall()
-     */
-    /* package */ static boolean answerAndEndActive(CallManager cm, Call ringing) {
-        if (DBG) log("answerAndEndActive()...");
-
-        // Unlike the answerCall() method, we *don't* need to stop the
-        // ringer or change audio modes here since the user is already
-        // in-call, which means that the audio mode is already set
-        // correctly, and that we wouldn't have started the ringer in the
-        // first place.
-
-        // hanging up the active call also accepts the waiting call
-        // while active call and waiting call are from the same phone
-        // i.e. both from GSM phone
-        Call fgCall = cm.getActiveFgCall();
-        if (!hangupActiveCall(fgCall)) {
-            Log.w(LOG_TAG, "end active call failed!");
-            return false;
-        }
-
-        mConnectionHandler.removeMessages(MSG_CHECK_STATUS_ANSWERCALL);
-        Message msg = mConnectionHandler.obtainMessage(MSG_CHECK_STATUS_ANSWERCALL);
-        msg.arg1 = 1;
-        msg.obj = new FgRingCalls(fgCall, ringing);
-        mConnectionHandler.sendMessage(msg);
-
-        return true;
-    }
-
-    /**
      * For a CDMA phone, advance the call state upon making a new
      * outgoing call.
      *
@@ -676,87 +346,6 @@
         return builder.toString();
     }
 
-    /**
-     * Wrapper function to control when to send an empty Flash command to the network.
-     * Mainly needed for CDMA networks, such as scenarios when we need to send a blank flash
-     * to the network prior to placing a 3-way call for it to be successful.
-     */
-    static void sendEmptyFlash(Phone phone) {
-        if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
-            Call fgCall = phone.getForegroundCall();
-            if (fgCall.getState() == Call.State.ACTIVE) {
-                // Send the empty flash
-                if (DBG) Log.d(LOG_TAG, "onReceive: (CDMA) sending empty flash to network");
-                switchHoldingAndActive(phone.getBackgroundCall());
-            }
-        }
-    }
-
-    static void swap() {
-        final PhoneGlobals mApp = PhoneGlobals.getInstance();
-        if (!okToSwapCalls(mApp.mCM)) {
-            // TODO: throw an error instead?
-            return;
-        }
-
-        // Swap the fg and bg calls.
-        // In the future we may provide some way for user to choose among
-        // multiple background calls, for now, always act on the first background call.
-        PhoneUtils.switchHoldingAndActive(mApp.mCM.getFirstActiveBgCall());
-    }
-
-    /**
-     * @param heldCall is the background call want to be swapped
-     */
-    static void switchHoldingAndActive(Call heldCall) {
-        log("switchHoldingAndActive()...");
-        try {
-            CallManager cm = PhoneGlobals.getInstance().mCM;
-            if (heldCall.isIdle()) {
-                // no heldCall, so it is to hold active call
-                cm.switchHoldingAndActive(cm.getFgPhone().getBackgroundCall());
-            } else {
-                // has particular heldCall, so to switch
-                cm.switchHoldingAndActive(heldCall);
-            }
-            setAudioMode(cm);
-        } catch (CallStateException ex) {
-            Log.w(LOG_TAG, "switchHoldingAndActive: caught " + ex, ex);
-        }
-    }
-
-    static void mergeCalls() {
-        mergeCalls(PhoneGlobals.getInstance().mCM);
-    }
-
-    static void mergeCalls(CallManager cm) {
-        int phoneType = cm.getFgPhone().getPhoneType();
-        if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
-            log("mergeCalls(): CDMA...");
-            PhoneGlobals app = PhoneGlobals.getInstance();
-            if (app.cdmaPhoneCallState.getCurrentCallState()
-                    == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
-                // Set the Phone Call State to conference
-                app.cdmaPhoneCallState.setCurrentCallState(
-                        CdmaPhoneCallState.PhoneCallState.CONF_CALL);
-
-                // Send flash cmd
-                // TODO: Need to change the call from switchHoldingAndActive to
-                // something meaningful as we are not actually trying to swap calls but
-                // instead are merging two calls by sending a Flash command.
-                log("- sending flash...");
-                switchHoldingAndActive(cm.getFirstActiveBgCall());
-            }
-        } else {
-            try {
-                log("mergeCalls(): calling cm.conference()...");
-                cm.conference(cm.getFirstActiveBgCall());
-            } catch (CallStateException ex) {
-                Log.w(LOG_TAG, "mergeCalls: caught " + ex, ex);
-            }
-        }
-    }
-
     static void separateCall(Connection c) {
         try {
             if (DBG) log("separateCall: " + toLogSafePhoneNumber(c.getAddress()));
@@ -1741,20 +1330,6 @@
         return audioManager.isSpeakerphoneOn();
     }
 
-    static boolean isInEmergencyCall(CallManager cm) {
-        Call fgCall = cm.getActiveFgCall();
-        // isIdle includes checks for the DISCONNECTING/DISCONNECTED state.
-        if(!fgCall.isIdle()) {
-            for (Connection cn : fgCall.getConnections()) {
-                if (PhoneNumberUtils.isLocalEmergencyNumber(PhoneGlobals.getInstance(),
-                        cn.getAddress())) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     /**
      * Get the mute state of foreground phone, which has the current
      * foreground call
diff --git a/src/com/android/phone/RoamingDialogFragment.java b/src/com/android/phone/RoamingDialogFragment.java
index 4d3bd47..384a120 100644
--- a/src/com/android/phone/RoamingDialogFragment.java
+++ b/src/com/android/phone/RoamingDialogFragment.java
@@ -27,14 +27,17 @@
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 
-import com.android.internal.telephony.Phone;
-
 /**
  * A dialog fragment that asks the user if they are sure they want to turn on data roaming
  * to avoid accidental charges.
  */
 public class RoamingDialogFragment extends DialogFragment implements OnClickListener {
 
+    public static final String SUB_ID_KEY = "sub_id_key";
+
+    private CarrierConfigManager mCarrierConfigManager;
+    private int mSubId;
+
     /**
      * The interface we expect a host activity to implement.
      */
@@ -45,15 +48,13 @@
     // the host activity which implements the listening interface
     private RoamingDialogListener mListener;
 
-    private Phone mPhone;
-
-    public void setPhone(Phone phone) {
-        mPhone = phone;
-    }
-
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
+        Bundle args = getArguments();
+        mSubId = args.getInt(SUB_ID_KEY);
+        mCarrierConfigManager = new CarrierConfigManager(context);
+
         // Verify host activity implemented callback interface
         FragmentManager fragmentManager = getFragmentManager();
         Fragment fragment = fragmentManager.findFragmentById(R.id.network_setting_content);
@@ -69,13 +70,10 @@
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
         int title = R.string.roaming_alert_title;
-        if (mPhone != null) {
-            PersistableBundle carrierConfig =
-                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
-            if (carrierConfig != null && carrierConfig.getBoolean(
-                    CarrierConfigManager.KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL)) {
-                title = R.string.roaming_check_price_warning;
-            }
+        PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
+        if (carrierConfig != null && carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL)) {
+            title = R.string.roaming_check_price_warning;
         }
         builder.setMessage(getResources().getString(R.string.roaming_warning))
                 .setTitle(title)
diff --git a/src/com/android/phone/ecc/IsoToEccProtobufRepository.java b/src/com/android/phone/ecc/IsoToEccProtobufRepository.java
new file mode 100644
index 0000000..817ff1d
--- /dev/null
+++ b/src/com/android/phone/ecc/IsoToEccProtobufRepository.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 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.ecc;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Provides a mapping table from country ISO to ECC info. The data is stored in Protocol Buffers
+ * binary format, compressed with GZIP.
+ */
+public class IsoToEccProtobufRepository implements IsoToEccRepository {
+    private static final String LOG_TAG = "EccRepository";
+
+    private Map<String, CountryEccInfo> mEccTable = null;
+
+    @Override
+    @Nullable
+    public CountryEccInfo getCountryEccInfo(@NonNull Context context, String iso)
+            throws IOException {
+        if (TextUtils.isEmpty(iso)) {
+            return null;
+        }
+
+        if (mEccTable == null) {
+            mEccTable = initMappingTable(context);
+        }
+        return mEccTable.get(iso.toUpperCase());
+    }
+
+    private Map<String, CountryEccInfo> initMappingTable(@NonNull Context context)
+            throws IOException {
+        ProtobufEccData.AllInfo allEccData = null;
+
+        long startTime = SystemClock.uptimeMillis();
+        allEccData = parseEccData(new BufferedInputStream(
+                context.getAssets().open("eccdata")));
+        long endTime = SystemClock.uptimeMillis();
+
+        if (allEccData == null) {
+            // Returns an empty table.
+            return new HashMap<>();
+        }
+
+        if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
+            Log.d(LOG_TAG, "Loading time = " + (endTime - startTime) + "ms"
+                    + ", Country Count = " + allEccData.getCountriesCount()
+                    + ", initialized = " + allEccData.isInitialized());
+        }
+
+        // Converts to run-time data from Protobuf data.
+        Map<String, CountryEccInfo> table = new HashMap<>();
+        for (ProtobufEccData.CountryInfo countryData : allEccData.getCountriesList()) {
+            if (countryData.hasIsoCode()) {
+                CountryEccInfo countryInfo = loadCountryEccInfo(countryData);
+                if (countryInfo != null) {
+                    table.put(countryData.getIsoCode().toUpperCase(), countryInfo);
+                }
+            }
+        }
+        return table;
+    }
+
+    private ProtobufEccData.AllInfo parseEccData(InputStream input) throws IOException {
+        return ProtobufEccData.AllInfo.parseFrom(new GZIPInputStream(input));
+    }
+
+    private EccInfo loadEccInfo(String isoCode, ProtobufEccData.EccInfo eccData) {
+        String phoneNumber = eccData.getPhoneNumber().trim();
+        if (phoneNumber.isEmpty()) {
+            Log.i(LOG_TAG, "Discard ecc " + phoneNumber
+                    + " for " + isoCode + " due to empty phone number");
+            return null;
+        }
+
+        ArraySet<EccInfo.Type> eccTypes = new ArraySet<>(eccData.getTypesCount());
+        for (ProtobufEccData.EccInfo.Type typeData : eccData.getTypesList()) {
+            switch (typeData) {
+                case POLICE:
+                    eccTypes.add(EccInfo.Type.POLICE);
+                    break;
+                case AMBULANCE:
+                    eccTypes.add(EccInfo.Type.AMBULANCE);
+                    break;
+                case FIRE:
+                    eccTypes.add(EccInfo.Type.FIRE);
+                    break;
+                default:
+                    // Ignores unknown types.
+            }
+        }
+
+        if (eccTypes.isEmpty()) {
+            Log.i(LOG_TAG, "Discard ecc " + phoneNumber
+                    + " for " + isoCode + " due to no valid type");
+            return null;
+        }
+        return new EccInfo(phoneNumber, eccTypes);
+    }
+
+    private CountryEccInfo loadCountryEccInfo(ProtobufEccData.CountryInfo countryData) {
+        ArrayMap<String, EccInfo> eccInfoMap = new ArrayMap<>(countryData.getEccsCount());
+        for (ProtobufEccData.EccInfo eccData : countryData.getEccsList()) {
+            EccInfo eccInfo = loadEccInfo(countryData.getIsoCode(), eccData);
+            String key = eccInfo.getNumber().trim();
+            EccInfo existentEccInfo = eccInfoMap.get(key);
+            if (existentEccInfo == null) {
+                eccInfoMap.put(key, eccInfo);
+            } else {
+                // Merges types of duplicated ECC info objects.
+                ArraySet<EccInfo.Type> eccTypes = new ArraySet<>(
+                        eccInfo.getTypesCount() + existentEccInfo.getTypesCount());
+                for (EccInfo.Type type : eccInfo.getTypes()) {
+                    eccTypes.add(type);
+                }
+                for (EccInfo.Type type : existentEccInfo.getTypes()) {
+                    eccTypes.add(type);
+                }
+                eccInfoMap.put(key, new EccInfo(eccInfo.getNumber(), eccTypes));
+            }
+        }
+
+        if (eccInfoMap.isEmpty() && !countryData.hasEccFallback()) {
+            Log.i(LOG_TAG, "Discard empty data for " + countryData.getIsoCode());
+            return null;
+        }
+        return new CountryEccInfo(countryData.getEccFallback(), eccInfoMap.values());
+    }
+}
diff --git a/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java b/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java
index 8187664..3ccd4a3 100644
--- a/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java
+++ b/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java
@@ -137,7 +137,7 @@
         prefs.edit()
                 .putString(key + VM_NUMBER_TAG, null)
                 .putInt(key + FWD_SETTINGS_TAG + FWD_SETTINGS_LENGTH_TAG, 0)
-                .commit();
+                .apply();
     }
 
     private static SharedPreferences getPrefs(Context context) {
diff --git a/src/com/android/services/telephony/CdmaConferenceController.java b/src/com/android/services/telephony/CdmaConferenceController.java
index 24c3870..5d987f7 100644
--- a/src/com/android/services/telephony/CdmaConferenceController.java
+++ b/src/com/android/services/telephony/CdmaConferenceController.java
@@ -211,6 +211,7 @@
             // 4) Add the conference to the connection service if it is new.
             if (isNewlyCreated) {
                 Log.d(this, "Adding the conference call");
+                mConference.updateCallRadioTechAfterCreation();
                 mConnectionService.addConference(mConference);
             }
         } else if (conferenceConnections.isEmpty()) {
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index b196d57..5722834 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -640,6 +640,7 @@
 
         setState(mConferenceHost.getState());
         updateStatusHints();
+        putExtras(mConferenceHost.getExtras());
     }
 
     /**
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index e96815c..e9eef46 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -264,6 +264,7 @@
                                 mTelephonyConference, connection);
                         mTelephonyConference.addConnection(connection);
                     }
+                    mTelephonyConference.updateCallRadioTechAfterCreation();
                     mConnectionService.addConference(mTelephonyConference);
                 } else {
                     Log.d(this, "Trigger recalculate later");
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 6904874..440a504 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -36,6 +36,7 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsCallProfile;
 import android.text.TextUtils;
@@ -96,6 +97,7 @@
     private static final int MSG_CDMA_VOICE_PRIVACY_ON = 15;
     private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16;
     private static final int MSG_HANGUP = 17;
+    private static final int MSG_SET_CALL_RADIO_TECH = 18;
 
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
@@ -240,6 +242,31 @@
                     int cause = (int) msg.obj;
                     hangup(cause);
                     break;
+
+                case MSG_SET_CALL_RADIO_TECH:
+                    int vrat = (int) msg.obj;
+                    // Check whether Wi-Fi call tech is changed, it means call radio tech is:
+                    //  a) changed from IWLAN to other value, or
+                    //  b) changed from other value to IWLAN.
+                    //
+                    // In other word, below conditions are all met:
+                    // 1) {@link #getCallRadioTech} is different from new vrat
+                    // 2) Current call radio technology indicates Wi-Fi call, i.e. {@link #isWifi}
+                    //    is true, or new vrat indicates Wi-Fi call.
+                    boolean isWifiTechChange = getCallRadioTech() != vrat
+                            && (isWifi() || vrat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
+
+                    // Step 1) Updates call radio tech firstly, so that afterwards Wi-Fi related
+                    // update actions are taken correctly.
+                    setCallRadioTech(vrat);
+
+                    // Step 2) Handles Wi-Fi call tech change.
+                    if (isWifiTechChange) {
+                        updateConnectionProperties();
+                        updateStatusHints();
+                        refreshDisableAddCall();
+                    }
+                    break;
             }
         }
     };
@@ -421,14 +448,14 @@
         }
 
         /**
-         * Used by {@link com.android.internal.telephony.Connection} to report a change in whether
-         * the call is being made over a wifi network.
+         * Used by {@link com.android.internal.telephony.Connection} to report a change for
+         * the call radio technology.
          *
-         * @param isWifi True if call is made over wifi.
+         * @param vrat the RIL Voice Radio Technology used for current connection.
          */
         @Override
-        public void onWifiChanged(boolean isWifi) {
-            setWifi(isWifi);
+        public void onCallRadioTechChanged(@ServiceState.RilRadioTechnology int vrat) {
+            mHandler.obtainMessage(MSG_SET_CALL_RADIO_TECH, vrat).sendToTarget();
         }
 
         /**
@@ -575,6 +602,11 @@
                 com.android.internal.telephony.Connection newConnection) {
             setOriginalConnection(newConnection);
         }
+
+        @Override
+        public void onIsNetworkEmergencyCallChanged(boolean isEmergencyCall) {
+            setIsNetworkIdentifiedEmergencyCall(isEmergencyCall);
+        }
     };
 
     protected com.android.internal.telephony.Connection mOriginalConnection;
@@ -599,13 +631,6 @@
     private int mOriginalConnectionCapabilities;
 
     /**
-     * Determines if the {@link TelephonyConnection} is using wifi.
-     * This is used when {@link TelephonyConnection#updateConnectionProperties()} is called to
-     * indicate whether a call has the {@link Connection#PROPERTY_WIFI} property.
-     */
-    private boolean mIsWifi;
-
-    /**
      * Determines the audio quality is high for the {@link TelephonyConnection}.
      * This is used when {@link TelephonyConnection#updateConnectionProperties}} is called to
      * indicate whether a call has the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property.
@@ -620,6 +645,15 @@
     private boolean mTreatAsEmergencyCall;
 
     /**
+     * Indicates whether the network has identified this call as an emergency call.  Where
+     * {@link #mTreatAsEmergencyCall} is based on comparing dialed numbers to a list of known
+     * emergency numbers, this property is based on whether the network itself has identified the
+     * call as an emergency call (which can be the case for an incoming call from emergency
+     * services).
+     */
+    private boolean mIsNetworkIdentifiedEmergencyCall;
+
+    /**
      * For video calls, indicates whether the outgoing video for the call can be paused using
      * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
      */
@@ -883,6 +917,13 @@
             Log.v(this, "Holding active call");
             try {
                 Phone phone = mOriginalConnection.getCall().getPhone();
+
+                // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
+                if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+                    ImsPhone imsPhone = (ImsPhone) phone;
+                    imsPhone.holdActiveCall();
+                    return;
+                }
                 Call ringingCall = phone.getRingingCall();
 
                 // Although the method says switchHoldingAndActive, it eventually calls a RIL method
@@ -912,6 +953,13 @@
         Log.v(this, "performUnhold");
         if (Call.State.HOLDING == mConnectionState) {
             try {
+                Phone phone = mOriginalConnection.getCall().getPhone();
+                // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
+                if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+                    ImsPhone imsPhone = (ImsPhone) phone;
+                    imsPhone.unholdHeldCall();
+                    return;
+                }
                 // Here's the deal--Telephony hold/unhold is weird because whenever there exists
                 // more than one call, one of them must always be active. In other words, if you
                 // have an active call and holding call, and you put the active call on hold, it
@@ -1016,7 +1064,7 @@
 
         newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO,
                 hasHighDefAudioProperty());
-        newProperties = changeBitmask(newProperties, PROPERTY_WIFI, mIsWifi);
+        newProperties = changeBitmask(newProperties, PROPERTY_WIFI, isWifi());
         newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL,
                 isExternalConnection());
         newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY,
@@ -1024,6 +1072,8 @@
         newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING_USED,
                 mIsUsingAssistedDialing);
         newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt());
+        newProperties = changeBitmask(newProperties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,
+                isNetworkIdentifiedEmergencyCall());
 
         if (getConnectionProperties() != newProperties) {
             setConnectionProperties(newProperties);
@@ -1103,12 +1153,14 @@
         // Set video state and capabilities
         setVideoState(mOriginalConnection.getVideoState());
         setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
-        setWifi(mOriginalConnection.isWifi());
+        setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall());
         setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
         setVideoProvider(mOriginalConnection.getVideoProvider());
         setAudioQuality(mOriginalConnection.getAudioQuality());
         setTechnologyTypeExtra();
 
+        setCallRadioTech(mOriginalConnection.getCallRadioTech());
+
         // Post update of extras to the handler; extras are updated via the handler to ensure thread
         // safety. The Extras Bundle is cloned in case the original extras are modified while they
         // are being added to mOriginalConnectionExtras in updateExtras.
@@ -1225,7 +1277,7 @@
 
         if (isCurrentVideoCall) {
             return true;
-        } else if (wasVideoCall && mIsWifi && !isVowifiEnabled) {
+        } else if (wasVideoCall && isWifi() && !isVowifiEnabled) {
             return true;
         }
         return false;
@@ -1260,7 +1312,7 @@
             return false;
         }
 
-        if (mIsWifi && !canWifiCallsBeHdAudio) {
+        if (isWifi() && !canWifiCallsBeHdAudio) {
             return false;
         }
 
@@ -1817,21 +1869,31 @@
     }
 
     /**
-     * Sets whether the call is using wifi. Used when rebuilding the capabilities to set or unset
-     * the {@link Connection#PROPERTY_WIFI} property.
-     */
-    public void setWifi(boolean isWifi) {
-        mIsWifi = isWifi;
-        updateConnectionProperties();
-        updateStatusHints();
-        refreshDisableAddCall();
-    }
-
-    /**
      * Whether the call is using wifi.
      */
     boolean isWifi() {
-        return mIsWifi;
+        return getCallRadioTech() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+    }
+
+    /**
+     * Sets whether this call has been identified by the network as an emergency call.
+     * @param isNetworkIdentifiedEmergencyCall {@code true} if the network has identified this call
+     * as an emergency call, {@code false} otherwise.
+     */
+    public void setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall) {
+        Log.d(this, "setIsNetworkIdentifiedEmergencyCall; callId=%s, "
+                + "isNetworkIdentifiedEmergencyCall=%b", getTelecomCallId(),
+                isNetworkIdentifiedEmergencyCall);
+        mIsNetworkIdentifiedEmergencyCall = isNetworkIdentifiedEmergencyCall;
+        updateConnectionProperties();
+    }
+
+    /**
+     * @return {@code true} if the network has identified this call as an emergency call,
+     * {@code false} otherwise.
+     */
+    public boolean isNetworkIdentifiedEmergencyCall() {
+        return mIsNetworkIdentifiedEmergencyCall;
     }
 
     /**
@@ -2011,7 +2073,7 @@
     }
 
     private void updateStatusHints() {
-        if (mIsWifi && getPhone() != null) {
+        if (isWifi() && getPhone() != null) {
             int labelId = isValidRingingCall()
                     ? R.string.status_hint_label_incoming_wifi_call
                     : R.string.status_hint_label_wifi_call;
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 195194c..1d2dfcb 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -1005,7 +1005,9 @@
         }
 
         Queue<Phone> cachedPhones = mEmergencyRetryCache.second;
-        Phone phoneUsed = c.getPhone();
+        // Need to refer default phone considering ImsPhone because
+        // cachedPhones is a list that contains default phones.
+        Phone phoneUsed = c.getPhone().getDefaultPhone();
         if (phoneUsed == null) {
             return;
         }
diff --git a/tests/src/com/android/phone/CdmaOptionsTest.java b/tests/src/com/android/phone/CdmaOptionsTest.java
index 446f2c5..c0c6317 100644
--- a/tests/src/com/android/phone/CdmaOptionsTest.java
+++ b/tests/src/com/android/phone/CdmaOptionsTest.java
@@ -15,53 +15,40 @@
  */
 package com.android.phone;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.when;
+import static com.google.common.truth.Truth.assertThat;
 
-import android.content.Context;
 import android.os.PersistableBundle;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.CarrierConfigManager;
 
-import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
 public class CdmaOptionsTest {
-    @Mock
-    private Phone mMockPhone;
-
-    private CdmaOptions mCdmaOptions;
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = InstrumentationRegistry.getContext();
-        mCdmaOptions = new CdmaOptions(mMockPhone);
+    @Test
+    public void shouldAddApnExpandPreference_doesNotExpandOnGsm() {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_SHOW_APN_SETTING_CDMA_BOOL, true);
+        assertThat(CdmaOptions.shouldAddApnExpandPreference(PhoneConstants.PHONE_TYPE_GSM, bundle))
+                .isFalse();
     }
 
     @Test
-    public void shouldAddApnExpandPreference_doesNotExpandOnGsm() {
-        when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+    public void shouldAddApnExpandPreference_showExpandOnCdma() {
         PersistableBundle bundle = new PersistableBundle();
         bundle.putBoolean(CarrierConfigManager.KEY_SHOW_APN_SETTING_CDMA_BOOL, true);
-        assertFalse(mCdmaOptions.shouldAddApnExpandPreference(bundle));
+        assertThat(CdmaOptions.shouldAddApnExpandPreference(PhoneConstants.PHONE_TYPE_CDMA, bundle))
+                .isTrue();
+    }
 
-        when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_CDMA);
+    @Test
+    public void shouldAddApnExpandPreference_doesNotExpandOnCdmaIfCarrierConfigDisabled() {
+        PersistableBundle bundle = new PersistableBundle();
         bundle.putBoolean(CarrierConfigManager.KEY_SHOW_APN_SETTING_CDMA_BOOL, false);
-        assertFalse(mCdmaOptions.shouldAddApnExpandPreference(bundle));
-
-        when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_CDMA);
-        bundle.putBoolean(CarrierConfigManager.KEY_SHOW_APN_SETTING_CDMA_BOOL, true);
-        assertTrue(mCdmaOptions.shouldAddApnExpandPreference(bundle));
+        assertThat(CdmaOptions.shouldAddApnExpandPreference(PhoneConstants.PHONE_TYPE_CDMA, bundle))
+                .isFalse();
     }
 }
diff --git a/tests/src/com/android/phone/PhoneSearchIndexablesProviderTest.java b/tests/src/com/android/phone/PhoneSearchIndexablesProviderTest.java
index 6b7f825..34e3937 100644
--- a/tests/src/com/android/phone/PhoneSearchIndexablesProviderTest.java
+++ b/tests/src/com/android/phone/PhoneSearchIndexablesProviderTest.java
@@ -51,6 +51,7 @@
     private class PhoneSearchIndexablesTestProvider extends PhoneSearchIndexablesProvider {
         private boolean mIsEuiccSettingsHidden = false;
         private boolean mIsEnhanced4gLteHidden = false;
+        private boolean mIsWifiCallingHidden = false;
 
         @Override boolean isEuiccSettingsHidden() {
             return mIsEuiccSettingsHidden;
@@ -60,6 +61,10 @@
             return mIsEnhanced4gLteHidden;
         }
 
+        @Override boolean isWifiCallingHidden() {
+            return mIsWifiCallingHidden;
+        }
+
         public void setIsEuiccSettingsHidden(boolean isEuiccSettingsHidden) {
             mIsEuiccSettingsHidden = isEuiccSettingsHidden;
         }
@@ -67,6 +72,10 @@
         public void setIsEnhanced4gLteHidden(boolean isEnhanced4gLteHidden) {
             mIsEnhanced4gLteHidden = isEnhanced4gLteHidden;
         }
+
+        public void setIsWifiCallingHidden(boolean isWifiCallingHidden) {
+            mIsWifiCallingHidden = isWifiCallingHidden;
+        }
     }
 
     @Before
@@ -115,25 +124,31 @@
         mProvider.setIsEnhanced4gLteHidden(false /* isEnhanced4gLteHidden */);
         mProvider.setIsEuiccSettingsHidden(false /* isEuiccSettingsHiden */);
         when(mUserManager.isAdminUser()).thenReturn(false);
-        Cursor cursor1 = mProvider.queryNonIndexableKeys(
+        Cursor cursor;
+        cursor = mProvider.queryNonIndexableKeys(
                 SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
-        assertThat(cursor1.getColumnNames()).isEqualTo(
+        assertThat(cursor.getColumnNames()).isEqualTo(
                 SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
-        assertThat(cursor1.getCount()).isEqualTo(16);
+        assertThat(cursor.getCount()).isEqualTo(16);
 
         when(mUserManager.isAdminUser()).thenReturn(true);
-        Cursor cursor2 = mProvider
+        cursor = mProvider
                 .queryNonIndexableKeys(SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
-        assertThat(cursor2.getCount()).isEqualTo(3);
+        assertThat(cursor.getCount()).isEqualTo(3);
 
         mProvider.setIsEuiccSettingsHidden(true /* isEuiccSettingsHidden */);
-        Cursor cursor3 = mProvider
+        cursor = mProvider
                 .queryNonIndexableKeys(SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
-        assertThat(cursor3.getCount()).isEqualTo(4);
+        assertThat(cursor.getCount()).isEqualTo(4);
 
         mProvider.setIsEnhanced4gLteHidden(true /* isEnhanced4gLteHidden */);
-        Cursor cursor4 = mProvider
+        cursor = mProvider
                 .queryNonIndexableKeys(SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
-        assertThat(cursor4.getCount()).isEqualTo(5);
+        assertThat(cursor.getCount()).isEqualTo(5);
+
+        mProvider.setIsWifiCallingHidden(true /* isWifiCallingHidden */);
+        cursor = mProvider
+                .queryNonIndexableKeys(SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
+        assertThat(cursor.getCount()).isEqualTo(6);
     }
 }
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 3bd2716..a18adb8 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -21,7 +21,6 @@
 import static junit.framework.Assert.fail;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -817,6 +816,7 @@
         testServiceState.setEmergencyOnly(isEmergencyOnly);
         when(phone.getServiceState()).thenReturn(testServiceState);
         when(phone.getPhoneId()).thenReturn(phoneId);
+        when(phone.getDefaultPhone()).thenReturn(phone);
         return phone;
     }
 
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 9040257..39e4579 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -74,6 +74,7 @@
                 any(Connection.PostDialListener.class));
         when(mMockPhone.getRingingCall()).thenReturn(mMockCall);
         when(mMockPhone.getContext()).thenReturn(null);
+        when(mMockPhone.getDefaultPhone()).thenReturn(mMockPhone);
         when(mMockCall.getState()).thenReturn(Call.State.ACTIVE);
         when(mMockCall.getPhone()).thenReturn(mMockPhone);
     }
@@ -107,6 +108,11 @@
         mLastConnectionEventExtras.add(extras);
     }
 
+    @Override
+    void clearOriginalConnection() {
+        // Do nothing since the original connection is mock object
+    }
+
     public int getNotifyPhoneAccountChangedCount() {
         return mNotifyPhoneAccountChangedCount;
     }