Merge "Make add-call a global property of telecom. (3/4)" into lmp-mr1-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5701dfb..a97d7c4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -183,6 +183,8 @@
     <string name="call_settings">Call settings</string>
     <!-- GSM Call settings screen, setting option name -->
     <string name="additional_gsm_call_settings">Additional settings</string>
+    <!-- GSM Call settings screen, setting option name, with label identifying the SIM the settings are for. -->
+    <string name="additional_gsm_call_settings_with_label">Additional settings (<xliff:g id="subscriptionlabel" example="Verizon">%s</xliff:g>)</string>
     <!-- GSM-only Call settings screen, setting option name-->
     <string name="sum_gsm_call_settings">Additional GSM only call settings</string>
     <!-- CDMA Call settings screen, setting option name -->
@@ -209,6 +211,8 @@
     <string name="sum_cw_disabled">During a call, notify me of incoming calls</string>
     <!-- Call forwarding settings screen, section heading -->
     <string name="call_forwarding_settings">Call forwarding settings</string>
+    <!-- Call forwarding settings screen, section heading, with a label identifying the SIM the settings are for. -->
+    <string name="call_forwarding_settings_with_label">Call forwarding settings (<xliff:g id="subscriptionlabel" example="Verizon">%s</xliff:g>)</string>
     <!-- Call settings screen, setting option name -->
     <string name="labelCF">Call forwarding</string>
 
diff --git a/res/xml/gsm_umts_additional_options.xml b/res/xml/gsm_umts_additional_options.xml
index 19ec56b..f2234e3 100644
--- a/res/xml/gsm_umts_additional_options.xml
+++ b/res/xml/gsm_umts_additional_options.xml
@@ -21,4 +21,5 @@
         android:summaryOn="@string/sum_cw_enabled"
         android:summaryOff="@string/sum_cw_disabled"
         android:enabled="false"/>
+
 </PreferenceScreen>
diff --git a/res/xml/gsm_umts_call_options.xml b/res/xml/gsm_umts_call_options.xml
index 2f3c235..5f3dfe4 100644
--- a/res/xml/gsm_umts_call_options.xml
+++ b/res/xml/gsm_umts_call_options.xml
@@ -1,25 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
+
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:phone="http://schemas.android.com/apk/res/com.android.phone"
      android:title="@string/labelGSMMore">
 
     <PreferenceScreen
-        android:key="button_cf_expand_key"
+        android:key="call_forwarding_key"
         android:title="@string/labelCF"
-        android:persistent="false">
-
-        <intent android:action="android.intent.action.MAIN"
-            android:targetPackage="com.android.phone"
-            android:targetClass="com.android.phone.GsmUmtsCallForwardOptions"/>
-    </PreferenceScreen>
+        android:persistent="false" />
 
     <PreferenceScreen
-        android:key="button_more_expand_key"
+        android:key="additional_gsm_call_settings_key"
         android:title="@string/additional_gsm_call_settings"
-        android:persistent="false">
+        android:persistent="false" />
 
-        <intent android:action="android.intent.action.MAIN"
-            android:targetPackage="com.android.phone"
-            android:targetClass="com.android.phone.GsmUmtsAdditionalCallOptions"/>
-    </PreferenceScreen>
 </PreferenceScreen>
diff --git a/src/com/android/phone/CLIRListPreference.java b/src/com/android/phone/CLIRListPreference.java
index 198bdb0..939caf0 100644
--- a/src/com/android/phone/CLIRListPreference.java
+++ b/src/com/android/phone/CLIRListPreference.java
@@ -23,15 +23,13 @@
     private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
     private final MyHandler mHandler = new MyHandler();
-    private final Phone mPhone;
+    private Phone mPhone;
     private TimeConsumingPreferenceListener mTcpListener;
 
     int clirArray[];
 
     public CLIRListPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
-
-        mPhone = PhoneGlobals.getPhone();
     }
 
     public CLIRListPreference(Context context) {
@@ -49,7 +47,9 @@
         }
     }
 
-    /* package */ void init(TimeConsumingPreferenceListener listener, boolean skipReading) {
+    /* package */ void init(
+            TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
+        mPhone = phone;
         mTcpListener = listener;
         if (!skipReading) {
             mPhone.getOutgoingCallerIdDisplay(mHandler.obtainMessage(MyHandler.MESSAGE_GET_CLIR,
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 5e65a4a..02bbf6d 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -163,8 +163,9 @@
 
     private static final String BUTTON_GSM_UMTS_OPTIONS = "button_gsm_more_expand_key";
     private static final String BUTTON_CDMA_OPTIONS = "button_cdma_more_expand_key";
+    private static final String CALL_FORWARDING_KEY = "call_forwarding_key";
+    private static final String ADDITIONAL_GSM_SETTINGS_KEY = "additional_gsm_call_settings_key";
 
-    private static final String DEFAULT_OUTGOING_ACCOUNT_KEY = "default_outgoing_account";
     private static final String PHONE_ACCOUNT_SETTINGS_KEY =
             "phone_account_settings_preference_screen";
 
@@ -367,32 +368,25 @@
             mAudioManager.setParameter(HAC_KEY, hac != 0 ? HAC_VAL_ON : HAC_VAL_OFF);
             return true;
         } else if (preference == mVoicemailSettings) {
+            if (DBG) log("onPreferenceTreeClick: Voicemail Settings Preference is clicked.");
+
             final Dialog dialog = mVoicemailSettings.getDialog();
             if (dialog != null) {
                 dialog.getActionBar().setDisplayHomeAsUpEnabled(false);
             }
-            if (DBG) log("onPreferenceTreeClick: Voicemail Settings Preference is clicked.");
+
             if (preference.getIntent() != null) {
-                if (DBG) {
-                    log("onPreferenceTreeClick: Invoking cfg intent "
-                            + preference.getIntent().getPackage());
-                }
+                if (DBG) log("Invoking cfg intent " + preference.getIntent().getPackage());
 
                 // onActivityResult() will be responsible for resetting some of variables.
                 this.startActivityForResult(preference.getIntent(), VOICEMAIL_PROVIDER_CFG_ID);
                 return true;
             } else {
-                if (DBG) {
-                    log("onPreferenceTreeClick:"
-                            + " No Intent is available. Use default behavior defined in xml.");
-                }
+                if (DBG) log("onPreferenceTreeClick(). No intent; use default behavior in xml.");
 
-                // There's no onActivityResult(), so we need to take care of some of variables
-                // which should be reset here.
+                // onActivityResult() will not be called, so reset variables here.
                 mPreviousVMProviderKey = DEFAULT_VM_PROVIDER_KEY;
                 mVMProviderSettingsForced = false;
-
-                // This should let the preference use default behavior in the xml.
                 return false;
             }
         } else if (preference == mVoicemailSettingsScreen) {
@@ -415,10 +409,7 @@
      */
     @Override
     public boolean onPreferenceChange(Preference preference, Object objValue) {
-        if (DBG) {
-            log("onPreferenceChange(). preference: \"" + preference + "\""
-                    + ", value: \"" + objValue + "\"");
-        }
+        if (DBG) log("onPreferenceChange: \"" + preference + "\" changed to \"" + objValue + "\"");
 
         if (preference == mButtonDTMF) {
             int index = mButtonDTMF.findIndexOfValue((String) objValue);
@@ -428,13 +419,10 @@
             handleTTYChange(preference, objValue);
         } else if (preference == mVoicemailProviders) {
             final String newProviderKey = (String) objValue;
-            if (DBG) {
-                log("Voicemail Provider changes from \"" + mPreviousVMProviderKey
-                    + "\" to \"" + newProviderKey + "\".");
-            }
+
             // If previous provider key and the new one is same, we don't need to handle it.
             if (mPreviousVMProviderKey.equals(newProviderKey)) {
-                if (DBG) log("No change is made toward VM provider setting.");
+                if (DBG) log("No change is made to the VM provider setting.");
                 return true;
             }
             updateVMPreferenceWidgets(newProviderKey);
@@ -479,14 +467,15 @@
                 return false;
             }
         }
-        // always let the preference setting proceed.
+
+        // Always let the preference setting proceed.
         return true;
     }
 
     @Override
     public void onDialogClosed(EditPhoneNumberPreference preference, int buttonClicked) {
-        if (DBG) log("onPreferenceClick: request preference click on dialog close: " +
-                buttonClicked);
+        if (DBG) log("onDialogClosed: Button clicked is " + buttonClicked);
+
         if (buttonClicked == DialogInterface.BUTTON_NEGATIVE) {
             return;
         }
@@ -528,67 +517,60 @@
 
     private void switchToPreviousVoicemailProvider() {
         if (DBG) log("switchToPreviousVoicemailProvider " + mPreviousVMProviderKey);
-        if (mPreviousVMProviderKey != null) {
-            if (mVMChangeCompletedSuccessfully || mFwdChangesRequireRollback) {
-                // we have to revert with carrier
-                if (DBG) {
-                    log("Needs to rollback."
-                            + " mVMChangeCompletedSuccessfully=" + mVMChangeCompletedSuccessfully
-                            + ", mFwdChangesRequireRollback=" + mFwdChangesRequireRollback);
-                }
 
-                showDialogIfForeground(VOICEMAIL_REVERTING_DIALOG);
-                final VoicemailProviderSettings prevSettings =
-                        mVmProviderSettingsUtil.load(mPreviousVMProviderKey);
-                if (prevSettings == null) {
-                    // prevSettings never becomes null since it should be already loaded!
-                    Log.e(LOG_TAG, "VoicemailProviderSettings for the key \""
-                            + mPreviousVMProviderKey + "\" becomes null, which is unexpected.");
-                    if (DBG) {
-                        Log.e(LOG_TAG,
-                                "mVMChangeCompletedSuccessfully: " + mVMChangeCompletedSuccessfully
-                                + ", mFwdChangesRequireRollback: " + mFwdChangesRequireRollback);
-                    }
-                }
-                if (mVMChangeCompletedSuccessfully) {
-                    mNewVMNumber = prevSettings.getVoicemailNumber();
-                    Log.i(LOG_TAG, "VM change is already completed successfully."
-                            + "Have to revert VM back to " + mNewVMNumber + " again.");
-                    mPhone.setVoiceMailNumber(
-                            mPhone.getVoiceMailAlphaTag().toString(),
-                            mNewVMNumber,
-                            Message.obtain(mRevertOptionComplete, EVENT_VOICEMAIL_CHANGED));
-                }
-                if (mFwdChangesRequireRollback) {
-                    Log.i(LOG_TAG, "Requested to rollback Fwd changes.");
-                    final CallForwardInfo[] prevFwdSettings = prevSettings.getForwardingSettings();
-                    if (prevFwdSettings != null) {
-                        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.
-                            AsyncResult result = results.get(fi.reason);
-                            if (result != null && result.exception == null) {
-                                mExpectedChangeResultReasons.add(fi.reason);
-                                CallForwardInfoUtil.setCallForwardingOption(mPhone, fi,
-                                        mRevertOptionComplete.obtainMessage(
-                                                EVENT_FORWARDING_CHANGED, i, 0));
-                            }
+        if (mPreviousVMProviderKey == null) {
+            return;
+        }
+
+        if (mVMChangeCompletedSuccessfully || mFwdChangesRequireRollback) {
+            showDialogIfForeground(VOICEMAIL_REVERTING_DIALOG);
+            final VoicemailProviderSettings prevSettings =
+                    mVmProviderSettingsUtil.load(mPreviousVMProviderKey);
+            if (prevSettings == null) {
+                Log.e(LOG_TAG, "VoicemailProviderSettings for the key \""
+                        + mPreviousVMProviderKey + "\" is null but should be loaded.");
+            }
+
+            if (mVMChangeCompletedSuccessfully) {
+                mNewVMNumber = prevSettings.getVoicemailNumber();
+                Log.i(LOG_TAG, "VM change is already completed successfully."
+                        + "Have to revert VM back to " + mNewVMNumber + " again.");
+                mPhone.setVoiceMailNumber(
+                        mPhone.getVoiceMailAlphaTag().toString(),
+                        mNewVMNumber,
+                        Message.obtain(mRevertOptionComplete, EVENT_VOICEMAIL_CHANGED));
+            }
+
+            if (mFwdChangesRequireRollback) {
+                Log.i(LOG_TAG, "Requested to rollback forwarding changes.");
+
+                final CallForwardInfo[] prevFwdSettings = prevSettings.getForwardingSettings();
+                if (prevFwdSettings != null) {
+                    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.
+                        AsyncResult result = results.get(fi.reason);
+                        if (result != null && result.exception == null) {
+                            mExpectedChangeResultReasons.add(fi.reason);
+                            CallForwardInfoUtil.setCallForwardingOption(mPhone, fi,
+                                    mRevertOptionComplete.obtainMessage(
+                                            EVENT_FORWARDING_CHANGED, i, 0));
                         }
                     }
                 }
-            } else {
-                if (DBG) log("No need to revert");
-                onRevertDone();
             }
+        } else {
+            if (DBG) log("No need to revert");
+            onRevertDone();
         }
     }
 
     private void onRevertDone() {
-        if (DBG) log("Flipping provider key back to " + mPreviousVMProviderKey);
-        mVoicemailProviders.setValue(mPreviousVMProviderKey);
+        if (DBG) log("onRevertDone: Changing provider key back to " + mPreviousVMProviderKey);
+
         updateVMPreferenceWidgets(mPreviousVMProviderKey);
         updateVoiceNumberField();
         if (mVMOrFwdSetError != 0) {
@@ -604,6 +586,7 @@
                     + ", resultCode: " + resultCode
                     + ", data: " + data);
         }
+
         // there are cases where the contact picker may end up sending us more than one
         // request.  We want to ignore the request if we're not in the correct state.
         if (requestCode == VOICEMAIL_PROVIDER_CFG_ID) {
@@ -646,12 +629,11 @@
                 }
             }
             if (failure) {
-                if (DBG) log("Failure in return from voicemail provider");
+                if (DBG) log("Failure in return from voicemail provider.");
                 if (isVMProviderSettingsForced) {
                     switchToPreviousVoicemailProvider();
-                } else {
-                    if (DBG) log("Not switching back the provider since this is not forced config");
                 }
+
                 return;
             }
             mChangingVMorFwdDueToProviderChange = isVMProviderSettingsForced;
@@ -661,8 +643,7 @@
             // send it to the provider when it's config is invoked so it can use this as default
             final int fwdNumTime = data.getIntExtra(FWD_NUMBER_TIME_EXTRA, 20);
 
-            if (DBG) log("onActivityResult: vm provider cfg result " +
-                    (fwdNum != null ? "has" : " does not have") + " forwarding number");
+            if (DBG) log("onActivityResult: cfg result has forwarding number " + fwdNum);
             saveVoiceMailAndForwardingNumber(getCurrentVoicemailProviderKey(),
                     new VoicemailProviderSettings(vmNum, fwdNum, fwdNumTime));
             return;
@@ -741,15 +722,8 @@
             String key, VoicemailProviderSettings newSettings) {
         if (DBG) log("saveVoiceMailAndForwardingNumber: " + newSettings.toString());
         mNewVMNumber = newSettings.getVoicemailNumber();
-        // empty vm number == clearing the vm number ?
-        if (mNewVMNumber == null) {
-            mNewVMNumber = "";
-        }
-
+        mNewVMNumber = (mNewVMNumber == null) ? "" : mNewVMNumber;
         mNewFwdSettings = newSettings.getForwardingSettings();
-        if (DBG) log("newFwdNumber "
-                + String.valueOf((mNewFwdSettings != null ? mNewFwdSettings.length : 0))
-                + " settings");
 
         // No fwd settings on CDMA
         if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
@@ -799,6 +773,7 @@
 
     private void handleForwardingSettingsReadResult(AsyncResult ar, int idx) {
         if (DBG) Log.d(LOG_TAG, "handleForwardingSettingsReadResult: " + idx);
+
         Throwable error = null;
         if (ar.exception != null) {
             error = ar.exception;
@@ -811,7 +786,7 @@
 
         // We may have already gotten an error and decided to ignore the other results.
         if (mForwardingReadResults == null) {
-            if (DBG) Log.d(LOG_TAG, "ignoring fwd reading result: " + idx);
+            if (DBG) Log.d(LOG_TAG, "Ignoring fwd reading result: " + idx);
             return;
         }
 
@@ -848,10 +823,9 @@
                 mReadingSettingsForDefaultProvider = false;
             }
             saveVoiceMailAndForwardingNumberStage2();
-        } else {
-            if (DBG) Log.d(LOG_TAG, "Not done receiving fwd info");
         }
     }
+
     private void resetForwardingChangeState() {
         mForwardingChangeResults = new HashMap<Integer, AsyncResult>();
         mExpectedChangeResultReasons = new HashSet<Integer>();
@@ -888,6 +862,7 @@
 
     private void setVMNumberWithCarrier() {
         if (DBG) log("save voicemail #: " + mNewVMNumber);
+
         mPhone.setVoiceMailNumber(
                 mPhone.getVoiceMailAlphaTag().toString(),
                 mNewVMNumber,
@@ -913,8 +888,6 @@
                     if (result.exception != null) {
                         Log.w(LOG_TAG, "Error in setting fwd# " + msg.arg1 + ": " +
                                 result.exception.getMessage());
-                    } else {
-                        if (DBG) log("Success in setting fwd# " + msg.arg1);
                     }
                     if (isForwardingCompleted()) {
                         if (isFwdChangeSuccess()) {
@@ -945,6 +918,7 @@
                 default:
                     // TODO: should never reach this, may want to throw exception
             }
+
             if (done) {
                 if (DBG) log("All VM provider related changes done");
                 if (mForwardingChangeResults != null) {
@@ -964,25 +938,25 @@
             AsyncResult result = (AsyncResult) msg.obj;
             switch (msg.what) {
                 case EVENT_VOICEMAIL_CHANGED:
-                    mVoicemailChangeResult = result;
                     if (DBG) log("VM revert complete msg");
+                    mVoicemailChangeResult = result;
                     break;
+
                 case EVENT_FORWARDING_CHANGED:
+                    if (DBG) log("FWD revert complete msg ");
                     mForwardingChangeResults.put(msg.arg1, result);
                     if (result.exception != null) {
                         if (DBG) log("Error in reverting fwd# " + msg.arg1 + ": " +
                                 result.exception.getMessage());
-                    } else {
-                        if (DBG) log("Success in reverting fwd# " + msg.arg1);
                     }
-                    if (DBG) log("FWD revert complete msg ");
                     break;
+
                 default:
                     // TODO: should never reach this, may want to throw exception
             }
-            final boolean done =
-                (!mVMChangeCompletedSuccessfully || mVoicemailChangeResult != null) &&
-                (!mFwdChangesRequireRollback || isForwardingCompleted());
+
+            final boolean done = (!mVMChangeCompletedSuccessfully || mVoicemailChangeResult != null)
+                    && (!mFwdChangesRequireRollback || isForwardingCompleted());
             if (done) {
                 if (DBG) log("All VM reverts done");
                 dismissDialogSafely(VOICEMAIL_REVERTING_DIALOG);
@@ -1032,8 +1006,6 @@
             Log.w(LOG_TAG, "Failed to change voicemail. Reason: " + msg);
             return false;
         }
-
-        if (DBG) log("VM change completed successfully.");
         return true;
     }
 
@@ -1045,7 +1017,6 @@
         } else if (!isVmChangeSuccess()) {
             handleVmOrFwdSetError(VM_RESPONSE_ERROR);
         } else {
-            if (DBG) log("change VM success!");
             handleVmAndFwdSetSuccess(VOICEMAIL_DIALOG_CONFIRM);
         }
     }
@@ -1074,10 +1045,8 @@
      * This updates a bunch of variables and show "success" dialog.
      */
     private void handleVmAndFwdSetSuccess(int dialogId) {
-        if (DBG) {
-            log("handleVmAndFwdSetSuccess(). current voicemail provider key: "
-                    + getCurrentVoicemailProviderKey());
-        }
+        if (DBG) log("handleVmAndFwdSetSuccess: key is " + getCurrentVoicemailProviderKey());
+
         mPreviousVMProviderKey = getCurrentVoicemailProviderKey();
         mChangingVMorFwdDueToProviderChange = false;
         showDialogIfForeground(dialogId);
@@ -1196,13 +1165,11 @@
     // Close button is mapped to BUTTON_POSITIVE for the errors that close the activity,
     // while those that are mapped to BUTTON_NEUTRAL only move the preference focus.
     public void onClick(DialogInterface dialog, int which) {
+        if (DBG) log("onClick: button clicked is " + which);
+
         dialog.dismiss();
         switch (which){
-            case DialogInterface.BUTTON_NEUTRAL:
-                if (DBG) log("Neutral button");
-                break;
             case DialogInterface.BUTTON_NEGATIVE:
-                if (DBG) log("Negative button");
                 if (mCurrentDialogId == FW_GET_RESPONSE_ERROR) {
                     // We failed to get current forwarding settings and the user
                     // does not wish to continue.
@@ -1210,7 +1177,6 @@
                 }
                 break;
             case DialogInterface.BUTTON_POSITIVE:
-                if (DBG) log("Positive button");
                 if (mCurrentDialogId == FW_GET_RESPONSE_ERROR) {
                     // We failed to get current forwarding settings but the user
                     // wishes to continue changing settings to the new vm provider
@@ -1222,6 +1188,7 @@
             default:
                 // just let the dialog close and go back to the input
         }
+
         // In all dialogs, all buttons except BUTTON_POSITIVE lead to the end of user interaction
         // with settings UI. If we were called to explicitly configure voice mail then
         // we finish the settings activity here to come back to whatever the user was doing.
@@ -1237,8 +1204,8 @@
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        if (DBG) log("onCreate(). Intent: " + getIntent());
-        mPhone = PhoneGlobals.getPhone();
+        if (DBG) log("onCreate: Intent is " + getIntent());
+
         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
         mVmProviderSettingsUtil = new VoicemailProviderSettingsUtil(getApplicationContext());
 
@@ -1250,6 +1217,7 @@
         mSubscriptionInfoHelper = new SubscriptionInfoHelper(getIntent());
         mSubscriptionInfoHelper.setActionBarTitle(
                 getActionBar(), getResources(), R.string.call_settings_with_label);
+        mPhone = mSubscriptionInfoHelper.getPhone();
    }
 
     private void initPhoneAccountPreferences() {
@@ -1265,11 +1233,6 @@
         }
     }
 
-    private boolean canLaunchIntent(Intent intent) {
-        PackageManager pm = getPackageManager();
-        return pm.resolveActivity(intent, PackageManager.GET_ACTIVITIES) != null;
-    }
-
     @Override
     protected void onResume() {
         super.onResume();
@@ -1305,7 +1268,6 @@
                 (CheckBoxPreference) findPreference(BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY);
         initVoiceMailProviders();
 
-
         if (getResources().getBoolean(R.bool.dtmf_type_enabled)) {
             mButtonDTMF.setOnPreferenceChangeListener(this);
             int dtmf = Settings.System.getInt(getContentResolver(),
@@ -1349,14 +1311,12 @@
         }
 
         if (!getResources().getBoolean(R.bool.world_phone)) {
-            Preference options = prefSet.findPreference(BUTTON_CDMA_OPTIONS);
-            if (options != null) {
-                prefSet.removePreference(options);
-            }
-            options = prefSet.findPreference(BUTTON_GSM_UMTS_OPTIONS);
-            if (options != null) {
-                prefSet.removePreference(options);
-            }
+            Preference cdmaOptions = prefSet.findPreference(BUTTON_CDMA_OPTIONS);
+            prefSet.removePreference(cdmaOptions);
+
+            // TODO: Support MSIM for this preference option.
+            Preference gsmOptions = prefSet.findPreference(BUTTON_GSM_UMTS_OPTIONS);
+            prefSet.removePreference(gsmOptions);
 
             int phoneType = mPhone.getPhoneType();
             Preference fdnButton = prefSet.findPreference(BUTTON_FDN_KEY);
@@ -1371,6 +1331,15 @@
 
                 if (getResources().getBoolean(R.bool.config_additional_call_setting)) {
                     addPreferencesFromResource(R.xml.gsm_umts_call_options);
+
+                    Preference callForwardingPref = prefSet.findPreference(CALL_FORWARDING_KEY);
+                    callForwardingPref.setIntent(mSubscriptionInfoHelper.getIntent(
+                            this, GsmUmtsCallForwardOptions.class));
+
+                    Preference additionalGsmSettingsPref =
+                            prefSet.findPreference(ADDITIONAL_GSM_SETTINGS_KEY);
+                    additionalGsmSettingsPref.setIntent(mSubscriptionInfoHelper.getIntent(
+                            this, GsmUmtsAdditionalCallOptions.class));
                 }
             } else {
                 throw new IllegalStateException("Unexpected phone type: " + phoneType);
@@ -1501,25 +1470,19 @@
          In this case we want to show the UI asking the user to select a voicemail provider as
          opposed to silently falling back to default one. */
         if (provider == null) {
-            if (DBG) {
-                log("updateVMPreferenceWidget: provider for the key \"" + key + "\" is null.");
-            }
+            if (DBG) log("updateVMPreferenceWidget: key: " + key + " -> null.");
+
             mVoicemailProviders.setSummary(getString(R.string.sum_voicemail_choose_provider));
             mVoicemailSettings.setEnabled(false);
             mVoicemailSettings.setIntent(null);
-
             mVoicemailNotificationVibrate.setEnabled(false);
         } else {
-            if (DBG) {
-                log("updateVMPreferenceWidget: provider for the key \"" + key + "\".."
-                        + "name: " + provider.name
-                        + ", intent: " + provider.intent);
-            }
+            if (DBG) log("updateVMPreferenceWidget: key: " + key + " -> " + provider.toString());
+
             final String providerName = provider.name;
             mVoicemailProviders.setSummary(providerName);
             mVoicemailSettings.setEnabled(true);
             mVoicemailSettings.setIntent(provider.intent);
-
             mVoicemailNotificationVibrate.setEnabled(true);
         }
     }
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index f925022..b176c27 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -36,14 +36,13 @@
     private int mServiceClass;
     private MyHandler mHandler = new MyHandler();
     int reason;
-    Phone phone;
+    private Phone mPhone;
     CallForwardInfo callForwardInfo;
-    TimeConsumingPreferenceListener tcpListener;
+    private TimeConsumingPreferenceListener mTcpListener;
 
     public CallForwardEditPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        phone = PhoneGlobals.getPhone();
         mSummaryOnTemplate = this.getSummaryOn();
 
         TypedArray a = context.obtainStyledAttributes(attrs,
@@ -61,16 +60,18 @@
         this(context, null);
     }
 
-    void init(TimeConsumingPreferenceListener listener, boolean skipReading) {
-        tcpListener = listener;
+    void init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
+        mPhone = phone;
+        mTcpListener = listener;
+
         if (!skipReading) {
-            phone.getCallForwardingOption(reason,
+            mPhone.getCallForwardingOption(reason,
                     mHandler.obtainMessage(MyHandler.MESSAGE_GET_CF,
                             // unused in this case
                             CommandsInterface.CF_ACTION_DISABLE,
                             MyHandler.MESSAGE_GET_CF, null));
-            if (tcpListener != null) {
-                tcpListener.onStarted(this, true);
+            if (mTcpListener != null) {
+                mTcpListener.onStarted(this, true);
             }
         }
     }
@@ -122,7 +123,7 @@
 
                 // the interface of Phone.setCallForwardingOption has error:
                 // should be action, reason...
-                phone.setCallForwardingOption(action,
+                mPhone.setCallForwardingOption(action,
                         reason,
                         number,
                         time,
@@ -130,8 +131,8 @@
                                 action,
                                 MyHandler.MESSAGE_SET_CF));
 
-                if (tcpListener != null) {
-                    tcpListener.onStarted(this, false);
+                if (mTcpListener != null) {
+                    mTcpListener.onStarted(this, false);
                 }
             }
         }
@@ -183,28 +184,24 @@
         private void handleGetCFResponse(Message msg) {
             if (DBG) Log.d(LOG_TAG, "handleGetCFResponse: done");
 
-            if (msg.arg2 == MESSAGE_SET_CF) {
-                tcpListener.onFinished(CallForwardEditPreference.this, false);
-            } else {
-                tcpListener.onFinished(CallForwardEditPreference.this, true);
-            }
+            mTcpListener.onFinished(CallForwardEditPreference.this, msg.arg2 != MESSAGE_SET_CF);
 
             AsyncResult ar = (AsyncResult) msg.obj;
 
             callForwardInfo = null;
             if (ar.exception != null) {
                 if (DBG) Log.d(LOG_TAG, "handleGetCFResponse: ar.exception=" + ar.exception);
-                tcpListener.onException(CallForwardEditPreference.this,
+                mTcpListener.onException(CallForwardEditPreference.this,
                         (CommandException) ar.exception);
             } else {
                 if (ar.userObj instanceof Throwable) {
-                    tcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
+                    mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
                 }
                 CallForwardInfo cfInfoArray[] = (CallForwardInfo[]) ar.result;
                 if (cfInfoArray.length == 0) {
                     if (DBG) Log.d(LOG_TAG, "handleGetCFResponse: cfInfoArray.length==0");
                     setEnabled(false);
-                    tcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
+                    mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
                 } else {
                     for (int i = 0, length = cfInfoArray.length; i < length; i++) {
                         if (DBG) Log.d(LOG_TAG, "handleGetCFResponse, cfInfoArray[" + i + "]="
@@ -258,7 +255,7 @@
                 // setEnabled(false);
             }
             if (DBG) Log.d(LOG_TAG, "handleSetCFResponse: re get");
-            phone.getCallForwardingOption(reason,
+            mPhone.getCallForwardingOption(reason,
                     obtainMessage(MESSAGE_GET_CF, msg.arg1, MESSAGE_SET_CF, ar.exception));
         }
     }
diff --git a/src/com/android/phone/CallWaitingCheckBoxPreference.java b/src/com/android/phone/CallWaitingCheckBoxPreference.java
index a2f5c70..ce2a420 100644
--- a/src/com/android/phone/CallWaitingCheckBoxPreference.java
+++ b/src/com/android/phone/CallWaitingCheckBoxPreference.java
@@ -20,13 +20,11 @@
     private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
     private final MyHandler mHandler = new MyHandler();
-    private final Phone mPhone;
+    private Phone mPhone;
     private TimeConsumingPreferenceListener mTcpListener;
 
     public CallWaitingCheckBoxPreference(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-
-        mPhone = PhoneGlobals.getPhone();
     }
 
     public CallWaitingCheckBoxPreference(Context context, AttributeSet attrs) {
@@ -37,7 +35,9 @@
         this(context, null);
     }
 
-    /* package */ void init(TimeConsumingPreferenceListener listener, boolean skipReading) {
+    /* package */ void init(
+            TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
+        mPhone = phone;
         mTcpListener = listener;
 
         if (!skipReading) {
diff --git a/src/com/android/phone/GsmUmtsAdditionalCallOptions.java b/src/com/android/phone/GsmUmtsAdditionalCallOptions.java
index cd400f9..0540547 100644
--- a/src/com/android/phone/GsmUmtsAdditionalCallOptions.java
+++ b/src/com/android/phone/GsmUmtsAdditionalCallOptions.java
@@ -8,10 +8,11 @@
 import android.util.Log;
 import android.view.MenuItem;
 
+import com.android.internal.telephony.Phone;
+
 import java.util.ArrayList;
 
-public class GsmUmtsAdditionalCallOptions extends
-        TimeConsumingPreferenceActivity {
+public class GsmUmtsAdditionalCallOptions extends TimeConsumingPreferenceActivity {
     private static final String LOG_TAG = "GsmUmtsAdditionalCallOptions";
     private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
@@ -22,7 +23,8 @@
     private CallWaitingCheckBoxPreference mCWButton;
 
     private final ArrayList<Preference> mPreferences = new ArrayList<Preference>();
-    private int mInitIndex= 0;
+    private int mInitIndex = 0;
+    private Phone mPhone;
 
     @Override
     protected void onCreate(Bundle icicle) {
@@ -30,6 +32,11 @@
 
         addPreferencesFromResource(R.xml.gsm_umts_additional_options);
 
+        SubscriptionInfoHelper subscriptionInfoHelper = new SubscriptionInfoHelper(getIntent());
+        subscriptionInfoHelper.setActionBarTitle(
+                getActionBar(), getResources(), R.string.additional_gsm_call_settings_with_label);
+        mPhone = subscriptionInfoHelper.getPhone();
+
         PreferenceScreen prefSet = getPreferenceScreen();
         mCLIRButton = (CLIRListPreference) prefSet.findPreference(BUTTON_CLIR_KEY);
         mCWButton = (CallWaitingCheckBoxPreference) prefSet.findPreference(BUTTON_CW_KEY);
@@ -39,19 +46,19 @@
 
         if (icicle == null) {
             if (DBG) Log.d(LOG_TAG, "start to init ");
-            mCLIRButton.init(this, false);
+            mCLIRButton.init(this, false, mPhone);
         } else {
             if (DBG) Log.d(LOG_TAG, "restore stored states");
             mInitIndex = mPreferences.size();
-            mCLIRButton.init(this, true);
-            mCWButton.init(this, true);
+            mCLIRButton.init(this, true, mPhone);
+            mCWButton.init(this, true, mPhone);
             int[] clirArray = icicle.getIntArray(mCLIRButton.getKey());
             if (clirArray != null) {
                 if (DBG) Log.d(LOG_TAG, "onCreate:  clirArray[0]="
                         + clirArray[0] + ", clirArray[1]=" + clirArray[1]);
                 mCLIRButton.handleGetCLIRResult(clirArray);
             } else {
-                mCLIRButton.init(this, false);
+                mCLIRButton.init(this, false, mPhone);
             }
         }
 
@@ -77,7 +84,7 @@
             mInitIndex++;
             Preference pref = mPreferences.get(mInitIndex);
             if (pref instanceof CallWaitingCheckBoxPreference) {
-                ((CallWaitingCheckBoxPreference) pref).init(this, false);
+                ((CallWaitingCheckBoxPreference) pref).init(this, false, mPhone);
             }
         }
         super.onFinished(preference, reading);
diff --git a/src/com/android/phone/GsmUmtsCallForwardOptions.java b/src/com/android/phone/GsmUmtsCallForwardOptions.java
index 8ecb1bf..3c4bebe 100644
--- a/src/com/android/phone/GsmUmtsCallForwardOptions.java
+++ b/src/com/android/phone/GsmUmtsCallForwardOptions.java
@@ -2,6 +2,7 @@
 
 import com.android.internal.telephony.CallForwardInfo;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
 
 import android.app.ActionBar;
 import android.content.Intent;
@@ -9,7 +10,6 @@
 import android.os.Bundle;
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.util.Log;
 import android.view.MenuItem;
 
@@ -20,7 +20,9 @@
     private static final String LOG_TAG = "GsmUmtsCallForwardOptions";
     private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
-    private static final String NUM_PROJECTION[] = {Phone.NUMBER};
+    private static final String NUM_PROJECTION[] = {
+        android.provider.ContactsContract.CommonDataKinds.Phone.NUMBER
+    };
 
     private static final String BUTTON_CFU_KEY   = "button_cfu_key";
     private static final String BUTTON_CFB_KEY   = "button_cfb_key";
@@ -42,6 +44,7 @@
 
     private boolean mFirstResume;
     private Bundle mIcicle;
+    private Phone mPhone;
 
     @Override
     protected void onCreate(Bundle icicle) {
@@ -49,9 +52,14 @@
 
         addPreferencesFromResource(R.xml.callforward_options);
 
+        SubscriptionInfoHelper subscriptionInfoHelper = new SubscriptionInfoHelper(getIntent());
+        subscriptionInfoHelper.setActionBarTitle(
+                getActionBar(), getResources(), R.string.call_forwarding_settings_with_label);
+        mPhone = subscriptionInfoHelper.getPhone();
+
         PreferenceScreen prefSet = getPreferenceScreen();
-        mButtonCFU   = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFU_KEY);
-        mButtonCFB   = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFB_KEY);
+        mButtonCFU = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFU_KEY);
+        mButtonCFB = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFB_KEY);
         mButtonCFNRy = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRY_KEY);
         mButtonCFNRc = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRC_KEY);
 
@@ -86,7 +94,7 @@
         if (mFirstResume) {
             if (mIcicle == null) {
                 if (DBG) Log.d(LOG_TAG, "start to init ");
-                mPreferences.get(mInitIndex).init(this, false);
+                mPreferences.get(mInitIndex).init(this, false, mPhone);
             } else {
                 mInitIndex = mPreferences.size();
 
@@ -97,11 +105,11 @@
                     cf.number = bundle.getString(KEY_NUMBER);
                     cf.status = bundle.getInt(KEY_STATUS);
                     pref.handleCallForwardResult(cf);
-                    pref.init(this, true);
+                    pref.init(this, true, mPhone);
                 }
             }
             mFirstResume = false;
-            mIcicle=null;
+            mIcicle = null;
         }
     }
 
@@ -124,7 +132,7 @@
     public void onFinished(Preference preference, boolean reading) {
         if (mInitIndex < mPreferences.size()-1 && !isFinishing()) {
             mInitIndex++;
-            mPreferences.get(mInitIndex).init(this, false);
+            mPreferences.get(mInitIndex).init(this, false, mPhone);
         }
 
         super.onFinished(preference, reading);
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index d618b5c..3c385f9 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -76,6 +76,10 @@
     private static final String BUTTON_ENABLED_NETWORKS_KEY = "enabled_networks_key";
     private static final String BUTTON_4G_LTE_KEY = "enhanced_4g_lte";
     private static final String BUTTON_CELL_BROADCAST_SETTINGS = "cell_broadcast_settings";
+    private static final String BUTTON_APN_EXPAND_KEY = "button_apn_key";
+    private static final String BUTTON_OPERATOR_SELECTION_EXPAND_KEY = "button_carrier_sel_key";
+    private static final String BUTTON_CARRIER_SETTINGS_KEY = "carrier_settings_key";
+    private static final String BUTTON_CDMA_SYSTEM_SELECT_KEY = "cdma_system_select_key";
 
     static final int preferredNetworkMode = Phone.PREFERRED_NT_MODE;
 
@@ -693,8 +697,13 @@
                         R.string.preferred_network_mode_lte_cdma_evdo_summary);
                 break;
             case Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
-                mButtonPreferredNetworkMode.setSummary(
-                        R.string.preferred_network_mode_global_summary);
+                if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+                    mButtonPreferredNetworkMode.setSummary(
+                            R.string.preferred_network_mode_global_summary);
+                } else {
+                    mButtonPreferredNetworkMode.setSummary(
+                            R.string.preferred_network_mode_lte_summary);
+                }
                 break;
             case Phone.NT_MODE_GLOBAL:
                 mButtonPreferredNetworkMode.setSummary(
@@ -737,9 +746,11 @@
                 }
                 break;
             case Phone.NT_MODE_LTE_GSM_WCDMA:
-                if(isWorldMode()) {
+                if (isWorldMode()) {
                     mButtonEnabledNetworks.setSummary(
                             R.string.preferred_network_mode_lte_gsm_umts_summary);
+                    controlCdmaOptions(false);
+                    controlGsmOptions(true);
                     break;
                 }
             case Phone.NT_MODE_LTE_ONLY:
@@ -756,9 +767,11 @@
                 }
                 break;
             case Phone.NT_MODE_LTE_CDMA_AND_EVDO:
-                if(isWorldMode()) {
+                if (isWorldMode()) {
                     mButtonEnabledNetworks.setSummary(
                             R.string.preferred_network_mode_lte_cdma_summary);
+                    controlCdmaOptions(true);
+                    controlGsmOptions(false);
                 } else {
                     mButtonEnabledNetworks.setValue(
                             Integer.toString(Phone.NT_MODE_LTE_CDMA_AND_EVDO));
@@ -778,9 +791,18 @@
                 mButtonEnabledNetworks.setSummary(R.string.network_1x);
                 break;
             case Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
+                if (isWorldMode()) {
+                    controlCdmaOptions(true);
+                    controlGsmOptions(false);
+                }
                 mButtonEnabledNetworks.setValue(
                         Integer.toString(Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA));
-                mButtonEnabledNetworks.setSummary(R.string.network_global);
+                if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+                    mButtonEnabledNetworks.setSummary(R.string.network_global);
+                } else {
+                    mButtonEnabledNetworks.setSummary((mShow4GForLTE == true)
+                            ? R.string.network_4G : R.string.network_lte);
+                }
                 break;
             default:
                 String errMsg = "Invalid Network Mode (" + NetworkMode + "). Ignore.";
@@ -860,4 +882,46 @@
 
         return worldModeOn;
     }
+
+    private void controlGsmOptions(boolean enable) {
+        PreferenceScreen prefSet = getPreferenceScreen();
+        if (prefSet == null) {
+            return;
+        }
+
+        if (enable && mGsmUmtsOptions == null) {
+            mGsmUmtsOptions = new GsmUmtsOptions(this, prefSet);
+        }
+        PreferenceScreen apnExpand =
+                (PreferenceScreen) prefSet.findPreference(BUTTON_APN_EXPAND_KEY);
+        PreferenceScreen operatorSelectionExpand =
+                (PreferenceScreen) prefSet.findPreference(BUTTON_OPERATOR_SELECTION_EXPAND_KEY);
+        PreferenceScreen carrierSettings =
+                (PreferenceScreen) prefSet.findPreference(BUTTON_CARRIER_SETTINGS_KEY);
+        if (apnExpand != null) {
+            apnExpand.setEnabled(enable);
+        }
+        if (operatorSelectionExpand != null) {
+            operatorSelectionExpand.setEnabled(enable);
+        }
+        if (carrierSettings != null) {
+            prefSet.removePreference(carrierSettings);
+        }
+    }
+
+    private void controlCdmaOptions(boolean enable) {
+        PreferenceScreen prefSet = getPreferenceScreen();
+        if (prefSet == null) {
+            return;
+        }
+        if (enable && mCdmaOptions == null) {
+            mCdmaOptions = new CdmaOptions(this, prefSet, mPhone);
+        }
+        CdmaSystemSelectListPreference systemSelect =
+                (CdmaSystemSelectListPreference)prefSet.findPreference
+                        (BUTTON_CDMA_SYSTEM_SELECT_KEY);
+        if (systemSelect != null) {
+            systemSelect.setEnabled(enable);
+        }
+    }
 }
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 5489c47..e06684c 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -392,7 +392,11 @@
 
             List<UserInfo> users = mUserManager.getUsers(true);
             for (int i = 0; i < users.size(); i++) {
-                UserHandle userHandle = users.get(i).getUserHandle();
+                final UserInfo user = users.get(i);
+                if (user.isManagedProfile()) {
+                    continue;
+                }
+                UserHandle userHandle = user.getUserHandle();
                 builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
                     mNotificationManager.notifyAsUser(
                             null /* tag */, CALL_FORWARD_NOTIFICATION, builder.build(), userHandle);
@@ -425,7 +429,11 @@
 
         List<UserInfo> users = mUserManager.getUsers(true);
         for (int i = 0; i < users.size(); i++) {
-            UserHandle userHandle = users.get(i).getUserHandle();
+            final UserInfo user = users.get(i);
+            if (user.isManagedProfile()) {
+                continue;
+            }
+            UserHandle userHandle = user.getUserHandle();
             builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
             final Notification notif =
                     new Notification.BigTextStyle(builder).bigText(contentText).build();
@@ -468,7 +476,11 @@
 
         List<UserInfo> users = mUserManager.getUsers(true);
         for (int i = 0; i < users.size(); i++) {
-            UserHandle userHandle = users.get(i).getUserHandle();
+            final UserInfo user = users.get(i);
+            if (user.isManagedProfile()) {
+                continue;
+            }
+            UserHandle userHandle = user.getUserHandle();
             builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
             mNotificationManager.notifyAsUser(
                     null /* tag */,
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 62eb272..e957924 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -162,6 +162,7 @@
 
     // True if we are beginning a call, but the phone state has not changed yet
     private boolean mBeginningCall;
+    private boolean mDataDisconnectedDueToRoaming = false;
 
     // Last phone state seen by updatePhoneState()
     private PhoneConstants.State mLastPhoneState = PhoneConstants.State.IDLE;
@@ -800,9 +801,11 @@
                         && "DISCONNECTED".equals(intent.getStringExtra(PhoneConstants.STATE_KEY))
                         && Phone.REASON_ROAMING_ON.equals(
                             intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY));
-                mHandler.sendEmptyMessage(disconnectedDueToRoaming
-                                          ? EVENT_DATA_ROAMING_DISCONNECTED
-                                          : EVENT_DATA_ROAMING_OK);
+                if (mDataDisconnectedDueToRoaming != disconnectedDueToRoaming) {
+                    mDataDisconnectedDueToRoaming = disconnectedDueToRoaming;
+                    mHandler.sendEmptyMessage(disconnectedDueToRoaming
+                            ? EVENT_DATA_ROAMING_DISCONNECTED : EVENT_DATA_ROAMING_OK);
+                }
             } else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) &&
                     (mPUKEntryActivity != null)) {
                 // if an attempt to un-PUK-lock the device was made, while we're
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index 6f436fd..5226e0d 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -18,7 +18,6 @@
 import android.util.Log;
 
 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;
@@ -31,10 +30,6 @@
                 Preference.OnPreferenceClickListener,
                 AccountSelectionPreference.AccountSelectionListener {
 
-    private static final Intent CONNECTION_SERVICE_CONFIGURE_INTENT =
-            new Intent(TelecomManager.ACTION_CONNECTION_SERVICE_CONFIGURE)
-                    .addCategory(Intent.CATEGORY_DEFAULT);
-
     private static final String ACCOUNTS_LIST_CATEGORY_KEY =
             "phone_accounts_accounts_list_category_key";
 
@@ -184,10 +179,20 @@
     @Override
     public boolean onPreferenceClick(Preference pref) {
         if (pref == mConfigureCallAssistant) {
-            try {
-                startActivity(CONNECTION_SERVICE_CONFIGURE_INTENT);
-            } catch (ActivityNotFoundException e) {
-                Log.d(LOG_TAG, "Could not resolve telecom connection service configure intent.");
+            String packageName = null;
+            PhoneAccountHandle handle = mTelecomManager.getSimCallManager();
+            if (handle != null) {
+                packageName = handle.getComponentName().getPackageName();
+            }
+            if (packageName != null) {
+                Intent intent = new Intent(TelecomManager.ACTION_CONNECTION_SERVICE_CONFIGURE)
+                        .addCategory(Intent.CATEGORY_DEFAULT)
+                        .setPackage(packageName);
+                try {
+                    startActivity(intent);
+                } catch (ActivityNotFoundException e) {
+                    Log.d(LOG_TAG, "Could not resolve call assistant configure intent: " + intent);
+                }
             }
             return true;
         }
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index 3242e64..c52a824 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -163,6 +163,15 @@
         return capabilities;
     }
 
+    @Override
+    public void performConference(TelephonyConnection otherConnection) {
+        if (isImsConnection()) {
+            super.performConference(otherConnection);
+        } else {
+            Log.w(this, "Non-IMS CDMA Connection attempted to call performConference.");
+        }
+    }
+
     void forceAsDialing(boolean isDialing) {
         if (isDialing) {
             setDialing();
diff --git a/src/com/android/services/telephony/GsmConnection.java b/src/com/android/services/telephony/GsmConnection.java
index da03fa1..d2e68c6 100644
--- a/src/com/android/services/telephony/GsmConnection.java
+++ b/src/com/android/services/telephony/GsmConnection.java
@@ -46,23 +46,6 @@
     }
 
     @Override
-    public void performConference(TelephonyConnection otherConnection) {
-        Log.d(this, "performConference - %s", this);
-        if (getPhone() != null) {
-            try {
-                // We dont use the "other" connection because there is no concept of that in the
-                // implementation of calls inside telephony. Basically, you can "conference" and it
-                // will conference with the background call.  We know that otherConnection is the
-                // background call because it would never have called setConferenceableConnections()
-                // otherwise.
-                getPhone().conference();
-            } catch (CallStateException e) {
-                Log.e(this, e, "Failed to conference call.");
-            }
-        }
-    }
-
-    @Override
     protected int buildCallCapabilities() {
         int capabilities = super.buildCallCapabilities();
         capabilities |= PhoneCapabilities.MUTE;
diff --git a/src/com/android/services/telephony/TelephonyConference.java b/src/com/android/services/telephony/TelephonyConference.java
index b5e264c..fc009bc 100644
--- a/src/com/android/services/telephony/TelephonyConference.java
+++ b/src/com/android/services/telephony/TelephonyConference.java
@@ -33,6 +33,12 @@
  */
 public class TelephonyConference extends Conference {
 
+    /**
+     * When {@code true}, indicates that conference participant information from an IMS conference
+     * event package has been received.
+     */
+    private boolean mParticipantsReceived = false;
+
     public TelephonyConference(PhoneAccountHandle phoneAccount) {
         super(phoneAccount);
         setCapabilities(
@@ -129,6 +135,23 @@
     }
 
     @Override
+    public void onConnectionAdded(Connection connection) {
+        // If the conference was an IMS connection currently or before, disable MANAGE_CONFERENCE
+        // as the default behavior. If there is a conference event package, this may be overridden.
+        // If a conference event package was received, do not attempt to remove manage conference.
+        if (connection instanceof TelephonyConnection &&
+                ((TelephonyConnection) connection).wasImsConnection() &&
+                !mParticipantsReceived) {
+            int capabilities = getCapabilities();
+            if (PhoneCapabilities.can(capabilities, PhoneCapabilities.MANAGE_CONFERENCE)) {
+                int newCapabilities =
+                        PhoneCapabilities.remove(capabilities, PhoneCapabilities.MANAGE_CONFERENCE);
+                setCapabilities(newCapabilities);
+            }
+        }
+    }
+
+    @Override
     public Connection getPrimaryConnection() {
         // Default to the first connection.
         Connection primaryConnection = getConnections().get(0);
@@ -176,4 +199,17 @@
         }
         return (TelephonyConnection) connections.get(0);
     }
+
+    /**
+     * Flags the conference to indicate that a conference event package has been received and there
+     * is now participant data present which would permit conference management.
+     */
+    public void setParticipantsReceived() {
+        if (!mParticipantsReceived) {
+            int capabilities = getCapabilities();
+            capabilities |= PhoneCapabilities.MANAGE_CONFERENCE;
+            setCapabilities(capabilities);
+        }
+        mParticipantsReceived = true;
+    }
 }
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index 43385fd..55c8338 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -61,23 +61,22 @@
         }
 
         /**
-         * Handles notifications from an connection that a participant in a conference has changed
+         * Handles notifications from an connection that participant(s) in a conference have changed
          * state.
          *
          * @param c The connection.
-         * @param participant The participant information.
+         * @param participants The participant information.
          */
         @Override
-        public void onConferenceParticipantChanged(Connection c,
-                ConferenceParticipant participant) {
+        public void onConferenceParticipantsChanged(Connection c,
+                List<ConferenceParticipant> participants) {
 
             if (c == null) {
                 return;
             }
+            Log.v(this, "onConferenceParticipantsChanged: %d participants", participants.size());
             TelephonyConnection telephonyConnection = (TelephonyConnection) c;
-
-            Log.v(this, "onConferenceParticipantChanged: %s", participant);
-            handleConferenceParticipantUpdate(telephonyConnection, participant);
+            handleConferenceParticipantsUpdate(telephonyConnection, participants);
         }
     };
 
@@ -118,8 +117,8 @@
     }
 
     private void recalculate() {
-        recalculateConferenceable();
         recalculateConference();
+        recalculateConferenceable();
     }
 
     private boolean isFullConference(Conference conference) {
@@ -184,6 +183,7 @@
                     nonConferencedConnections.add(c);
                 }
             }
+            Log.v(this, "conference conferenceable: %s", nonConferencedConnections);
             mTelephonyConference.setConferenceableConnections(nonConferencedConnections);
         }
 
@@ -219,6 +219,8 @@
         Log.d(this, "Recalculate conference calls %s %s.",
                 mTelephonyConference, conferencedConnections);
 
+        boolean wasParticipantsAdded = false;
+
         // 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.
@@ -240,7 +242,8 @@
                 List<Connection> existingConnections = mTelephonyConference.getConnections();
                 // Remove any that no longer exist
                 for (Connection connection : existingConnections) {
-                    if (!conferencedConnections.contains(connection)) {
+                    if (connection instanceof TelephonyConnection &&
+                            !conferencedConnections.contains(connection)) {
                         mTelephonyConference.removeConnection(connection);
                     }
                 }
@@ -256,8 +259,9 @@
                 for (Connection conferenceParticipant :
                         mConferenceParticipantConnections.values()) {
 
-                    if (conferenceParticipant.getState() == Connection.STATE_ACTIVE) {
+                    if (conferenceParticipant.getState() == Connection.STATE_NEW) {
                         if (!existingConnections.contains(conferenceParticipant)) {
+                            wasParticipantsAdded = true;
                             mTelephonyConference.addConnection(conferenceParticipant);
                         }
                     }
@@ -273,12 +277,19 @@
                 // Add the conference participants
                 for (Connection conferenceParticipant :
                         mConferenceParticipantConnections.values()) {
+                    wasParticipantsAdded = true;
                     mTelephonyConference.addConnection(conferenceParticipant);
                 }
 
                 mConnectionService.addConference(mTelephonyConference);
             }
 
+            // If we added conference participants (e.g. via an IMS conference event package),
+            // notify the conference so that the MANAGE_CONFERENCE capability can be added.
+            if (wasParticipantsAdded) {
+                mTelephonyConference.setParticipantsReceived();
+            }
+
             // Set the conference state to the same state as its child connections.
             Connection conferencedConnection = mTelephonyConference.getPrimaryConnection();
             switch (conferencedConnection.getState()) {
@@ -310,21 +321,40 @@
     }
 
     /**
-     * Handles state changes for a conference participant.
+     * Handles state changes for conference participant(s).
      *
      * @param parent The connection which was notified of the conference participant.
-     * @param participant The conference participant.
+     * @param participants The conference participant information.
      */
-    private void handleConferenceParticipantUpdate(
-            TelephonyConnection parent, ConferenceParticipant participant) {
+    private void handleConferenceParticipantsUpdate(
+            TelephonyConnection parent, List<ConferenceParticipant> participants) {
 
-        Uri endpoint = participant.getEndpoint();
-        if (!mConferenceParticipantConnections.containsKey(endpoint)) {
-            createConferenceParticipantConnection(parent, participant);
-        } else {
-            ConferenceParticipantConnection connection =
-                    mConferenceParticipantConnections.get(endpoint);
-            connection.updateState(participant.getState());
+        boolean recalculateConference = false;
+        ArrayList<ConferenceParticipant> newParticipants = new ArrayList<>(participants.size());
+
+        for (ConferenceParticipant participant : participants) {
+            Uri endpoint = participant.getEndpoint();
+            if (!mConferenceParticipantConnections.containsKey(endpoint)) {
+                createConferenceParticipantConnection(parent, participant);
+                newParticipants.add(participant);
+                recalculateConference = true;
+            } else {
+                ConferenceParticipantConnection connection =
+                        mConferenceParticipantConnections.get(endpoint);
+                connection.updateState(participant.getState());
+            }
+        }
+
+        if (recalculateConference) {
+            // Recalculate to add new connections to the conference.
+            recalculateConference();
+
+            // Now that conference is established, set the state for all participants.
+            for (ConferenceParticipant newParticipant : newParticipants) {
+                ConferenceParticipantConnection connection =
+                        mConferenceParticipantConnections.get(newParticipant.getEndpoint());
+                connection.updateState(newParticipant.getState());
+            }
         }
     }
 
@@ -346,13 +376,9 @@
         ConferenceParticipantConnection connection = new ConferenceParticipantConnection(
                 parent, participant);
         connection.addConnectionListener(mConnectionListener);
-        connection.updateState(Connection.STATE_HOLDING);
         mConferenceParticipantConnections.put(participant.getEndpoint(), connection);
         PhoneAccountHandle phoneAccountHandle =
-                TelecomAccountRegistry.makePstnPhoneAccountHandle(parent.getPhone());
+                 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 b9f9375..056f62f 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -34,7 +34,11 @@
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 
 import java.lang.Override;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Base class for CDMA and GSM connections.
@@ -78,6 +82,15 @@
         }
     };
 
+    /**
+     * A listener/callback mechanism that is specific communication from TelephonyConnections
+     * to TelephonyConnectionService (for now). It is more specific that Connection.Listener
+     * because it is only exposed in Telephony.
+     */
+    public abstract static class TelephonyConnectionListener {
+        public void onOriginalConnectionConfigured(TelephonyConnection c) {}
+    }
+
     private final PostDialListener mPostDialListener = new PostDialListener() {
         @Override
         public void onPostDialWait() {
@@ -143,14 +156,14 @@
         }
 
         /**
-         * Handles a change in the state of a conference participant, as reported by the
+         * Handles a change in the state of conference participant(s), as reported by the
          * {@link com.android.internal.telephony.Connection}.
          *
-         * @param participant The participant which changed.
+         * @param participants The participant(s) which changed.
          */
         @Override
-        public void onConferenceParticipantChanged(ConferenceParticipant participant) {
-            updateConferenceParticipant(participant);
+        public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {
+            updateConferenceParticipants(participants);
         }
     };
 
@@ -186,6 +199,12 @@
      */
     private int mAudioQuality;
 
+    /**
+     * Listeners to our TelephonyConnection specific callbacks
+     */
+    private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap(
+            new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1));
+
     protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection) {
         if (originalConnection != null) {
             setOriginalConnection(originalConnection);
@@ -289,25 +308,6 @@
         }
     }
 
-    @Override
-    public void onConferenceChanged() {
-        Conference conference = getConference();
-        if (conference == null) {
-            return;
-        }
-
-        // If the conference was an IMS connection currently or before, disable MANAGE_CONFERENCE
-        // as the default behavior. If there is a conference event package, this may be overridden.
-        if (mWasImsConnection) {
-            int capabilities = conference.getCapabilities();
-            if (PhoneCapabilities.can(capabilities, PhoneCapabilities.MANAGE_CONFERENCE)) {
-                int newCapabilities =
-                        PhoneCapabilities.remove(capabilities, PhoneCapabilities.MANAGE_CONFERENCE);
-                conference.setCapabilities(newCapabilities);
-            }
-        }
-    }
-
     public void performHold() {
         Log.v(this, "performHold");
         // TODO: Can dialing calls be put on hold as well since they take up the
@@ -373,7 +373,21 @@
         }
     }
 
-    public void performConference(TelephonyConnection otherConnection) {}
+    public void performConference(TelephonyConnection otherConnection) {
+        Log.d(this, "performConference - %s", this);
+        if (getPhone() != null) {
+            try {
+                // We dont use the "other" connection because there is no concept of that in the
+                // implementation of calls inside telephony. Basically, you can "conference" and it
+                // will conference with the background call.  We know that otherConnection is the
+                // background call because it would never have called setConferenceableConnections()
+                // otherwise.
+                getPhone().conference();
+            } catch (CallStateException e) {
+                Log.e(this, e, "Failed to conference call.");
+            }
+        }
+    }
 
     /**
      * Builds call capabilities common to all TelephonyConnections. Namely, apply IMS-based
@@ -455,6 +469,7 @@
             mWasImsConnection = true;
         }
 
+        fireOnOriginalConnectionConfigured();
         updateAddress();
     }
 
@@ -778,6 +793,15 @@
         return getOriginalConnection() instanceof ImsPhoneConnection;
     }
 
+    /**
+     * Whether the original connection was ever an IMS connection, either before or now.
+     * @return {@code True} if the original connection was ever an IMS connection, {@code false}
+     *     otherwise.
+     */
+    public boolean wasImsConnection() {
+        return mWasImsConnection;
+    }
+
     private static Uri getAddressFromNumber(String number) {
         // Address can be null for blocked calls.
         if (number == null) {
@@ -809,4 +833,42 @@
         int newCapabilities = capabilities & ~capability;
         return newCapabilities;
     }
+
+    /**
+     * Register a listener for {@link TelephonyConnection} specific triggers.
+     * @param l The instance of the listener to add
+     * @return The connection being listened to
+     */
+    public final TelephonyConnection addTelephonyConnectionListener(TelephonyConnectionListener l) {
+        mTelephonyListeners.add(l);
+        // If we already have an original connection, let's call back immediately.
+        // This would be the case for incoming calls.
+        if (mOriginalConnection != null) {
+            fireOnOriginalConnectionConfigured();
+        }
+        return this;
+    }
+
+    /**
+     * Remove a listener for {@link TelephonyConnection} specific triggers.
+     * @param l The instance of the listener to remove
+     * @return The connection being listened to
+     */
+    public final TelephonyConnection removeTelephonyConnectionListener(
+            TelephonyConnectionListener l) {
+        if (l != null) {
+            mTelephonyListeners.remove(l);
+        }
+        return this;
+    }
+
+    /**
+     * Fire a callback to the various listeners for when the original connection is
+     * set in this {@link TelephonyConnection}
+     */
+    private final void fireOnOriginalConnectionConfigured() {
+        for (TelephonyConnectionListener l : mTelephonyListeners) {
+            l.onOriginalConnectionConfigured(this);
+        }
+    }
 }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 91f164b..8ef9ae8 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -56,6 +56,17 @@
     private EmergencyCallHelper mEmergencyCallHelper;
     private EmergencyTonePlayer mEmergencyTonePlayer;
 
+    /**
+     * A listener to actionable events specific to the TelephonyConnection.
+     */
+    private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
+            new TelephonyConnection.TelephonyConnectionListener() {
+        @Override
+        public void onOriginalConnectionConfigured(TelephonyConnection c) {
+            addConnectionToConferenceController(c);
+        }
+    };
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -345,24 +356,20 @@
             Phone phone,
             com.android.internal.telephony.Connection originalConnection,
             boolean isOutgoing) {
+        TelephonyConnection returnConnection = null;
         int phoneType = phone.getPhoneType();
         if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
-            GsmConnection connection = new GsmConnection(originalConnection);
-            mTelephonyConferenceController.add(connection);
-            return connection;
+            returnConnection = new GsmConnection(originalConnection);
         } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
             boolean allowMute = allowMute(phone);
-            CdmaConnection connection = new CdmaConnection(
+            returnConnection = new CdmaConnection(
                     originalConnection, mEmergencyTonePlayer, allowMute, isOutgoing);
-            if (connection.isImsConnection()) {
-                mTelephonyConferenceController.add(connection);
-            } else {
-                mCdmaConferenceController.add(connection);
-            }
-            return connection;
-        } else {
-            return null;
         }
+        if (returnConnection != null) {
+            // Listen to Telephony specific callbacks from the connection
+            returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
+        }
+        return returnConnection;
     }
 
     private boolean isOriginalConnectionKnown(
@@ -418,4 +425,40 @@
 
         return true;
     }
+
+    @Override
+    public void removeConnection(Connection connection) {
+        super.removeConnection(connection);
+        if (connection instanceof TelephonyConnection) {
+            TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
+            telephonyConnection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
+        }
+    }
+
+    /**
+     * When a {@link TelephonyConnection} has its underlying original connection configured,
+     * we need to add it to the correct conference controller.
+     *
+     * @param connection The connection to be added to the controller
+     */
+    public void addConnectionToConferenceController(TelephonyConnection connection) {
+        // TODO: Do we need to handle the case of the original connection changing
+        // and triggering this callback multiple times for the same connection?
+        // If that is the case, we might want to remove this connection from all
+        // conference controllers first before re-adding it.
+        if (connection.isImsConnection()) {
+            Log.d(this, "Adding IMS connection to conference controller: " + connection);
+            mTelephonyConferenceController.add(connection);
+        } else {
+            int phoneType = connection.getCall().getPhone().getPhoneType();
+            if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
+                Log.d(this, "Adding GSM connection to conference controller: " + connection);
+                mTelephonyConferenceController.add(connection);
+            } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA &&
+                    connection instanceof CdmaConnection) {
+                Log.d(this, "Adding CDMA connection to conference controller: " + connection);
+                mCdmaConferenceController.add((CdmaConnection)connection);
+            }
+        }
+    }
 }