Merge "Add option to set external temp dir"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f86cdef..c9281d1 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -259,6 +259,21 @@
             android:targetActivity="MobileNetworkSettings" />
 
         <!-- networks setting -->
+        <!-- "Choose network" screen. Used only when the Automatically
+             select network turned off-->
+        <activity android:name="NetworkSelectSettingActivity"
+            android:label="@string/choose_network_title"
+            android:theme="@style/NetworkOperatorsSettingsTheme"
+            android:configChanges="orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <action android:name="android.intent.action.MAIN" />
+                <action android:name="android.settings.NETWORK_OPERATOR_SETTINGS" />
+                <action android:name="android.settings.DATA_ROAMING_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <!-- service to handle network query requests sent to RIL -->
         <service android:name="NetworkQueryService" />
 
diff --git a/res/layout/choose_network.xml b/res/layout/choose_network.xml
new file mode 100644
index 0000000..2c327c56
--- /dev/null
+++ b/res/layout/choose_network.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/choose_network_content"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/choose_network_progress_header.xml b/res/layout/choose_network_progress_header.xml
new file mode 100644
index 0000000..671c297
--- /dev/null
+++ b/res/layout/choose_network_progress_header.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+-->
+
+<FrameLayout
+    android:layout_width="match_parent"
+    android:layout_height="3dp"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <View
+        android:id="@+id/progress_bar_background"
+        style="@style/TrimmedHorizontalProgressBar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorSecondary" />
+    <ProgressBar
+        android:id="@+id/progress_bar_animation"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        style="@style/TrimmedHorizontalProgressBar"
+        android:indeterminate="true" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index cd3f462..0f8b7e9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -283,10 +283,10 @@
     <string name="exception_error">Network or SIM card error.</string>
     <!-- Status message displayed in the "Call settings error" dialog when
          current SS request is modified to a different request by STK CC -->
-    <string name="stk_cc_ss_to_dial_error">SS request modified to DIAL request.</string>
-    <string name="stk_cc_ss_to_ussd_error">SS request modified to USSD request.</string>
-    <string name="stk_cc_ss_to_ss_error">SS request modified to new SS request.</string>
-    <string name="stk_cc_ss_to_dial_video_error">SS request modified to Video DIAL request.</string>
+    <string name="stk_cc_ss_to_dial_error">SS request changed to regular call</string>
+    <string name="stk_cc_ss_to_ussd_error">SS request changed to USSD request</string>
+    <string name="stk_cc_ss_to_ss_error">Changed to new SS request</string>
+    <string name="stk_cc_ss_to_dial_video_error">SS request changed to video call</string>
 
     <!-- Status message displayed in the "Call settings error" dialog when operation fails due to FDN
          [CHAR LIMIT=NONE] -->
@@ -369,7 +369,7 @@
     <!-- Available networks screen, text when no networks are found -->
     <string name="empty_networks_list">No networks found.</string>
     <!-- Available networks screen, toast when an error is encountered when searching for networks -->
-    <string name="network_query_error">Error while searching for networks.</string>
+    <string name="network_query_error">Couldn\'t find networks. Try again.</string>
     <!-- Available networks screen, toast when registering on a specific network -->
     <string name="register_on_network">Registering on <xliff:g id="network">%s</xliff:g>\u2026</string>
     <!-- Available networks screen, toast when SIM card isn't allowed on a network -->
@@ -378,6 +378,8 @@
     <string name="connect_later">Can\'t connect to this network right now. Try again later.</string>
     <!-- Available networks screen, toast when registered on a specific network -->
     <string name="registration_done">Registered on network.</string>
+    <!-- Available networks, toast when user selects automatic selection  -->
+    <string name="already_auto">Already in automatic selection.</string>
     <!-- Available networks screen, name of switch button for whether to select network automatically -->
     <string name="select_automatically">Automatically select network</string>
     <!-- Available networks screen, name of button when user wants to select network manually  -->
@@ -534,7 +536,9 @@
     <!-- Mobile network settings UI: notification message shown when you
          lose data connectivity because you're roaming and you have the
          "data roaming" feature turned off. -->
-    <string name="roaming_reenable_message">You\'ve lost data connectivity because you left your home network with data roaming turned off.</string>
+    <string name="roaming_reenable_message">Data roaming is turned off. Tap to turn on.</string>
+    <!-- Roaming notification tile, notifying lost of roaming data connection -->
+    <string name="roaming_notification_title">Lost mobile data connection</string>
     <!-- Mobile network settings screen, dialog message when user selects the Data roaming check box -->
     <string name="roaming_warning">You may incur significant charges.</string>
     <!-- Mobile network settings screen, dialog message title when user selects the Data roaming check box -->
diff --git a/res/xml/choose_network.xml b/res/xml/choose_network.xml
new file mode 100644
index 0000000..fdf2aae
--- /dev/null
+++ b/res/xml/choose_network.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    android:title="@string/choose_network_title">
+
+    <PreferenceCategory
+        android:key="connected_network_operator_preference"/>
+
+    <PreferenceCategory
+        android:key="network_operators_preference"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/gsm_umts_options.xml b/res/xml/gsm_umts_options.xml
index e3e2617..1df4c84 100644
--- a/res/xml/gsm_umts_options.xml
+++ b/res/xml/gsm_umts_options.xml
@@ -31,6 +31,11 @@
             android:key="button_network_select_key"
             android:title="@string/network_select_title"
             android:persistent="false"/>
+
+        <Preference
+            android:key="button_choose_network_key"
+            android:title="@string/choose_network_title"
+            android:fragment="com.android.phone.NetworkSelectSetting" />
     </com.android.phone.NetworkOperators>
 
     <!--We want separate APN setting from reset of settings because-->
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 8da980c..fd85585 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -281,7 +281,9 @@
                         addPreferencesFromResource(R.xml.cdma_call_privacy);
                     }
                 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
-
+                    if (mPhone.getIccCard() == null || !mPhone.getIccCard().getIccFdnAvailable()) {
+                        prefSet.removePreference(fdnButton);
+                    }
                     if (carrierConfig.getBoolean(
                             CarrierConfigManager.KEY_ADDITIONAL_CALL_SETTING_BOOL)) {
                         addPreferencesFromResource(R.xml.gsm_umts_call_options);
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 2bb8d6f..86f0aa4 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -56,6 +56,7 @@
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 
@@ -777,12 +778,23 @@
     @Override public
     @NonNull
     PersistableBundle getConfigForSubId(int subId) {
+        // TODO(b/73136824): Migrate to TelephonyPermissions#checkCallingOrSelfReadPhoneState.
         try {
             mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE, null);
             // SKIP checking run-time READ_PHONE_STATE since using PRIVILEGED
         } catch (SecurityException e) {
-            mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, null);
+            try {
+                mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, null);
+            } catch (SecurityException securityException) {
+                // If we don't have the runtime permission, but do have carrier privileges, that
+                // suffices for reading phone state.
+                if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+                    throw securityException;
+                }
+                TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(subId, null);
+            }
         }
+
         int phoneId = SubscriptionManager.getPhoneId(subId);
         PersistableBundle retConfig = CarrierConfigManager.getDefaultConfig();
         if (SubscriptionManager.isValidPhoneId(phoneId)) {
diff --git a/src/com/android/phone/CellInfoUtil.java b/src/com/android/phone/CellInfoUtil.java
new file mode 100644
index 0000000..c0409d8
--- /dev/null
+++ b/src/com/android/phone/CellInfoUtil.java
@@ -0,0 +1,169 @@
+/*
+ * 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;
+
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityCdma;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityWcdma;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoCdma;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoWcdma;
+import android.telephony.TelephonyManager;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.OperatorInfo;
+
+/**
+ * Add static Utility functions to get information from the CellInfo object.
+ * TODO: Modify {@link CellInfo} for simplify those functions
+ */
+public final class CellInfoUtil {
+    private static final String TAG = "NetworkSelectSetting";
+
+    private CellInfoUtil() {
+    }
+
+    /**
+     * Get the network type from a CellInfo. Network types include
+     * {@link TelephonyManager#NETWORK_TYPE_LTE}, {@link TelephonyManager#NETWORK_TYPE_UMTS},
+     * {@link TelephonyManager#NETWORK_TYPE_GSM}, {@link TelephonyManager#NETWORK_TYPE_CDMA} and
+     * {@link TelephonyManager#NETWORK_TYPE_UNKNOWN}
+     * @return network types
+     */
+    public static int getNetworkType(CellInfo cellInfo) {
+        if (cellInfo instanceof CellInfoLte) {
+            return TelephonyManager.NETWORK_TYPE_LTE;
+        } else if (cellInfo instanceof CellInfoWcdma) {
+            return TelephonyManager.NETWORK_TYPE_UMTS;
+        } else if (cellInfo instanceof CellInfoGsm) {
+            return TelephonyManager.NETWORK_TYPE_GSM;
+        } else if (cellInfo instanceof CellInfoCdma) {
+            return TelephonyManager.NETWORK_TYPE_CDMA;
+        } else {
+            Log.e(TAG, "Invalid CellInfo type");
+            return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Get signal level as an int from 0..4.
+     * @return Signal strength level
+     */
+    public static int getLevel(CellInfo cellInfo) {
+        if (cellInfo instanceof CellInfoLte) {
+            return ((CellInfoLte) cellInfo).getCellSignalStrength().getLevel();
+        } else if (cellInfo instanceof CellInfoWcdma) {
+            return ((CellInfoWcdma) cellInfo).getCellSignalStrength().getLevel();
+        } else if (cellInfo instanceof CellInfoGsm) {
+            return ((CellInfoGsm) cellInfo).getCellSignalStrength().getLevel();
+        } else if (cellInfo instanceof CellInfoCdma) {
+            return ((CellInfoCdma) cellInfo).getCellSignalStrength().getLevel();
+        } else {
+            Log.e(TAG, "Invalid CellInfo type");
+            return 0;
+        }
+    }
+
+    /**
+     * Wrap a CellIdentity into a CellInfo.
+     */
+    public static CellInfo wrapCellInfoWithCellIdentity(CellIdentity cellIdentity) {
+        if (cellIdentity instanceof CellIdentityLte) {
+            CellInfoLte cellInfo = new CellInfoLte();
+            cellInfo.setCellIdentity((CellIdentityLte) cellIdentity);
+            return cellInfo;
+        } else if (cellIdentity instanceof CellIdentityCdma) {
+            CellInfoCdma cellInfo = new CellInfoCdma();
+            cellInfo.setCellIdentity((CellIdentityCdma) cellIdentity);
+            return cellInfo;
+        }  else if (cellIdentity instanceof CellIdentityWcdma) {
+            CellInfoWcdma cellInfo = new CellInfoWcdma();
+            cellInfo.setCellIdentity((CellIdentityWcdma) cellIdentity);
+            return cellInfo;
+        } else if (cellIdentity instanceof CellIdentityGsm) {
+            CellInfoGsm cellInfo = new CellInfoGsm();
+            cellInfo.setCellIdentity((CellIdentityGsm) cellIdentity);
+            return cellInfo;
+        } else {
+            Log.e(TAG, "Invalid CellInfo type");
+            return null;
+        }
+    }
+
+    /**
+     * Returns the title of the network obtained in the manual search.
+     *
+     * @param cellInfo contains the information of the network.
+     * @return Long Name if not null/empty, otherwise Short Name if not null/empty,
+     * else MCCMNC string.
+     */
+    public static String getNetworkTitle(CellInfo cellInfo) {
+        OperatorInfo oi = getOperatorInfoFromCellInfo(cellInfo);
+
+        if (!TextUtils.isEmpty(oi.getOperatorAlphaLong())) {
+            return oi.getOperatorAlphaLong();
+        } else if (!TextUtils.isEmpty(oi.getOperatorAlphaShort())) {
+            return oi.getOperatorAlphaShort();
+        } else {
+            BidiFormatter bidiFormatter = BidiFormatter.getInstance();
+            return bidiFormatter.unicodeWrap(oi.getOperatorNumeric(), TextDirectionHeuristics.LTR);
+        }
+    }
+
+    /**
+     * Wrap a cell info into an operator info.
+     */
+    public static OperatorInfo getOperatorInfoFromCellInfo(CellInfo cellInfo) {
+        OperatorInfo oi;
+        if (cellInfo instanceof CellInfoLte) {
+            CellInfoLte lte = (CellInfoLte) cellInfo;
+            oi = new OperatorInfo(
+                    (String) lte.getCellIdentity().getOperatorAlphaLong(),
+                    (String) lte.getCellIdentity().getOperatorAlphaShort(),
+                    lte.getCellIdentity().getMobileNetworkOperator());
+        } else if (cellInfo instanceof CellInfoWcdma) {
+            CellInfoWcdma wcdma = (CellInfoWcdma) cellInfo;
+            oi = new OperatorInfo(
+                    (String) wcdma.getCellIdentity().getOperatorAlphaLong(),
+                    (String) wcdma.getCellIdentity().getOperatorAlphaShort(),
+                    wcdma.getCellIdentity().getMobileNetworkOperator());
+        } else if (cellInfo instanceof CellInfoGsm) {
+            CellInfoGsm gsm = (CellInfoGsm) cellInfo;
+            oi = new OperatorInfo(
+                    (String) gsm.getCellIdentity().getOperatorAlphaLong(),
+                    (String) gsm.getCellIdentity().getOperatorAlphaShort(),
+                    gsm.getCellIdentity().getMobileNetworkOperator());
+        } else if (cellInfo instanceof CellInfoCdma) {
+            CellInfoCdma cdma = (CellInfoCdma) cellInfo;
+            oi = new OperatorInfo(
+                    (String) cdma.getCellIdentity().getOperatorAlphaLong(),
+                    (String) cdma.getCellIdentity().getOperatorAlphaShort(),
+                    "" /* operator numeric */);
+        } else {
+            Log.e(TAG, "Invalid CellInfo type");
+            oi = new OperatorInfo("", "", "");
+        }
+        return oi;
+    }
+}
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 4cbfb38..a98c201 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -33,6 +33,7 @@
 import android.os.PersistableBundle;
 import android.provider.Settings;
 import android.telecom.PhoneAccount;
+import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.SubscriptionManager;
@@ -587,10 +588,8 @@
                 playTone(ToneGenerator.TONE_PROP_NACK);
                 return;
             }
-            Intent intent = new Intent(Intent.ACTION_CALL_EMERGENCY);
-            intent.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, mLastNumber, null));
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            startActivity(intent);
+            TelecomManager tm = (TelecomManager) getSystemService(TELECOM_SERVICE);
+            tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, mLastNumber, null), null);
         } else {
             if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
 
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index 47d9f05..99950a8 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -242,6 +242,8 @@
 
         //Information that needs to save into Bundle.
         private static final String EXPAND_ADVANCED_FIELDS = "expand_advanced_fields";
+        //Intent extra to indicate expand all fields.
+        private static final String EXPAND_EXTRA = "expandable";
 
         private SubscriptionManager mSubscriptionManager;
         private TelephonyManager mTelephonyManager;
@@ -669,6 +671,8 @@
 
             if (icicle != null) {
                 mExpandAdvancedFields = icicle.getBoolean(EXPAND_ADVANCED_FIELDS, false);
+            } else if (getActivity().getIntent().getBooleanExtra(EXPAND_EXTRA, false)) {
+                mExpandAdvancedFields = true;
             }
 
             bindNetworkQueryService();
diff --git a/src/com/android/phone/NetworkOperatorPreference.java b/src/com/android/phone/NetworkOperatorPreference.java
new file mode 100644
index 0000000..f29c038
--- /dev/null
+++ b/src/com/android/phone/NetworkOperatorPreference.java
@@ -0,0 +1,127 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.preference.Preference;
+import android.telephony.CellInfo;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.Gravity;
+
+import com.android.settingslib.graph.SignalDrawable;
+
+/**
+ * A Preference represents a network operator in the NetworkSelectSetting fragment.
+ */
+public class NetworkOperatorPreference extends Preference {
+
+    private static final String TAG = "NetworkOperatorPref";
+    private static final boolean DBG = true;
+    // number of signal strength level
+    public static final int NUMBER_OF_LEVELS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+    private CellInfo mCellInfo;
+    private int mLevel = -1;
+
+    // The following constants are used to draw signal icon.
+    private static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
+    private static final int NO_CELL_DATA_CONNECTED_ICON = 0;
+
+    public NetworkOperatorPreference(CellInfo cellinfo, Context context) {
+        super(context);
+        mCellInfo = cellinfo;
+        refresh();
+    }
+
+    public CellInfo getCellInfo() {
+        return mCellInfo;
+    }
+
+    /**
+     * Refresh the NetworkOperatorPreference by updating the title and the icon.
+     */
+    public void refresh() {
+        if (DBG) Log.d(TAG, "refresh the network: " + CellInfoUtil.getNetworkTitle(mCellInfo));
+        setTitle(CellInfoUtil.getNetworkTitle(mCellInfo));
+        int level = CellInfoUtil.getLevel(mCellInfo);
+        if (DBG) Log.d(TAG, "refresh level: " + String.valueOf(level));
+        if (mLevel != level) {
+            mLevel = level;
+            updateIcon(mLevel);
+        }
+    }
+
+    /**
+     * Update the icon according to the input signal strength level.
+     */
+    public void setIcon(int level) {
+        updateIcon(level);
+    }
+
+    private int getIconId(int networkType) {
+        if (networkType == TelephonyManager.NETWORK_TYPE_CDMA) {
+            return R.drawable.signal_strength_1x;
+        } else if (networkType == TelephonyManager.NETWORK_TYPE_LTE) {
+            return R.drawable.signal_strength_lte;
+        } else if (networkType == TelephonyManager.NETWORK_TYPE_UMTS) {
+            return R.drawable.signal_strength_3g;
+        } else if (networkType == TelephonyManager.NETWORK_TYPE_GSM) {
+            return R.drawable.signal_strength_g;
+        } else {
+            return 0;
+        }
+    }
+
+    private void updateIcon(int level) {
+        if (level < 0 || level >= NUMBER_OF_LEVELS) return;
+        Context context = getContext();
+        // Make the signal strength drawable
+        int iconId = 0;
+        if (DBG) Log.d(TAG, "updateIcon level: " + String.valueOf(level));
+        iconId = SignalDrawable.getState(level, NUMBER_OF_LEVELS, false /* cutOut */);
+
+        SignalDrawable signalDrawable = new SignalDrawable(getContext());
+        signalDrawable.setLevel(iconId);
+        signalDrawable.setDarkIntensity(0);
+
+        // Make the network type drawable
+        int iconType = getIconId(CellInfoUtil.getNetworkType(mCellInfo));
+        Drawable networkDrawable =
+                iconType == NO_CELL_DATA_CONNECTED_ICON
+                        ? EMPTY_DRAWABLE
+                        : getContext()
+                        .getResources().getDrawable(iconType, getContext().getTheme());
+
+        // Overlay the two drawables
+        Drawable[] layers = {networkDrawable, signalDrawable};
+        final int iconSize =
+                context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
+
+        LayerDrawable icons = new LayerDrawable(layers);
+        // Set the network type icon at the top left
+        icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
+        // Set the signal strength icon at the bottom right
+        icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
+        icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
+        setIcon(icons);
+    }
+}
diff --git a/src/com/android/phone/NetworkOperators.java b/src/com/android/phone/NetworkOperators.java
index 05cfe42..babd283 100644
--- a/src/com/android/phone/NetworkOperators.java
+++ b/src/com/android/phone/NetworkOperators.java
@@ -18,6 +18,7 @@
 
 import android.app.ProgressDialog;
 import android.content.Context;
+import android.content.Intent;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
@@ -51,17 +52,24 @@
     //String keys for preference lookup
     public static final String BUTTON_NETWORK_SELECT_KEY = "button_network_select_key";
     public static final String BUTTON_AUTO_SELECT_KEY = "button_auto_select_key";
+    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 int mSubId;
     private ProgressDialog mProgressDialog;
 
+    // There's two sets of Auto-Select UI in this class. {@link mNetworkSelect} is used for all
+    // pre-Pixel 3 devices, while {@link mChooseNetwork} is used for all devices after Pixel3.
+    boolean mEnableNewManualSelectNetworkUI;
+
     public NetworkOperators(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -74,10 +82,16 @@
      * Initialize NetworkOperators instance.
      */
     public void initialize() {
-        mNetworkSelect =
-                (NetworkSelectListPreference) findPreference(BUTTON_NETWORK_SELECT_KEY);
-        mAutoSelect =
-                (TwoStatePreference) findPreference(BUTTON_AUTO_SELECT_KEY);
+        mEnableNewManualSelectNetworkUI = getContext().getResources().getBoolean(
+                com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI);
+        mAutoSelect = (TwoStatePreference) findPreference(BUTTON_AUTO_SELECT_KEY);
+        mChooseNetwork = findPreference(BUTTON_CHOOSE_NETWORK_KEY);
+        mNetworkSelect = (NetworkSelectListPreference) findPreference(BUTTON_NETWORK_SELECT_KEY);
+        if (mEnableNewManualSelectNetworkUI) {
+            this.removePreference(mNetworkSelect);
+        } else {
+            this.removePreference(mChooseNetwork);
+        }
         mProgressDialog = new ProgressDialog(getContext());
     }
 
@@ -95,10 +109,22 @@
             mAutoSelect.setOnPreferenceChangeListener(this);
         }
 
-        if (mNetworkSelect != null) {
-            mNetworkSelect.initialize(mSubId, queryService, this, mProgressDialog);
+        if (mEnableNewManualSelectNetworkUI) {
+            if (mChooseNetwork != null) {
+                TelephonyManager telephonyManager = (TelephonyManager)
+                        getContext().getSystemService(Context.TELEPHONY_SERVICE);
+                logd("data connection status " + telephonyManager.getDataState());
+                if (telephonyManager.getDataState() == telephonyManager.DATA_CONNECTED) {
+                    mChooseNetwork.setSummary(telephonyManager.getNetworkOperatorName());
+                } else {
+                    mChooseNetwork.setSummary(R.string.network_disconnected);
+                }
+            }
+        } else {
+            if (mNetworkSelect != null) {
+                mNetworkSelect.initialize(mSubId, queryService, this, mProgressDialog);
+            }
         }
-
         getNetworkSelectionMode();
     }
 
@@ -113,6 +139,7 @@
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         if (preference == mAutoSelect) {
             boolean autoSelect = (Boolean) newValue;
+            logd("onPreferenceChange autoSelect: " + String.valueOf(autoSelect));
             selectNetworkAutomatic(autoSelect);
             MetricsLogger.action(getContext(),
                     MetricsEvent.ACTION_MOBILE_NETWORK_AUTO_SELECT_NETWORK_TOGGLE, autoSelect);
@@ -136,7 +163,7 @@
                         displayNetworkSelectionFailed(ar.exception);
                     } else {
                         if (DBG) logd("automatic network selection: succeeded!");
-                        displayNetworkSelectionSucceeded();
+                        displayNetworkSelectionSucceeded(msg.arg1);
                     }
 
                     break;
@@ -155,8 +182,14 @@
                             if (mAutoSelect != null) {
                                 mAutoSelect.setChecked(autoSelect);
                             }
-                            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.");
@@ -171,10 +204,9 @@
     // 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) {
+                == CommandException.Error.ILLEGAL_SIM_OR_ME) {
             status = getContext().getResources().getString(R.string.not_allowed);
         } else {
             status = getContext().getResources().getString(R.string.connect_later);
@@ -195,8 +227,13 @@
     }
 
     // Used by both mAutoSelect and mNetworkSelect buttons.
-    protected void displayNetworkSelectionSucceeded() {
-        String status = getContext().getResources().getString(R.string.registration_done);
+    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(
@@ -204,9 +241,17 @@
     }
 
     private void selectNetworkAutomatic(boolean autoSelect) {
-        if (mNetworkSelect != null) {
-            mNetworkSelect.setEnabled(!autoSelect);
+        logd("selectNetworkAutomatic: " + String.valueOf(autoSelect));
+        if (mEnableNewManualSelectNetworkUI) {
+            if (mChooseNetwork != null) {
+                mChooseNetwork.setEnabled(!autoSelect);
+            }
+        } else {
+            if (mNetworkSelect != null) {
+                mNetworkSelect.setEnabled(!autoSelect);
+            }
         }
+
         if (autoSelect) {
             if (DBG) logd("select network automatically...");
             showAutoSelectProgressBar();
@@ -216,8 +261,17 @@
             if (phone != null) {
                 phone.setNetworkSelectionModeAutomatic(msg);
             }
-        } else if (mNetworkSelect != null) {
-            mNetworkSelect.onClick();
+        } else {
+            if (mEnableNewManualSelectNetworkUI) {
+                if (mChooseNetwork != null) {
+                    // Open the choose Network page automatically when user turn off the auto-select
+                    openChooseNetworkPage();
+                }
+            } else {
+                if (mNetworkSelect != null) {
+                    mNetworkSelect.onClick();
+                }
+            }
         }
     }
 
@@ -245,8 +299,24 @@
         mProgressDialog.show();
     }
 
+    /**
+     * Open the Choose netwotk page via {@alink NetworkSelectSettingActivity}
+     */
+    public void openChooseNetworkPage() {
+        Intent intent = NetworkSelectSettingActivity.getIntent(getContext(), mPhoneId);
+        getContext().startActivity(intent);
+    }
+
     protected boolean preferenceTreeClick(Preference preference) {
-        return (preference == mAutoSelect || preference == mNetworkSelect);
+        if (mEnableNewManualSelectNetworkUI) {
+            logd("enable New AutoSelectNetwork UI");
+            if (preference == mChooseNetwork) {
+                openChooseNetworkPage();
+            }
+            return (preference == mAutoSelect || preference == mChooseNetwork);
+        } else {
+            return (preference == mAutoSelect || preference == mNetworkSelect);
+        }
     }
 
     private void logd(String msg) {
@@ -256,4 +326,4 @@
     private void loge(String msg) {
         Log.e(LOG_TAG, "[NetworksList] " + msg);
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/phone/NetworkQueryService.java b/src/com/android/phone/NetworkQueryService.java
index 86f4b11..22b5509 100644
--- a/src/com/android/phone/NetworkQueryService.java
+++ b/src/com/android/phone/NetworkQueryService.java
@@ -79,7 +79,7 @@
     private static final boolean INCREMENTAL_RESULTS = true;
     // The parameters below are in seconds
     private static final int SEARCH_PERIODICITY_SEC = 5;
-    private static final int MAX_SEARCH_TIME_SEC = 60;
+    private static final int MAX_SEARCH_TIME_SEC = 300;
     private static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3;
 
     /**
diff --git a/src/com/android/phone/NetworkSelectListPreference.java b/src/com/android/phone/NetworkSelectListPreference.java
index cc54b09..a8f29c9 100644
--- a/src/com/android/phone/NetworkSelectListPreference.java
+++ b/src/com/android/phone/NetworkSelectListPreference.java
@@ -124,7 +124,7 @@
                             logd("manual network selection: succeeded! "
                                     + getNetworkTitle(mCellInfo));
                         }
-                        mNetworkOperators.displayNetworkSelectionSucceeded();
+                        mNetworkOperators.displayNetworkSelectionSucceeded(msg.arg1);
                     }
                     mNetworkOperators.getNetworkSelectionMode();
                     break;
@@ -411,7 +411,7 @@
                 String networkTitle = getNetworkTitle(cellInfo);
                 if (!networkEntriesList.contains(networkTitle)) {
                     networkEntriesList.add(networkTitle);
-                    networkEntryValuesList.add(Integer.toString(networkEntriesList.size() + 1));
+                    networkEntryValuesList.add(getOperatorNumeric(cellInfo));
                 }
             }
             setEntries(networkEntriesList.toArray(new CharSequence[networkEntriesList.size()]));
@@ -510,6 +510,16 @@
     }
 
     /**
+     * Returns the operator numeric (MCCMNC) obtained in the manual search.
+     *
+     * @param cellInfo contains the information of the network.
+     * @return MCCMNC string.
+     */
+    private String getOperatorNumeric(CellInfo cellInfo) {
+        return getOperatorInfoFromCellInfo(cellInfo).getOperatorNumeric();
+    }
+
+    /**
      * Wrap a cell info into an operator info.
      */
     private OperatorInfo getOperatorInfoFromCellInfo(CellInfo cellInfo) {
diff --git a/src/com/android/phone/NetworkSelectSetting.java b/src/com/android/phone/NetworkSelectSetting.java
new file mode 100644
index 0000000..7ef3dea
--- /dev/null
+++ b/src/com/android/phone/NetworkSelectSetting.java
@@ -0,0 +1,634 @@
+/*
+ * 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;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.metrics.LogMaker;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.preference.Preference;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CellIdentity;
+import android.telephony.CellInfo;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.telephony.OperatorInfo;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * "Choose network" settings UI for the Phone app.
+ */
+public class NetworkSelectSetting extends PreferenceFragment {
+
+    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_NETWORK_SCAN_RESULTS = 2;
+    private static final int EVENT_NETWORK_SCAN_ERROR = 3;
+    private static final int EVENT_NETWORK_SCAN_COMPLETED = 4;
+
+    private static final String PREF_KEY_CONNECTED_NETWORK_OPERATOR =
+            "connected_network_operator_preference";
+    private static final String PREF_KEY_NETWORK_OPERATORS = "network_operators_preference";
+
+    // used to add/remove NetworkOperatorsPreference.
+    private PreferenceCategory mNetworkOperatorsPreferences;
+    // used to add/remove connected NetworkOperatorPreference.
+    private PreferenceCategory mConnectedNetworkOperatorsPreference;
+    // manage the progress bar on the top of the page.
+    private View mProgressHeader;
+    private Preference mStatusMessagePreference;
+    private List<CellInfo> mCellInfoList;
+    private int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
+    private ViewGroup mFrameLayout;
+    private NetworkOperatorPreference mSelectedNetworkOperatorPreference;
+    private TelephonyManager mTelephonyManager;
+    private NetworkOperators mNetworkOperators;
+
+    private final Runnable mUpdateNetworkOperatorsRunnable = () -> {
+        updateNetworkOperatorsPreferenceCategory();
+    };
+
+    /**
+     * Create a new instance of this fragment.
+     */
+    public static NetworkSelectSetting newInstance(int phoneId) {
+        Bundle args = new Bundle();
+        args.putInt(NetworkSelectSettingActivity.KEY_PHONE_ID, phoneId);
+        NetworkSelectSetting fragment = new NetworkSelectSetting();
+        fragment.setArguments(args);
+
+        return fragment;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        logd("onCreate");
+        super.onCreate(icicle);
+
+        mPhoneId = getArguments().getInt(NetworkSelectSettingActivity.KEY_PHONE_ID);
+
+        addPreferencesFromResource(R.xml.choose_network);
+        mConnectedNetworkOperatorsPreference =
+                (PreferenceCategory) findPreference(PREF_KEY_CONNECTED_NETWORK_OPERATOR);
+        mNetworkOperatorsPreferences =
+                (PreferenceCategory) findPreference(PREF_KEY_NETWORK_OPERATORS);
+        mStatusMessagePreference = new Preference(getContext());
+        mSelectedNetworkOperatorPreference = null;
+        mTelephonyManager = (TelephonyManager)
+                getContext().getSystemService(Context.TELEPHONY_SERVICE);
+        mNetworkOperators = new NetworkOperators(getContext());
+        setRetainInstance(true);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        logd("onViewCreated");
+        super.onViewCreated(view, savedInstanceState);
+
+        if (getListView() != null) {
+            getListView().setDivider(null);
+        }
+        // Inflate progress bar
+        final Activity activity = getActivity();
+        if (activity != null) {
+            mFrameLayout = activity.findViewById(R.id.choose_network_content);
+            final LayoutInflater inflater = activity.getLayoutInflater();
+            final View pinnedHeader =
+                    inflater.inflate(R.layout.choose_network_progress_header, mFrameLayout, false);
+            mFrameLayout.addView(pinnedHeader);
+            mFrameLayout.setVisibility(View.VISIBLE);
+            mProgressHeader = pinnedHeader.findViewById(R.id.progress_bar_animation);
+            setProgressBarVisible(false);
+        }
+        forceConfigConnectedNetworkOperatorsPreferenceCategory();
+    }
+
+    @Override
+    public void onStart() {
+        if (DBG) logd("onStart");
+        super.onStart();
+
+        // Bind the NetworkQueryService
+        bindNetworkQueryService();
+    }
+
+    /**
+     * Invoked on each preference click in this hierarchy, overrides
+     * PreferenceActivity's implementation.  Used to make sure we track the
+     * preference click events.
+     * Since the connected network operator is either faked (when no data connection) or already
+     * connected, we do not allow user to click the connected network operator.
+     */
+    @Override
+    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
+                                         Preference preference) {
+        if (DBG) logd("User clicked the screen");
+        stopNetworkQuery();
+        setProgressBarVisible(false);
+        if (preference instanceof  NetworkOperatorPreference) {
+            // Refresh the last selected item in case users reselect network.
+            if (mSelectedNetworkOperatorPreference != null) {
+                mSelectedNetworkOperatorPreference.setSummary("");
+            }
+
+            mSelectedNetworkOperatorPreference = (NetworkOperatorPreference) preference;
+            CellInfo cellInfo = mSelectedNetworkOperatorPreference.getCellInfo();
+            if (DBG) logd("User click a NetworkOperatorPreference: " + cellInfo.toString());
+
+            // Send metrics event
+            final LogMaker logMaker = new LogMaker(
+                    MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK)
+                    .setType(MetricsProto.MetricsEvent.TYPE_ACTION);
+            if (CellInfoUtil.getNetworkTitle(cellInfo) != null) {
+                // Since operator list is loaded dynamically from modem, we cannot know which
+                // network user chooses if we only record integer index of newValue. So a new tag
+                // and a string value (network) is added in this MetricsEvent.
+                logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_MOBILE_NETWORK,
+                        CellInfoUtil.getNetworkTitle(cellInfo));
+            }
+            MetricsLogger.action(logMaker);
+
+            // Connect to the network
+            Message msg = mHandler.obtainMessage(EVENT_NETWORK_SELECTION_DONE);
+            Phone phone = PhoneFactory.getPhone(mPhoneId);
+            if (phone != null) {
+                if (DBG) {
+                    logd("Connect to the network: " + CellInfoUtil.getNetworkTitle(cellInfo));
+                }
+                // Set summary as "Connecting" to the selected network.
+                mSelectedNetworkOperatorPreference.setSummary(R.string.network_connecting);
+
+                // Set summary as "Disconnected" to the previously connected network
+                if (mConnectedNetworkOperatorsPreference.getPreferenceCount() > 0) {
+                    NetworkOperatorPreference connectedNetworkOperator = (NetworkOperatorPreference)
+                            (mConnectedNetworkOperatorsPreference.getPreference(0));
+                    if (!CellInfoUtil.getNetworkTitle(cellInfo).equals(
+                            CellInfoUtil.getNetworkTitle(connectedNetworkOperator.getCellInfo()))) {
+                        connectedNetworkOperator.setSummary(R.string.network_disconnected);
+                    }
+                }
+
+                // Select network manually via Phone
+                OperatorInfo operatorInfo = CellInfoUtil.getOperatorInfoFromCellInfo(cellInfo);
+                if (DBG) logd("manually selected network operator: " + operatorInfo.toString());
+                phone.selectNetworkManually(operatorInfo, true, msg);
+                setProgressBarVisible(true);
+                return true;
+            } else {
+                loge("Error selecting network. phone is null.");
+                mSelectedNetworkOperatorPreference = null;
+                return false;
+            }
+
+        } else {
+            preferenceScreen.setEnabled(false);
+            return false;
+        }
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        if (!(getActivity() instanceof NetworkSelectSettingActivity)) {
+            throw new IllegalStateException("Parent activity is not NetworkSelectSettingActivity");
+        }
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (DBG) logd("onStop");
+        getView().removeCallbacks(mUpdateNetworkOperatorsRunnable);
+        stopNetworkQuery();
+        // Unbind the NetworkQueryService
+        unbindNetworkQueryService();
+    }
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            AsyncResult ar;
+            switch (msg.what) {
+                case EVENT_NETWORK_SELECTION_DONE:
+                    if (DBG) logd("network selection done: hide the progress header");
+                    setProgressBarVisible(false);
+
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        mNetworkOperators.displayNetworkSelectionFailed(ar.exception);
+                        // 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! ");
+                        mNetworkOperators.displayNetworkSelectionSucceeded(msg.arg1);
+                        // Set summary as "Connected" to the selected network.
+                        mSelectedNetworkOperatorPreference.setSummary(R.string.network_connected);
+                    }
+                    break;
+
+                case EVENT_NETWORK_SCAN_RESULTS:
+                    List<CellInfo> results = aggregateCellInfoList((List<CellInfo>) msg.obj);
+                    mCellInfoList = new ArrayList<>(results);
+                    if (DBG) logd("after aggregate: " + mCellInfoList.toString());
+                    if (mCellInfoList != null && mCellInfoList.size() != 0) {
+                        updateNetworkOperators();
+                    } else {
+                        addMessagePreference(R.string.empty_networks_list);
+                    }
+
+                    break;
+
+                case EVENT_NETWORK_SCAN_ERROR:
+                    int error = msg.arg1;
+                    if (DBG) logd("error while querying available networks " + error);
+                    stopNetworkQuery();
+                    addMessagePreference(R.string.network_query_error);
+                    break;
+
+                case EVENT_NETWORK_SCAN_COMPLETED:
+                    stopNetworkQuery();
+                    if (DBG) logd("scan complete");
+                    setProgressBarVisible(false);
+                    if (mCellInfoList == null) {
+                        // In case the scan timeout before getting any results
+                        addMessagePreference(R.string.empty_networks_list);
+                    }
+                    break;
+            }
+            return;
+        }
+    };
+
+    private void loadNetworksList() {
+        if (DBG) logd("load networks list...");
+        setProgressBarVisible(true);
+        try {
+            if (mNetworkQueryService != null) {
+                if (DBG) logd("start network query");
+                mNetworkQueryService
+                        .startNetworkQuery(mCallback, mPhoneId, true /* is incremental result */);
+            } else {
+                if (DBG) logd("unable to start network query, mNetworkQueryService is null");
+                addMessagePreference(R.string.network_query_error);
+            }
+        } catch (RemoteException e) {
+            loge("loadNetworksList: exception from startNetworkQuery " + e);
+            addMessagePreference(R.string.network_query_error);
+        }
+    }
+
+    /**
+     * This implementation of INetworkQueryServiceCallback is used to receive
+     * callback notifications from the network query service.
+     */
+    private final INetworkQueryServiceCallback mCallback = new INetworkQueryServiceCallback.Stub() {
+
+        /** Returns the scan results to the user, this callback will be called at lease one time. */
+        public void onResults(List<CellInfo> results) {
+            if (DBG) logd("get scan results.");
+            Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results);
+            msg.sendToTarget();
+        }
+
+        /**
+         * Informs the user that the scan has stopped.
+         *
+         * This callback will be called when the scan is finished or cancelled by the user.
+         * The related NetworkScanRequest will be deleted after this callback.
+         */
+        public void onComplete() {
+            if (DBG) logd("network scan completed.");
+            Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED);
+            msg.sendToTarget();
+        }
+
+        /**
+         * Informs the user that there is some error about the scan.
+         *
+         * This callback will be called whenever there is any error about the scan, and the scan
+         * will be terminated. onComplete() will NOT be called.
+         */
+        public void onError(int error) {
+            if (DBG) logd("get onError callback with error code: " + error);
+            Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR, error, 0 /* arg2 */);
+            msg.sendToTarget();
+        }
+    };
+
+    /**
+     * Updates network operators from {@link INetworkQueryServiceCallback#onResults()}.
+     */
+    private void updateNetworkOperators() {
+        if (DBG) logd("updateNetworkOperators");
+        if (getActivity() != null) {
+            final View view = getView();
+            final Handler handler = view.getHandler();
+            if (handler != null && handler.hasCallbacks(mUpdateNetworkOperatorsRunnable)) {
+                return;
+            }
+            view.post(mUpdateNetworkOperatorsRunnable);
+        }
+    }
+
+    /**
+     * Update the currently available network operators list, which only contains the unregistered
+     * network operators. So if the device has no data and the network operator in the connected
+     * network operator category shows "Disconnected", it will also exist in the available network
+     * operator category for user to select. On the other hand, if the device has data and the
+     * network operator in the connected network operator category shows "Connected", it will not
+     * exist in the available network category.
+     */
+    private void updateNetworkOperatorsPreferenceCategory() {
+        mNetworkOperatorsPreferences.removeAll();
+
+        configConnectedNetworkOperatorsPreferenceCategory();
+        for (int index = 0; index < mCellInfoList.size(); index++) {
+            if (!mCellInfoList.get(index).isRegistered()) {
+                NetworkOperatorPreference pref =
+                        new NetworkOperatorPreference(mCellInfoList.get(index), getContext());
+                pref.setKey(CellInfoUtil.getNetworkTitle(mCellInfoList.get(index)));
+                pref.setOrder(index);
+                mNetworkOperatorsPreferences.addPreference(pref);
+            }
+        }
+    }
+
+    /**
+     * Config the connected network operator preference when the page was created. When user get
+     * into this page, the device might or might not have data connection.
+     *   - If the device has data:
+     *     1. use {@code ServiceState#getNetworkRegistrationStates()} to get the currently
+     *        registered cellIdentity, wrap it into a CellInfo;
+     *     2. set the signal strength level as strong;
+     *     3. use {@link TelephonyManager#getNetworkOperatorName()} to get the title of the
+     *        previously connected network operator, since the CellIdentity got from step 1 only has
+     *        PLMN.
+     *   - If the device has no data, we will remove the connected network operators list from the
+     *     screen.
+     */
+    private void forceConfigConnectedNetworkOperatorsPreferenceCategory() {
+        if (DBG) logd("Force config ConnectedNetworkOperatorsPreferenceCategory");
+        if (mTelephonyManager.getDataState() == mTelephonyManager.DATA_CONNECTED) {
+            // Try to get the network registration states
+            ServiceState ss = mTelephonyManager.getServiceStateForSubscriber(mPhoneId);
+            List<NetworkRegistrationState> networkList =
+                    ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN);
+            if (networkList == null || networkList.size() == 0) {
+                loge("getNetworkRegistrationStates return null");
+                // Remove the connected network operators category
+                removeConnectedNetworkOperatorPreference();
+                return;
+            }
+            CellIdentity cellIdentity = networkList.get(0).getCellIdentity();
+            CellInfo cellInfo = CellInfoUtil.wrapCellInfoWithCellIdentity(cellIdentity);
+            if (cellInfo != null) {
+                if (DBG) logd("Currently registered cell: " + cellInfo.toString());
+                NetworkOperatorPreference pref =
+                        new NetworkOperatorPreference(cellInfo, getContext());
+                pref.setTitle(mTelephonyManager.getNetworkOperatorName());
+                pref.setSummary(R.string.network_connected);
+                // Update the signal strength icon, since the default signalStrength value would be
+                // zero (it would be quite confusing why the connected network has no signal)
+                pref.setIcon(NetworkOperatorPreference.NUMBER_OF_LEVELS - 1);
+
+                mConnectedNetworkOperatorsPreference.addPreference(pref);
+            } else {
+                loge("Invalid CellIfno: " + cellInfo.toString());
+                // Remove the connected network operators category
+                removeConnectedNetworkOperatorPreference();
+            }
+        } else {
+            if (DBG) logd("No currently registered cell");
+            // Remove the connected network operators category
+            removeConnectedNetworkOperatorPreference();
+        }
+    }
+
+    /**
+     * Configure the ConnectedNetworkOperatorsPreferenceCategory. The category only need to be
+     * configured if the category is currently empty or the operator network title of the previous
+     * connected network is different from the new one.
+     */
+    private void configConnectedNetworkOperatorsPreferenceCategory() {
+        if (DBG) logd("config ConnectedNetworkOperatorsPreferenceCategory");
+        // Remove the category if the CellInfo list is empty or does not have registered cell.
+        if (mCellInfoList.size() == 0) {
+            if (DBG) logd("empty cellinfo list");
+            removeConnectedNetworkOperatorPreference();
+        }
+        CellInfo connectedNetworkOperator = null;
+        for (CellInfo cellInfo: mCellInfoList) {
+            if (cellInfo.isRegistered()) {
+                connectedNetworkOperator = cellInfo;
+                break;
+            }
+        }
+        if (connectedNetworkOperator == null) {
+            if (DBG) logd("no registered network");
+            removeConnectedNetworkOperatorPreference();
+            return;
+        }
+
+        // config the category if it is empty.
+        if (mConnectedNetworkOperatorsPreference.getPreferenceCount() == 0) {
+            if (DBG) logd("ConnectedNetworkSelectList is empty, add one");
+            addConnectedNetworkOperatorPreference(connectedNetworkOperator);
+            return;
+        }
+        NetworkOperatorPreference previousConnectedNetworkOperator = (NetworkOperatorPreference)
+                (mConnectedNetworkOperatorsPreference.getPreference(0));
+
+        // config the category if the network title of the previous connected network is different
+        // from the new one.
+        String cTitle = CellInfoUtil.getNetworkTitle(connectedNetworkOperator);
+        String pTitle = CellInfoUtil.getNetworkTitle(
+                previousConnectedNetworkOperator.getCellInfo());
+        if (!cTitle.equals(pTitle)) {
+            if (DBG) logd("reconfig the category: connected network changed");
+            addConnectedNetworkOperatorPreference(connectedNetworkOperator);
+            return;
+        }
+        if (DBG) logd("same network operator is connected, only refresh the connected network");
+        // Otherwise same network operator is connected, only refresh the connected network
+        // operator preference (first and the only one in this category).
+        ((NetworkOperatorPreference) mConnectedNetworkOperatorsPreference.getPreference(0))
+                .refresh();
+        return;
+    }
+
+    /**
+     * Creates a Preference for the given {@link CellInfo} and adds it to the
+     * {@link #mConnectedNetworkOperatorsPreference}.
+     */
+    private void addConnectedNetworkOperatorPreference(CellInfo cellInfo) {
+        if (DBG) logd("addConnectedNetworkOperatorPreference");
+        // Remove the current ConnectedNetworkOperatorsPreference
+        removeConnectedNetworkOperatorPreference();
+        final NetworkOperatorPreference pref =
+                new NetworkOperatorPreference(cellInfo, getContext());
+        pref.setSummary(R.string.network_connected);
+        mConnectedNetworkOperatorsPreference.addPreference(pref);
+        PreferenceScreen preferenceScreen = getPreferenceScreen();
+        preferenceScreen.addPreference(mConnectedNetworkOperatorsPreference);
+    }
+
+    /** Removes all preferences and hide the {@link #mConnectedNetworkOperatorsPreference}. */
+    private void removeConnectedNetworkOperatorPreference() {
+        mConnectedNetworkOperatorsPreference.removeAll();
+        PreferenceScreen preferenceScreen = getPreferenceScreen();
+        preferenceScreen.removePreference(mConnectedNetworkOperatorsPreference);
+    }
+
+    protected void setProgressBarVisible(boolean visible) {
+        if (mProgressHeader != null) {
+            mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE);
+        }
+    }
+
+    private void addMessagePreference(int messageId) {
+        if (DBG) logd("remove callback");
+        getView().removeCallbacks(mUpdateNetworkOperatorsRunnable);
+        setProgressBarVisible(false);
+        if (DBG) logd("addMessagePreference");
+        mStatusMessagePreference.setTitle(messageId);
+        removeConnectedNetworkOperatorPreference();
+        mNetworkOperatorsPreferences.removeAll();
+        mNetworkOperatorsPreferences.addPreference(mStatusMessagePreference);
+    }
+
+    /**
+     * The Scan results may contains several cell infos with different radio technologies and signal
+     * strength for one network operator. Aggregate the CellInfoList by retaining only the cell info
+     * with the strongest signal strength.
+     */
+    private List<CellInfo> aggregateCellInfoList(List<CellInfo> cellInfoList) {
+        if (DBG) logd("before aggregate: " + cellInfoList.toString());
+        Map<String, CellInfo> map = new HashMap<>();
+        for (CellInfo cellInfo: cellInfoList) {
+            String networkTitle = CellInfoUtil.getNetworkTitle(cellInfo);
+            if (cellInfo.isRegistered() || !map.containsKey(networkTitle)) {
+                map.put(networkTitle, cellInfo);
+            } else {
+                if (map.get(networkTitle).isRegistered()
+                        || CellInfoUtil.getLevel(map.get(networkTitle))
+                        > CellInfoUtil.getLevel(cellInfo)) {
+                    // Skip if the stored cellInfo is registered or has higher signal strength level
+                    continue;
+                }
+                // Otherwise replace it with the new CellInfo
+                map.put(networkTitle, cellInfo);
+            }
+        }
+        return new ArrayList<>(map.values());
+    }
+
+    /**
+     * Service connection code for the NetworkQueryService.
+     * Handles the work of binding to a local object so that we can make
+     * the appropriate service calls.
+     */
+
+    /** Local service interface */
+    private INetworkQueryService mNetworkQueryService = null;
+    /** Flag indicating whether we have called bind on the service. */
+    boolean mShouldUnbind;
+
+    /** Service connection */
+    private final ServiceConnection mNetworkQueryServiceConnection = new ServiceConnection() {
+
+        /** Handle the task of binding the local object to the service */
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (DBG) logd("connection created, binding local service.");
+            mNetworkQueryService = ((NetworkQueryService.LocalBinder) service).getService();
+            // Load the network list only when the service is well connected.
+            loadNetworksList();
+        }
+
+        /** Handle the task of cleaning up the local binding */
+        public void onServiceDisconnected(ComponentName className) {
+            if (DBG) logd("connection disconnected, cleaning local binding.");
+            mNetworkQueryService = null;
+        }
+    };
+
+    private void bindNetworkQueryService() {
+        if (DBG) logd("bindNetworkQueryService");
+        getContext().bindService(new Intent(getContext(), NetworkQueryService.class).setAction(
+                NetworkQueryService.ACTION_LOCAL_BINDER),
+                mNetworkQueryServiceConnection, Context.BIND_AUTO_CREATE);
+        mShouldUnbind = true;
+    }
+
+    private void unbindNetworkQueryService() {
+        if (DBG) logd("unbindNetworkQueryService");
+        if (mShouldUnbind) {
+            if (DBG) logd("mShouldUnbind is true");
+            // unbind the service.
+            getContext().unbindService(mNetworkQueryServiceConnection);
+            mShouldUnbind = false;
+        }
+    }
+
+    private void stopNetworkQuery() {
+        // Stop the network query process
+        try {
+            if (mNetworkQueryService != null) {
+                if (DBG) logd("Stop network query");
+                mNetworkQueryService.stopNetworkQuery();
+                mNetworkQueryService.unregisterCallback(mCallback);
+            }
+        } catch (RemoteException e) {
+            loge("Exception from stopNetworkQuery " + e);
+        }
+    }
+
+    private void logd(String msg) {
+        Log.d(TAG, msg);
+    }
+
+    private void loge(String msg) {
+        Log.e(TAG, msg);
+    }
+}
diff --git a/src/com/android/phone/NetworkSelectSettingActivity.java b/src/com/android/phone/NetworkSelectSettingActivity.java
new file mode 100644
index 0000000..2f730ad
--- /dev/null
+++ b/src/com/android/phone/NetworkSelectSettingActivity.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+
+/**
+ * Activity associated with NetworkSelectSetting fragment
+ */
+public class NetworkSelectSettingActivity extends Activity {
+    private static final String TAG = "NetworkSelectSettingActivity";
+    public static final String KEY_PHONE_ID = "phone_id";
+
+    /**
+     * Returns the Android Intent that led to this Activity being created.
+     */
+    public static Intent getIntent(Context context, int phoneId) {
+        Intent intent = new Intent(context, NetworkSelectSettingActivity.class);
+        intent.putExtra(KEY_PHONE_ID, phoneId);
+        return intent;
+    }
+
+    @Override
+    public void onCreate(Bundle savedState) {
+        Log.d(TAG, "onCreate()");
+        super.onCreate(savedState);
+        int phoneId = getIntent().getExtras().getInt(KEY_PHONE_ID);
+        setContentView(R.layout.choose_network);
+
+        FragmentManager fragmentManager = getFragmentManager();
+        Fragment fragment = fragmentManager.findFragmentById(R.id.choose_network_content);
+        if (fragment == null) {
+            fragmentManager.beginTransaction()
+                    .add(R.id.choose_network_content,
+                            NetworkSelectSetting.newInstance(phoneId), TAG)
+                    .commit();
+        }
+    }
+}
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index d36c761..f790c07 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -539,7 +539,7 @@
 
         final Notification.Builder builder = new Notification.Builder(mContext)
                 .setSmallIcon(android.R.drawable.stat_sys_warning)
-                .setContentTitle(mContext.getText(R.string.roaming))
+                .setContentTitle(mContext.getText(R.string.roaming_notification_title))
                 .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
                 .setContentText(contentText)
                 .setChannel(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
@@ -613,24 +613,37 @@
                 PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
         if (TelephonyCapabilities.supportsNetworkSelection(phone)) {
             if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                // get the shared preference of network_selection.
-                // empty is auto mode, otherwise it is the operator alpha name
-                // in case there is no operator name, check the operator numeric
-                SharedPreferences sp =
-                        PreferenceManager.getDefaultSharedPreferences(mContext);
-                String networkSelection =
-                        sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, "");
-                if (TextUtils.isEmpty(networkSelection)) {
-                    networkSelection =
-                            sp.getString(Phone.NETWORK_SELECTION_KEY + subId, "");
+                // if restoring manual selection is controlled by framework, then get network
+                // selection from shared preference, otherwise get from real network indicators.
+                boolean restoreSelection = !mContext.getResources().getBoolean(
+                        com.android.internal.R.bool.skip_restoring_network_selection);
+                String selectedNetworkOperatorName;
+                boolean isManualSelection;
+                if (restoreSelection) {
+                    SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+                    selectedNetworkOperatorName =
+                            sp.getString(Phone.NETWORK_SELECTION_NAME_KEY + subId, "");
+                    // get the shared preference of network_selection.
+                    // empty is auto mode, otherwise it is the operator alpha name
+                    // in case there is no operator name, check the operator numeric
+                    if (TextUtils.isEmpty(selectedNetworkOperatorName)) {
+                        selectedNetworkOperatorName =
+                                sp.getString(Phone.NETWORK_SELECTION_KEY + subId, "");
+                    }
+                    isManualSelection = !TextUtils.isEmpty(selectedNetworkOperatorName);
+                } else {
+                    selectedNetworkOperatorName = phone.getServiceStateTracker().mSS
+                            .getOperatorAlpha();
+                    isManualSelection = phone.getServiceStateTracker().mSS.getIsManualSelection();
                 }
 
-                if (DBG) log("updateNetworkSelection()..." + "state = " +
-                        serviceState + " new network " + networkSelection);
+                if (DBG) {
+                    log("updateNetworkSelection()..." + "state = " + serviceState + " new network "
+                            + (isManualSelection ? selectedNetworkOperatorName : ""));
+                }
 
-                if (serviceState == ServiceState.STATE_OUT_OF_SERVICE
-                        && !TextUtils.isEmpty(networkSelection)) {
-                    showNetworkSelection(networkSelection, subId);
+                if (serviceState == ServiceState.STATE_OUT_OF_SERVICE && isManualSelection) {
+                    showNetworkSelection(selectedNetworkOperatorName, subId);
                     mSelectedUnavailableNotify = true;
                 } else {
                     if (mSelectedUnavailableNotify) {
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index c55ce17..de41481 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -1257,7 +1257,7 @@
     @Override
     public boolean isOffhookForSubscriber(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "isOffhookForSubscriber")) {
+                mApp, subId, callingPackage, "isOffhookForSubscriber")) {
             return false;
         }
 
@@ -1277,7 +1277,7 @@
     @Override
     public boolean isRingingForSubscriber(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "isRingingForSubscriber")) {
+                mApp, subId, callingPackage, "isRingingForSubscriber")) {
             return false;
         }
 
@@ -1297,7 +1297,7 @@
     @Override
     public boolean isIdleForSubscriber(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "isIdleForSubscriber")) {
+                mApp, subId, callingPackage, "isIdleForSubscriber")) {
             return false;
         }
 
@@ -1473,7 +1473,7 @@
     @Override
     public boolean isRadioOnForSubscriber(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "isRadioOnForSubscriber")) {
+                mApp, subId, callingPackage, "isRadioOnForSubscriber")) {
             return false;
         }
         return isRadioOnForSubscriber(subId);
@@ -1786,32 +1786,44 @@
 
     @Override
     public String getImeiForSlot(int slotIndex, String callingPackage) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getImeiForSlot")) {
+        Phone phone = PhoneFactory.getPhone(slotIndex);
+        if (phone == null) {
             return null;
         }
-        Phone phone = PhoneFactory.getPhone(slotIndex);
-        return phone == null ? null : phone.getImei();
+        int subId = phone.getSubId();
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                mApp, subId, callingPackage, "getImeiForSlot")) {
+            return null;
+        }
+        return phone.getImei();
     }
 
     @Override
     public String getMeidForSlot(int slotIndex, String callingPackage) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getMeidForSlot")) {
+        Phone phone = PhoneFactory.getPhone(slotIndex);
+        if (phone == null) {
             return null;
         }
-        Phone phone = PhoneFactory.getPhone(slotIndex);
-        return phone == null ? null : phone.getMeid();
+        int subId = phone.getSubId();
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                mApp, subId, callingPackage, "getMeidForSlot")) {
+            return null;
+        }
+        return phone.getMeid();
     }
 
     @Override
     public String getDeviceSoftwareVersionForSlot(int slotIndex, String callingPackage) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getDeviceSoftwareVersionForSlot")) {
+        Phone phone = PhoneFactory.getPhone(slotIndex);
+        if (phone == null) {
             return null;
         }
-        Phone phone = PhoneFactory.getPhone(slotIndex);
-        return phone == null ? null : phone.getDeviceSvn();
+        int subId = phone.getSubId();
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                mApp, subId, callingPackage, "getDeviceSoftwareVersionForSlot")) {
+            return null;
+        }
+        return phone.getDeviceSvn();
     }
 
     @Override
@@ -1900,7 +1912,7 @@
     @Override
     public int getCdmaEriIconIndexForSubscriber(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getCdmaEriIconIndexForSubscriber")) {
+                mApp, subId, callingPackage, "getCdmaEriIconIndexForSubscriber")) {
             return -1;
         }
         final Phone phone = getPhone(subId);
@@ -1924,7 +1936,7 @@
     @Override
     public int getCdmaEriIconModeForSubscriber(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getCdmaEriIconModeForSubscriber")) {
+                mApp, subId, callingPackage, "getCdmaEriIconModeForSubscriber")) {
             return -1;
         }
         final Phone phone = getPhone(subId);
@@ -1946,7 +1958,7 @@
     @Override
     public String getCdmaEriTextForSubscriber(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getCdmaEriIconTextForSubscriber")) {
+                mApp, subId, callingPackage, "getCdmaEriIconTextForSubscriber")) {
             return null;
         }
         final Phone phone = getPhone(subId);
@@ -2023,10 +2035,16 @@
     public String getVisualVoicemailPackageName(String callingPackage, int subId) {
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getVisualVoicemailPackageName")) {
+                mApp, subId, callingPackage, "getVisualVoicemailPackageName")) {
             return null;
         }
-        return RemoteVvmTaskManager.getRemotePackage(mPhone.getContext(), subId).getPackageName();
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return RemoteVvmTaskManager
+                    .getRemotePackage(mPhone.getContext(), subId).getPackageName();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     @Override
@@ -2118,7 +2136,7 @@
     @Override
     public int getVoiceActivationState(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getVoiceActivationStateForSubscriber")) {
+                mApp, subId, callingPackage, "getVoiceActivationStateForSubscriber")) {
             return TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
         }
         final Phone phone = getPhone(subId);
@@ -2135,7 +2153,7 @@
     @Override
     public int getDataActivationState(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getDataActivationStateForSubscriber")) {
+                mApp, subId, callingPackage, "getDataActivationStateForSubscriber")) {
             return TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
         }
         final Phone phone = getPhone(subId);
@@ -2214,7 +2232,7 @@
     @Override
     public int getNetworkTypeForSubscriber(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getNetworkTypeForSubscriber")) {
+                mApp, subId, callingPackage, "getNetworkTypeForSubscriber")) {
             return TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
 
@@ -2240,7 +2258,7 @@
     @Override
     public int getDataNetworkTypeForSubscriber(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getDataNetworkTypeForSubscriber")) {
+                mApp, subId, callingPackage, "getDataNetworkTypeForSubscriber")) {
             return TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
 
@@ -2258,7 +2276,7 @@
     @Override
     public int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getDataNetworkTypeForSubscriber")) {
+                mApp, subId, callingPackage, "getDataNetworkTypeForSubscriber")) {
             return TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
 
@@ -2309,7 +2327,7 @@
     @Override
     public int getLteOnCdmaModeForSubscriber(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getLteOnCdmaModeForSubscriber")) {
+                mApp, subId, callingPackage, "getLteOnCdmaModeForSubscriber")) {
             return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
         }
 
@@ -2508,6 +2526,7 @@
      * on a particular subscription
      */
     public String[] getForbiddenPlmns(int subId, int appType) {
+        // TODO(b/73884967): Migrate to TelephonyPermissions check.
         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
                 "Requires READ_PHONE_STATE");
         if (appType != TelephonyManager.APPTYPE_USIM && appType != TelephonyManager.APPTYPE_SIM) {
@@ -2612,7 +2631,7 @@
 
     public String[] getPcscfAddress(String apnType, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getPcscfAddress")) {
+                mApp, mPhone.getSubId(), callingPackage, "getPcscfAddress")) {
             return new String[0];
         }
 
@@ -2775,7 +2794,7 @@
     @Override
     public int getCalculatedPreferredNetworkType(String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getCalculatedPreferredNetworkType")) {
+                mApp, mPhone.getSubId(), callingPackage, "getCalculatedPreferredNetworkType")) {
             return RILConstants.PREFERRED_NETWORK_MODE;
         }
 
@@ -3120,7 +3139,7 @@
     public String getLine1NumberForDisplay(int subId, String callingPackage) {
         // This is open to apps with WRITE_SMS.
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneNumber(
-                mApp, callingPackage, "getLine1NumberForDisplay")) {
+                mApp, subId, callingPackage, "getLine1NumberForDisplay")) {
             if (DBG_MERGE) log("getLine1NumberForDisplay returning null due to permission");
             return null;
         }
@@ -3141,7 +3160,7 @@
     @Override
     public String getLine1AlphaTagForDisplay(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getLine1AlphaTagForDisplay")) {
+                mApp, subId, callingPackage, "getLine1AlphaTagForDisplay")) {
             return null;
         }
 
@@ -3155,8 +3174,11 @@
 
     @Override
     public String[] getMergedSubscriberIds(String callingPackage) {
+        // This API isn't public, so no need to provide a valid subscription ID - we're not worried
+        // about carrier-privileged callers not having access.
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getMergedSubscriberIds")) {
+                mApp, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
+                "getMergedSubscriberIds")) {
             return null;
         }
         final Context context = mPhone.getContext();
@@ -3259,8 +3281,13 @@
 
     @Override
     public int getRadioAccessFamily(int phoneId, String callingPackage) {
+        Phone phone = PhoneFactory.getPhone(phoneId);
+        if (phone == null) {
+            return RadioAccessFamily.RAF_UNKNOWN;
+        }
+        int subId = phone.getSubId();
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getRadioAccessFamily")) {
+                mApp, subId, callingPackage, "getRadioAccessFamily")) {
             return RadioAccessFamily.RAF_UNKNOWN;
         }
 
@@ -3276,7 +3303,7 @@
     @Override
     public boolean isVideoCallingEnabled(String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "isVideoCallingEnabled")) {
+                mApp, mPhone.getSubId(), callingPackage, "isVideoCallingEnabled")) {
             return false;
         }
 
@@ -3330,17 +3357,16 @@
      */
     @Override
     public String getDeviceId(String callingPackage) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getDeviceId")) {
-            return null;
-        }
-
         final Phone phone = PhoneFactory.getPhone(0);
-        if (phone != null) {
-            return phone.getDeviceId();
-        } else {
+        if (phone == null) {
             return null;
         }
+        int subId = phone.getSubId();
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                mApp, subId, callingPackage, "getDeviceId")) {
+            return null;
+        }
+        return phone.getDeviceId();
     }
 
     /**
@@ -3549,7 +3575,7 @@
     public ServiceState getServiceStateForSubscriber(int subId, String callingPackage) {
 
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getServiceStateForSubscriber")) {
+                mApp, subId, callingPackage, "getServiceStateForSubscriber")) {
             return null;
         }
 
@@ -3935,7 +3961,7 @@
     @Override
     public List<ClientRequestStats> getClientRequestStats(String callingPackage, int subId) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, callingPackage, "getClientRequestStats")) {
+                mApp, subId, callingPackage, "getClientRequestStats")) {
             return null;
         }
 
@@ -4055,7 +4081,12 @@
             }
 
             infos[i] = new UiccSlotInfo(
-                    slot.isActive(), slot.isEuicc(), cardId, cardState, slot.getPhoneId());
+                    slot.isActive(),
+                    slot.isEuicc(),
+                    cardId,
+                    cardState,
+                    slot.getPhoneId(),
+                    slot.isExtendedApduSupported());
         }
         return infos;
     }
diff --git a/src/com/android/phone/PhoneSearchIndexablesProvider.java b/src/com/android/phone/PhoneSearchIndexablesProvider.java
index 9c5f354..75e6bab 100644
--- a/src/com/android/phone/PhoneSearchIndexablesProvider.java
+++ b/src/com/android/phone/PhoneSearchIndexablesProvider.java
@@ -99,8 +99,8 @@
         MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS);
 
         if (!mUserManager.isAdminUser()) {
-            final String[] values = new String[]{"preferred_network_mode_key", "button_roaming_key",
-                    "cdma_lte_data_service_key", "enabled_networks_key", "enhanced_4g_lte",
+            final String[] values = new String[]{"preferred_network_mode_key",
+                    "button_roaming_key", "cdma_lte_data_service_key", "enhanced_4g_lte",
                     "button_apn_key", "button_network_select_key", "carrier_settings_key",
                     "cdma_system_select_key", "esim_list_profile", "mobile_data_enable",
                     "data_usage_summary", "wifi_calling_key", "video_calling_key"};
@@ -115,6 +115,11 @@
                 cursor.addRow(createNonIndexableRow("enhanced_4g_lte" /* key */));
             }
         }
+        // enabled_networks button and preferred_network_mode button share the same title
+        // "Preferred network type"and are mutual exclusive. Thus we remove one from search
+        // result to avoid duplicate search result.
+        // TODO: b/63381516 all hidden buttons should dynamically be removed from search result.
+        cursor.addRow(createNonIndexableRow("enabled_networks_key" /* key */));
         cursor.addRow(createNonIndexableRow("carrier_settings_euicc_key" /* key */));
         cursor.addRow(createNonIndexableRow("advanced_options" /* key */));
         return cursor;
diff --git a/src/com/android/phone/vvm/RemoteVvmTaskManager.java b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
index 4fc8c57..cf5011e 100644
--- a/src/com/android/phone/vvm/RemoteVvmTaskManager.java
+++ b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
@@ -119,6 +119,8 @@
         TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
         List<String> packages = new ArrayList<>();
         packages.add(telecomManager.getDefaultDialerPackage());
+        // TODO(b/73136824): Check permissions in the calling function and avoid relying on the
+        // binder caller's permissions to access the carrier config.
         PersistableBundle carrierConfig = context
                 .getSystemService(CarrierConfigManager.class).getConfigForSubId(subId);
         packages.add(
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 4bae058..971dd7b 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -389,5 +389,8 @@
                         android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY)));
         connection.destroy();
         mImsConferences.add(conference);
+        // If one of the participants failed to join the conference, recalculate will set the
+        // conferenceable connections for the conference to show merge calls option.
+        recalculateConferenceable();
     }
 }
diff --git a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
index 7bd0f70..6ddebb8 100644
--- a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
+++ b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
@@ -31,8 +31,9 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.telephony.MbmsDownloadSession;
+import android.telephony.mbms.DownloadProgressListener;
 import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.mbms.DownloadStatusListener;
 import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.FileServiceInfo;
 import android.telephony.mbms.MbmsDownloadSessionCallback;
@@ -143,9 +144,16 @@
         }
 
         @Override
-        public int registerStateCallback(DownloadRequest downloadRequest,
-                DownloadStateCallback callback) throws RemoteException {
-            mDownloadStateCallbacks.put(downloadRequest, callback);
+        public int addStatusListener(DownloadRequest downloadRequest,
+                DownloadStatusListener callback) throws RemoteException {
+            mDownloadStatusCallbacks.put(downloadRequest, callback);
+            return MbmsErrors.SUCCESS;
+        }
+
+        @Override
+        public int addProgressListener(DownloadRequest downloadRequest,
+                DownloadProgressListener callback) throws RemoteException {
+            mDownloadProgressCallbacks.put(downloadRequest, callback);
             return MbmsErrors.SUCCESS;
         }
 
@@ -183,7 +191,9 @@
     // A map of app-identifiers to (maps of service-ids to sets of temp file uris in use)
     private final Map<FrontendAppIdentifier, Map<String, Set<Uri>>> mTempFilesInUse =
             new ConcurrentHashMap<>();
-    private final Map<DownloadRequest, DownloadStateCallback> mDownloadStateCallbacks =
+    private final Map<DownloadRequest, DownloadStatusListener> mDownloadStatusCallbacks =
+            new ConcurrentHashMap<>();
+    private final Map<DownloadRequest, DownloadProgressListener> mDownloadProgressCallbacks =
             new ConcurrentHashMap<>();
 
     private HandlerThread mHandlerThread;
@@ -334,13 +344,14 @@
             UriPathPair tempFile, FileInfo fileToDownload) {
         int result = MbmsDownloadSession.RESULT_SUCCESSFUL;
         // Test Callback
-        DownloadStateCallback c = mDownloadStateCallbacks.get(request);
-        if (c != null) {
-            c.onProgressUpdated(request, fileToDownload, 0, 10, 0, 10);
+        DownloadStatusListener statusListener = mDownloadStatusCallbacks.get(request);
+        DownloadProgressListener progressListener = mDownloadProgressCallbacks.get(request);
+        if (progressListener != null) {
+            progressListener.onProgressUpdated(request, fileToDownload, 0, 10, 0, 10);
         }
         // Test Callback
-        if (c != null) {
-            c.onStateUpdated(request, fileToDownload,
+        if (statusListener != null) {
+            statusListener.onStatusUpdated(request, fileToDownload,
                     MbmsDownloadSession.STATUS_ACTIVELY_DOWNLOADING);
         }
         try {
@@ -367,8 +378,8 @@
             result = MbmsDownloadSession.RESULT_CANCELLED;
         }
         // Test Callback
-        if (c != null) {
-            c.onProgressUpdated(request, fileToDownload, 10, 10, 10, 10);
+        if (progressListener != null) {
+            progressListener.onProgressUpdated(request, fileToDownload, 10, 10, 10, 10);
         }
         // Take a round-trip through the download request serialization to exercise it
         DownloadRequest request1 = DownloadRequest.Builder.fromSerializedRequest(
diff --git a/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java b/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java
index 8b8a67d..acdbdcb 100644
--- a/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java
+++ b/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java
@@ -27,8 +27,9 @@
 import android.support.v7.widget.RecyclerView;
 import android.telephony.MbmsDownloadSession;
 import android.telephony.SubscriptionManager;
+import android.telephony.mbms.DownloadProgressListener;
 import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.mbms.DownloadStatusListener;
 import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.FileServiceInfo;
 import android.telephony.mbms.MbmsDownloadSessionCallback;
@@ -306,8 +307,8 @@
                         "No DownloadRequest Pending for progress...", Toast.LENGTH_SHORT).show();
                 return;
             }
-            mDownloadManager.registerStateCallback(req, sInstance.getMainThreadHandler()::post,
-                    new DownloadStateCallback(DownloadStateCallback.PROGRESS_UPDATES) {
+            mDownloadManager.addProgressListener(req, sInstance.getMainThreadHandler()::post,
+                    new DownloadProgressListener() {
                         @Override
                         public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
                                 int currentDownloadSize, int fullDownloadSize,
@@ -317,16 +318,6 @@
                                             + " fd: " + fullDownloadSize, Toast.LENGTH_SHORT)
                                     .show();
                         }
-
-                        @Override
-                        public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
-                                @MbmsDownloadSession.DownloadStatus int state) {
-                            // only registered for state callback, this shouldn't happen!
-                            Toast.makeText(EmbmsTestDownloadApp.this,
-                                    "State ERROR: received state update for callback that didn't"
-                                            + " filter it",
-                                    Toast.LENGTH_SHORT).show();
-                        }
                     });
         });
 
@@ -344,21 +335,10 @@
                         "No DownloadRequest Pending for state...", Toast.LENGTH_SHORT).show();
                 return;
             }
-            mDownloadManager.registerStateCallback(req, sInstance.getMainThreadHandler()::post,
-                    new DownloadStateCallback(DownloadStateCallback.STATE_UPDATES) {
+            mDownloadManager.addStatusListener(req, sInstance.getMainThreadHandler()::post,
+                    new DownloadStatusListener() {
                         @Override
-                        public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
-                                int currentDownloadSize, int fullDownloadSize,
-                                int currentDecodedSize, int fullDecodedSize) {
-                            // only registered for state callback, this shouldn't happen!
-                            Toast.makeText(EmbmsTestDownloadApp.this,
-                                    "Progress ERROR: received progress update for"
-                                            + " callback that didn't "
-                                            + "filter it", Toast.LENGTH_SHORT).show();
-                        }
-
-                        @Override
-                        public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+                        public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
                                 @MbmsDownloadSession.DownloadStatus int state) {
                             Toast.makeText(EmbmsTestDownloadApp.this,
                                     "State Updated (" + fileInfo + ") state: " + state,
@@ -381,8 +361,20 @@
                         "No DownloadRequest Pending for state...", Toast.LENGTH_SHORT).show();
                 return;
             }
-            mDownloadManager.registerStateCallback(req, sInstance.getMainThreadHandler()::post,
-                    new DownloadStateCallback() {
+
+            mDownloadManager.addStatusListener(req, sInstance.getMainThreadHandler()::post,
+                    new DownloadStatusListener() {
+                        @Override
+                        public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
+                                @MbmsDownloadSession.DownloadStatus int state) {
+                            Toast.makeText(EmbmsTestDownloadApp.this,
+                                    "State Updated (" + fileInfo + ") state: " + state,
+                                    Toast.LENGTH_SHORT).show();
+                        }
+                    });
+
+            mDownloadManager.addProgressListener(req, sInstance.getMainThreadHandler()::post,
+                    new DownloadProgressListener() {
                         @Override
                         public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
                                 int currentDownloadSize, int fullDownloadSize,
@@ -392,14 +384,6 @@
                                             + " fd: " + fullDownloadSize, Toast.LENGTH_SHORT)
                                     .show();
                         }
-
-                        @Override
-                        public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
-                                @MbmsDownloadSession.DownloadStatus int state) {
-                            Toast.makeText(EmbmsTestDownloadApp.this,
-                                    "State Updated (" + fileInfo + ") state: " + state,
-                                    Toast.LENGTH_SHORT).show();
-                        }
                     });
         });
     }
diff --git a/tests/src/com/android/phone/CallFeaturesSettingTest.java b/tests/src/com/android/phone/CallFeaturesSettingTest.java
new file mode 100644
index 0000000..816642a
--- /dev/null
+++ b/tests/src/com/android/phone/CallFeaturesSettingTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.FlakyTest;
+import android.support.test.rule.ActivityTestRule;
+
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.Field;
+
+public class CallFeaturesSettingTest {
+    @Mock
+    Phone mMockPhone;
+    @Mock
+    IccCard mMockIccCard;
+    @Rule
+    public ActivityTestRule<CallFeaturesSetting> mRule =
+            new ActivityTestRule<>(CallFeaturesSetting.class);
+    private CallFeaturesSetting mActivity;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mActivity = mRule.getActivity();
+    }
+
+    @FlakyTest
+    @Test
+    public void onResume_fdnIsAvailable_shouldShowFdnMenu() throws NoSuchFieldException,
+            IllegalAccessException {
+        when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+        when(mMockPhone.getIccCard()).thenReturn(mMockIccCard);
+        when(mMockIccCard.getIccFdnAvailable()).thenReturn(true);
+        getField("mPhone").set(mActivity, mMockPhone);
+
+        mActivity.onResume();
+
+        // Check the FDN menu is displayed.
+        onView(withText(R.string.fdn)).check(matches(isDisplayed()));
+    }
+
+    @FlakyTest
+    @Test
+    public void onResume_iccCardIsNull_shouldNotShowFdnMenu() throws NoSuchFieldException,
+            IllegalAccessException {
+        when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+        when(mMockPhone.getIccCard()).thenReturn(null);
+        getField("mPhone").set(mActivity, mMockPhone);
+
+        mActivity.onResume();
+
+        // Check the FDN menu is not displayed.
+        onView(withText(R.string.fdn)).check(doesNotExist());
+    }
+
+    @FlakyTest
+    @Test
+    public void onResume_fdnIsNotAvailable_shouldNotShowFdnMenu() throws NoSuchFieldException,
+            IllegalAccessException {
+        when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+        when(mMockPhone.getIccCard()).thenReturn(mMockIccCard);
+        when(mMockIccCard.getIccFdnAvailable()).thenReturn(false);
+        getField("mPhone").set(mActivity, mMockPhone);
+
+        mActivity.onResume();
+
+        // Check the FDN menu is not displayed.
+        onView(withText(R.string.fdn)).check(doesNotExist());
+    }
+
+    private Field getField(String fieldName) throws NoSuchFieldException {
+        Field field = mActivity.getClass().getDeclaredField(fieldName);
+        field.setAccessible(true);
+        return field;
+    }
+}
diff --git a/tests/src/com/android/phone/PhoneSearchIndexablesProviderTest.java b/tests/src/com/android/phone/PhoneSearchIndexablesProviderTest.java
index 2176d6b..6b7f825 100644
--- a/tests/src/com/android/phone/PhoneSearchIndexablesProviderTest.java
+++ b/tests/src/com/android/phone/PhoneSearchIndexablesProviderTest.java
@@ -124,16 +124,16 @@
         when(mUserManager.isAdminUser()).thenReturn(true);
         Cursor cursor2 = mProvider
                 .queryNonIndexableKeys(SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
-        assertThat(cursor2.getCount()).isEqualTo(2);
+        assertThat(cursor2.getCount()).isEqualTo(3);
 
         mProvider.setIsEuiccSettingsHidden(true /* isEuiccSettingsHidden */);
         Cursor cursor3 = mProvider
                 .queryNonIndexableKeys(SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
-        assertThat(cursor3.getCount()).isEqualTo(3);
+        assertThat(cursor3.getCount()).isEqualTo(4);
 
         mProvider.setIsEnhanced4gLteHidden(true /* isEnhanced4gLteHidden */);
         Cursor cursor4 = mProvider
                 .queryNonIndexableKeys(SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS);
-        assertThat(cursor4.getCount()).isEqualTo(4);
+        assertThat(cursor4.getCount()).isEqualTo(5);
     }
 }