Merge "SubId are Integers." into lmp-mr1-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4c7f558..5701dfb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -180,7 +180,7 @@
     <!-- Cell Broadcast settings title.  [CHAR LIMIT=50] -->
     <string name="cell_broadcast_settings">Emergency broadcasts</string>
     <!-- Call settings screen title -->
-    <string name="call_settings">Call Settings</string>
+    <string name="call_settings">Call settings</string>
     <!-- GSM Call settings screen, setting option name -->
     <string name="additional_gsm_call_settings">Additional settings</string>
     <!-- GSM-only Call settings screen, setting option name-->
@@ -260,6 +260,8 @@
 
     <!-- Title of the progress dialog displayed while updating Call settings -->
     <string name="updating_title">Call settings</string>
+    <!-- Title of the "Call settings" settings screen, with a text label identifying which SIM the settings are for. -->
+    <string name="call_settings_with_label">Call settings (<xliff:g id="subscriptionlabel" example="Verizon">%s</xliff:g>)</string>
     <!-- Title of the alert dialog displayed if an error occurs while updating Call settings -->
     <string name="error_updating_title">Call settings error</string>
     <!-- Toast in Call settings dialog while settings are being read -->
@@ -688,11 +690,17 @@
     <string name="carrier_settings_title">Carrier settings</string>
 
     <!-- FDN settings strings -->
-    <!-- Call settings screen, setting option name -->
+    <!-- Label for "Fixed Dialing Number" settings in call settings. -->
     <string name="fdn">Fixed Dialing Numbers</string>
-    <!-- Call settings screen, button label that takes you to
-         the Fixed Dialing Number management screen -->
+    <!-- Title for "Fixed Dialing Number" settings, with a label to identify the SIM the settings
+        apply to. -->
+    <string name="fdn_with_label">Fixed Dialing Numbers (<xliff:g id="subscriptionlabel" example="Verizon">%s</xliff:g>)</string>
+
+    <!-- Call settings screen, button label that takes you to the Fixed Dialing Number management screen -->
     <string name="manage_fdn_list">FDN list</string>
+    <!-- Title for settings screen to manage Fixed Dialing Number contacts, with a label to identify
+         the SIM the settings apply to. -->
+    <string name="fdn_list_with_label">FDN list (<xliff:g id="subscriptionlabel" example="Verizon">%s</xliff:g>)</string>
     <!-- Call settings screen, preference item label -->
     <string name="fdn_activation">FDN activation</string>
     <!-- Call settings setting option name when FDN is enabled -->
@@ -854,9 +862,6 @@
     <!-- SIM PIN screen: button label -->
     <string name="doneButton">Done</string>
 
-    <!-- In-call screen: status label for a conference call -->
-    <string name="caller_manage_header">Conference call <xliff:g id="conf_call_time">%s</xliff:g></string>
-
     <!-- Used in FakePhoneActivity test code.  DO NOT TRANSLATE. -->
     <string name="fake_phone_activity_phoneNumber_text" translatable="false">(650) 555-1234</string>
     <!-- Used in FakePhoneActivity test code.  DO NOT TRANSLATE. -->
@@ -884,9 +889,6 @@
     <string name="card_title_hanging_up">Hanging up</string>
     <!-- In-call screen: status label for a call that's in CDMA flash mode -->
     <string name="card_title_in_call">In call</string>
-    <!-- In-call screen: special status label that shows your own phone
-         number during emergency callback mode (ECM) [CHAR LIMIT=30] -->
-    <string name="card_title_my_phone_number">My number is <xliff:g id="my_phone_number">%s</xliff:g></string>
 
     <!-- Notification strings -->
     <!-- The "label" of the in-call Notification for a dialing call, used
diff --git a/res/xml/call_feature_setting.xml b/res/xml/call_feature_setting.xml
index bcda6b0..0182ac0 100644
--- a/res/xml/call_feature_setting.xml
+++ b/res/xml/call_feature_setting.xml
@@ -15,7 +15,7 @@
 -->
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:phone="http://schemas.android.com/apk/res/com.android.phone"
-    android:title="@string/updating_title">
+    android:title="@string/call_settings">
 
     <PreferenceScreen
         android:key="phone_account_settings_preference_screen"
@@ -81,13 +81,7 @@
     <PreferenceScreen
         android:key="button_fdn_key"
         android:title="@string/fdn"
-        android:persistent="false">
-
-        <intent android:action="android.intent.action.MAIN"
-            android:targetPackage="com.android.phone"
-            android:targetClass="com.android.phone.settings.fdn.FdnSetting" />
-
-    </PreferenceScreen>
+        android:persistent="false" />
 
     <CheckBoxPreference
         android:key="button_enable_video_calling"
diff --git a/res/xml/fdn_setting.xml b/res/xml/fdn_setting.xml
index d03cf7c..7501dc8 100644
--- a/res/xml/fdn_setting.xml
+++ b/res/xml/fdn_setting.xml
@@ -30,17 +30,12 @@
         android:summary="@string/sum_fdn_change_pin"
         android:persistent="false"/>
 
+    <!-- The intent to launch the FDN List is set on this PreferenceScreen in FdnSetting, so that
+         its extras can be dynamically changed to include subscription information. -->
     <PreferenceScreen
-        android:key="button_fdn_list_key"
+        android:key="fdn_list_pref_screen_key"
         android:title="@string/manage_fdn_list"
         android:summary="@string/sum_fdn_manage_list"
-        android:persistent="false">
-
-        <!-- Intent to lauch the FDN list. -->
-        <intent android:action="android.intent.action.MAIN"
-            android:targetPackage="com.android.phone"
-            android:targetClass="com.android.phone.settings.fdn.FdnList" />
-
-    </PreferenceScreen>
+        android:persistent="false"/>
 
 </PreferenceScreen>
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index d56a274..f996922 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -57,16 +57,18 @@
 
 import com.android.ims.ImsManager;
 import com.android.internal.telephony.CallForwardInfo;
-import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.phone.common.util.SettingsUtil;
 import com.android.phone.settings.AccountSelectionPreference;
+import com.android.phone.settings.CallForwardInfoUtil;
 import com.android.phone.settings.VoicemailProviderSettings;
 import com.android.phone.settings.VoicemailProviderSettingsUtil;
+import com.android.phone.settings.fdn.FdnSetting;
 import com.android.services.telephony.sip.SipUtil;
 
 import java.lang.String;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -114,13 +116,6 @@
     // to trigger its configuration UI
     public static final String ACTION_CONFIGURE_VOICEMAIL =
             "com.android.phone.CallFeaturesSetting.CONFIGURE_VOICEMAIL";
-    // Extra on intent to Call Settings containing the id of the subscription to modify.
-    public static final String SUB_ID_EXTRA =
-            "com.android.phone.CallFeaturesSetting.SubscriptionId";
-    // Extra on intent to Call Settings containing the label of the subscription to modify.
-    public static final String SUB_LABEL_EXTRA =
-            "com.android.phone.CallFeaturesSetting.SubscriptionLabel";
-
     // Extra put in the return from VM provider config containing voicemail number to set
     public static final String VM_NUMBER_EXTRA = "com.android.phone.VoicemailNumber";
     // Extra put in the return from VM provider config containing call forwarding number to set
@@ -206,25 +201,12 @@
     private static final int VOICEMAIL_FWD_READING_DIALOG = 602;
     private static final int VOICEMAIL_REVERTING_DIALOG = 603;
 
-    // status message sent back from handlers
-    private static final int MSG_OK = 100;
-
-    // special statuses for voicemail controls.
-    private static final int MSG_VM_EXCEPTION = 400;
-    private static final int MSG_FW_SET_EXCEPTION = 401;
-    private static final int MSG_FW_GET_EXCEPTION = 402;
-    private static final int MSG_VM_OK = 600;
-    private static final int MSG_VM_NOCHANGE = 700;
-
-    /**
-     * @see CallForwardInfo#status
-     */
-    private static final int CALL_FORWARD_INFO_INACTIVE_STATUS = 0;
-
     // voicemail notification vibration string constants
     private static final String VOICEMAIL_VIBRATION_ALWAYS = "always";
     private static final String VOICEMAIL_VIBRATION_NEVER = "never";
 
+    private SubscriptionInfoHelper mSubscriptionInfoHelper;
+
     private EditPhoneNumberPreference mSubMenuVoicemailSettings;
 
     /** Whether dialpad plays DTMF tone or not. */
@@ -240,12 +222,17 @@
     private CheckBoxPreference mEnableVideoCalling;
 
     private class VoiceMailProvider {
+        public String name;
+        public Intent intent;
+
         public VoiceMailProvider(String name, Intent intent) {
             this.name = name;
             this.intent = intent;
         }
-        public String name;
-        public Intent intent;
+
+        public String toString() {
+            return "[ Name: " + name + ", Intent: " + intent + " ]";
+        }
     }
 
     /**
@@ -576,24 +563,16 @@
                     Log.i(LOG_TAG, "Requested to rollback Fwd changes.");
                     final CallForwardInfo[] prevFwdSettings = prevSettings.getForwardingSettings();
                     if (prevFwdSettings != null) {
-                        Map<Integer, AsyncResult> results =
-                            mForwardingChangeResults;
+                        Map<Integer, AsyncResult> results = mForwardingChangeResults;
                         resetForwardingChangeState();
                         for (int i = 0; i < prevFwdSettings.length; i++) {
                             CallForwardInfo fi = prevFwdSettings[i];
                             if (DBG) log("Reverting fwd #: " + i + ": " + fi.toString());
-                            // Only revert the settings for which the update
-                            // succeeded
+                            // Only revert the settings for which the update succeeded.
                             AsyncResult result = results.get(fi.reason);
                             if (result != null && result.exception == null) {
                                 mExpectedChangeResultReasons.add(fi.reason);
-                                mPhone.setCallForwardingOption(
-                                        (fi.status == 1 ?
-                                                CommandsInterface.CF_ACTION_REGISTRATION :
-                                                CommandsInterface.CF_ACTION_DISABLE),
-                                        fi.reason,
-                                        fi.number,
-                                        fi.timeSeconds,
+                                CallForwardInfoUtil.setCallForwardingOption(mPhone, fi,
                                         mRevertOptionComplete.obtainMessage(
                                                 EVENT_FORWARDING_CHANGED, i, 0));
                             }
@@ -613,7 +592,7 @@
         updateVMPreferenceWidgets(mPreviousVMProviderKey);
         updateVoiceNumberField();
         if (mVMOrFwdSetError != 0) {
-            showVMDialog(mVMOrFwdSetError);
+            showDialogIfForeground(mVMOrFwdSetError);
             mVMOrFwdSetError = 0;
         }
     }
@@ -739,6 +718,9 @@
     // actual work to handle these events whether or not we're in the
     // foreground (see the Handler code in mSetOptionComplete for
     // example.)
+    //
+    // TODO: It's a bit worrisome that we don't do anything in error cases when we're not in the
+    // foreground. Consider displaying a toast instead.
     private void showDialogIfForeground(int id) {
         if (mForeground) {
             showDialog(id);
@@ -778,7 +760,7 @@
         // Throw a warning if the voicemail is the same and we did not change forwarding.
         if (mNewVMNumber.equals(mOldVmNumber)
                 && mNewFwdSettings == VoicemailProviderSettings.NO_FORWARDING) {
-            showVMDialog(MSG_VM_NOCHANGE);
+            showDialogIfForeground(VM_NOCHANGE_ERROR);
             return;
         }
 
@@ -819,14 +801,12 @@
         if (DBG) Log.d(LOG_TAG, "handleForwardingSettingsReadResult: " + idx);
         Throwable error = null;
         if (ar.exception != null) {
-            if (DBG) Log.d(LOG_TAG, "FwdRead: ar.exception=" +
-                    ar.exception.getMessage());
             error = ar.exception;
+            if (DBG) Log.d(LOG_TAG, "FwdRead: ar.exception=" + error.getMessage());
         }
         if (ar.userObj instanceof Throwable) {
-            if (DBG) Log.d(LOG_TAG, "FwdRead: userObj=" +
-                    ((Throwable)ar.userObj).getMessage());
-            error = (Throwable)ar.userObj;
+            error = (Throwable) ar.userObj;
+            if (DBG) Log.d(LOG_TAG, "FwdRead: userObj=" + error.getMessage());
         }
 
         // We may have already gotten an error and decided to ignore the other results.
@@ -840,37 +820,14 @@
             if (DBG) Log.d(LOG_TAG, "Error discovered for fwd read : " + idx);
             mForwardingReadResults = null;
             dismissDialogSafely(VOICEMAIL_FWD_READING_DIALOG);
-            showVMDialog(MSG_FW_GET_EXCEPTION);
+            showDialogIfForeground(FW_GET_RESPONSE_ERROR);
             return;
         }
 
-        // Get the forwarding info
-        final CallForwardInfo cfInfoArray[] = (CallForwardInfo[]) ar.result;
-        CallForwardInfo fi = null;
-        for (int i = 0 ; i < cfInfoArray.length; i++) {
-            if ((cfInfoArray[i].serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0) {
-                fi = cfInfoArray[i];
-                break;
-            }
-        }
-        if (fi == null) {
-
-            // In case we go nothing it means we need this reason disabled
-            // so create a CallForwardInfo for capturing this
-            if (DBG) Log.d(LOG_TAG, "Creating default info for " + idx);
-            fi = new CallForwardInfo();
-            fi.status = 0;
-            fi.reason = VoicemailProviderSettings.FORWARDING_SETTINGS_REASONS[idx];
-            fi.serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
-        } else {
-            // if there is not a forwarding number, ensure the entry is set to "not active."
-            if (fi.number == null || fi.number.length() == 0) {
-                fi.status = 0;
-            }
-
-            if (DBG) Log.d(LOG_TAG, "Got  " + fi.toString() + " for " + idx);
-        }
-        mForwardingReadResults[idx] = fi;
+        // Get the forwarding info.
+        mForwardingReadResults[idx] = CallForwardInfoUtil.getCallForwardInfo(
+                (CallForwardInfo[]) ar.result,
+                VoicemailProviderSettings.FORWARDING_SETTINGS_REASONS[idx]);
 
         // Check if we got all the results already
         boolean done = true;
@@ -880,9 +837,11 @@
                 break;
             }
         }
+
         if (done) {
             if (DBG) Log.d(LOG_TAG, "Done receiving fwd info");
             dismissDialogSafely(VOICEMAIL_FWD_READING_DIALOG);
+
             if (mReadingSettingsForDefaultProvider) {
                 mVmProviderSettingsUtil.save(DEFAULT_VM_PROVIDER_KEY,
                         new VoicemailProviderSettings(this.mOldVmNumber, mForwardingReadResults));
@@ -893,34 +852,6 @@
             if (DBG) Log.d(LOG_TAG, "Not done receiving fwd info");
         }
     }
-
-    private CallForwardInfo infoForReason(CallForwardInfo[] infos, int reason) {
-        CallForwardInfo result = null;
-        if (null != infos) {
-            for (CallForwardInfo info : infos) {
-                if (info.reason == reason) {
-                    result = info;
-                    break;
-                }
-            }
-        }
-        return result;
-    }
-
-    private boolean isUpdateRequired(CallForwardInfo oldInfo, CallForwardInfo newInfo) {
-        if (oldInfo == null) {
-            return true;
-        }
-
-        // If we're disabling a type of forwarding, don't make any change if it's already disabled.
-        if (newInfo.status == CALL_FORWARD_INFO_INACTIVE_STATUS
-                && oldInfo.status == CALL_FORWARD_INFO_INACTIVE_STATUS) {
-            return false;
-        }
-
-        return true;
-    }
-
     private void resetForwardingChangeState() {
         mForwardingChangeResults = new HashMap<Integer, AsyncResult>();
         mExpectedChangeResultReasons = new HashSet<Integer>();
@@ -935,21 +866,15 @@
             resetForwardingChangeState();
             for (int i = 0; i < mNewFwdSettings.length; i++) {
                 CallForwardInfo fi = mNewFwdSettings[i];
-
-                final boolean doUpdate = isUpdateRequired(infoForReason(
-                            mForwardingReadResults, fi.reason), fi);
+                CallForwardInfo fiForReason =
+                        CallForwardInfoUtil.infoForReason(mForwardingReadResults, fi.reason);
+                final boolean doUpdate = CallForwardInfoUtil.isUpdateRequired(fiForReason, fi);
 
                 if (doUpdate) {
                     if (DBG) log("Setting fwd #: " + i + ": " + fi.toString());
                     mExpectedChangeResultReasons.add(i);
 
-                    mPhone.setCallForwardingOption(
-                            fi.status == 1 ?
-                                    CommandsInterface.CF_ACTION_REGISTRATION :
-                                    CommandsInterface.CF_ACTION_DISABLE,
-                            fi.reason,
-                            fi.number,
-                            fi.timeSeconds,
+                    CallForwardInfoUtil.setCallForwardingOption(mPhone, fi,
                             mSetOptionComplete.obtainMessage(
                                     EVENT_FORWARDING_CHANGED, fi.reason, 0));
                 }
@@ -1116,12 +1041,12 @@
         if (DBG) log("handleSetVMMessage: set VM request complete");
 
         if (!isFwdChangeSuccess()) {
-            handleVmOrFwdSetError(MSG_FW_SET_EXCEPTION);
+            handleVmOrFwdSetError(FW_SET_RESPONSE_ERROR);
         } else if (!isVmChangeSuccess()) {
-            handleVmOrFwdSetError(MSG_VM_EXCEPTION);
+            handleVmOrFwdSetError(VM_RESPONSE_ERROR);
         } else {
             if (DBG) log("change VM success!");
-            handleVmAndFwdSetSuccess(MSG_VM_OK);
+            handleVmAndFwdSetSuccess(VOICEMAIL_DIALOG_CONFIRM);
         }
     }
 
@@ -1129,18 +1054,18 @@
      * Called when Voicemail Provider or its forwarding settings failed. Rolls back partly made
      * changes to those settings and show "failure" dialog.
      *
-     * @param msgId Message ID used for the specific error case. {@link #MSG_FW_SET_EXCEPTION} or
-     * {@link #MSG_VM_EXCEPTION}
+     * @param dialogId ID of the dialog to show for the specific error case. Either
+     *     {@link #FW_SET_RESPONSE_ERROR} or {@link #VM_RESPONSE_ERROR}
      */
-    private void handleVmOrFwdSetError(int msgId) {
+    private void handleVmOrFwdSetError(int dialogId) {
         if (mChangingVMorFwdDueToProviderChange) {
-            mVMOrFwdSetError = msgId;
+            mVMOrFwdSetError = dialogId;
             mChangingVMorFwdDueToProviderChange = false;
             switchToPreviousVoicemailProvider();
             return;
         }
         mChangingVMorFwdDueToProviderChange = false;
-        showVMDialog(msgId);
+        showDialogIfForeground(dialogId);
         updateVoiceNumberField();
     }
 
@@ -1148,14 +1073,14 @@
      * Called when Voicemail Provider and its forwarding settings were successfully finished.
      * This updates a bunch of variables and show "success" dialog.
      */
-    private void handleVmAndFwdSetSuccess(int msg) {
+    private void handleVmAndFwdSetSuccess(int dialogId) {
         if (DBG) {
             log("handleVmAndFwdSetSuccess(). current voicemail provider key: "
                     + getCurrentVoicemailProviderKey());
         }
         mPreviousVMProviderKey = getCurrentVoicemailProviderKey();
         mChangingVMorFwdDueToProviderChange = false;
-        showVMDialog(msg);
+        showDialogIfForeground(dialogId);
         updateVoiceNumberField();
     }
 
@@ -1257,7 +1182,7 @@
         } else if (id == VOICEMAIL_FWD_SAVING_DIALOG || id == VOICEMAIL_FWD_READING_DIALOG ||
                 id == VOICEMAIL_REVERTING_DIALOG) {
             ProgressDialog dialog = new ProgressDialog(this);
-            dialog.setTitle(getText(R.string.updating_title));
+            dialog.setTitle(getText(R.string.call_settings));
             dialog.setIndeterminate(true);
             dialog.setCancelable(false);
             dialog.setMessage(getText(
@@ -1310,32 +1235,6 @@
         }
     }
 
-    // set the app state with optional status.
-    private void showVMDialog(int msgStatus) {
-        switch (msgStatus) {
-            // It's a bit worrisome to punt in the error cases here when we're
-            // not in the foreground; maybe toast instead?
-            case MSG_VM_EXCEPTION:
-                showDialogIfForeground(VM_RESPONSE_ERROR);
-                break;
-            case MSG_FW_SET_EXCEPTION:
-                showDialogIfForeground(FW_SET_RESPONSE_ERROR);
-                break;
-            case MSG_FW_GET_EXCEPTION:
-                showDialogIfForeground(FW_GET_RESPONSE_ERROR);
-                break;
-            case MSG_VM_NOCHANGE:
-                showDialogIfForeground(VM_NOCHANGE_ERROR);
-                break;
-            case MSG_VM_OK:
-                showDialogIfForeground(VOICEMAIL_DIALOG_CONFIRM);
-                break;
-            case MSG_OK:
-            default:
-                // This should never happen.
-        }
-    }
-
     /*
      * Activity class methods
      */
@@ -1352,7 +1251,11 @@
         // ACTION_ADD_VOICEMAIL action.
         mShowVoicemailPreference = (icicle == null) &&
                 getIntent().getAction().equals(ACTION_ADD_VOICEMAIL);
-    }
+
+        mSubscriptionInfoHelper = new SubscriptionInfoHelper(getIntent());
+        mSubscriptionInfoHelper.setActionBarTitle(
+                getActionBar(), getResources(), R.string.call_settings_with_label);
+   }
 
     private void initPhoneAccountPreferences() {
         mPhoneAccountSettingsPreference = findPreference(PHONE_ACCOUNT_SETTINGS_KEY);
@@ -1383,6 +1286,7 @@
         }
 
         addPreferencesFromResource(R.xml.call_feature_setting);
+
         initPhoneAccountPreferences();
 
         PreferenceScreen prefSet = getPreferenceScreen();
@@ -1460,15 +1364,16 @@
             }
 
             int phoneType = mPhone.getPhoneType();
+            Preference fdnButton = prefSet.findPreference(BUTTON_FDN_KEY);
             if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
-                Preference fdnButton = prefSet.findPreference(BUTTON_FDN_KEY);
-                if (fdnButton != null) {
-                    prefSet.removePreference(fdnButton);
-                }
+                prefSet.removePreference(fdnButton);
+
                 if (!getResources().getBoolean(R.bool.config_voice_privacy_disable)) {
                     addPreferencesFromResource(R.xml.cdma_call_privacy);
                 }
             } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
+                fdnButton.setIntent(mSubscriptionInfoHelper.getIntent(this, FdnSetting.class));
+
                 if (getResources().getBoolean(R.bool.config_additional_call_setting)) {
                     addPreferencesFromResource(R.xml.gsm_umts_call_options);
                 }
@@ -1635,81 +1540,59 @@
         if (DBG) log("initVoiceMailProviders()");
 
         String providerToIgnore = null;
-        if (getIntent().getAction().equals(ACTION_ADD_VOICEMAIL)) {
-            if (getIntent().hasExtra(IGNORE_PROVIDER_EXTRA)) {
-                providerToIgnore = getIntent().getStringExtra(IGNORE_PROVIDER_EXTRA);
-            }
-            if (DBG) log("Found ACTION_ADD_VOICEMAIL. providerToIgnore=" + providerToIgnore);
-            if (providerToIgnore != null) {
-                // IGNORE_PROVIDER_EXTRA implies we want to remove the choice from the list.
+        if (getIntent().getAction().equals(ACTION_ADD_VOICEMAIL)
+                && getIntent().hasExtra(IGNORE_PROVIDER_EXTRA)) {
+            providerToIgnore = getIntent().getStringExtra(IGNORE_PROVIDER_EXTRA);
+            // Remove this provider from the list.
+            if (!TextUtils.isEmpty(providerToIgnore)) {
+                if (DBG) log("Found ACTION_ADD_VOICEMAIL. providerToIgnore= " + providerToIgnore);
                 mVmProviderSettingsUtil.delete(providerToIgnore);
             }
         }
 
         mVMProvidersData.clear();
 
-        // Stick the default element which is always there
+        List<String> entries = new ArrayList<String>();
+        List<String> values = new ArrayList<String>();
+
+        // Add default voicemail provider.
         final String myCarrier = getString(R.string.voicemail_default);
         mVMProvidersData.put(DEFAULT_VM_PROVIDER_KEY, new VoiceMailProvider(myCarrier, null));
+        entries.add(myCarrier);
+        values.add(DEFAULT_VM_PROVIDER_KEY);
 
-        // Enumerate providers
+        // Add other voicemail providers.
         PackageManager pm = getPackageManager();
-        Intent intent = new Intent();
-        intent.setAction(ACTION_CONFIGURE_VOICEMAIL);
+        Intent intent = new Intent(ACTION_CONFIGURE_VOICEMAIL);
         List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
-        int len = resolveInfos.size() + 1; // +1 for the default choice we will insert.
-
-        // Go through the list of discovered providers populating the data map
-        // skip the provider we were instructed to ignore if there was one
         for (int i = 0; i < resolveInfos.size(); i++) {
             final ResolveInfo ri= resolveInfos.get(i);
             final ActivityInfo currentActivityInfo = ri.activityInfo;
             final String key = currentActivityInfo.name;
+
             if (key.equals(providerToIgnore)) {
-                if (DBG) log("Ignoring key: " + key);
-                len--;
                 continue;
             }
+
             if (DBG) log("Loading key: " + key);
             final String nameForDisplay = ri.loadLabel(pm).toString();
             Intent providerIntent = new Intent();
             providerIntent.setAction(ACTION_CONFIGURE_VOICEMAIL);
-            providerIntent.setClassName(currentActivityInfo.packageName,
-                    currentActivityInfo.name);
-            if (DBG) {
-                log("Store loaded VoiceMailProvider. key: " + key
-                        + " -> name: " + nameForDisplay + ", intent: " + providerIntent);
-            }
-            mVMProvidersData.put(
-                    key,
-                    new VoiceMailProvider(nameForDisplay, providerIntent));
+            providerIntent.setClassName(currentActivityInfo.packageName, currentActivityInfo.name);
+            VoiceMailProvider vmProvider = new VoiceMailProvider(nameForDisplay, providerIntent);
 
+            if (DBG) log("Store VoiceMailProvider. Key: " + key + " -> " + vmProvider.toString());
+            mVMProvidersData.put(key, vmProvider);
+            entries.add(vmProvider.name);
+            values.add(key);
         }
 
-        // Now we know which providers to display - create entries and values array for
-        // the list preference
-        String [] entries = new String [len];
-        String [] values = new String [len];
-        entries[0] = myCarrier;
-        values[0] = DEFAULT_VM_PROVIDER_KEY;
-        int entryIdx = 1;
-        for (int i = 0; i < resolveInfos.size(); i++) {
-            final String key = resolveInfos.get(i).activityInfo.name;
-            if (!mVMProvidersData.containsKey(key)) {
-                continue;
-            }
-            entries[entryIdx] = mVMProvidersData.get(key).name;
-            values[entryIdx] = key;
-            entryIdx++;
-        }
+        mVoicemailProviders.setEntries(entries.toArray(new String[0]));
+        mVoicemailProviders.setEntryValues(values.toArray(new String[0]));
 
-        // ListPreference is now updated.
-        mVoicemailProviders.setEntries(entries);
-        mVoicemailProviders.setEntryValues(values);
-
-        // Remember the current Voicemail Provider key as a "previous" key. This will be used
-        // when we fail to update Voicemail Provider, which requires rollback.
-        // We will update this when the VM Provider setting is successfully updated.
+        // Remember the current Voicemail Provider key as a "previous" key. This will be used when
+        // we fail to update Voicemail Provider, which requires rollback. We will update this when
+        // the VM Provider setting is successfully updated.
         mPreviousVMProviderKey = getCurrentVoicemailProviderKey();
         if (DBG) log("Set up the first mPreviousVMProviderKey: " + mPreviousVMProviderKey);
 
diff --git a/src/com/android/phone/SubscriptionInfoHelper.java b/src/com/android/phone/SubscriptionInfoHelper.java
new file mode 100644
index 0000000..926a156
--- /dev/null
+++ b/src/com/android/phone/SubscriptionInfoHelper.java
@@ -0,0 +1,116 @@
+/**
+ * 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
+ */
+
+package com.android.phone;
+
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.telephony.SubInfoRecord;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+
+import com.android.phone.PhoneGlobals;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+
+/**
+ * Helper for manipulating intents or components with subscription-related information.
+ *
+ * In settings, subscription ids and labels are passed along to indicate that settings
+ * are being changed for particular subscriptions. This helper provides functions for
+ * helping extract this info and perform common operations using this info.
+ */
+public class SubscriptionInfoHelper {
+    private static final int NO_SUB_ID = -1;
+
+    // Extra on intent containing the id of a subscription.
+    private static final String SUB_ID_EXTRA =
+            "com.android.phone.settings.SubscriptionInfoHelper.SubscriptionId";
+    // Extra on intent containing the label of a subscription.
+    private static final String SUB_LABEL_EXTRA =
+            "com.android.phone.settings.SubscriptionInfoHelper.SubscriptionLabel";
+
+    private static int mSubId = NO_SUB_ID;
+    private static String mSubLabel;
+
+    /**
+     * Instantiates the helper, by extracting the subscription id and label from the intent.
+     */
+    public SubscriptionInfoHelper(Intent intent) {
+        mSubId = intent.getIntExtra(SUB_ID_EXTRA, NO_SUB_ID);
+        mSubLabel = intent.getStringExtra(SUB_LABEL_EXTRA);
+    }
+
+    /**
+     * @param context The context.
+     * @param newActivityClass The class of the activity for the intent to start.
+     * @return Intent containing extras for the subscription id and label if they exist.
+     */
+    public Intent getIntent(Context context, Class newActivityClass) {
+        Intent intent = new Intent(context, newActivityClass);
+
+        if (hasSubId()) {
+            intent.putExtra(SUB_ID_EXTRA, mSubId);
+        }
+
+        if (!TextUtils.isEmpty(mSubLabel)) {
+            intent.putExtra(SUB_LABEL_EXTRA, mSubLabel);
+        }
+
+        return intent;
+    }
+
+    public static void addExtrasToIntent(Intent intent, SubInfoRecord subscription) {
+        intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, subscription.getSubscriptionId());
+        intent.putExtra(
+                SubscriptionInfoHelper.SUB_LABEL_EXTRA, subscription.getDisplayName().toString());
+    }
+
+    /**
+     * @return Phone object. If a subscription id exists, it returns the phone for the id.
+     */
+    public Phone getPhone() {
+        return hasSubId()
+                ? PhoneFactory.getPhone(SubscriptionManager.getPhoneId(mSubId))
+                : PhoneGlobals.getPhone();
+    }
+
+    /**
+     * Sets the action bar title to the string specified by the given resource id, formatting
+     * it with the subscription label. This assumes the resource string is formattable with a
+     * string-type specifier.
+     *
+     * If the subscription label does not exists, leave the existing title.
+     */
+    public void setActionBarTitle(ActionBar actionBar, Resources res, int resId) {
+        if (actionBar == null || TextUtils.isEmpty(mSubLabel)) {
+            return;
+        }
+
+        String title = String.format(res.getString(resId), mSubLabel);
+        actionBar.setTitle(title);
+    }
+
+    public boolean hasSubId() {
+        return mSubId != NO_SUB_ID;
+    }
+
+    public int getSubId() {
+        return mSubId;
+    }
+}
diff --git a/src/com/android/phone/settings/CallForwardInfoUtil.java b/src/com/android/phone/settings/CallForwardInfoUtil.java
new file mode 100644
index 0000000..1983fab
--- /dev/null
+++ b/src/com/android/phone/settings/CallForwardInfoUtil.java
@@ -0,0 +1,129 @@
+/**
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.settings;
+
+import android.os.Message;
+import android.util.Log;
+
+import com.android.internal.telephony.CallForwardInfo;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+import com.android.phone.PhoneGlobals;
+
+public class CallForwardInfoUtil {
+    private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+    private static final String LOG_TAG = CallForwardInfoUtil.class.getSimpleName();
+
+    /**
+     * @see CallForwardInfo#status
+     */
+    private static final int CALL_FORWARD_INFO_INACTIVE_STATUS = 0;
+    private static final int CALL_FORWARD_INFO_ACTIVE_STATUS = 1;
+
+    /**
+     * Returns the first CallForwardInfo in infos which has the specified reason.
+     * @param infos array of CallForwardInfo objects.
+     * @param reason The reason we want to find a CallForwardInfo for.
+     */
+    public static CallForwardInfo infoForReason(CallForwardInfo[] infos, int reason) {
+        if (infos == null) {
+            return null;
+        }
+
+        CallForwardInfo result = null;
+        for (int i = 0; i < infos.length; i++) {
+            if (infos[i].reason == reason) {
+                return infos[i];
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Update, unless we're disabling a type of forwarding and it's already disabled.
+     */
+    public static boolean isUpdateRequired(CallForwardInfo oldInfo, CallForwardInfo newInfo) {
+        if (oldInfo == null) {
+            return true;
+        }
+
+        if (newInfo.status == CALL_FORWARD_INFO_INACTIVE_STATUS
+                && oldInfo.status == CALL_FORWARD_INFO_INACTIVE_STATUS) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Sets the call forwarding option on the phone, with the command interface action set to the
+     * appropriate value depending on whether the CallForwardInfo is active or inactive.
+     */
+    public static void setCallForwardingOption(Phone phone, CallForwardInfo info, Message message) {
+        int commandInterfaceCfAction = info.status == CALL_FORWARD_INFO_ACTIVE_STATUS
+                ? CommandsInterface.CF_ACTION_REGISTRATION
+                : CommandsInterface.CF_ACTION_DISABLE;
+
+        phone.setCallForwardingOption(commandInterfaceCfAction,
+                info.reason,
+                info.number,
+                info.timeSeconds,
+                message);
+    }
+
+    /**
+     * Retrieves a CallForwardInfo object of type {@link CommandInterface.SERVICE_CLASS_VOICE} from
+     * the array of CallForwardInfo objects. If one does not exist, instantiates an CallForwardInfo
+     * object which disables the specified reason.
+     */
+    public static CallForwardInfo getCallForwardInfo(CallForwardInfo[] infos, int reason) {
+        CallForwardInfo info = null;
+        for (int i = 0 ; i < infos.length; i++) {
+            if (isServiceClassVoice(infos[i])) {
+                info = infos[i];
+                break;
+            }
+        }
+
+        if (info == null) {
+            // If there is  no info, create a CallForwardInfo to disable this reason.
+            info = new CallForwardInfo();
+            info.status = CALL_FORWARD_INFO_INACTIVE_STATUS;
+            info.reason = reason;
+            info.serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
+
+            if (DBG) Log.d(LOG_TAG, "Created default info for reason: " + reason);
+        } else {
+            if (!hasForwardingNumber(info)) {
+                info.status = CALL_FORWARD_INFO_INACTIVE_STATUS;
+            }
+
+            if (DBG) Log.d(LOG_TAG, "Retrieved  " + info.toString() + " for " + reason);
+        }
+
+        return info;
+    }
+
+    private static boolean isServiceClassVoice(CallForwardInfo info) {
+        return (info.serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0;
+    }
+
+    private static boolean hasForwardingNumber(CallForwardInfo info) {
+        return info.number != null && info.number.length() > 0;
+    }
+}
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index 09083ac..6f436fd 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -19,6 +19,7 @@
 
 import com.android.phone.R;
 import com.android.phone.CallFeaturesSetting;
+import com.android.phone.SubscriptionInfoHelper;
 import com.android.services.telephony.sip.SipAccountRegistry;
 import com.android.services.telephony.sip.SipSharedPreferences;
 import com.android.services.telephony.sip.SipUtil;
@@ -294,8 +295,8 @@
         for (SubInfoRecord subscription : SubscriptionManager.getActiveSubInfoList()) {
             CharSequence label = subscription.getDisplayName();
             Intent intent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
-            intent.putExtra(CallFeaturesSetting.SUB_ID_EXTRA, subscription.getSubscriptionId());
-            intent.putExtra(CallFeaturesSetting.SUB_LABEL_EXTRA, label);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            SubscriptionInfoHelper.addExtrasToIntent(intent, subscription);
 
             Preference accountPreference = new Preference(mApplicationContext);
             accountPreference.setTitle(label);
diff --git a/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java b/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
index c0fc1e8..d54de43 100644
--- a/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
@@ -31,6 +31,7 @@
 
 import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
+import com.android.phone.SubscriptionInfoHelper;
 
 import static android.view.Window.PROGRESS_VISIBILITY_OFF;
 import static android.view.Window.PROGRESS_VISIBILITY_ON;
@@ -47,6 +48,8 @@
 
     private static final int PIN2_REQUEST_CODE = 100;
 
+    private SubscriptionInfoHelper mSubscriptionInfoHelper;
+
     private String mName;
     private String mNumber;
     private String mPin2;
@@ -92,6 +95,8 @@
     private void resolveIntent() {
         Intent intent = getIntent();
 
+        mSubscriptionInfoHelper = new SubscriptionInfoHelper(intent);
+
         mName =  intent.getStringExtra(INTENT_EXTRA_NAME);
         mNumber =  intent.getStringExtra(INTENT_EXTRA_NUMBER);
 
@@ -114,7 +119,7 @@
         buf.append(mPin2);
         buf.append("'");
 
-        Uri uri = Uri.parse("content://icc/fdn");
+        Uri uri = FdnList.getContentUri(mSubscriptionInfoHelper);
 
         mQueryHandler = new QueryHandler(getContentResolver());
         mQueryHandler.startDelete(0, null, uri, buf.toString(), null);
@@ -124,6 +129,7 @@
     private void authenticatePin2() {
         Intent intent = new Intent();
         intent.setClass(this, GetPin2Screen.class);
+        intent.setData(FdnList.getContentUri(mSubscriptionInfoHelper));
         startActivityForResult(intent, PIN2_REQUEST_CODE);
     }
 
diff --git a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
index 3064a7a..944eaad 100644
--- a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
@@ -49,6 +49,7 @@
 
 import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
+import com.android.phone.SubscriptionInfoHelper;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
 
@@ -68,6 +69,8 @@
 
     private static final int PIN2_REQUEST_CODE = 100;
 
+    private SubscriptionInfoHelper mSubscriptionInfoHelper;
+
     private String mName;
     private String mNumber;
     private String mPin2;
@@ -107,8 +110,7 @@
         getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
         setContentView(R.layout.edit_fdn_contact_screen);
         setupView();
-        setTitle(mAddContact ?
-                R.string.add_fdn_contact : R.string.edit_fdn_contact);
+        setTitle(mAddContact ? R.string.add_fdn_contact : R.string.edit_fdn_contact);
 
         displayProgress(false);
     }
@@ -214,6 +216,8 @@
     private void resolveIntent() {
         Intent intent = getIntent();
 
+        mSubscriptionInfoHelper = new SubscriptionInfoHelper(intent);
+
         mName =  intent.getStringExtra(INTENT_EXTRA_NAME);
         mNumber =  intent.getStringExtra(INTENT_EXTRA_NUMBER);
 
@@ -266,10 +270,6 @@
         return mNumberField.getText().toString();
     }
 
-    private Uri getContentURI() {
-        return Uri.parse("content://icc/fdn");
-    }
-
     /**
       * @param number is voice mail number
       * @return true if number length is less than 20-digit limit
@@ -291,7 +291,7 @@
             return;
         }
 
-        Uri uri = getContentURI();
+        Uri uri = FdnList.getContentUri(mSubscriptionInfoHelper);
 
         ContentValues bundle = new ContentValues(3);
         bundle.put("tag", getNameFromTextField());
@@ -314,7 +314,7 @@
             handleResult(false, true);
             return;
         }
-        Uri uri = getContentURI();
+        Uri uri = FdnList.getContentUri(mSubscriptionInfoHelper);
 
         ContentValues bundle = new ContentValues();
         bundle.put("tag", mName);
@@ -335,8 +335,7 @@
     private void deleteSelected() {
         // delete ONLY if this is NOT a new contact.
         if (!mAddContact) {
-            Intent intent = new Intent();
-            intent.setClass(this, DeleteFdnContactScreen.class);
+            Intent intent = mSubscriptionInfoHelper.getIntent(this, DeleteFdnContactScreen.class);
             intent.putExtra(INTENT_EXTRA_NAME, mName);
             intent.putExtra(INTENT_EXTRA_NUMBER, mNumber);
             startActivity(intent);
@@ -347,6 +346,7 @@
     private void authenticatePin2() {
         Intent intent = new Intent();
         intent.setClass(this, GetPin2Screen.class);
+        intent.setData(FdnList.getContentUri(mSubscriptionInfoHelper));
         startActivityForResult(intent, PIN2_REQUEST_CODE);
     }
 
diff --git a/src/com/android/phone/settings/fdn/FdnList.java b/src/com/android/phone/settings/fdn/FdnList.java
index 0f189d4..d7bfde2 100644
--- a/src/com/android/phone/settings/fdn/FdnList.java
+++ b/src/com/android/phone/settings/fdn/FdnList.java
@@ -26,8 +26,9 @@
 import android.view.View;
 import android.widget.ListView;
 
-import com.android.phone.R;
 import com.android.phone.ADNList;
+import com.android.phone.R;
+import com.android.phone.SubscriptionInfoHelper;
 
 /**
  * Fixed Dialing Number (FDN) List UI for the Phone app. FDN is a feature of the service provider
@@ -41,6 +42,11 @@
     private static final String INTENT_EXTRA_NAME = "name";
     private static final String INTENT_EXTRA_NUMBER = "number";
 
+    private static final Uri FDN_CONTENT_URI = Uri.parse("content://icc/fdn");
+    private static final String FDN_CONTENT_PATH_WITH_SUB_ID = "content://icc/fdn/subId/";
+
+    private SubscriptionInfoHelper mSubscriptionInfoHelper;
+
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -50,12 +56,16 @@
             // android.R.id.home will be triggered in onOptionsItemSelected()
             actionBar.setDisplayHomeAsUpEnabled(true);
         }
+
+        mSubscriptionInfoHelper = new SubscriptionInfoHelper(getIntent());
+        mSubscriptionInfoHelper.setActionBarTitle(
+                getActionBar(), getResources(), R.string.fdn_list_with_label);
     }
 
     @Override
     protected Uri resolveIntent() {
         Intent intent = getIntent();
-        intent.setData(Uri.parse("content://icc/fdn"));
+        intent.setData(getContentUri(mSubscriptionInfoHelper));
         return intent.getData();
     }
 
@@ -91,7 +101,7 @@
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
             case android.R.id.home:  // See ActionBar#setDisplayHomeAsUpEnabled()
-                Intent intent = new Intent(this, FdnSetting.class);
+                Intent intent = mSubscriptionInfoHelper.getIntent(this, FdnSetting.class);
                 intent.setAction(Intent.ACTION_MAIN);
                 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                 startActivity(intent);
@@ -121,10 +131,8 @@
     }
 
     private void addContact() {
-        // if we don't put extras "name" when starting this activity, then
-        // EditFdnContactScreen treats it like add contact.
-        Intent intent = new Intent();
-        intent.setClass(this, EditFdnContactScreen.class);
+        //If there is no INTENT_EXTRA_NAME provided, EditFdnContactScreen treats it as an "add".
+        Intent intent = mSubscriptionInfoHelper.getIntent(this, EditFdnContactScreen.class);
         startActivity(intent);
     }
 
@@ -146,8 +154,7 @@
             String name = mCursor.getString(NAME_COLUMN);
             String number = mCursor.getString(NUMBER_COLUMN);
 
-            Intent intent = new Intent();
-            intent.setClass(this, EditFdnContactScreen.class);
+            Intent intent = mSubscriptionInfoHelper.getIntent(this, EditFdnContactScreen.class);
             intent.putExtra(INTENT_EXTRA_NAME, name);
             intent.putExtra(INTENT_EXTRA_NUMBER, number);
             startActivity(intent);
@@ -159,11 +166,20 @@
             String name = mCursor.getString(NAME_COLUMN);
             String number = mCursor.getString(NUMBER_COLUMN);
 
-            Intent intent = new Intent();
-            intent.setClass(this, DeleteFdnContactScreen.class);
+            Intent intent = mSubscriptionInfoHelper.getIntent(this, DeleteFdnContactScreen.class);
             intent.putExtra(INTENT_EXTRA_NAME, name);
             intent.putExtra(INTENT_EXTRA_NUMBER, number);
             startActivity(intent);
         }
     }
+
+    /**
+     * Returns the uri for updating the ICC FDN entry, taking into account the subscription id.
+     */
+    public static Uri getContentUri(SubscriptionInfoHelper subscriptionInfoHelper) {
+        return subscriptionInfoHelper.hasSubId()
+                ? Uri.parse(FDN_CONTENT_PATH_WITH_SUB_ID + subscriptionInfoHelper.getSubId())
+                : FDN_CONTENT_URI;
+    }
+
 }
diff --git a/src/com/android/phone/settings/fdn/FdnSetting.java b/src/com/android/phone/settings/fdn/FdnSetting.java
index 35e15e5..8716d37 100644
--- a/src/com/android/phone/settings/fdn/FdnSetting.java
+++ b/src/com/android/phone/settings/fdn/FdnSetting.java
@@ -35,6 +35,7 @@
 import com.android.phone.CallFeaturesSetting;
 import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
+import com.android.phone.SubscriptionInfoHelper;
 
 /**
  * FDN settings UI for the Phone app.
@@ -46,6 +47,7 @@
     private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
     private static final boolean DBG = false;
 
+    private SubscriptionInfoHelper mSubscriptionInfoHelper;
     private Phone mPhone;
 
     /**
@@ -56,10 +58,9 @@
     private static final int EVENT_PIN2_CHANGE_COMPLETE = 200;
 
     // String keys for preference lookup
-    // We only care about the pin preferences here, the manage FDN contacts
-    // Preference is handled solely in xml.
     private static final String BUTTON_FDN_ENABLE_KEY = "button_fdn_enable_key";
     private static final String BUTTON_CHANGE_PIN2_KEY = "button_change_pin2_key";
+    private static final String FDN_LIST_PREF_SCREEN_KEY = "fdn_list_pref_screen_key";
 
     private EditPinPreference mButtonEnableFDN;
     private EditPinPreference mButtonChangePin2;
@@ -454,9 +455,10 @@
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        addPreferencesFromResource(R.xml.fdn_setting);
+        mSubscriptionInfoHelper = new SubscriptionInfoHelper(getIntent());
+        mPhone = mSubscriptionInfoHelper.getPhone();
 
-        mPhone = PhoneGlobals.getPhone();
+        addPreferencesFromResource(R.xml.fdn_setting);
 
         //get UI object references
         PreferenceScreen prefSet = getPreferenceScreen();
@@ -469,6 +471,10 @@
 
         mButtonChangePin2.setOnPinEnteredListener(this);
 
+        PreferenceScreen fdnListPref =
+                (PreferenceScreen) prefSet.findPreference(FDN_LIST_PREF_SCREEN_KEY);
+        fdnListPref.setIntent(mSubscriptionInfoHelper.getIntent(this, FdnList.class));
+
         // Only reset the pin change dialog if we're not in the middle of changing it.
         if (icicle == null) {
             resetPinChangeState();
@@ -485,13 +491,15 @@
         if (actionBar != null) {
             // android.R.id.home will be triggered in onOptionsItemSelected()
             actionBar.setDisplayHomeAsUpEnabled(true);
+            mSubscriptionInfoHelper.setActionBarTitle(
+                    actionBar, getResources(), R.string.fdn_with_label);
         }
     }
 
     @Override
     protected void onResume() {
         super.onResume();
-        mPhone = PhoneGlobals.getPhone();
+        mPhone = mSubscriptionInfoHelper.getPhone();
         updateEnableFDN();
     }
 
diff --git a/src/com/android/services/telephony/ConferenceParticipantConnection.java b/src/com/android/services/telephony/ConferenceParticipantConnection.java
index 7874404..e748634 100644
--- a/src/com/android/services/telephony/ConferenceParticipantConnection.java
+++ b/src/com/android/services/telephony/ConferenceParticipantConnection.java
@@ -61,6 +61,10 @@
      * @param newState The new state.
      */
     public void updateState(int newState) {
+        if (newState == getState()) {
+            return;
+        }
+
         switch (newState) {
             case STATE_INITIALIZING:
                 setInitializing();
@@ -78,7 +82,8 @@
                 setActive();
                 break;
             case STATE_DISCONNECTED:
-                setDisconnected(new DisconnectCause(DisconnectCause.REMOTE));
+                setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
+                destroy();
                 break;
             default:
                 setActive();
@@ -86,6 +91,21 @@
     }
 
     /**
+     * Disconnects the current {@code ConferenceParticipantConnection} from the conference.
+     * <p>
+     * Sends a participant disconnect signal to the associated parent connection.  The participant
+     * connection is not disconnected and cleaned up here.  On successful disconnection of the
+     * participant, the conference server will send an update to the conference controller
+     * indicating the disconnection was successful.
+     */
+    @Override
+    public void onDisconnect() {
+        Log.v(this, "onDisconnect");
+
+        mParentConnection.onDisconnectConferenceParticipant(mEndpoint);
+    }
+
+    /**
      * Configures the {@link android.telecom.PhoneCapabilities} applicable to this connection.  A
      * conference participant can only be disconnected from a conference since there is not
      * actual connection to the participant which could be split from the conference.
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index 892f3f9..43385fd 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -31,6 +31,8 @@
 import android.telecom.PhoneAccountHandle;
 
 import com.android.internal.telephony.Call;
+import com.android.internal.telephony.gsm.GsmConnection;
+import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 
 /**
  * Maintains a list of all the known TelephonyConnections connections and controls GSM and
@@ -55,7 +57,7 @@
 
         @Override
         public void onDestroyed(Connection connection) {
-            remove((TelephonyConnection) connection);
+            remove(connection);
         }
 
         /**
@@ -103,9 +105,15 @@
         recalculate();
     }
 
-    void remove(TelephonyConnection connection) {
+    void remove(Connection connection) {
         connection.removeConnectionListener(mConnectionListener);
-        mTelephonyConnections.remove(connection);
+
+        if (connection instanceof ConferenceParticipantConnection) {
+            mConferenceParticipantConnections.remove(connection);
+        } else {
+            mTelephonyConnections.remove(connection);
+        }
+
         recalculate();
     }
 
@@ -185,6 +193,9 @@
     private void recalculateConference() {
         Set<Connection> conferencedConnections = new HashSet<>();
 
+        int numGsmConnections = 0;
+        int numImsConnections = 0;
+
         for (TelephonyConnection connection : mTelephonyConnections) {
             com.android.internal.telephony.Connection radioConnection =
                 connection.getOriginalConnection();
@@ -194,22 +205,30 @@
                 Call call = radioConnection.getCall();
                 if ((state == Call.State.ACTIVE || state == Call.State.HOLDING) &&
                         (call != null && call.isMultiparty())) {
+
+                    if (radioConnection instanceof GsmConnection) {
+                        numGsmConnections++;
+                    } else if (radioConnection instanceof ImsPhoneConnection) {
+                        numImsConnections++;
+                    }
                     conferencedConnections.add(connection);
                 }
             }
         }
 
-        // Include conference participants in the list of conferenced connections.
-        for (ConferenceParticipantConnection participant :
-                mConferenceParticipantConnections.values()) {
-            conferencedConnections.add(participant);
-        }
-
         Log.d(this, "Recalculate conference calls %s %s.",
                 mTelephonyConference, conferencedConnections);
 
-        if (conferencedConnections.size() < 2) {
-            Log.d(this, "less than two conference calls!");
+        // If the number of telephony connections drops below the limit, the conference can be
+        // considered terminated.
+        // We must have less than 2 GSM connections and less than 1 IMS connection.
+        if (numGsmConnections < 2 && numImsConnections < 1) {
+            Log.d(this, "not enough connections to be a conference!");
+
+            // The underlying telephony connections have been disconnected -- disconnect the
+            // conference participants now.
+            disconnectConferenceParticipants();
+
             // No more connections are conferenced, destroy any existing conference.
             if (mTelephonyConference != null) {
                 Log.d(this, "with a conference to destroy!");
@@ -232,6 +251,17 @@
                         mTelephonyConference.addConnection(connection);
                     }
                 }
+
+                // Add new conference participants
+                for (Connection conferenceParticipant :
+                        mConferenceParticipantConnections.values()) {
+
+                    if (conferenceParticipant.getState() == Connection.STATE_ACTIVE) {
+                        if (!existingConnections.contains(conferenceParticipant)) {
+                            mTelephonyConference.addConnection(conferenceParticipant);
+                        }
+                    }
+                }
             } else {
                 mTelephonyConference = new TelephonyConference(null);
                 for (Connection connection : conferencedConnections) {
@@ -239,6 +269,13 @@
                             mTelephonyConference, connection);
                     mTelephonyConference.addConnection(connection);
                 }
+
+                // Add the conference participants
+                for (Connection conferenceParticipant :
+                        mConferenceParticipantConnections.values()) {
+                    mTelephonyConference.addConnection(conferenceParticipant);
+                }
+
                 mConnectionService.addConference(mTelephonyConference);
             }
 
@@ -256,6 +293,23 @@
     }
 
     /**
+     * Disconnects all conference participants from the conference.
+     */
+    private void disconnectConferenceParticipants() {
+        for (Connection connection : mConferenceParticipantConnections.values()) {
+            // Disconnect listener so that the connection doesn't fire events on the conference
+            // controller, causing a recursive call.
+            connection.removeConnectionListener(mConnectionListener);
+            mConferenceParticipantConnections.remove(connection);
+
+            // Mark disconnect cause as cancelled to ensure that the call is not logged in the
+            // call log.
+            connection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
+            connection.destroy();
+        }
+    }
+
+    /**
      * Handles state changes for a conference participant.
      *
      * @param parent The connection which was notified of the conference participant.
@@ -297,8 +351,8 @@
         PhoneAccountHandle phoneAccountHandle =
                 TelecomAccountRegistry.makePstnPhoneAccountHandle(parent.getPhone());
         mConnectionService.addExistingConnection(phoneAccountHandle, connection);
-
         // Recalculate to add to the conference and set its state appropriately.
         recalculateConference();
+        connection.updateState(participant.getState());
     }
 }
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 3af7481..e478d11 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -211,6 +211,23 @@
         hangup(android.telephony.DisconnectCause.LOCAL);
     }
 
+    /**
+     * Notifies this Connection of a request to disconnect a participant of the conference managed
+     * by the connection.
+     *
+     * @param endpoint the {@link Uri} of the participant to disconnect.
+     */
+    @Override
+    public void onDisconnectConferenceParticipant(Uri endpoint) {
+        Log.v(this, "onDisconnectConferenceParticipant %s", endpoint);
+
+        if (mOriginalConnection == null) {
+            return;
+        }
+
+        mOriginalConnection.onDisconnectConferenceParticipant(endpoint);
+    }
+
     @Override
     public void onSeparate() {
         Log.v(this, "onSeparate");