Merge "Enabled APNs option for Verizon SIMs" into lmp-mr1-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9095828..b202f2a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1276,4 +1276,7 @@
     <string name="sim_description_default">SIM card, slot: <xliff:g id="slot_id">%s</xliff:g></string>
     <!-- Configuration setting for world mode Format is <true;GID if any to be checked>-->
     <string translatable="false" name="config_world_mode"/>
+
+    <!-- DO NOT TRANSLATE. Internal key for a voicemail notification preference. -->
+    <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 2ad10f2..18a55c2 100644
--- a/res/xml/call_feature_setting.xml
+++ b/res/xml/call_feature_setting.xml
@@ -65,14 +65,13 @@
         </PreferenceScreen>
 
         <com.android.phone.settings.VoicemailRingtonePreference
-            android:key="button_voicemail_notification_ringtone_key"
+            android:key="voicemail_notification_ringtone_key"
             android:title="@string/voicemail_notification_ringtone_title"
-            android:persistent="true"
-            android:ringtoneType="notification"
-            android:defaultValue="content://settings/system/notification_sound" />
+            android:persistent="false"
+            android:ringtoneType="notification" />
 
         <CheckBoxPreference
-            android:key="button_voicemail_notification_vibrate_key"
+            android:key="@string/voicemail_notification_vibrate_key"
             android:title="@string/voicemail_notification_vibrate_when_title"
             android:persistent="true" />
 
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 0286e71..7207fa2 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -25,8 +25,6 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -41,7 +39,6 @@
 import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
-import android.preference.PreferenceManager;
 import android.preference.PreferenceScreen;
 import android.provider.ContactsContract.CommonDataKinds;
 import android.provider.Settings;
@@ -64,6 +61,7 @@
 import com.android.phone.settings.VoicemailDialogUtil;
 import com.android.phone.settings.VoicemailProviderSettings;
 import com.android.phone.settings.VoicemailProviderSettingsUtil;
+import com.android.phone.settings.VoicemailNotificationSettingsUtil;
 import com.android.phone.settings.fdn.FdnSetting;
 import com.android.services.telephony.sip.SipUtil;
 
@@ -141,19 +139,14 @@
 
     // String keys for preference lookup
     // TODO: Naming these "BUTTON_*" is confusing since they're not actually buttons(!)
+    // TODO: Consider moving these strings to strings.xml, so that they are not duplicated here and
+    // in the layout files. These strings need to be treated carefully; if the setting is
+    // persistent, they are used as the key to store shared preferences and the name should not be
+    // changed unless the settings are also migrated.
     private static final String VOICEMAIL_SETTING_SCREEN_PREF_KEY = "button_voicemail_category_key";
     private static final String BUTTON_VOICEMAIL_KEY = "button_voicemail_key";
     private static final String BUTTON_VOICEMAIL_PROVIDER_KEY = "button_voicemail_provider_key";
     private static final String BUTTON_VOICEMAIL_SETTING_KEY = "button_voicemail_setting_key";
-    // New preference key for voicemail notification vibration
-    /* package */ static final String BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY =
-            "button_voicemail_notification_vibrate_key";
-    // Old preference key for voicemail notification vibration. Used for migration to the new
-    // preference key only.
-    /* package */ static final String BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_WHEN_KEY =
-            "button_voicemail_notification_vibrate_when_key";
-    /* package */ static final String BUTTON_VOICEMAIL_NOTIFICATION_RINGTONE_KEY =
-            "button_voicemail_notification_ringtone_key";
     private static final String BUTTON_FDN_KEY   = "button_fdn_key";
 
     private static final String BUTTON_DTMF_KEY        = "button_dtmf_settings";
@@ -176,8 +169,6 @@
     private static final int EVENT_FORWARDING_CHANGED       = 501;
     private static final int EVENT_FORWARDING_GET_COMPLETED = 502;
 
-    private static final int MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY = 1;
-
     public static final String HAC_KEY = "HACSetting";
     public static final String HAC_VAL_ON = "ON";
     public static final String HAC_VAL_OFF = "OFF";
@@ -190,10 +181,6 @@
     private AudioManager mAudioManager;
     private VoicemailProviderSettingsUtil mVmProviderSettingsUtil;
 
-    // voicemail notification vibration string constants
-    private static final String VOICEMAIL_VIBRATION_ALWAYS = "always";
-    private static final String VOICEMAIL_VIBRATION_NEVER = "never";
-
     private SubscriptionInfoHelper mSubscriptionInfoHelper;
 
     private EditPhoneNumberPreference mSubMenuVoicemailSettings;
@@ -432,6 +419,9 @@
                 mChangingVMorFwdDueToProviderChange = true;
                 saveVoiceMailAndForwardingNumber(newProviderKey, newProviderSettings);
             }
+        } else if (preference.getKey().equals(mVoicemailNotificationVibrate.getKey())) {
+            VoicemailNotificationSettingsUtil.setVibrationEnabled(
+                    mPhone.getContext(), Boolean.TRUE.equals(objValue));
         } else if (preference == mEnableVideoCalling) {
             if (ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mPhone.getContext())) {
                 PhoneGlobals.getInstance().phoneMgr.enableVideoCalling((boolean) objValue);
@@ -1183,15 +1173,19 @@
         mButtonAutoRetry = (CheckBoxPreference) findPreference(BUTTON_RETRY_KEY);
         mButtonHAC = (CheckBoxPreference) findPreference(BUTTON_HAC_KEY);
         mButtonTTY = (ListPreference) findPreference(BUTTON_TTY_KEY);
-        mVoicemailProviders = (ListPreference) findPreference(BUTTON_VOICEMAIL_PROVIDER_KEY);
         mEnableVideoCalling = (CheckBoxPreference) findPreference(ENABLE_VIDEO_CALLING_KEY);
 
+        mVoicemailProviders = (ListPreference) findPreference(BUTTON_VOICEMAIL_PROVIDER_KEY);
         mVoicemailProviders.setOnPreferenceChangeListener(this);
+
         mVoicemailSettingsScreen =
                 (PreferenceScreen) findPreference(VOICEMAIL_SETTING_SCREEN_PREF_KEY);
         mVoicemailSettings = (PreferenceScreen)findPreference(BUTTON_VOICEMAIL_SETTING_KEY);
-        mVoicemailNotificationVibrate =
-                (CheckBoxPreference) findPreference(BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY);
+
+        mVoicemailNotificationVibrate = (CheckBoxPreference) findPreference(
+                getResources().getString(R.string.voicemail_notification_vibrate_key));
+        mVoicemailNotificationVibrate.setOnPreferenceChangeListener(this);
+
         initVoiceMailProviders();
 
         if (getResources().getBoolean(R.bool.dtmf_type_enabled)) {
@@ -1303,12 +1297,8 @@
         updateVoiceNumberField();
         mVMProviderSettingsForced = false;
 
-        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
-                mPhone.getContext());
-        if (migrateVoicemailVibrationSettingsIfNeeded(prefs)) {
-            mVoicemailNotificationVibrate.setChecked(prefs.getBoolean(
-                    BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY, false));
-        }
+        mVoicemailNotificationVibrate.setChecked(
+                VoicemailNotificationSettingsUtil.isVibrationEnabled(mPhone.getContext()));
 
         if (ImsManager.isVtEnabledByPlatform(mPhone.getContext()) && ENABLE_VT_FLAG) {
             boolean currentValue =
@@ -1321,25 +1311,6 @@
         }
     }
 
-    // Migrate settings from BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_WHEN_KEY to
-    // BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY, if the latter does not exist.
-    // Returns true if migration was performed.
-    public static boolean migrateVoicemailVibrationSettingsIfNeeded(SharedPreferences prefs) {
-        if (!prefs.contains(BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY)) {
-            String vibrateWhen = prefs.getString(
-                    BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_WHEN_KEY, VOICEMAIL_VIBRATION_NEVER);
-            // If vibrateWhen is always, then voicemailVibrate should be True.
-            // otherwise if vibrateWhen is "only in silent mode", or "never", then
-            // voicemailVibrate = False.
-            boolean voicemailVibrate = vibrateWhen.equals(VOICEMAIL_VIBRATION_ALWAYS);
-            final SharedPreferences.Editor editor = prefs.edit();
-            editor.putBoolean(BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY, voicemailVibrate);
-            editor.commit();
-            return true;
-        }
-        return false;
-    }
-
     private void handleTTYChange(Preference preference, Object objValue) {
         int buttonTtyMode;
         buttonTtyMode = Integer.valueOf((String) objValue).intValue();
@@ -1526,6 +1497,7 @@
         }
         return super.onOptionsItemSelected(item);
     }
+
     /**
      * Finish current Activity and go up to the top level Settings ({@link CallFeaturesSetting}).
      * This is useful for implementing "HomeAsUp" capability for second-level Settings.
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index bc13010..01a2449 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -143,7 +143,7 @@
                                     BluetoothProfile.HEADSET);
         }
 
-        TelephonyManager telephonyManager = (TelephonyManager)app.getSystemService(
+        TelephonyManager telephonyManager = (TelephonyManager) app.getSystemService(
                 Context.TELEPHONY_SERVICE);
         telephonyManager.listen(mPhoneStateListener,
                 PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
@@ -191,10 +191,6 @@
                 onUnknownConnectionAppeared((AsyncResult) msg.obj);
                 break;
 
-            case CallStateMonitor.INTERNAL_PHONE_MWI_CHANGED:
-                onMwiChanged(mApplication.phone.getMessageWaitingIndicator());
-                break;
-
             case CallStateMonitor.PHONE_STATE_DISPLAYINFO:
                 if (DBG) log("Received PHONE_STATE_DISPLAYINFO event");
                 onDisplayInfo((AsyncResult) msg.obj);
@@ -245,13 +241,15 @@
 
     PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
         @Override
-        public void onMessageWaitingIndicatorChanged(boolean mwi) {
-            onMwiChanged(mwi);
+        public void onMessageWaitingIndicatorChanged(boolean visible) {
+            if (VDBG) log("onMessageWaitingIndicatorChanged(): " + visible);
+            mApplication.notificationMgr.updateMwi(visible);
         }
 
         @Override
-        public void onCallForwardingIndicatorChanged(boolean cfi) {
-            onCfiChanged(cfi);
+        public void onCallForwardingIndicatorChanged(boolean visible) {
+            if (VDBG) log("onCallForwardingIndicatorChanged(): " + visible);
+            mApplication.notificationMgr.updateCfi(visible);
         }
     };
 
@@ -610,38 +608,6 @@
         PhoneUtils.setAudioMode(mCM);
     }
 
-    private void onMwiChanged(boolean visible) {
-        if (VDBG) log("onMwiChanged(): " + visible);
-
-        // "Voicemail" is meaningless on non-voice-capable devices,
-        // so ignore MWI events.
-        if (!PhoneGlobals.sVoiceCapable) {
-            // ...but still log a warning, since we shouldn't have gotten this
-            // event in the first place!
-            // (PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR events
-            // *should* be blocked at the telephony layer on non-voice-capable
-            // capable devices.)
-            Log.w(LOG_TAG, "Got onMwiChanged() on non-voice-capable device! Ignoring...");
-            return;
-        }
-
-        mApplication.notificationMgr.updateMwi(visible);
-    }
-
-    /**
-     * Posts a delayed PHONE_MWI_CHANGED event, to schedule a "retry" for a
-     * failed NotificationMgr.updateMwi() call.
-     */
-    /* package */ void sendMwiChangedDelayed(long delayMillis) {
-        Message message = Message.obtain(this, CallStateMonitor.INTERNAL_PHONE_MWI_CHANGED);
-        sendMessageDelayed(message, delayMillis);
-    }
-
-    private void onCfiChanged(boolean visible) {
-        if (VDBG) log("onCfiChanged(): " + visible);
-        mApplication.notificationMgr.updateCfi(visible);
-    }
-
     /**
      * Helper class to play tones through the earpiece (or speaker / BT)
      * during a call, using the ToneGenerator.
diff --git a/src/com/android/phone/CallStateMonitor.java b/src/com/android/phone/CallStateMonitor.java
index 9b8a653..e150c68 100644
--- a/src/com/android/phone/CallStateMonitor.java
+++ b/src/com/android/phone/CallStateMonitor.java
@@ -60,7 +60,6 @@
     // Events generated internally.
     // We should store all the possible event type values in one place to make sure that
     // they don't step on each others' toes.
-    public static final int INTERNAL_PHONE_MWI_CHANGED = 21;
     public static final int INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE = 22;
     public static final int INTERNAL_UPDATE_IN_CALL_NOTIFICATION = 23;
 
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index e06684c..badca74 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -35,6 +35,8 @@
 import android.telecom.PhoneAccount;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.text.TextUtils;
 import android.util.Log;
 import android.widget.Toast;
@@ -42,6 +44,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.TelephonyCapabilities;
+import com.android.phone.settings.VoicemailNotificationSettingsUtil;
 
 import java.util.List;
 
@@ -55,7 +58,7 @@
  * @see PhoneGlobals.notificationMgr
  */
 public class NotificationMgr {
-    private static final String LOG_TAG = "NotificationMgr";
+    private static final String LOG_TAG = NotificationMgr.class.getSimpleName();
     private static final boolean DBG =
             (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
     // Do not check in with VDBG = true, since that may write PII to the system log.
@@ -86,11 +89,6 @@
     // used to track the notification of selected network unavailable
     private boolean mSelectedUnavailableNotify = false;
 
-    // Retry params for the getVoiceMailNumber() call; see updateMwi().
-    private static final int MAX_VM_NUMBER_RETRIES = 5;
-    private static final int VM_NUMBER_RETRY_DELAY_MILLIS = 10000;
-    private int mVmNumberRetriesRemaining = MAX_VM_NUMBER_RETRIES;
-
     /**
      * Private constructor (this is a singleton).
      * @see #init(PhoneGlobals)
@@ -105,6 +103,15 @@
         mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
         mPhone = app.phone;  // TODO: better style to use mCM.getDefaultPhone() everywhere instead
         statusBarHelper = new StatusBarHelper();
+
+        SubscriptionManager.from(mContext).registerOnSubscriptionsChangedListener(
+                new OnSubscriptionsChangedListener() {
+                    @Override
+                    public void onSubscriptionsChanged() {
+                        // Update the message waiting indicator if the SIM is changed.
+                        updateMwi(mPhone.getMessageWaitingIndicator());
+                    }
+                });
     }
 
     /**
@@ -236,6 +243,13 @@
     /* package */ void updateMwi(boolean visible) {
         if (DBG) log("updateMwi(): " + visible);
 
+        if (!PhoneGlobals.sVoiceCapable) {
+            // Do not show the message waiting indicator on devices which are not voice capable.
+            // These events *should* be blocked at the telephony layer for such devices.
+            Log.w(LOG_TAG, "Called updateMwi() on non-voice-capable device! Ignoring...");
+            return;
+        }
+
         if (visible) {
             int resId = android.R.drawable.stat_notify_voicemail;
 
@@ -253,45 +267,16 @@
             String vmNumber = mPhone.getVoiceMailNumber();
             if (DBG) log("- got vm number: '" + vmNumber + "'");
 
-            // Watch out: vmNumber may be null, for two possible reasons:
-            //
-            //   (1) This phone really has no voicemail number
-            //
-            //   (2) This phone *does* have a voicemail number, but
-            //       the SIM isn't ready yet.
-            //
-            // Case (2) *does* happen in practice if you have voicemail
-            // messages when the device first boots: we get an MWI
-            // notification as soon as we register on the network, but the
-            // SIM hasn't finished loading yet.
-            //
-            // So handle case (2) by retrying the lookup after a short
-            // delay.
-
+            // The voicemail number may be null because:
+            //   (1) This phone has no voicemail number.
+            //   (2) This phone has a voicemail number, but the SIM isn't ready yet. This may
+            //       happen when the device first boots if we get a MWI notification when we
+            //       register on the network before the SIM has loaded. In this case, the
+            //       SubscriptionListener this class registers on the SubscriptionManager will
+            //       call this method again once the SIM is loaded.
             if ((vmNumber == null) && !mPhone.getIccRecordsLoaded()) {
                 if (DBG) log("- Null vm number: SIM records not loaded (yet)...");
-
-                // TODO: rather than retrying after an arbitrary delay, it
-                // would be cleaner to instead just wait for a
-                // SIM_RECORDS_LOADED notification.
-                // (Unfortunately right now there's no convenient way to
-                // get that notification in phone app code.  We'd first
-                // want to add a call like registerForSimRecordsLoaded()
-                // to Phone.java and GSMPhone.java, and *then* we could
-                // listen for that in the CallNotifier class.)
-
-                // Limit the number of retries (in case the SIM is broken
-                // or missing and can *never* load successfully.)
-                if (mVmNumberRetriesRemaining-- > 0) {
-                    if (DBG) log("  - Retrying in " + VM_NUMBER_RETRY_DELAY_MILLIS + " msec...");
-                    mApp.notifier.sendMwiChangedDelayed(VM_NUMBER_RETRY_DELAY_MILLIS);
-                    return;
-                } else {
-                    Log.w(LOG_TAG, "NotificationMgr.updateMwi: getVoiceMailNumber() failed after "
-                          + MAX_VM_NUMBER_RETRIES + " retries; giving up.");
-                    // ...and continue with vmNumber==null, just as if the
-                    // phone had no VM number set up in the first place.
-                }
+                return;
             }
 
             if (TelephonyCapabilities.supportsVoiceMessageCount(mPhone)) {
@@ -313,16 +298,7 @@
             Intent intent = new Intent(Intent.ACTION_CALL,
                     Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", null));
             PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
-
-            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
-            Uri ringtoneUri;
-            String uriString = prefs.getString(
-                    CallFeaturesSetting.BUTTON_VOICEMAIL_NOTIFICATION_RINGTONE_KEY, null);
-            if (!TextUtils.isEmpty(uriString)) {
-                ringtoneUri = Uri.parse(uriString);
-            } else {
-                ringtoneUri = Settings.System.DEFAULT_NOTIFICATION_URI;
-            }
+            Uri ringtoneUri = VoicemailNotificationSettingsUtil.getRingtoneUri(mContext);
 
             Notification.Builder builder = new Notification.Builder(mContext);
             builder.setSmallIcon(resId)
@@ -334,10 +310,7 @@
                     .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
                     .setOngoing(true);
 
-            CallFeaturesSetting.migrateVoicemailVibrationSettingsIfNeeded(prefs);
-            final boolean vibrate = prefs.getBoolean(
-                    CallFeaturesSetting.BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY, false);
-            if (vibrate) {
+            if (VoicemailNotificationSettingsUtil.isVibrationEnabled(mContext)) {
                 builder.setDefaults(Notification.DEFAULT_VIBRATE);
             }
 
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index e957924..fe40b17 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -48,6 +48,7 @@
 import android.preference.PreferenceManager;
 import android.provider.Settings.System;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
 import android.util.Log;
 
 import com.android.internal.telephony.Call;
@@ -58,11 +59,15 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.TelephonyCapabilities;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.phone.common.CallLogAsync;
 import com.android.server.sip.SipService;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Global state for the telephony subsystem when running in the primary
  * phone process.
@@ -480,12 +485,26 @@
     }
 
     /**
-     * Returns the Phone associated with this instance
+     * Returns the Phone associated with this instance.
+     * WARNING: This method should be used carefully, now that there may be multiple phones.
      */
     public static Phone getPhone() {
         return getInstance().phone;
     }
 
+    /**
+     * Returns a list of the currently active phones for the Telephony package.
+     */
+    public static List<Phone> getPhones() {
+        int[] subIds = SubscriptionController.getInstance().getActiveSubIdList();
+        List<Phone> phones = new ArrayList<Phone>(subIds.length);
+
+        for (int i = 0; i < subIds.length; i++) {
+            phones.add(PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subIds[i])));
+        }
+        return phones;
+    }
+
     /* package */ BluetoothManager getBluetoothManager() {
         return bluetoothManager;
     }
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 7366d44..0deefd2 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -1900,6 +1900,27 @@
     }
 
     /**
+     * Check TETHER_DUN_REQUIRED and TETHER_DUN_APN settings, net.tethering.noprovisioning
+     * SystemProperty, and config_tether_apndata to decide whether DUN APN is required for
+     * tethering.
+     *
+     * @return 0: Not required. 1: required. 2: Not set.
+     * @hide
+     */
+    @Override
+    public int getTetherApnRequired() {
+        enforceModifyPermissionOrCarrierPrivilege();
+        int dunRequired = Settings.Global.getInt(mPhone.getContext().getContentResolver(),
+                Settings.Global.TETHER_DUN_REQUIRED, 2);
+        // If not set, check net.tethering.noprovisioning, TETHER_DUN_APN setting and
+        // config_tether_apndata.
+        if (dunRequired == 2 && mPhone.hasMatchedTetherApnSetting()) {
+            dunRequired = 1;
+        }
+        return dunRequired;
+    }
+
+    /**
      * Set mobile data enabled
      * Used by the user through settings etc to turn on/off mobile data
      *
diff --git a/src/com/android/phone/settings/VoicemailNotificationSettingsUtil.java b/src/com/android/phone/settings/VoicemailNotificationSettingsUtil.java
new file mode 100644
index 0000000..3a93f11
--- /dev/null
+++ b/src/com/android/phone/settings/VoicemailNotificationSettingsUtil.java
@@ -0,0 +1,97 @@
+/*
+ * 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.SharedPreferences;
+import android.net.Uri;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+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";
+
+    // Old voicemail notification vibration string constants used for migration.
+    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 =
+            "button_voicemail_notification_ringtone_key";
+    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);
+        SharedPreferences.Editor editor = prefs.edit();
+        editor.putBoolean(VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY, 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 */);
+    }
+
+    /**
+     * Migrate settings from OLD_VIBRATE_WHEN_KEY to VOICEMAIL_NOTIFICATION_VIBRATE_KEY if the
+     * latter does not exist.
+     */
+    private static void migrateVoicemailVibrationSettingsIfNeeded(SharedPreferences prefs) {
+        if (!prefs.contains(VOICEMAIL_NOTIFICATION_VIBRATION_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(
+                    OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY, OLD_VOICEMAIL_VIBRATION_NEVER);
+            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)
+                    .commit();
+        }
+    }
+
+    public static void setRingtoneUri(Context context, Uri ringtoneUri) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        String ringtoneUriStr = ringtoneUri != null ? ringtoneUri.toString() : "";
+
+        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;
+        }
+        String uriString = prefs.getString(
+                VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY, null /* defValue */);
+        return !TextUtils.isEmpty(uriString) ? Uri.parse(uriString) : null;
+    }
+
+    public static String getRingtoneSharedPreferencesKey() {
+        return VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY;
+    }
+}
diff --git a/src/com/android/phone/settings/VoicemailRingtonePreference.java b/src/com/android/phone/settings/VoicemailRingtonePreference.java
index 3cc48af..48e5367 100644
--- a/src/com/android/phone/settings/VoicemailRingtonePreference.java
+++ b/src/com/android/phone/settings/VoicemailRingtonePreference.java
@@ -45,7 +45,7 @@
                         preference.getContext(),
                         mVoicemailRingtoneLookupComplete,
                         RingtoneManager.TYPE_NOTIFICATION,
-                        preference,
+                        VoicemailNotificationSettingsUtil.getRingtoneSharedPreferencesKey(),
                         MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY);
             }
         };
@@ -54,8 +54,16 @@
     }
 
     @Override
+    protected Uri onRestoreRingtone() {
+        return VoicemailNotificationSettingsUtil.getRingtoneUri(getContext());
+    }
+
+    @Override
     protected void onSaveRingtone(Uri ringtoneUri) {
-        super.onSaveRingtone(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);
+
         updateRingtoneName();
     }
 
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index f551eca..9877269 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -192,7 +192,7 @@
     /**
      * Determines the current audio quality for the {@link TelephonyConnection}.
      * This is used when {@link TelephonyConnection#updateConnectionCapabilities}} is called to
-     * indicate whether a call has the {@link Connection#CAPABILITY_VoLTE} capability.
+     * indicate whether a call has the {@link Connection#CAPABILITY_HIGH_DEF_AUDIO} capability.
      */
     private int mAudioQuality;
 
@@ -684,8 +684,7 @@
 
     /**
      * Applies the audio capabilities to the {@code CallCapabilities} bit-mask.  A call with high
-     * definition audio is considered to have the {@code VoLTE} call capability as VoLTE uses high
-     * definition audio.
+     * definition audio is considered to have the {@code HIGH_DEF_AUDIO} call capability.
      *
      * @param capabilities The {@code CallCapabilities} bit-mask.
      * @return The capabilities with the audio capabilities applied.
@@ -695,9 +694,9 @@
 
         if (mAudioQuality ==
                 com.android.internal.telephony.Connection.AUDIO_QUALITY_HIGH_DEFINITION) {
-            currentCapabilities = applyCapability(currentCapabilities, CAPABILITY_VoLTE);
+            currentCapabilities = applyCapability(currentCapabilities, CAPABILITY_HIGH_DEF_AUDIO);
         } else {
-            currentCapabilities = removeCapability(currentCapabilities, CAPABILITY_VoLTE);
+            currentCapabilities = removeCapability(currentCapabilities, CAPABILITY_HIGH_DEF_AUDIO);
         }
 
         return currentCapabilities;
@@ -765,7 +764,7 @@
 
     /**
      * Sets the current call audio quality.  Used during rebuild of the capabilities
-     * to set or unset the {@link Connection#CAPABILITY_VoLTE} capability.
+     * to set or unset the {@link Connection#CAPABILITY_HIGH_DEF_AUDIO} capability.
      *
      * @param audioQuality The audio quality.
      */
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 7ce5005..43f8651 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -101,7 +101,7 @@
                 Log.d(this, "onCreateOutgoingConnection, phone is null");
                 return Connection.createFailedConnection(
                         DisconnectCauseUtil.toTelecomDisconnectCause(
-                                android.telephony.DisconnectCause.OUTGOING_FAILURE,
+                                android.telephony.DisconnectCause.OUT_OF_SERVICE,
                                 "Phone is null"));
             }
             number = phone.getVoiceMailNumber();
@@ -142,7 +142,7 @@
             Log.d(this, "onCreateOutgoingConnection, phone is null");
             return Connection.createFailedConnection(
                     DisconnectCauseUtil.toTelecomDisconnectCause(
-                            android.telephony.DisconnectCause.OUTGOING_FAILURE, "Phone is null"));
+                            android.telephony.DisconnectCause.OUT_OF_SERVICE, "Phone is null"));
         }
 
         int state = phone.getServiceState().getState();