Merge "Make sure the exception is a CommandException before casting it to one." into lmp-mr1-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b202f2a..30af301 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1278,5 +1278,6 @@
     <string translatable="false" name="config_world_mode"/>
 
     <!-- DO NOT TRANSLATE. Internal key for a voicemail notification preference. -->
+    <string name="voicemail_notification_ringtone_key">voicemail_notification_ringtone_key</string>
     <string name="voicemail_notification_vibrate_key">voicemail_notification_vibrate_key</string>
 </resources>
diff --git a/res/xml/call_feature_setting.xml b/res/xml/call_feature_setting.xml
index 18a55c2..cef723d 100644
--- a/res/xml/call_feature_setting.xml
+++ b/res/xml/call_feature_setting.xml
@@ -32,7 +32,7 @@
         android:title="@string/voicemail"
         android:persistent="false">
 
-        <ListPreference
+        <com.android.phone.settings.VoicemailProviderListPreference
             android:key="button_voicemail_provider_key"
             android:title="@string/voicemail_provider"
             android:summary="@string/sum_voicemail_choose_provider"
@@ -65,7 +65,7 @@
         </PreferenceScreen>
 
         <com.android.phone.settings.VoicemailRingtonePreference
-            android:key="voicemail_notification_ringtone_key"
+            android:key="@string/voicemail_notification_ringtone_key"
             android:title="@string/voicemail_notification_ringtone_title"
             android:persistent="false"
             android:ringtoneType="notification" />
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index e67071a..de57fbc 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -25,9 +25,6 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.database.Cursor;
 import android.media.AudioManager;
 import android.os.AsyncResult;
@@ -59,9 +56,12 @@
 import com.android.phone.settings.AccountSelectionPreference;
 import com.android.phone.settings.CallForwardInfoUtil;
 import com.android.phone.settings.VoicemailDialogUtil;
+import com.android.phone.settings.VoicemailNotificationSettingsUtil;
+import com.android.phone.settings.VoicemailProviderListPreference;
+import com.android.phone.settings.VoicemailProviderListPreference.VoicemailProvider;
 import com.android.phone.settings.VoicemailProviderSettings;
 import com.android.phone.settings.VoicemailProviderSettingsUtil;
-import com.android.phone.settings.VoicemailNotificationSettingsUtil;
+import com.android.phone.settings.VoicemailRingtonePreference;
 import com.android.phone.settings.fdn.FdnSetting;
 import com.android.services.telephony.sip.SipUtil;
 
@@ -124,9 +124,6 @@
     // choose another VM provider
     public static final String SIGNOUT_EXTRA = "com.android.phone.Signout";
 
-    // Key identifying the default vocie mail provider
-    public static final String DEFAULT_VM_PROVIDER_KEY = "";
-
     /**
      * String Extra put into ACTION_ADD_VOICEMAIL call to indicate which provider should be hidden
      * in the list of providers presented to the user. This allows a provider which is being
@@ -190,26 +187,13 @@
     private ListPreference mButtonDTMF;
     private ListPreference mButtonTTY;
     private Preference mPhoneAccountSettingsPreference;
-    private ListPreference mVoicemailProviders;
+    private VoicemailProviderListPreference mVoicemailProviders;
     private PreferenceScreen mVoicemailSettingsScreen;
     private PreferenceScreen mVoicemailSettings;
+    private VoicemailRingtonePreference mVoicemailNotificationRingtone;
     private CheckBoxPreference mVoicemailNotificationVibrate;
     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 toString() {
-            return "[ Name: " + name + ", Intent: " + intent + " ]";
-        }
-    }
-
     /**
      * Results of reading forwarding settings
      */
@@ -273,17 +257,6 @@
      */
     private int mVMOrFwdSetError = 0;
 
-    /**
-     * Data about discovered voice mail settings providers.
-     * Is populated by querying which activities can handle ACTION_CONFIGURE_VOICEMAIL.
-     * They key in this map is package name + activity name.
-     * We always add an entry for the default provider with a key of empty
-     * string and intent value of null.
-     * @see #initVoiceMailProviders()
-     */
-    private final Map<String, VoiceMailProvider> mVMProvidersData =
-            new HashMap<String, VoiceMailProvider>();
-
     /** string to hold old voicemail number as it is being updated. */
     private String mOldVmNumber;
 
@@ -341,10 +314,13 @@
             // Update HAC Value in AudioManager
             mAudioManager.setParameter(HAC_KEY, hac != 0 ? HAC_VAL_ON : HAC_VAL_OFF);
             return true;
-        } else if (preference == mVoicemailSettings) {
+        } else if (preference.getKey().equals(mVoicemailSettings.getKey())) {
+            // Check key instead of comparing reference because closing the voicemail notification
+            // ringtone dialog invokes onResume(), but leaves the old preference screen up,
+            // TODO: Revert to checking reference after migrating voicemail to its own activity.
             if (DBG) log("onPreferenceTreeClick: Voicemail Settings Preference is clicked.");
 
-            final Dialog dialog = mVoicemailSettings.getDialog();
+            final Dialog dialog = ((PreferenceScreen) preference).getDialog();
             if (dialog != null) {
                 dialog.getActionBar().setDisplayHomeAsUpEnabled(false);
             }
@@ -359,7 +335,7 @@
                 if (DBG) log("onPreferenceTreeClick(). No intent; use default behavior in xml.");
 
                 // onActivityResult() will not be called, so reset variables here.
-                mPreviousVMProviderKey = DEFAULT_VM_PROVIDER_KEY;
+                mPreviousVMProviderKey = VoicemailProviderListPreference.DEFAULT_KEY;
                 mVMProviderSettingsForced = false;
                 return false;
             }
@@ -419,8 +395,11 @@
                 saveVoiceMailAndForwardingNumber(newProviderKey, newProviderSettings);
             }
         } else if (preference.getKey().equals(mVoicemailNotificationVibrate.getKey())) {
+            // Check key instead of comparing reference because closing the voicemail notification
+            // ringtone dialog invokes onResume(), but leaves the old preference screen up,
+            // TODO: Revert to checking reference after migrating voicemail to its own activity.
             VoicemailNotificationSettingsUtil.setVibrationEnabled(
-                    mPhone.getContext(), Boolean.TRUE.equals(objValue));
+                    mPhone, Boolean.TRUE.equals(objValue));
         } else if (preference == mEnableVideoCalling) {
             if (ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mPhone.getContext())) {
                 PhoneGlobals.getInstance().phoneMgr.enableVideoCalling((boolean) objValue);
@@ -461,7 +440,7 @@
             VoicemailProviderSettings newSettings = new VoicemailProviderSettings(
                     mSubMenuVoicemailSettings.getPhoneNumber(),
                     VoicemailProviderSettings.NO_FORWARDING);
-            saveVoiceMailAndForwardingNumber(getCurrentVoicemailProviderKey(), newSettings);
+            saveVoiceMailAndForwardingNumber(mVoicemailProviders.getKey(), newSettings);
         }
     }
 
@@ -589,7 +568,7 @@
                             if (DBG) log("Going back to previous provider on signout");
                             switchToPreviousVoicemailProvider();
                         } else {
-                            final String victim = getCurrentVoicemailProviderKey();
+                            final String victim = mVoicemailProviders.getKey();
                             if (DBG) log("Relaunching activity and ignoring " + victim);
                             Intent i = new Intent(ACTION_ADD_VOICEMAIL);
                             i.putExtra(IGNORE_PROVIDER_EXTRA, victim);
@@ -621,7 +600,7 @@
             final int fwdNumTime = data.getIntExtra(FWD_NUMBER_TIME_EXTRA, 20);
 
             if (DBG) log("onActivityResult: cfg result has forwarding number " + fwdNum);
-            saveVoiceMailAndForwardingNumber(getCurrentVoicemailProviderKey(),
+            saveVoiceMailAndForwardingNumber(mVoicemailProviders.getKey(),
                     new VoicemailProviderSettings(vmNum, fwdNum, fwdNumTime));
             return;
         }
@@ -721,7 +700,7 @@
         mVMOrFwdSetError = 0;
         if (!key.equals(mPreviousVMProviderKey)) {
             mReadingSettingsForDefaultProvider =
-                    mPreviousVMProviderKey.equals(DEFAULT_VM_PROVIDER_KEY);
+                    mPreviousVMProviderKey.equals(VoicemailProviderListPreference.DEFAULT_KEY);
             if (DBG) log("Reading current forwarding settings");
             int numSettingsReasons = VoicemailProviderSettings.FORWARDING_SETTINGS_REASONS.length;
             mForwardingReadResults = new CallForwardInfo[numSettingsReasons];
@@ -795,7 +774,8 @@
             dismissDialogSafely(VoicemailDialogUtil.VM_FWD_READING_DIALOG);
 
             if (mReadingSettingsForDefaultProvider) {
-                VoicemailProviderSettingsUtil.save(this, DEFAULT_VM_PROVIDER_KEY,
+                VoicemailProviderSettingsUtil.save(mPhone.getContext(),
+                        VoicemailProviderListPreference.DEFAULT_KEY,
                         new VoicemailProviderSettings(this.mOldVmNumber, mForwardingReadResults));
                 mReadingSettingsForDefaultProvider = false;
             }
@@ -1022,9 +1002,9 @@
      * This updates a bunch of variables and show "success" dialog.
      */
     private void handleVmAndFwdSetSuccess(int dialogId) {
-        if (DBG) log("handleVmAndFwdSetSuccess: key is " + getCurrentVoicemailProviderKey());
+        if (DBG) log("handleVmAndFwdSetSuccess: key is " + mVoicemailProviders.getKey());
 
-        mPreviousVMProviderKey = getCurrentVoicemailProviderKey();
+        mPreviousVMProviderKey = mVoicemailProviders.getKey();
         mChangingVMorFwdDueToProviderChange = false;
         showDialogIfForeground(dialogId);
         updateVoiceNumberField();
@@ -1171,20 +1151,28 @@
         mButtonAutoRetry = (CheckBoxPreference) findPreference(BUTTON_RETRY_KEY);
         mButtonHAC = (CheckBoxPreference) findPreference(BUTTON_HAC_KEY);
         mButtonTTY = (ListPreference) findPreference(BUTTON_TTY_KEY);
-        mEnableVideoCalling = (CheckBoxPreference) findPreference(ENABLE_VIDEO_CALLING_KEY);
 
-        mVoicemailProviders = (ListPreference) findPreference(BUTTON_VOICEMAIL_PROVIDER_KEY);
+        mVoicemailProviders = (VoicemailProviderListPreference) findPreference(
+                BUTTON_VOICEMAIL_PROVIDER_KEY);
+        mVoicemailProviders.init(mPhone, getIntent());
         mVoicemailProviders.setOnPreferenceChangeListener(this);
+        mPreviousVMProviderKey = mVoicemailProviders.getValue();
 
         mVoicemailSettingsScreen =
                 (PreferenceScreen) findPreference(VOICEMAIL_SETTING_SCREEN_PREF_KEY);
-        mVoicemailSettings = (PreferenceScreen)findPreference(BUTTON_VOICEMAIL_SETTING_KEY);
+        mVoicemailSettings = (PreferenceScreen) findPreference(BUTTON_VOICEMAIL_SETTING_KEY);
+
+        mVoicemailNotificationRingtone = (VoicemailRingtonePreference) findPreference(
+                getResources().getString(R.string.voicemail_notification_ringtone_key));
+        mVoicemailNotificationRingtone.init(mPhone);
 
         mVoicemailNotificationVibrate = (CheckBoxPreference) findPreference(
                 getResources().getString(R.string.voicemail_notification_vibrate_key));
         mVoicemailNotificationVibrate.setOnPreferenceChangeListener(this);
 
-        initVoiceMailProviders();
+        updateVMPreferenceWidgets(mVoicemailProviders.getValue());
+
+        mEnableVideoCalling = (CheckBoxPreference) findPreference(ENABLE_VIDEO_CALLING_KEY);
 
         if (getResources().getBoolean(R.bool.dtmf_type_enabled)) {
             mButtonDTMF.setOnPreferenceChangeListener(this);
@@ -1279,15 +1267,13 @@
         // the selection for the VM provider, otherwise bring up a VM number dialog.
         // We only bring up the dialog the first time we are called (not after orientation change)
         if (mShowVoicemailPreference) {
-            if (DBG) {
-                log("ACTION_ADD_VOICEMAIL Intent is thrown. current VM data size: "
-                        + mVMProvidersData.size());
-            }
-            if (mVMProvidersData.size() > 1) {
+            if (DBG) log("ACTION_ADD_VOICEMAIL Intent is thrown");
+            if (mVoicemailProviders.hasMoreThanOneVoicemailProvider()) {
+                if (DBG) log("Voicemail data has more than one provider.");
                 simulatePreferenceClick(mVoicemailProviders);
             } else {
-                onPreferenceChange(mVoicemailProviders, DEFAULT_VM_PROVIDER_KEY);
-                mVoicemailProviders.setValue(DEFAULT_VM_PROVIDER_KEY);
+                onPreferenceChange(mVoicemailProviders, VoicemailProviderListPreference.DEFAULT_KEY);
+                mVoicemailProviders.setValue(VoicemailProviderListPreference.DEFAULT_KEY);
             }
             mShowVoicemailPreference = false;
         }
@@ -1296,7 +1282,7 @@
         mVMProviderSettingsForced = false;
 
         mVoicemailNotificationVibrate.setChecked(
-                VoicemailNotificationSettingsUtil.isVibrationEnabled(mPhone.getContext()));
+                VoicemailNotificationSettingsUtil.isVibrationEnabled(mPhone));
 
         if (ImsManager.isVtEnabledByPlatform(mPhone.getContext()) && ENABLE_VT_FLAG) {
             boolean currentValue =
@@ -1362,12 +1348,13 @@
 
     /**
      * Updates the look of the VM preference widgets based on current VM provider settings.
-     * Note that the provider name is loaded form the found activity via loadLabel in
-     * {@link #initVoiceMailProviders()} in order for it to be localizable.
+     * Note that the provider name is loaded fxrorm the found activity via loadLabel in
+     * {@link VoicemailProviderListPreference#initVoiceMailProviders()} in order for it to be
+     * localizable.
      */
     private void updateVMPreferenceWidgets(String currentProviderSetting) {
         final String key = currentProviderSetting;
-        final VoiceMailProvider provider = mVMProvidersData.get(key);
+        final VoicemailProvider provider = mVoicemailProviders.getVoicemailProvider(key);
 
         /* This is the case when we are coming up on a freshly wiped phone and there is no
          persisted value for the list preference mVoicemailProviders.
@@ -1391,76 +1378,6 @@
         }
     }
 
-    /**
-     * Enumerates existing VM providers and puts their data into the list and populates
-     * the preference list objects with their names.
-     * In case we are called with ACTION_ADD_VOICEMAIL intent the intent may have
-     * an extra string called IGNORE_PROVIDER_EXTRA with "package.activityName" of the provider
-     * which should be hidden when we bring up the list of possible VM providers to choose.
-     */
-    private void initVoiceMailProviders() {
-        if (DBG) log("initVoiceMailProviders()");
-
-        String providerToIgnore = null;
-        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);
-                VoicemailProviderSettingsUtil.delete(this, providerToIgnore);
-            }
-        }
-
-        mVMProvidersData.clear();
-
-        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);
-
-        // Add other voicemail providers.
-        PackageManager pm = getPackageManager();
-        Intent intent = new Intent(ACTION_CONFIGURE_VOICEMAIL);
-        List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
-        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)) {
-                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);
-            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);
-        }
-
-        mVoicemailProviders.setEntries(entries.toArray(new String[0]));
-        mVoicemailProviders.setEntryValues(values.toArray(new String[0]));
-
-        // 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);
-
-        // Finally update the preference texts.
-        updateVMPreferenceWidgets(mPreviousVMProviderKey);
-    }
 
     /**
      * Simulates user clicking on a passed preference.
@@ -1481,11 +1398,6 @@
         }
     }
 
-    private String getCurrentVoicemailProviderKey() {
-        final String key = mVoicemailProviders.getValue();
-        return (key != null) ? key : DEFAULT_VM_PROVIDER_KEY;
-    }
-
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         final int itemId = item.getItemId();
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index badca74..6e372c2 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -298,7 +298,7 @@
             Intent intent = new Intent(Intent.ACTION_CALL,
                     Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", null));
             PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
-            Uri ringtoneUri = VoicemailNotificationSettingsUtil.getRingtoneUri(mContext);
+            Uri ringtoneUri = VoicemailNotificationSettingsUtil.getRingtoneUri(mPhone);
 
             Notification.Builder builder = new Notification.Builder(mContext);
             builder.setSmallIcon(resId)
@@ -310,7 +310,7 @@
                     .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
                     .setOngoing(true);
 
-            if (VoicemailNotificationSettingsUtil.isVibrationEnabled(mContext)) {
+            if (VoicemailNotificationSettingsUtil.isVibrationEnabled(mPhone)) {
                 builder.setDefaults(Notification.DEFAULT_VIBRATE);
             }
 
diff --git a/src/com/android/phone/settings/VoicemailNotificationSettingsUtil.java b/src/com/android/phone/settings/VoicemailNotificationSettingsUtil.java
index 3a93f11..7d9ce4b 100644
--- a/src/com/android/phone/settings/VoicemailNotificationSettingsUtil.java
+++ b/src/com/android/phone/settings/VoicemailNotificationSettingsUtil.java
@@ -21,17 +21,23 @@
 import android.net.Uri;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
+import com.android.internal.telephony.Phone;
 import com.android.phone.R;
 
 public class VoicemailNotificationSettingsUtil {
-    private static final String VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY =
-            "button_voicemail_notification_ringtone_key";
-    private static final String VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY =
-            "button_voicemail_notification_vibrate_key";
+    private static final String VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY_PREFIX =
+            "voicemail_notification_ringtone_";
+    private static final String VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY_PREFIX =
+            "voicemail_notification_vibrate_";
 
     // Old voicemail notification vibration string constants used for migration.
+    private static final String OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY =
+            "button_voicemail_notification_ringtone_key";
+    private static final String OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY =
+            "button_voicemail_notification_vibrate_key";
     private static final String OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY =
             "button_voicemail_notification_vibrate_when_key";
     private static final String OLD_VOICEMAIL_RINGTONE_SHARED_PREFS_KEY =
@@ -39,26 +45,63 @@
     private static final String OLD_VOICEMAIL_VIBRATION_ALWAYS = "always";
     private static final String OLD_VOICEMAIL_VIBRATION_NEVER = "never";
 
-    public static void setVibrationEnabled(Context context, boolean isEnabled) {
-        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+    public static void setVibrationEnabled(Phone phone, boolean isEnabled) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(phone.getContext());
         SharedPreferences.Editor editor = prefs.edit();
-        editor.putBoolean(VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY, isEnabled);
+        editor.putBoolean(getVoicemailVibrationSharedPrefsKey(phone), isEnabled);
         editor.commit();
     }
 
-    public static boolean isVibrationEnabled(Context context) {
-        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
-        migrateVoicemailVibrationSettingsIfNeeded(prefs);
-        return prefs.getBoolean(
-                VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY, false /* defValue */);
+    public static boolean isVibrationEnabled(Phone phone) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(phone.getContext());
+        migrateVoicemailVibrationSettingsIfNeeded(phone, prefs);
+        return prefs.getBoolean(getVoicemailVibrationSharedPrefsKey(phone), false /* defValue */);
+    }
+
+   public static void setRingtoneUri(Phone phone, Uri ringtoneUri) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(phone.getContext());
+        String ringtoneUriStr = ringtoneUri != null ? ringtoneUri.toString() : "";
+
+        SharedPreferences.Editor editor = prefs.edit();
+        editor.putString(getVoicemailRingtoneSharedPrefsKey(phone), ringtoneUriStr);
+        editor.commit();
+    }
+
+    public static Uri getRingtoneUri(Phone phone) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(phone.getContext());
+        migrateVoicemailRingtoneSettingsIfNeeded(phone, prefs);
+        String uriString = prefs.getString(
+                getVoicemailRingtoneSharedPrefsKey(phone), Settings.System.NOTIFICATION_SOUND);
+        return !TextUtils.isEmpty(uriString) ? Uri.parse(uriString) : null;
     }
 
     /**
-     * Migrate settings from OLD_VIBRATE_WHEN_KEY to VOICEMAIL_NOTIFICATION_VIBRATE_KEY if the
-     * latter does not exist.
+     * Migrate voicemail settings from {@link #OLD_VIBRATE_WHEN_KEY} or
+     * {@link #OLD_VOICEMAIL_NOTIFICATION_VIBRATE_KEY}.
+     *
+     * TODO: Add helper which migrates settings from old version to new version.
      */
-    private static void migrateVoicemailVibrationSettingsIfNeeded(SharedPreferences prefs) {
-        if (!prefs.contains(VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY)) {
+    private static void migrateVoicemailVibrationSettingsIfNeeded(
+            Phone phone, SharedPreferences prefs) {
+        String key = getVoicemailVibrationSharedPrefsKey(phone);
+        TelephonyManager telephonyManager = TelephonyManager.from(phone.getContext());
+
+        // Skip if a preference exists, or if phone is MSIM.
+        if (prefs.contains(key) || telephonyManager.getPhoneCount() != 1) {
+            return;
+        }
+
+        if (prefs.contains(OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY)) {
+            boolean voicemailVibrate = prefs.getBoolean(
+                    OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY, false /* defValue */);
+
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putBoolean(key, voicemailVibrate)
+                    .remove(OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY)
+                    .commit();
+        }
+
+        if (prefs.contains(OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY)) {
             // If vibrateWhen is always, then voicemailVibrate should be true.
             // If it is "only in silent mode", or "never", then voicemailVibrate should be false.
             String vibrateWhen = prefs.getString(
@@ -66,32 +109,43 @@
             boolean voicemailVibrate = vibrateWhen.equals(OLD_VOICEMAIL_VIBRATION_ALWAYS);
 
             SharedPreferences.Editor editor = prefs.edit();
-            editor.putBoolean(VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY, voicemailVibrate)
-                    .remove(OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY)
+            editor.putBoolean(key, voicemailVibrate)
+                    .remove(OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY)
                     .commit();
         }
     }
 
-    public static void setRingtoneUri(Context context, Uri ringtoneUri) {
-        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
-        String ringtoneUriStr = ringtoneUri != null ? ringtoneUri.toString() : "";
+    /**
+     * Migrate voicemail settings from OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY.
+     *
+     * TODO: Add helper which migrates settings from old version to new version.
+     */
+    private static void migrateVoicemailRingtoneSettingsIfNeeded(
+            Phone phone, SharedPreferences prefs) {
+        String key = getVoicemailRingtoneSharedPrefsKey(phone);
+        TelephonyManager telephonyManager = TelephonyManager.from(phone.getContext());
 
-        SharedPreferences.Editor editor = prefs.edit();
-        editor.putString(VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY, ringtoneUriStr);
-        editor.commit();
-    }
-
-    public static Uri getRingtoneUri(Context context) {
-        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
-        if (!prefs.contains(VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY)) {
-            return Settings.System.DEFAULT_NOTIFICATION_URI;
+        // Skip if a preference exists, or if phone is MSIM.
+        if (prefs.contains(key) || telephonyManager.getPhoneCount() != 1) {
+            return;
         }
-        String uriString = prefs.getString(
-                VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY, null /* defValue */);
-        return !TextUtils.isEmpty(uriString) ? Uri.parse(uriString) : null;
+
+        if (prefs.contains(OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY)) {
+            String uriString = prefs.getString(
+                    OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY, null /* defValue */);
+
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putString(key, uriString)
+                    .remove(OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY)
+                    .commit();
+        }
     }
 
-    public static String getRingtoneSharedPreferencesKey() {
-        return VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY;
+    private static String getVoicemailVibrationSharedPrefsKey(Phone phone) {
+        return VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY_PREFIX + phone.getSubId();
+    }
+
+    public static String getVoicemailRingtoneSharedPrefsKey(Phone phone) {
+        return VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY_PREFIX + phone.getSubId();
     }
 }
diff --git a/src/com/android/phone/settings/VoicemailProviderListPreference.java b/src/com/android/phone/settings/VoicemailProviderListPreference.java
new file mode 100644
index 0000000..44a1c7c
--- /dev/null
+++ b/src/com/android/phone/settings/VoicemailProviderListPreference.java
@@ -0,0 +1,171 @@
+/*
+ * 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.settings;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.preference.ListPreference;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.internal.telephony.Phone;
+import com.android.phone.CallFeaturesSetting;
+import com.android.phone.PhoneGlobals;
+import com.android.phone.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class VoicemailProviderListPreference extends ListPreference {
+    private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+    private static final String LOG_TAG = VoicemailProviderListPreference.class.getSimpleName();
+
+    // Key identifying the default voice mail provider
+    public static final String DEFAULT_KEY = "";
+
+    public class VoicemailProvider {
+        public String name;
+        public Intent intent;
+
+        public VoicemailProvider(String name, Intent intent) {
+            this.name = name;
+            this.intent = intent;
+        }
+
+        public String toString() {
+            return "[ Name: " + name + ", Intent: " + intent + " ]";
+        }
+    }
+
+    private Phone mPhone;
+
+    /**
+     * Data about discovered voice mail settings providers.
+     * Is populated by querying which activities can handle ACTION_CONFIGURE_VOICEMAIL.
+     * They key in this map is package name + activity name.
+     * We always add an entry for the default provider with a key of empty
+     * string and intent value of null.
+     * @see #initVoicemailProviders()
+     */
+    private final Map<String, VoicemailProvider> mVmProvidersData =
+            new HashMap<String, VoicemailProvider>();
+
+
+    public VoicemailProviderListPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void init(Phone phone, Intent intent) {
+        mPhone = phone;
+
+        initVoicemailProviders(intent);
+    }
+
+    /**
+     * Enumerates existing VM providers and puts their data into the list and populates
+     * the preference list objects with their names.
+     * In case we are called with ACTION_ADD_VOICEMAIL intent the intent may have
+     * an extra string called IGNORE_PROVIDER_EXTRA with "package.activityName" of the provider
+     * which should be hidden when we bring up the list of possible VM providers to choose.
+     */
+    private void initVoicemailProviders(Intent activityIntent) {
+        if (DBG) log("initVoicemailProviders()");
+
+        String providerToIgnore = null;
+        if (activityIntent.getAction().equals(CallFeaturesSetting.ACTION_ADD_VOICEMAIL)
+                && activityIntent.hasExtra(CallFeaturesSetting.IGNORE_PROVIDER_EXTRA)) {
+            // Remove this provider from the list.
+            if (DBG) log("Found ACTION_ADD_VOICEMAIL.");
+            providerToIgnore =
+                    activityIntent.getStringExtra(CallFeaturesSetting.IGNORE_PROVIDER_EXTRA);
+            VoicemailProviderSettingsUtil.delete(mPhone.getContext(), providerToIgnore);
+        }
+
+        mVmProvidersData.clear();
+
+        List<String> entries = new ArrayList<String>();
+        List<String> values = new ArrayList<String>();
+
+        // Add default voicemail provider.
+        final String myCarrier =
+                mPhone.getContext().getResources().getString(R.string.voicemail_default);
+        mVmProvidersData.put(VoicemailProviderListPreference.DEFAULT_KEY,
+                new VoicemailProvider(myCarrier, null));
+        entries.add(myCarrier);
+        values.add(VoicemailProviderListPreference.DEFAULT_KEY);
+
+        // Add other voicemail providers.
+        PackageManager pm = mPhone.getContext().getPackageManager();
+        Intent intent = new Intent(CallFeaturesSetting.ACTION_CONFIGURE_VOICEMAIL);
+        List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
+        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)) {
+                continue;
+            }
+
+            if (DBG) log("Loading key: " + key);
+            CharSequence label = ri.loadLabel(pm);
+            if (TextUtils.isEmpty(label)) {
+                Log.w(LOG_TAG, "Adding voicemail provider with no name for display.");
+            }
+            String nameForDisplay = (label != null) ? label.toString() : "";
+            Intent providerIntent = new Intent();
+            providerIntent.setAction(CallFeaturesSetting.ACTION_CONFIGURE_VOICEMAIL);
+            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);
+        }
+
+        setEntries(entries.toArray(new String[0]));
+        setEntryValues(values.toArray(new String[0]));
+    }
+
+    @Override
+    public String getValue() {
+        final String providerKey = super.getValue();
+        return (providerKey != null) ? providerKey : DEFAULT_KEY;
+    }
+
+    public VoicemailProvider getVoicemailProvider(String key) {
+        return mVmProvidersData.get(key);
+    }
+
+    public boolean hasMoreThanOneVoicemailProvider() {
+        return mVmProvidersData.size() > 1;
+    }
+
+    private static void log(String msg) {
+        Log.d(LOG_TAG, msg);
+    }
+}
diff --git a/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java b/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java
index 62e4798..8187664 100644
--- a/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java
+++ b/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.telephony.CallForwardInfo;
@@ -128,6 +129,10 @@
     public static void delete(Context context, String key) {
         if (DBG) log("Deleting settings for" + key);
 
+        if (TextUtils.isEmpty(key)) {
+            return;
+        }
+
         SharedPreferences prefs = getPrefs(context);
         prefs.edit()
                 .putString(key + VM_NUMBER_TAG, null)
diff --git a/src/com/android/phone/settings/VoicemailRingtonePreference.java b/src/com/android/phone/settings/VoicemailRingtonePreference.java
index 48e5367..fa3cc70 100644
--- a/src/com/android/phone/settings/VoicemailRingtonePreference.java
+++ b/src/com/android/phone/settings/VoicemailRingtonePreference.java
@@ -10,6 +10,7 @@
 import android.preference.RingtonePreference;
 import android.util.AttributeSet;
 
+import com.android.internal.telephony.Phone;
 import com.android.phone.PhoneGlobals;
 import com.android.phone.common.util.SettingsUtil;
 
@@ -23,6 +24,8 @@
     private Runnable mVoicemailRingtoneLookupRunnable;
     private Handler mVoicemailRingtoneLookupComplete;
 
+    private Phone mPhone;
+
     public VoicemailRingtonePreference(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -38,6 +41,8 @@
         };
 
         final Preference preference = this;
+        final String preferenceKey =
+                VoicemailNotificationSettingsUtil.getVoicemailRingtoneSharedPrefsKey(mPhone);
         mVoicemailRingtoneLookupRunnable = new Runnable() {
             @Override
             public void run() {
@@ -45,7 +50,7 @@
                         preference.getContext(),
                         mVoicemailRingtoneLookupComplete,
                         RingtoneManager.TYPE_NOTIFICATION,
-                        VoicemailNotificationSettingsUtil.getRingtoneSharedPreferencesKey(),
+                        preferenceKey,
                         MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY);
             }
         };
@@ -53,16 +58,20 @@
         updateRingtoneName();
     }
 
+    public void init(Phone phone) {
+        mPhone = phone;
+    }
+
     @Override
     protected Uri onRestoreRingtone() {
-        return VoicemailNotificationSettingsUtil.getRingtoneUri(getContext());
+        return VoicemailNotificationSettingsUtil.getRingtoneUri(mPhone);
     }
 
     @Override
     protected void onSaveRingtone(Uri ringtoneUri) {
         // Don't call superclass method because it uses the pref key as the SharedPreferences key.
         // Delegate to the voicemail notification utility to save the ringtone instead.
-        VoicemailNotificationSettingsUtil.setRingtoneUri(getContext(), ringtoneUri);
+        VoicemailNotificationSettingsUtil.setRingtoneUri(mPhone, ringtoneUri);
 
         updateRingtoneName();
     }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 43f8651..46bad01 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -377,8 +377,8 @@
     private boolean isOriginalConnectionKnown(
             com.android.internal.telephony.Connection originalConnection) {
         for (Connection connection : getAllConnections()) {
-            TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
             if (connection instanceof TelephonyConnection) {
+                TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
                 if (telephonyConnection.getOriginalConnection() == originalConnection) {
                     return true;
                 }