Merge "Update activity titles for fragments without preference screen."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4bc2c6d..bc63d14 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1031,10 +1031,6 @@
             <intent-filter android:priority="3">
                 <action android:name="com.android.settings.action.SETTINGS" />
             </intent-filter>
-            <meta-data android:name="com.android.settings.category"
-                android:value="com.android.settings.category.ia.development" />
-            <meta-data android:name="com.android.settings.summary"
-                android:resource="@string/summary_empty" />
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                 android:value="com.android.settings.applications.ProcessStatsSummary" />
         </activity>
diff --git a/res/color/sliding_tab_title_text_color.xml b/res/color/sliding_tab_title_text_color.xml
new file mode 100644
index 0000000..d6bfbcc
--- /dev/null
+++ b/res/color/sliding_tab_title_text_color.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="false"
+          android:color="?android:attr/textColorSecondary"/>
+    <item android:color="?android:attr/textColorPrimary"/>
+</selector>
diff --git a/res/layout/sliding_tab_indicator_view.xml b/res/layout/sliding_tab_indicator_view.xml
new file mode 100644
index 0000000..b594c8b
--- /dev/null
+++ b/res/layout/sliding_tab_indicator_view.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<View
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/sliding_tab_selected_indicator"
+    android:layout_width="wrap_content"
+    android:layout_height="@dimen/pager_tabs_selected_indicator_height"
+    android:background="?android:attr/colorAccent"
+    android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent" />
diff --git a/res/layout/sliding_tab_title_view.xml b/res/layout/sliding_tab_title_view.xml
new file mode 100644
index 0000000..5dead02
--- /dev/null
+++ b/res/layout/sliding_tab_title_view.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 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.
+-->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dp"
+    android:layout_weight="1"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:ellipsize="end"
+    android:fontFamily="sans-serif-medium"
+    android:gravity="center"
+    android:maxLines="1"
+    android:padding="@dimen/pager_tabs_title_padding"
+    android:textColor="@color/sliding_tab_title_text_color"
+    android:textAllCaps="true"
+    android:theme="?android:attr/actionBarTheme"/>
diff --git a/res/layout/wifi_calling_settings_preferences.xml b/res/layout/wifi_calling_settings_preferences.xml
new file mode 100644
index 0000000..4e64f40
--- /dev/null
+++ b/res/layout/wifi_calling_settings_preferences.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/tabs_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <com.android.settings.widget.SwitchBar
+        android:id="@+id/switch_bar"
+        android:layout_height="?android:attr/actionBarSize"
+        android:layout_width="match_parent"
+        android:background="@drawable/switchbar_background"
+        android:theme="?attr/switchBarTheme" />
+
+    <FrameLayout
+        android:id="@android:id/tabcontent"
+        android:layout_width="0dip"
+        android:layout_height="0dip" />
+
+    <FrameLayout
+        android:id="@+id/prefs_container"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:smoothScrollbar="false" />
+
+</LinearLayout>
diff --git a/res/layout/wifi_calling_settings_tabs.xml b/res/layout/wifi_calling_settings_tabs.xml
new file mode 100644
index 0000000..1e27b47
--- /dev/null
+++ b/res/layout/wifi_calling_settings_tabs.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/tabs_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <com.android.settings.widget.SlidingTabLayout
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/sliding_tabs"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:scrollbars="none"
+            android:fillViewport="true"/>
+
+        <com.android.settings.widget.RtlCompatibleViewPager
+            android:id="@+id/view_pager"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+
+</LinearLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2d90e2f..fdb9c32 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -51,6 +51,8 @@
     <dimen name="appwidget_min_height">40dip</dimen>
 
     <dimen name="pager_tabs_padding">0dp</dimen>
+    <dimen name="pager_tabs_title_padding">16dp</dimen>
+    <dimen name="pager_tabs_selected_indicator_height">3dp</dimen>
 
     <!-- Minimum width for the popup for updating a user's photo. -->
     <dimen name="update_user_photo_popup_min_width">300dip</dimen>
diff --git a/res/xml/development_prefs.xml b/res/xml/development_prefs.xml
index e60dbe3..20482b7 100644
--- a/res/xml/development_prefs.xml
+++ b/res/xml/development_prefs.xml
@@ -18,6 +18,14 @@
         xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
         android:key="development_prefs_screen"
         android:title="@string/development_settings_title">
+
+    <Preference
+        android:key="memory"
+        android:icon="@drawable/ic_settings_memory"
+        android:title="@string/memory_settings_title"
+        android:summary="@string/summary_empty"
+        android:fragment="com.android.settings.applications.ProcessStatsSummary" />
+
     <com.android.settings.BugreportPreference
             android:key="bugreport"
             android:title="@*android:string/bugreport_title"
diff --git a/res/xml/manage_assist.xml b/res/xml/manage_assist.xml
index a96fb6b..120309a 100644
--- a/res/xml/manage_assist.xml
+++ b/res/xml/manage_assist.xml
@@ -22,7 +22,7 @@
     <com.android.settings.widget.GearPreference
         android:key="default_assist"
         android:title="@string/default_assist_title"
-        android:summary="@string/app_list_preference_none"
+        android:summary="@string/summary_placeholder"
         android:fragment="com.android.settings.applications.assist.DefaultAssistPicker"/>
 
     <Preference
diff --git a/res/xml/pick_up_gesture_settings.xml b/res/xml/pick_up_gesture_settings.xml
index 0b4a1de..e1414cd 100644
--- a/res/xml/pick_up_gesture_settings.xml
+++ b/res/xml/pick_up_gesture_settings.xml
@@ -18,6 +18,7 @@
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:key="gesture_pick_up_screen"
     android:title="@string/ambient_display_pickup_title">
 
     <com.android.settings.widget.VideoPreference
diff --git a/res/xml/tts_engine_picker.xml b/res/xml/tts_engine_picker.xml
index c0a464c..92bfede 100644
--- a/res/xml/tts_engine_picker.xml
+++ b/res/xml/tts_engine_picker.xml
@@ -15,6 +15,7 @@
 -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:key="tts_engine_picker_screen"
         android:title="@string/tts_engine_preference_title">
 
     <PreferenceCategory android:key="tts_engine_preference_category"/>
diff --git a/src/com/android/settings/WifiCallingSettings.java b/src/com/android/settings/WifiCallingSettings.java
index cb661ed..e872fb8 100644
--- a/src/com/android/settings/WifiCallingSettings.java
+++ b/src/com/android/settings/WifiCallingSettings.java
@@ -16,190 +16,38 @@
 
 package com.android.settings;
 
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
+import android.app.Fragment;
+import android.app.FragmentManager;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.Bundle;
-import android.os.PersistableBundle;
-import android.support.v7.preference.ListPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.Preference.OnPreferenceClickListener;
-import android.support.v7.preference.PreferenceScreen;
-import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.util.Log;
-import android.widget.Switch;
-import android.widget.TextView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
 
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsManager;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.telephony.Phone;
-import com.android.settings.widget.SwitchBar;
+import com.android.settings.widget.RtlCompatibleViewPager;
+import com.android.settings.widget.SlidingTabLayout;
+
+import java.util.List;
 
 /**
- * "Wi-Fi Calling settings" screen.  This preference screen lets you
- * enable/disable Wi-Fi Calling and change Wi-Fi Calling mode.
+ * "Wi-Fi Calling settings" screen. This is the container fragment which holds
+ * {@link WifiCallingSettingsForSub} fragments.
  */
-public class WifiCallingSettings extends SettingsPreferenceFragment
-        implements SwitchBar.OnSwitchChangeListener,
-        Preference.OnPreferenceChangeListener {
-
+public class WifiCallingSettings extends SettingsPreferenceFragment {
     private static final String TAG = "WifiCallingSettings";
-
-    //String keys for preference lookup
-    private static final String BUTTON_WFC_MODE = "wifi_calling_mode";
-    private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
-    private static final String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key";
-
-    private static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1;
-
-    public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP";
-
-    public static final int LAUCH_APP_ACTIVATE = 0;
-    public static final int LAUCH_APP_UPDATE = 1;
+    private List<SubscriptionInfo> mSil;
 
     //UI objects
-    private SwitchBar mSwitchBar;
-    private Switch mSwitch;
-    private ListPreference mButtonWfcMode;
-    private ListPreference mButtonWfcRoamingMode;
-    private Preference mUpdateAddress;
-    private TextView mEmptyView;
-
-    private boolean mValidListener = false;
-    private boolean mEditableWfcMode = true;
-    private boolean mEditableWfcRoamingMode = true;
-
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
-        /*
-         * Enable/disable controls when in/out of a call and depending on
-         * TTY mode and TTY support over VoLTE.
-         * @see android.telephony.PhoneStateListener#onCallStateChanged(int,
-         * java.lang.String)
-         */
-        @Override
-        public void onCallStateChanged(int state, String incomingNumber) {
-            final SettingsActivity activity = (SettingsActivity) getActivity();
-            boolean isNonTtyOrTtyOnVolteEnabled = ImsManager
-                    .isNonTtyOrTtyOnVolteEnabled(activity);
-            final SwitchBar switchBar = activity.getSwitchBar();
-            boolean isWfcEnabled = switchBar.getSwitch().isChecked()
-                    && isNonTtyOrTtyOnVolteEnabled;
-
-            switchBar.setEnabled((state == TelephonyManager.CALL_STATE_IDLE)
-                    && isNonTtyOrTtyOnVolteEnabled);
-
-            boolean isWfcModeEditable = true;
-            boolean isWfcRoamingModeEditable = false;
-            final CarrierConfigManager configManager = (CarrierConfigManager)
-                    activity.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            if (configManager != null) {
-                PersistableBundle b = configManager.getConfig();
-                if (b != null) {
-                    isWfcModeEditable = b.getBoolean(
-                            CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
-                    isWfcRoamingModeEditable = b.getBoolean(
-                            CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
-                }
-            }
-
-            Preference pref = getPreferenceScreen().findPreference(BUTTON_WFC_MODE);
-            if (pref != null) {
-                pref.setEnabled(isWfcEnabled && isWfcModeEditable
-                        && (state == TelephonyManager.CALL_STATE_IDLE));
-            }
-            Preference pref_roam = getPreferenceScreen().findPreference(BUTTON_WFC_ROAMING_MODE);
-            if (pref_roam != null) {
-                pref_roam.setEnabled(isWfcEnabled && isWfcRoamingModeEditable
-                        && (state == TelephonyManager.CALL_STATE_IDLE));
-            }
-        }
-    };
-
-    private final OnPreferenceClickListener mUpdateAddressListener =
-            new OnPreferenceClickListener() {
-                /*
-                 * Launch carrier emergency address managemnent activity
-                 */
-                @Override
-                public boolean onPreferenceClick(Preference preference) {
-                    final Context context = getActivity();
-                    Intent carrierAppIntent = getCarrierActivityIntent(context);
-                    if (carrierAppIntent != null) {
-                        carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_UPDATE);
-                        startActivity(carrierAppIntent);
-                    }
-                    return true;
-                }
-    };
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-
-        final SettingsActivity activity = (SettingsActivity) getActivity();
-
-        mSwitchBar = activity.getSwitchBar();
-        mSwitch = mSwitchBar.getSwitch();
-        mSwitchBar.show();
-
-        mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
-        setEmptyView(mEmptyView);
-        String emptyViewText = activity.getString(R.string.wifi_calling_off_explanation)
-                + activity.getString(R.string.wifi_calling_off_explanation_2);
-        mEmptyView.setText(emptyViewText);
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        mSwitchBar.hide();
-    }
-
-    private void showAlert(Intent intent) {
-        Context context = getActivity();
-
-        CharSequence title = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_TITLE);
-        CharSequence message = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_MESSAGE);
-
-        AlertDialog.Builder builder = new AlertDialog.Builder(context);
-        builder.setMessage(message)
-                .setTitle(title)
-                .setIcon(android.R.drawable.ic_dialog_alert)
-                .setPositiveButton(android.R.string.ok, null);
-        AlertDialog dialog = builder.create();
-        dialog.show();
-    }
-
-    private IntentFilter mIntentFilter;
-
-    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (action.equals(ImsManager.ACTION_IMS_REGISTRATION_ERROR)) {
-                // If this fragment is active then we are immediately
-                // showing alert on screen. There is no need to add
-                // notification in this case.
-                //
-                // In order to communicate to ImsPhone that it should
-                // not show notification, we are changing result code here.
-                setResultCode(Activity.RESULT_CANCELED);
-
-                // UX requirement is to disable WFC in case of "permanent" registration failures.
-                mSwitch.setChecked(false);
-
-                showAlert(intent);
-            }
-        }
-    };
+    private RtlCompatibleViewPager mViewPager;
+    private WifiCallingViewPagerAdapter mPagerAdapter;
+    private SlidingTabLayout mTabLayout;
 
     @Override
     public int getMetricsCategory() {
@@ -207,242 +55,81 @@
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View view = inflater.inflate(R.layout.wifi_calling_settings_tabs, container, false);
 
-        addPreferencesFromResource(R.xml.wifi_calling_settings);
+        mTabLayout = view.findViewById(R.id.sliding_tabs);
+        mViewPager = (RtlCompatibleViewPager) view.findViewById(R.id.view_pager);
 
-        mButtonWfcMode = (ListPreference) findPreference(BUTTON_WFC_MODE);
-        mButtonWfcMode.setOnPreferenceChangeListener(this);
+        mPagerAdapter = new WifiCallingViewPagerAdapter(getChildFragmentManager(), mViewPager);
+        mViewPager.setAdapter(mPagerAdapter);
 
-        mButtonWfcRoamingMode = (ListPreference) findPreference(BUTTON_WFC_ROAMING_MODE);
-        mButtonWfcRoamingMode.setOnPreferenceChangeListener(this);
-
-        mUpdateAddress = (Preference) findPreference(PREFERENCE_EMERGENCY_ADDRESS);
-        mUpdateAddress.setOnPreferenceClickListener(mUpdateAddressListener);
-
-        mIntentFilter = new IntentFilter();
-        mIntentFilter.addAction(ImsManager.ACTION_IMS_REGISTRATION_ERROR);
-
-        CarrierConfigManager configManager = (CarrierConfigManager)
-                getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        boolean isWifiOnlySupported = true;
-        if (configManager != null) {
-            PersistableBundle b = configManager.getConfig();
-            if (b != null) {
-                mEditableWfcMode = b.getBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
-                mEditableWfcRoamingMode = b.getBoolean(
-                        CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
-                isWifiOnlySupported = b.getBoolean(
-                        CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true);
-            }
-        }
-
-        if (!isWifiOnlySupported) {
-            mButtonWfcMode.setEntries(R.array.wifi_calling_mode_choices_without_wifi_only);
-            mButtonWfcMode.setEntryValues(R.array.wifi_calling_mode_values_without_wifi_only);
-            mButtonWfcRoamingMode.setEntries(
-                    R.array.wifi_calling_mode_choices_v2_without_wifi_only);
-            mButtonWfcRoamingMode.setEntryValues(
-                    R.array.wifi_calling_mode_values_without_wifi_only);
-        }
+        return view;
     }
 
     @Override
-    public void onResume() {
-        super.onResume();
-
-        final Context context = getActivity();
-
-        // NOTE: Buttons will be enabled/disabled in mPhoneStateListener
-        boolean wfcEnabled = ImsManager.isWfcEnabledByUser(context)
-                && ImsManager.isNonTtyOrTtyOnVolteEnabled(context);
-        mSwitch.setChecked(wfcEnabled);
-        int wfcMode = ImsManager.getWfcMode(context, false);
-        int wfcRoamingMode = ImsManager.getWfcMode(context, true);
-        mButtonWfcMode.setValue(Integer.toString(wfcMode));
-        mButtonWfcRoamingMode.setValue(Integer.toString(wfcRoamingMode));
-        updateButtonWfcMode(context, wfcEnabled, wfcMode, wfcRoamingMode);
-
-        if (ImsManager.isWfcEnabledByPlatform(context)) {
-            TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
-            tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
-
-            mSwitchBar.addOnSwitchChangeListener(this);
-
-            mValidListener = true;
-        }
-
-        context.registerReceiver(mIntentReceiver, mIntentFilter);
-
-        Intent intent = getActivity().getIntent();
-        if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) {
-            showAlert(intent);
-        }
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        // TODO: besides in onCreate, we should also update subList when SIM / Sub status
+        // changes.
+        updateSubList();
     }
 
     @Override
-    public void onPause() {
-        super.onPause();
+    public void onStart() {
+        super.onStart();
 
-        final Context context = getActivity();
-
-        if (mValidListener) {
-            mValidListener = false;
-
-            TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
-            tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
-
-            mSwitchBar.removeOnSwitchChangeListener(this);
-        }
-
-        context.unregisterReceiver(mIntentReceiver);
-    }
-
-    /**
-     * Listens to the state change of the switch.
-     */
-    @Override
-    public void onSwitchChanged(Switch switchView, boolean isChecked) {
-        final Context context = getActivity();
-        Log.d(TAG, "onSwitchChanged(" + isChecked + ")");
-
-        if (!isChecked) {
-            updateWfcMode(context, false);
-            return;
-        }
-
-        // Call address management activity before turning on WFC
-        Intent carrierAppIntent = getCarrierActivityIntent(context);
-        if (carrierAppIntent != null) {
-            carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE);
-            startActivityForResult(carrierAppIntent, REQUEST_CHECK_WFC_EMERGENCY_ADDRESS);
+        if (mSil != null && mSil.size() > 1) {
+            mTabLayout.setViewPager(mViewPager);
         } else {
-            updateWfcMode(context, true);
+            mTabLayout.setVisibility(View.GONE);
         }
     }
 
-    /*
-     * Get the Intent to launch carrier emergency address management activity.
-     * Return null when no activity found.
-     */
-    private static Intent getCarrierActivityIntent(Context context) {
-        // Retrive component name from carrirt config
-        CarrierConfigManager configManager = context.getSystemService(CarrierConfigManager.class);
-        if (configManager == null) return null;
+    private final class WifiCallingViewPagerAdapter extends FragmentPagerAdapter {
+        private final RtlCompatibleViewPager mViewPager;
 
-        PersistableBundle bundle = configManager.getConfig();
-        if (bundle == null) return null;
-
-        String carrierApp = bundle.getString(
-                CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING);
-        if (TextUtils.isEmpty(carrierApp)) return null;
-
-        ComponentName componentName = ComponentName.unflattenFromString(carrierApp);
-        if (componentName == null) return null;
-
-        // Build and return intent
-        Intent intent = new Intent();
-        intent.setComponent(componentName);
-        return intent;
-    }
-
-    /*
-     * Turn on/off WFC mode with ImsManager and update UI accordingly
-     */
-    private void updateWfcMode(Context context, boolean wfcEnabled) {
-        Log.i(TAG, "updateWfcMode(" + wfcEnabled + ")");
-        ImsManager.setWfcSetting(context, wfcEnabled);
-
-        int wfcMode = ImsManager.getWfcMode(context, false);
-        int wfcRoamingMode = ImsManager.getWfcMode(context, true);
-        updateButtonWfcMode(context, wfcEnabled, wfcMode, wfcRoamingMode);
-        if (wfcEnabled) {
-            mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), wfcMode);
-        } else {
-            mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), -1);
+        public WifiCallingViewPagerAdapter(FragmentManager fragmentManager,
+                RtlCompatibleViewPager viewPager) {
+            super(fragmentManager);
+            mViewPager = viewPager;
         }
-    }
 
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        super.onActivityResult(requestCode, resultCode, data);
-
-        final Context context = getActivity();
-
-        if (requestCode == REQUEST_CHECK_WFC_EMERGENCY_ADDRESS) {
-            Log.d(TAG, "WFC emergency address activity result = " + resultCode);
-
-            if (resultCode == Activity.RESULT_OK) {
-                updateWfcMode(context, true);
-            }
+        @Override
+        public CharSequence getPageTitle(int position) {
+            return String.valueOf(mSil.get(position).getDisplayName());
         }
-    }
 
-    private void updateButtonWfcMode(Context context, boolean wfcEnabled,
-                                     int wfcMode, int wfcRoamingMode) {
-        mButtonWfcMode.setSummary(getWfcModeSummary(context, wfcMode));
-        mButtonWfcMode.setEnabled(wfcEnabled && mEditableWfcMode);
-        // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value.
-        mButtonWfcRoamingMode.setEnabled(wfcEnabled && mEditableWfcRoamingMode);
+        @Override
+        public Fragment getItem(int position) {
+            Log.d(TAG, "Adapter getItem " + position);
+            final Bundle args = new Bundle();
+            args.putInt(WifiCallingSettingsForSub.FRAGMENT_BUNDLE_SUBID,
+                    mSil.get(position).getSubscriptionId());
+            WifiCallingSettingsForSub fragment = new WifiCallingSettingsForSub();
+            fragment.setArguments(args);
 
-        final PreferenceScreen preferenceScreen = getPreferenceScreen();
-        boolean updateAddressEnabled = (getCarrierActivityIntent(context) != null);
-        if (wfcEnabled) {
-            if (mEditableWfcMode) {
-                preferenceScreen.addPreference(mButtonWfcMode);
+            return fragment;
+        }
+
+        @Override
+        public Object instantiateItem(ViewGroup container, int position) {
+            Log.d(TAG, "Adapter instantiateItem " + position);
+            return super.instantiateItem(container,
+                    mViewPager.getRtlAwareIndex(position));
+        }
+
+        @Override
+        public int getCount() {
+            if (mSil == null) {
+                Log.d(TAG, "Adapter getCount null mSil ");
+                return 0;
             } else {
-                // Don't show WFC (home) preference if it's not editable.
-                preferenceScreen.removePreference(mButtonWfcMode);
-            }
-            if (mEditableWfcRoamingMode) {
-                preferenceScreen.addPreference(mButtonWfcRoamingMode);
-            } else {
-                // Don't show WFC roaming preference if it's not editable.
-                preferenceScreen.removePreference(mButtonWfcRoamingMode);
-            }
-            if (updateAddressEnabled) {
-                preferenceScreen.addPreference(mUpdateAddress);
-            } else {
-                preferenceScreen.removePreference(mUpdateAddress);
-            }
-        } else {
-            preferenceScreen.removePreference(mButtonWfcMode);
-            preferenceScreen.removePreference(mButtonWfcRoamingMode);
-            preferenceScreen.removePreference(mUpdateAddress);
-        }
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        final Context context = getActivity();
-        if (preference == mButtonWfcMode) {
-            mButtonWfcMode.setValue((String) newValue);
-            int buttonMode = Integer.valueOf((String) newValue);
-            int currentWfcMode = ImsManager.getWfcMode(context, false);
-            if (buttonMode != currentWfcMode) {
-                ImsManager.setWfcMode(context, buttonMode, false);
-                mButtonWfcMode.setSummary(getWfcModeSummary(context, buttonMode));
-                mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode);
-            }
-            if (!mEditableWfcRoamingMode) {
-                int currentWfcRoamingMode = ImsManager.getWfcMode(context, true);
-                if (buttonMode != currentWfcRoamingMode) {
-                    ImsManager.setWfcMode(context, buttonMode, true);
-                    // mButtonWfcRoamingMode.setSummary is not needed; summary is selected value
-                }
-            }
-        } else if (preference == mButtonWfcRoamingMode) {
-            mButtonWfcRoamingMode.setValue((String) newValue);
-            int buttonMode = Integer.valueOf((String) newValue);
-            int currentMode = ImsManager.getWfcMode(context, true);
-            if (buttonMode != currentMode) {
-                ImsManager.setWfcMode(context, buttonMode, true);
-                // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value.
-                mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode);
+                Log.d(TAG, "Adapter getCount " + mSil.size());
+                return mSil.size();
             }
         }
-        return true;
     }
 
     public static int getWfcModeSummary(Context context, int wfcMode) {
@@ -464,4 +151,22 @@
         }
         return resId;
     }
+
+    private void updateSubList() {
+        mSil = SubscriptionManager.from(getActivity()).getActiveSubscriptionInfoList();
+
+        // Only config Wfc if it's enabled by platform.
+        if (mSil == null) {
+            return;
+        }
+        for (int i = 0; i < mSil.size();) {
+            ImsManager imsManager = ImsManager.getInstance(getActivity(),
+                    mSil.get(i).getSimSlotIndex());
+            if (!imsManager.isWfcEnabledByPlatform()) {
+                mSil.remove(i);
+            } else {
+                i++;
+            }
+        }
+    }
 }
diff --git a/src/com/android/settings/WifiCallingSettingsForSub.java b/src/com/android/settings/WifiCallingSettingsForSub.java
new file mode 100644
index 0000000..57a4ab2
--- /dev/null
+++ b/src/com/android/settings/WifiCallingSettingsForSub.java
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.Preference.OnPreferenceClickListener;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsManager;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.telephony.Phone;
+import com.android.settings.widget.SwitchBar;
+
+/**
+ * This is the inner class of {@link WifiCallingSettings} fragment.
+ * The preference screen lets you enable/disable Wi-Fi Calling and change Wi-Fi Calling mode.
+ */
+public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
+        implements SwitchBar.OnSwitchChangeListener,
+        Preference.OnPreferenceChangeListener {
+    private static final String TAG = "WifiCallingSettingsForSub";
+
+    //String keys for preference lookup
+    private static final String BUTTON_WFC_MODE = "wifi_calling_mode";
+    private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
+    private static final String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key";
+
+    private static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1;
+
+    public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP";
+
+    protected static final String FRAGMENT_BUNDLE_SUBID = "subId";
+
+    public static final int LAUCH_APP_ACTIVATE = 0;
+    public static final int LAUCH_APP_UPDATE = 1;
+
+    //UI objects
+    private SwitchBar mSwitchBar;
+    private Switch mSwitch;
+    private ListPreference mButtonWfcMode;
+    private ListPreference mButtonWfcRoamingMode;
+    private Preference mUpdateAddress;
+    private TextView mEmptyView;
+
+    private boolean mValidListener = false;
+    private boolean mEditableWfcMode = true;
+    private boolean mEditableWfcRoamingMode = true;
+
+    private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private ImsManager mImsManager;
+
+    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        /*
+         * Enable/disable controls when in/out of a call and depending on
+         * TTY mode and TTY support over VoLTE.
+         * @see android.telephony.PhoneStateListener#onCallStateChanged(int,
+         * java.lang.String)
+         */
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            final SettingsActivity activity = (SettingsActivity) getActivity();
+            boolean isNonTtyOrTtyOnVolteEnabled = mImsManager.isNonTtyOrTtyOnVolteEnabled();
+            boolean isWfcEnabled = mSwitchBar.isChecked()
+                    && isNonTtyOrTtyOnVolteEnabled;
+
+            mSwitchBar.setEnabled((state == TelephonyManager.CALL_STATE_IDLE)
+                    && isNonTtyOrTtyOnVolteEnabled);
+
+            boolean isWfcModeEditable = true;
+            boolean isWfcRoamingModeEditable = false;
+            final CarrierConfigManager configManager = (CarrierConfigManager)
+                    activity.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            if (configManager != null) {
+                PersistableBundle b = configManager.getConfigForSubId(mSubId);
+                if (b != null) {
+                    isWfcModeEditable = b.getBoolean(
+                            CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
+                    isWfcRoamingModeEditable = b.getBoolean(
+                            CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
+                }
+            }
+
+            Preference pref = getPreferenceScreen().findPreference(BUTTON_WFC_MODE);
+            if (pref != null) {
+                pref.setEnabled(isWfcEnabled && isWfcModeEditable
+                        && (state == TelephonyManager.CALL_STATE_IDLE));
+            }
+            Preference pref_roam =
+                    getPreferenceScreen().findPreference(BUTTON_WFC_ROAMING_MODE);
+            if (pref_roam != null) {
+                pref_roam.setEnabled(isWfcEnabled && isWfcRoamingModeEditable
+                        && (state == TelephonyManager.CALL_STATE_IDLE));
+            }
+        }
+    };
+
+    @Override
+    protected int getHelpResource() {
+        // Helper resource is already defined in the container fragment.
+        return 0;
+    }
+
+    private final OnPreferenceClickListener mUpdateAddressListener =
+            new OnPreferenceClickListener() {
+                /*
+                 * Launch carrier emergency address managemnent activity
+                 */
+                @Override
+                public boolean onPreferenceClick(Preference preference) {
+                    Intent carrierAppIntent = getCarrierActivityIntent();
+                    if (carrierAppIntent != null) {
+                        carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_UPDATE);
+                        startActivity(carrierAppIntent);
+                    }
+                    return true;
+                }
+            };
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        final SettingsActivity activity = (SettingsActivity) getActivity();
+
+        mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
+        setEmptyView(mEmptyView);
+        String emptyViewText = activity.getString(R.string.wifi_calling_off_explanation)
+                + activity.getString(R.string.wifi_calling_off_explanation_2);
+        mEmptyView.setText(emptyViewText);
+
+        mSwitchBar = getView().findViewById(R.id.switch_bar);
+        mSwitchBar.show();
+        mSwitch = mSwitchBar.getSwitch();
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mSwitchBar.hide();
+    }
+
+    private void showAlert(Intent intent) {
+        Context context = getActivity();
+
+        CharSequence title = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_TITLE);
+        CharSequence message = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_MESSAGE);
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(context);
+        builder.setMessage(message)
+                .setTitle(title)
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setPositiveButton(android.R.string.ok, null);
+        AlertDialog dialog = builder.create();
+        dialog.show();
+    }
+
+    private IntentFilter mIntentFilter;
+
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(ImsManager.ACTION_IMS_REGISTRATION_ERROR)) {
+                // If this fragment is active then we are immediately
+                // showing alert on screen. There is no need to add
+                // notification in this case.
+                //
+                // In order to communicate to ImsPhone that it should
+                // not show notification, we are changing result code here.
+                setResultCode(Activity.RESULT_CANCELED);
+
+                // UX requirement is to disable WFC in case of "permanent" registration failures.
+                mSwitch.setChecked(false);
+
+                showAlert(intent);
+            }
+        }
+    };
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.WIFI_CALLING_FOR_SUB;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        addPreferencesFromResource(R.xml.wifi_calling_settings);
+
+        // SubId should always be specified when creating this fragment. Either through
+        // fragment.setArguments() or through savedInstanceState.
+        if (getArguments() != null && getArguments().containsKey(FRAGMENT_BUNDLE_SUBID))
+        {
+            mSubId = getArguments().getInt(FRAGMENT_BUNDLE_SUBID);
+        } else if (savedInstanceState != null) {
+            mSubId = savedInstanceState.getInt(
+                    FRAGMENT_BUNDLE_SUBID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        }
+
+        mImsManager = ImsManager.getInstance(
+                getActivity(), SubscriptionManager.getPhoneId(mSubId));
+
+        mButtonWfcMode = (ListPreference) findPreference(BUTTON_WFC_MODE);
+        mButtonWfcMode.setOnPreferenceChangeListener(this);
+
+        mButtonWfcRoamingMode = (ListPreference) findPreference(BUTTON_WFC_ROAMING_MODE);
+        mButtonWfcRoamingMode.setOnPreferenceChangeListener(this);
+
+        mUpdateAddress = (Preference) findPreference(PREFERENCE_EMERGENCY_ADDRESS);
+        mUpdateAddress.setOnPreferenceClickListener(mUpdateAddressListener);
+
+        mIntentFilter = new IntentFilter();
+        mIntentFilter.addAction(ImsManager.ACTION_IMS_REGISTRATION_ERROR);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        outState.putInt(FRAGMENT_BUNDLE_SUBID, mSubId);
+        super.onSaveInstanceState(outState);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+
+        View view = inflater.inflate(
+                R.layout.wifi_calling_settings_preferences, container, false);
+
+        final ViewGroup prefs_container = view.findViewById(R.id.prefs_container);
+        Utils.prepareCustomPreferencesList(container, view, prefs_container, false);
+        View prefs = super.onCreateView(inflater, prefs_container, savedInstanceState);
+        prefs_container.addView(prefs);
+
+        return view;
+    }
+
+    private void updateBody() {
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        boolean isWifiOnlySupported = true;
+
+        if (configManager != null) {
+            PersistableBundle b = configManager.getConfigForSubId(mSubId);
+            if (b != null) {
+                mEditableWfcMode = b.getBoolean(
+                        CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL);
+                mEditableWfcRoamingMode = b.getBoolean(
+                        CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL);
+                isWifiOnlySupported = b.getBoolean(
+                        CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true);
+            }
+        }
+
+        if (!isWifiOnlySupported) {
+            mButtonWfcMode.setEntries(R.array.wifi_calling_mode_choices_without_wifi_only);
+            mButtonWfcMode.setEntryValues(R.array.wifi_calling_mode_values_without_wifi_only);
+            mButtonWfcRoamingMode.setEntries(
+                    R.array.wifi_calling_mode_choices_v2_without_wifi_only);
+            mButtonWfcRoamingMode.setEntryValues(
+                    R.array.wifi_calling_mode_values_without_wifi_only);
+        }
+
+
+        // NOTE: Buttons will be enabled/disabled in mPhoneStateListener
+        boolean wfcEnabled = mImsManager.isWfcEnabledByUser()
+                && mImsManager.isNonTtyOrTtyOnVolteEnabled();
+        mSwitch.setChecked(wfcEnabled);
+        int wfcMode = mImsManager.getWfcMode(false);
+        int wfcRoamingMode = mImsManager.getWfcMode(true);
+        mButtonWfcMode.setValue(Integer.toString(wfcMode));
+        mButtonWfcRoamingMode.setValue(Integer.toString(wfcRoamingMode));
+        updateButtonWfcMode(wfcEnabled, wfcMode, wfcRoamingMode);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        final Context context = getActivity();
+
+        updateBody();
+
+        if (mImsManager.isWfcEnabledByPlatform()) {
+            TelephonyManager tm =
+                    (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+            tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+
+            mSwitchBar.addOnSwitchChangeListener(this);
+
+            mValidListener = true;
+        }
+
+        context.registerReceiver(mIntentReceiver, mIntentFilter);
+
+        Intent intent = getActivity().getIntent();
+        if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) {
+            showAlert(intent);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+
+        final Context context = getActivity();
+
+        if (mValidListener) {
+            mValidListener = false;
+
+            TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+            tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+
+            mSwitchBar.removeOnSwitchChangeListener(this);
+        }
+
+        context.unregisterReceiver(mIntentReceiver);
+    }
+
+    /**
+     * Listens to the state change of the switch.
+     */
+    @Override
+    public void onSwitchChanged(Switch switchView, boolean isChecked) {
+        Log.d(TAG, "onSwitchChanged(" + isChecked + ")");
+
+        if (!isChecked) {
+            updateWfcMode(false);
+            return;
+        }
+
+        // Call address management activity before turning on WFC
+        Intent carrierAppIntent = getCarrierActivityIntent();
+        if (carrierAppIntent != null) {
+            carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE);
+            startActivityForResult(carrierAppIntent, REQUEST_CHECK_WFC_EMERGENCY_ADDRESS);
+        } else {
+            updateWfcMode(true);
+        }
+    }
+
+    /*
+     * Get the Intent to launch carrier emergency address management activity.
+     * Return null when no activity found.
+     */
+    private Intent getCarrierActivityIntent() {
+        // Retrive component name from carrier config
+        CarrierConfigManager configManager =
+                getActivity().getSystemService(CarrierConfigManager.class);
+        if (configManager == null) return null;
+
+        PersistableBundle bundle = configManager.getConfigForSubId(mSubId);
+        if (bundle == null) return null;
+
+        String carrierApp = bundle.getString(
+                CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING);
+        if (TextUtils.isEmpty(carrierApp)) return null;
+
+        ComponentName componentName = ComponentName.unflattenFromString(carrierApp);
+        if (componentName == null) return null;
+
+        // Build and return intent
+        Intent intent = new Intent();
+        intent.setComponent(componentName);
+        return intent;
+    }
+
+    /*
+     * Turn on/off WFC mode with ImsManager and update UI accordingly
+     */
+    private void updateWfcMode(boolean wfcEnabled) {
+        Log.i(TAG, "updateWfcMode(" + wfcEnabled + ")");
+        mImsManager.setWfcSetting(wfcEnabled);
+
+        int wfcMode = mImsManager.getWfcMode(false);
+        int wfcRoamingMode = mImsManager.getWfcMode(true);
+        updateButtonWfcMode(wfcEnabled, wfcMode, wfcRoamingMode);
+        if (wfcEnabled) {
+            mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), wfcMode);
+        } else {
+            mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), -1);
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        final Context context = getActivity();
+
+        if (requestCode == REQUEST_CHECK_WFC_EMERGENCY_ADDRESS) {
+            Log.d(TAG, "WFC emergency address activity result = " + resultCode);
+
+            if (resultCode == Activity.RESULT_OK) {
+                updateWfcMode(true);
+            }
+        }
+    }
+
+    private void updateButtonWfcMode(boolean wfcEnabled,
+            int wfcMode, int wfcRoamingMode) {
+        mButtonWfcMode.setSummary(getWfcModeSummary(wfcMode));
+        mButtonWfcMode.setEnabled(wfcEnabled && mEditableWfcMode);
+        // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value.
+        mButtonWfcRoamingMode.setEnabled(wfcEnabled && mEditableWfcRoamingMode);
+
+        final PreferenceScreen preferenceScreen = getPreferenceScreen();
+        boolean updateAddressEnabled = (getCarrierActivityIntent() != null);
+        if (wfcEnabled) {
+            if (mEditableWfcMode) {
+                preferenceScreen.addPreference(mButtonWfcMode);
+            } else {
+                // Don't show WFC (home) preference if it's not editable.
+                preferenceScreen.removePreference(mButtonWfcMode);
+            }
+            if (mEditableWfcRoamingMode) {
+                preferenceScreen.addPreference(mButtonWfcRoamingMode);
+            } else {
+                // Don't show WFC roaming preference if it's not editable.
+                preferenceScreen.removePreference(mButtonWfcRoamingMode);
+            }
+            if (updateAddressEnabled) {
+                preferenceScreen.addPreference(mUpdateAddress);
+            } else {
+                preferenceScreen.removePreference(mUpdateAddress);
+            }
+        } else {
+            preferenceScreen.removePreference(mButtonWfcMode);
+            preferenceScreen.removePreference(mButtonWfcRoamingMode);
+            preferenceScreen.removePreference(mUpdateAddress);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference == mButtonWfcMode) {
+            Log.d(TAG, "onPreferenceChange mButtonWfcMode " + newValue);
+            mButtonWfcMode.setValue((String) newValue);
+            int buttonMode = Integer.valueOf((String) newValue);
+            int currentWfcMode = mImsManager.getWfcMode(false);
+            if (buttonMode != currentWfcMode) {
+                mImsManager.setWfcMode(buttonMode, false);
+                mButtonWfcMode.setSummary(getWfcModeSummary(buttonMode));
+                mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode);
+            }
+            if (!mEditableWfcRoamingMode) {
+                int currentWfcRoamingMode = mImsManager.getWfcMode(true);
+                if (buttonMode != currentWfcRoamingMode) {
+                    mImsManager.setWfcMode(buttonMode, true);
+                    // mButtonWfcRoamingMode.setSummary is not needed; summary is selected value
+                }
+            }
+        } else if (preference == mButtonWfcRoamingMode) {
+            mButtonWfcRoamingMode.setValue((String) newValue);
+            int buttonMode = Integer.valueOf((String) newValue);
+            int currentMode = mImsManager.getWfcMode(true);
+            if (buttonMode != currentMode) {
+                mImsManager.setWfcMode(buttonMode, true);
+                // mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value.
+                mMetricsFeatureProvider.action(getActivity(), getMetricsCategory(), buttonMode);
+            }
+        }
+        return true;
+    }
+
+    private int getWfcModeSummary(int wfcMode) {
+        int resId = com.android.internal.R.string.wifi_calling_off_summary;
+        if (mImsManager.isWfcEnabledByUser()) {
+            switch (wfcMode) {
+                case ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY:
+                    resId = com.android.internal.R.string.wfc_mode_wifi_only_summary;
+                    break;
+                case ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED:
+                    resId = com.android.internal.R.string.wfc_mode_cellular_preferred_summary;
+                    break;
+                case ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED:
+                    resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary;
+                    break;
+                default:
+                    Log.e(TAG, "Unexpected WFC mode value: " + wfcMode);
+            }
+        }
+        return resId;
+    }
+}
diff --git a/src/com/android/settings/applications/ProcessStatsBase.java b/src/com/android/settings/applications/ProcessStatsBase.java
index 3f66789..b98d0ba 100644
--- a/src/com/android/settings/applications/ProcessStatsBase.java
+++ b/src/com/android/settings/applications/ProcessStatsBase.java
@@ -44,7 +44,7 @@
     // smaller than the actual time selected instead of bumping up to 3 hours
     // beyond it.
     private static final long DURATION_QUANTUM = ProcessStats.COMMIT_PERIOD;
-    protected static long[] sDurations = new long[] {
+    public  static long[] sDurations = new long[] {
         3 * 60 * 60 * 1000 - DURATION_QUANTUM / 2, 6 * 60 *60 * 1000 - DURATION_QUANTUM / 2,
         12 * 60 * 60 * 1000 - DURATION_QUANTUM / 2, 24 * 60 * 60 * 1000 - DURATION_QUANTUM / 2
     };
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index d532136..83f395f 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -352,6 +352,7 @@
             Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment,
             BluetoothA2dpConfigStore bluetoothA2dpConfigStore) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new MemoryUsagePreferenceController(context));
         controllers.add(new BugReportPreferenceControllerV2(context));
         controllers.add(new LocalBackupPasswordPreferenceController(context));
         controllers.add(new StayAwakePreferenceController(context, lifecycle));
diff --git a/src/com/android/settings/development/MemoryUsagePreferenceController.java b/src/com/android/settings/development/MemoryUsagePreferenceController.java
new file mode 100644
index 0000000..1b589fd
--- /dev/null
+++ b/src/com/android/settings/development/MemoryUsagePreferenceController.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.format.Formatter;
+
+import com.android.settings.R;
+import com.android.settings.applications.ProcStatsData;
+import com.android.settings.applications.ProcessStatsBase;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+public class MemoryUsagePreferenceController extends DeveloperOptionsPreferenceController implements
+        PreferenceControllerMixin {
+
+    private static final String MEMORY_USAGE_KEY = "memory";
+
+    private Preference mPreference;
+    private ProcStatsData mProcStatsData;
+
+    public MemoryUsagePreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return MEMORY_USAGE_KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mPreference = screen.findPreference(getPreferenceKey());
+        mProcStatsData = getProcStatsData();
+        setDuration();
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        mProcStatsData.refreshStats(true);
+        final ProcStatsData.MemInfo memInfo = mProcStatsData.getMemInfo();
+        final String usedResult = Formatter.formatShortFileSize(mContext,
+                (long) memInfo.realUsedRam);
+        final String totalResult = Formatter.formatShortFileSize(mContext,
+                (long) memInfo.realTotalRam);
+        mPreference.setSummary(mContext.getString(R.string.memory_summary,
+                usedResult, totalResult));
+    }
+
+    @VisibleForTesting
+    void setDuration() {
+        mProcStatsData.setDuration(ProcessStatsBase.sDurations[0] /* 3 hours */);
+    }
+
+    @VisibleForTesting
+    ProcStatsData getProcStatsData() {
+        return new ProcStatsData(mContext, false);
+    }
+}
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 7252e2d..89924bb 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -86,7 +86,6 @@
 import com.android.settings.users.UserSettings;
 import com.android.settings.wallpaper.WallpaperTypeSettings;
 import com.android.settings.wifi.ConfigureWifiSettings;
-import com.android.settings.wifi.SavedAccessPointsWifiSettings;
 import com.android.settings.wifi.WifiSettings;
 
 import java.util.Collection;
@@ -129,7 +128,6 @@
         addIndex(WifiSettings.class);
         addIndex(NetworkDashboardFragment.class);
         addIndex(ConfigureWifiSettings.class);
-        addIndex(SavedAccessPointsWifiSettings.class);
         addIndex(BluetoothSettings.class);
         addIndex(SimSettings.class);
         addIndex(DataUsageSummary.class);
diff --git a/src/com/android/settings/widget/SlidingTabLayout.java b/src/com/android/settings/widget/SlidingTabLayout.java
new file mode 100644
index 0000000..7099646
--- /dev/null
+++ b/src/com/android/settings/widget/SlidingTabLayout.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 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.settings.widget;
+
+import android.content.Context;
+import android.support.v4.view.PagerAdapter;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as
+ * to the user's scroll progress.
+ */
+public final class SlidingTabLayout extends FrameLayout implements View.OnClickListener {
+
+    private final LinearLayout mTitleView;
+    private final View mIndicatorView;
+    private final LayoutInflater mLayoutInflater;
+
+    private RtlCompatibleViewPager mViewPager;
+    private int mSelectedPosition;
+    private float mSelectionOffset;
+
+    public SlidingTabLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mLayoutInflater = LayoutInflater.from(context);
+        mTitleView = new LinearLayout(context);
+        mTitleView.setGravity(Gravity.CENTER_HORIZONTAL);
+        mIndicatorView = mLayoutInflater.inflate(R.layout.sliding_tab_indicator_view, this, false);
+
+        addView(mTitleView, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+        addView(mIndicatorView, mIndicatorView.getLayoutParams());
+    }
+
+    /**
+     * Sets the associated view pager. Note that the assumption here is that the pager content
+     * (number of tabs and tab titles) does not change after this call has been made.
+     */
+    public void setViewPager(RtlCompatibleViewPager viewPager) {
+        mTitleView.removeAllViews();
+
+        mViewPager = viewPager;
+        if (viewPager != null) {
+            viewPager.addOnPageChangeListener(new InternalViewPagerListener());
+            populateTabStrip();
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        final int titleCount = mTitleView.getChildCount();
+        if (titleCount > 0) {
+            final int width = MeasureSpec.makeMeasureSpec(
+                    mTitleView.getMeasuredWidth() / titleCount, MeasureSpec.EXACTLY);
+            final int height = MeasureSpec.makeMeasureSpec(
+                    mIndicatorView.getMeasuredHeight(), MeasureSpec.EXACTLY);
+            mIndicatorView.measure(width, height);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (mTitleView.getChildCount() > 0) {
+            final int indicatorBottom = getMeasuredHeight();
+            final int indicatorHeight = mIndicatorView.getMeasuredHeight();
+            final int indicatorWidth = mIndicatorView.getMeasuredWidth();
+            final int totalWidth = getMeasuredWidth();
+            final int leftPadding = getPaddingLeft();
+            final int rightPadding = getPaddingRight();
+
+            mTitleView.layout(leftPadding, 0, mTitleView.getMeasuredWidth() + rightPadding,
+                    mTitleView.getMeasuredHeight());
+            // IndicatorView should start on the right when RTL mode is enabled
+            if (isRtlMode()) {
+                mIndicatorView.layout(totalWidth - indicatorWidth,
+                        indicatorBottom - indicatorHeight, totalWidth,
+                        indicatorBottom);
+            } else {
+                mIndicatorView.layout(0, indicatorBottom - indicatorHeight,
+                        indicatorWidth, indicatorBottom);
+            }
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        final int titleCount = mTitleView.getChildCount();
+        for (int i = 0; i < titleCount; i++) {
+            if (v == mTitleView.getChildAt(i)) {
+                mViewPager.setCurrentItem(i);
+                return;
+            }
+        }
+    }
+
+    private void onViewPagerPageChanged(int position, float positionOffset) {
+        mSelectedPosition = position;
+        mSelectionOffset = positionOffset;
+        // Translation should be reversed in RTL mode
+        final int leftIndicator = isRtlMode() ? -getIndicatorLeft() : getIndicatorLeft();
+        mIndicatorView.setTranslationX(leftIndicator);
+    }
+
+    private void populateTabStrip() {
+        final PagerAdapter adapter = mViewPager.getAdapter();
+
+        for (int i = 0; i < adapter.getCount(); i++) {
+            final TextView tabTitleView = (TextView) mLayoutInflater.inflate(
+                    R.layout.sliding_tab_title_view, mTitleView, false);
+
+            tabTitleView.setText(adapter.getPageTitle(i));
+            tabTitleView.setOnClickListener(this);
+
+            mTitleView.addView(tabTitleView);
+            tabTitleView.setSelected(i == mViewPager.getCurrentItem());
+        }
+    }
+
+    private int getIndicatorLeft() {
+        View selectedTitle = mTitleView.getChildAt(mSelectedPosition);
+        int left = selectedTitle.getLeft();
+        if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+            View nextTitle = mTitleView.getChildAt(mSelectedPosition + 1);
+            left = (int) (mSelectionOffset * nextTitle.getLeft()
+                    + (1.0f - mSelectionOffset) * left);
+        }
+        return left;
+    }
+
+    private boolean isRtlMode() {
+        return getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+    }
+
+    private final class InternalViewPagerListener implements
+            RtlCompatibleViewPager.OnPageChangeListener {
+        private int mScrollState;
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            final int titleCount = mTitleView.getChildCount();
+            if ((titleCount == 0) || (position < 0) || (position >= titleCount)) {
+                return;
+            }
+            onViewPagerPageChanged(position, positionOffset);
+        }
+
+        @Override
+        public void onPageScrollStateChanged(int state) {
+            mScrollState = state;
+        }
+
+        @Override
+        public void onPageSelected(int position) {
+            position = mViewPager.getRtlAwareIndex(position);
+            if (mScrollState == RtlCompatibleViewPager.SCROLL_STATE_IDLE) {
+                onViewPagerPageChanged(position, 0f);
+            }
+            final int titleCount = mTitleView.getChildCount();
+            for (int i = 0; i < titleCount; i++) {
+                mTitleView.getChildAt(i).setSelected(position == i);
+            }
+        }
+    }
+}
diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable
index 091ba12..91a6649 100644
--- a/tests/robotests/assets/grandfather_not_implementing_indexable
+++ b/tests/robotests/assets/grandfather_not_implementing_indexable
@@ -74,6 +74,7 @@
 com.android.settings.SecuritySettings$SecuritySubSettings
 com.android.settings.PrivacySettings
 com.android.settings.WifiCallingSettings
+com.android.settings.WifiCallingSettingsForSub
 com.android.settings.password.SetupChooseLockGeneric$SetupChooseLockGenericFragment
 com.android.settings.SetupRedactionInterstitial$SetupRedactionInterstitialFragment
 com.android.settings.TrustAgentSettings
diff --git a/tests/robotests/src/com/android/settings/development/MemoryUsagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/MemoryUsagePreferenceControllerTest.java
new file mode 100644
index 0000000..a949eef
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/MemoryUsagePreferenceControllerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.applications.ProcStatsData;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class MemoryUsagePreferenceControllerTest {
+
+    @Mock
+    private Preference mPreference;
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private ProcStatsData mProcStatsData;
+    @Mock
+    private ProcStatsData.MemInfo mMemInfo;
+
+    private Context mContext;
+    private MemoryUsagePreferenceController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mController = spy(new MemoryUsagePreferenceController(mContext));
+        doReturn(mProcStatsData).when(mController).getProcStatsData();
+        doNothing().when(mController).setDuration();
+        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+        when(mProcStatsData.getMemInfo()).thenReturn(mMemInfo);
+        mController.displayPreference(mScreen);
+    }
+
+    @Test
+    public void updateState_shouldUpdatePreferenceSummary() {
+        mController.updateState(mPreference);
+
+        verify(mPreference).setSummary(anyString());
+    }
+}
diff --git a/tests/unit/Android.mk b/tests/unit/Android.mk
index a91dcb1..ec0f0b6 100644
--- a/tests/unit/Android.mk
+++ b/tests/unit/Android.mk
@@ -5,7 +5,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_CERTIFICATE := platform
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
diff --git a/tests/unit/src/com/android/settings/UniquePreferenceTest.java b/tests/unit/src/com/android/settings/UniquePreferenceTest.java
new file mode 100644
index 0000000..2236b94
--- /dev/null
+++ b/tests/unit/src/com/android/settings/UniquePreferenceTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import static junit.framework.Assert.fail;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.provider.SearchIndexableResource;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.settings.search.DatabaseIndexingUtils;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.SearchIndexableResources;
+import com.android.settings.search.XmlParserUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class UniquePreferenceTest {
+
+    private static final String TAG = "UniquePreferenceTest";
+    private static final List<String> SUPPORTED_PREF_TYPES = Arrays.asList(
+            "Preference", "PreferenceCategory", "PreferenceScreen");
+
+    private Context mContext;
+
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    /**
+     * All preferences should have their unique key. It's especially important for many parts of
+     * Settings to work properly: we assume pref keys are unique in displaying, search ranking,\
+     * search result suppression, and many other areas.
+     * <p/>
+     * So in this test we are checking preferences participating in search.
+     * <p/>
+     * Note: Preference is not limited to just <Preference/> object. Everything in preference xml
+     * should have a key.
+     */
+    @Test
+    public void allPreferencesShouldHaveUniqueKey()
+            throws IOException, XmlPullParserException, Resources.NotFoundException {
+        final Set<String> uniqueKeys = new HashSet<>();
+        final Set<String> nullKeyClasses = new HashSet<>();
+        final Set<String> duplicatedKeys = new HashSet<>();
+        for (SearchIndexableResource sir : SearchIndexableResources.values()) {
+            verifyPreferenceIdInXml(uniqueKeys, duplicatedKeys, nullKeyClasses, sir);
+        }
+
+        if (!nullKeyClasses.isEmpty()) {
+            final StringBuilder nullKeyErrors = new StringBuilder()
+                    .append("Each preference must have a key, ")
+                    .append("the following classes have pref without keys:\n");
+            for (String c : nullKeyClasses) {
+                nullKeyErrors.append(c).append("\n");
+            }
+            fail(nullKeyErrors.toString());
+        }
+
+        if (!duplicatedKeys.isEmpty()) {
+            final StringBuilder dupeKeysError = new StringBuilder(
+                    "The following keys are not unique\n");
+            for (String c : duplicatedKeys) {
+                dupeKeysError.append(c).append("\n");
+            }
+            fail(dupeKeysError.toString());
+        }
+    }
+
+    private void verifyPreferenceIdInXml(Set<String> uniqueKeys, Set<String> duplicatedKeys,
+            Set<String> nullKeyClasses, SearchIndexableResource page)
+            throws IOException, XmlPullParserException, Resources.NotFoundException {
+        final Class<?> clazz = DatabaseIndexingUtils.getIndexableClass(page.className);
+
+        final Indexable.SearchIndexProvider provider =
+                DatabaseIndexingUtils.getSearchIndexProvider(clazz);
+        final List<SearchIndexableResource> resourcesToIndex =
+                provider.getXmlResourcesToIndex(mContext, true);
+        if (resourcesToIndex == null) {
+            Log.d(TAG, page.className + "is not providing SearchIndexableResource, skipping");
+            return;
+        }
+
+        for (SearchIndexableResource sir : resourcesToIndex) {
+            if (sir.xmlResId <= 0) {
+                Log.d(TAG, page.className + " doesn't have a valid xml to index.");
+                continue;
+            }
+            final XmlResourceParser parser = mContext.getResources().getXml(sir.xmlResId);
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+                // Parse next until start tag is found
+            }
+            final int outerDepth = parser.getDepth();
+
+            do {
+                if (type != XmlPullParser.START_TAG) {
+                    continue;
+                }
+                final String nodeName = parser.getName();
+                if (!SUPPORTED_PREF_TYPES.contains(nodeName) && !nodeName.endsWith("Preference")) {
+                    continue;
+                }
+                final AttributeSet attrs = Xml.asAttributeSet(parser);
+                final String key = XmlParserUtils.getDataKey(mContext, attrs);
+                if (TextUtils.isEmpty(key)) {
+                    Log.e(TAG, "Every preference must have an key; found null key"
+                            + " in " + page.className
+                            + " at " + parser.getPositionDescription());
+                    nullKeyClasses.add(page.className);
+                    continue;
+                }
+                if (uniqueKeys.contains(key)) {
+                    Log.e(TAG, "Every preference key must unique; found " + nodeName
+                            + " in " + page.className
+                            + " at " + parser.getPositionDescription());
+                    duplicatedKeys.add(key);
+                }
+                uniqueKeys.add(key);
+            } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth));
+        }
+    }
+}
diff --git a/tests/unit/src/com/android/settings/utils/MockedServiceManager.java b/tests/unit/src/com/android/settings/utils/MockedServiceManager.java
new file mode 100644
index 0000000..ea04974
--- /dev/null
+++ b/tests/unit/src/com/android/settings/utils/MockedServiceManager.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.testutils;
+
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+// This class is for replacing existing system service with the mocked service.
+// Copied from CellBroadcastReceiver app.
+public final class MockedServiceManager {
+
+    private final String TAG = MockedServiceManager.class.getSimpleName();
+
+    private final HashMap<String, IBinder> mServiceManagerMockedServices = new HashMap<>();
+
+    private final HashMap<InstanceKey, Object> mOldInstances = new HashMap<>();
+
+    private final LinkedList<InstanceKey> mInstanceKeys = new LinkedList<>();
+
+    private static class InstanceKey {
+        final Class mClass;
+        final String mInstName;
+        final Object mObj;
+
+        InstanceKey(final Class c, final String instName, final Object obj) {
+            mClass = c;
+            mInstName = instName;
+            mObj = obj;
+        }
+
+        @Override
+        public int hashCode() {
+            return (mClass.getName().hashCode() * 31 + mInstName.hashCode()) * 31;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null || obj.getClass() != getClass()) {
+                return false;
+            }
+
+            InstanceKey other = (InstanceKey) obj;
+            return (other.mClass == mClass && other.mInstName.equals(mInstName)
+                    && other.mObj == mObj);
+        }
+    }
+
+    public MockedServiceManager() throws Exception {
+        replaceInstance(ServiceManager.class, "sCache", null, mServiceManagerMockedServices);
+    }
+
+    public void replaceService(String key, IBinder binder) {
+        mServiceManagerMockedServices.put(key, binder);
+    }
+
+    public void restoreAllServices() throws Exception {
+        restoreInstances();
+    }
+
+    public synchronized void replaceInstance(final Class c, final String instanceName,
+            final Object obj, final Object newValue)
+            throws Exception {
+        Field field = c.getDeclaredField(instanceName);
+        field.setAccessible(true);
+
+        InstanceKey key = new InstanceKey(c, instanceName, obj);
+        if (!mOldInstances.containsKey(key)) {
+            mOldInstances.put(key, field.get(obj));
+            mInstanceKeys.add(key);
+        }
+        field.set(obj, newValue);
+    }
+
+    public synchronized void restoreInstances() throws Exception {
+        Iterator<InstanceKey> it = mInstanceKeys.descendingIterator();
+
+        while (it.hasNext()) {
+            InstanceKey key = it.next();
+            Field field = key.mClass.getDeclaredField(key.mInstName);
+            field.setAccessible(true);
+            field.set(key.mObj, mOldInstances.get(key));
+        }
+
+        mInstanceKeys.clear();
+        mOldInstances.clear();
+    }
+}
diff --git a/tests/unit/src/com/android/settings/wifi/WifiCallingSettingUiTest.java b/tests/unit/src/com/android/settings/wifi/WifiCallingSettingUiTest.java
new file mode 100644
index 0000000..16617d0
--- /dev/null
+++ b/tests/unit/src/com/android/settings/wifi/WifiCallingSettingUiTest.java
@@ -0,0 +1,299 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+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.isCompletelyDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isSelected;
+import static android.support.test.espresso.matcher.ViewMatchers.withResourceName;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.anything;
+import static org.junit.Assert.assertEquals;
+import static org.junit.matchers.JUnitMatchers.containsString;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewInteraction;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.telephony.SubscriptionInfo;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsManager;
+import com.android.internal.telephony.SubscriptionController;
+import com.android.settings.testutils.MockedServiceManager;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class WifiCallingSettingUiTest {
+    private static final String SUBSCRIPTION0_NAME = "SUB0";
+    private static final String SUBSCRIPTION1_NAME = "SUB1";
+    private static final String WFC_MODE_TITLE = "Calling preference";
+    private static final String WFC_MODE_WIFI_ONLY = "Wi-Fi only";
+    private static final String WFC_MODE_WIFI_PREFERRED = "Wi-Fi preferred";
+    private static final String WFC_MODE_CELLULAR_PREFERRED = "Mobile preferred";
+
+    private Instrumentation mInstrumentation;
+    private Context mContext;
+    private UiDevice mDevice;
+    @Mock
+    SubscriptionController mSubscriptionController;
+    MockedServiceManager mMockedServiceManager;
+    protected HashMap<Integer, ImsManager> mImsManagerInstances = new HashMap<>();
+    List<SubscriptionInfo> mSils = new ArrayList();
+    @Mock
+    SubscriptionInfo mSubscriptionInfo0;
+    @Mock
+    SubscriptionInfo mSubscriptionInfo1;
+    @Mock
+    ImsManager mImsManager0;
+    @Mock
+    ImsManager mImsManager1;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getTargetContext();
+        mDevice = UiDevice.getInstance(mInstrumentation);
+
+        mMockedServiceManager = new MockedServiceManager();
+        mMockedServiceManager.replaceService("isub", mSubscriptionController);
+
+        mMockedServiceManager.replaceInstance(
+                ImsManager.class, "sImsManagerInstances", null, mImsManagerInstances);
+        mMockedServiceManager.replaceInstance(
+                SubscriptionController.class, "sInstance", null, mSubscriptionController);
+        doReturn(mSubscriptionController)
+                .when(mSubscriptionController).queryLocalInterface(anyString());
+        mImsManagerInstances.put(0, mImsManager0);
+        mImsManagerInstances.put(1, mImsManager1);
+        doReturn(mSils).when(mSubscriptionController).getActiveSubscriptionInfoList(anyString());
+        doReturn(0).when(mSubscriptionController).getPhoneId(0);
+        doReturn(1).when(mSubscriptionController).getPhoneId(1);
+        doReturn(0).when(mSubscriptionInfo0).getSubscriptionId();
+        doReturn(1).when(mSubscriptionInfo1).getSubscriptionId();
+        doReturn(0).when(mSubscriptionInfo0).getSimSlotIndex();
+        doReturn(1).when(mSubscriptionInfo1).getSimSlotIndex();
+        doReturn(SUBSCRIPTION0_NAME).when(mSubscriptionInfo0).getDisplayName();
+        doReturn(SUBSCRIPTION1_NAME).when(mSubscriptionInfo1).getDisplayName();
+
+        doReturn(true).when(mImsManager0).isWfcEnabledByPlatform();
+        doReturn(true).when(mImsManager0).isNonTtyOrTtyOnVolteEnabled();
+        doReturn(true).when(mImsManager1).isWfcEnabledByPlatform();
+        doReturn(true).when(mImsManager1).isNonTtyOrTtyOnVolteEnabled();
+
+        mDevice.wakeUp();
+        mDevice.pressMenu();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mMockedServiceManager.restoreAllServices();
+    }
+
+    @Test
+    public void testSingleSimUi() throws InterruptedException {
+        configureSingleSim();
+        doReturn(true).when(mImsManager0).isWfcEnabledByUser();
+        doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED)
+                .when(mImsManager0).getWfcMode();
+        doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED)
+                .when(mImsManager0).getWfcMode(anyBoolean());
+
+        mInstrumentation.startActivitySync(createActivityIntent());
+
+        checkSingleSimUi();
+
+        try {
+            mDevice.setOrientationLeft();
+        } catch (Exception e) {
+            Assert.fail("Exception " + e);
+        }
+
+        // Re-check after rotation. Fragment should be recreated properly.
+        checkSingleSimUi();
+
+        try {
+            mDevice.setOrientationNatural();
+        } catch (Exception e) {
+            Assert.fail("Exception " + e);
+        }
+
+        // Re-check after rotation. Fragment should be resumed properly.
+        checkSingleSimUi();
+    }
+
+    private void checkSingleSimUi() {
+        assertEquals(false, checkExists(onView(withText(SUBSCRIPTION0_NAME))));
+        assertEquals(false, checkExists(onView(withText(SUBSCRIPTION1_NAME))));
+        assertEquals(true, checkExists(onView(withText(WFC_MODE_TITLE))));
+        assertEquals(true, checkExists(onView(withText(WFC_MODE_WIFI_PREFERRED))));
+        checkSwitchBarStatus(true, true);
+        checkEmptyViewStatus(false);
+    }
+
+    @Test
+    public void testNoValidSub() throws InterruptedException {
+        configureDualSim();
+        doReturn(false).when(mImsManager0).isWfcEnabledByPlatform();
+        doReturn(false).when(mImsManager0).isNonTtyOrTtyOnVolteEnabled();
+        doReturn(false).when(mImsManager1).isWfcEnabledByPlatform();
+        doReturn(false).when(mImsManager1).isNonTtyOrTtyOnVolteEnabled();
+        doReturn(false).when(mImsManager0).isWfcEnabledByUser();
+        doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED)
+                .when(mImsManager0).getWfcMode();
+        doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED)
+                .when(mImsManager0).getWfcMode(anyBoolean());
+
+        Activity activity = mInstrumentation.startActivitySync(createActivityIntent());
+
+        assertEquals(false, checkExists(onView(withText(SUBSCRIPTION0_NAME))));
+        assertEquals(false, checkExists(onView(withText(SUBSCRIPTION1_NAME))));
+        assertEquals(false, checkExists(onView(withText(WFC_MODE_TITLE))));
+
+        checkSwitchBarStatus(false, false);
+        checkEmptyViewStatus(false);
+    }
+
+    @Test
+    public void testWfcDisabled() throws InterruptedException {
+        configureSingleSim();
+        doReturn(false).when(mImsManager0).isWfcEnabledByUser();
+        doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED)
+                .when(mImsManager0).getWfcMode();
+        doReturn(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED)
+                .when(mImsManager0).getWfcMode(anyBoolean());
+
+        Activity activity = mInstrumentation.startActivitySync(createActivityIntent());
+
+        assertEquals(false, checkExists(onView(withText(SUBSCRIPTION0_NAME))));
+        assertEquals(false, checkExists(onView(withText(SUBSCRIPTION1_NAME))));
+        assertEquals(false, checkExists(onView(withText(WFC_MODE_TITLE))));
+
+        checkSwitchBarStatus(true, false);
+        checkEmptyViewStatus(true);
+    }
+
+    @Test
+    public void testDualSimUi() throws InterruptedException {
+        configureDualSim();
+        doReturn(true).when(mImsManager0).isWfcEnabledByUser();
+        doReturn(false).when(mImsManager1).isWfcEnabledByUser();
+        doReturn(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED)
+                .when(mImsManager0).getWfcMode();
+        doReturn(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED)
+                .when(mImsManager0).getWfcMode(anyBoolean());
+
+        mInstrumentation.startActivitySync(createActivityIntent());
+
+        assertEquals(true, checkExists(onView(withText(SUBSCRIPTION0_NAME))));
+        assertEquals(true, checkExists(onView(withText(SUBSCRIPTION1_NAME))));
+        assertEquals(true, checkExists(onView(withText(WFC_MODE_TITLE))));
+        assertEquals(true, checkExists(onView(withText(WFC_MODE_CELLULAR_PREFERRED))));
+
+        onView(withText(SUBSCRIPTION0_NAME)).check(matches(isSelected()));
+        checkSwitchBarStatus(true, true);
+        checkEmptyViewStatus(false);
+
+        // Switch to SUB1.
+        onView(withText(SUBSCRIPTION1_NAME)).perform(click());
+
+        checkSwitchBarStatus(true, false);
+        checkEmptyViewStatus(true);
+        onView(withText(SUBSCRIPTION1_NAME)).check(matches(isSelected()));
+    }
+
+    private boolean checkExists(ViewInteraction v) {
+        try {
+            v.check(matches(isCompletelyDisplayed()));
+            return true;
+        } catch (NoMatchingViewException e) {
+            return false;
+        }
+    }
+
+    private Intent createActivityIntent() {
+        Intent intent = new Intent(mContext,
+                com.android.settings.Settings.WifiCallingSettingsActivity.class);
+        intent.setPackage("com.android.settings");
+        intent.setAction("android.intent.action.MAIN");
+        return intent;
+    }
+
+    private void configureSingleSim() {
+        mSils.clear();
+        mSils.add(mSubscriptionInfo0);
+    }
+
+    private void configureDualSim() {
+        mSils.clear();
+        mSils.add(mSubscriptionInfo0);
+        mSils.add(mSubscriptionInfo1);
+    }
+
+    private void checkSwitchBarStatus(boolean shouldDisplay, boolean statusOn) {
+        if (shouldDisplay) {
+            try {
+                onView(allOf(withResourceName("switch_text"), isCompletelyDisplayed()))
+                        .check(matches(withText(containsString(statusOn ? "On" : "Off"))));
+            } catch (Exception e) {
+                Assert.fail("Exception " + e);
+            }
+        } else {
+            onView(allOf(withResourceName("switch_text"), isCompletelyDisplayed()))
+                    .check(doesNotExist());
+        }
+    }
+
+    private void checkEmptyViewStatus(boolean shouldDisplay) {
+        try {
+            if (!shouldDisplay) {
+                onView(allOf(withResourceName("empty"), isCompletelyDisplayed()))
+                        .check(doesNotExist());
+            } else {
+                onView(allOf(withResourceName("empty"), isCompletelyDisplayed()))
+                        .check(matches(anything()));
+            }
+        } catch (Exception e) {
+            Assert.fail("Exception " + e);
+        }
+    }
+}