Merge "Set "has no children" capability on IMS conferences." into mnc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1ba8ee0..c01cdb4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -536,7 +536,7 @@
                 android:theme="@android:style/Theme.NoDisplay"
                 android:excludeFromRecents="true">
             <intent-filter>
-                <action android:name="android.telecom.action.CONNECTION_SERVICE_CONFIGURE" />
+                <action android:name="android.telecom.action.CONFIGURE_PHONE_ACCOUNT" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 91f4806..075680c 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -370,7 +370,7 @@
     <string name="fdn_failed" msgid="540018079008319747">"‏פעולת FDN נכשלה."</string>
     <string name="simContacts_emptyLoading" msgid="2203331234764498011">"‏קורא מכרטיס SIM…"</string>
     <string name="simContacts_empty" msgid="5270660846489561932">"‏אין אנשי קשר בכרטיס ה-SIM."</string>
-    <string name="simContacts_title" msgid="1861472842524839921">"בחר אנשי קשר ליבוא"</string>
+    <string name="simContacts_title" msgid="1861472842524839921">"בחר אנשי קשר לייבוא"</string>
     <string name="simContacts_airplaneMode" msgid="5254946758982621072">"‏בטל את מצב טיסה כדי לייבא אנשי קשר מכרטיס ה-SIM."</string>
     <string name="enable_pin" msgid="5422767284133234860">"‏הפוך לפעיל/השבת PIN של SIM"</string>
     <string name="change_pin" msgid="9174186126330785343">"‏שנה PIN של SIM"</string>
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 11f81ff..00c9170 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -52,6 +52,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.phone.common.util.SettingsUtil;
 import com.android.phone.settings.AccountSelectionPreference;
+import com.android.phone.settings.PhoneAccountSettingsFragment;
 import com.android.phone.settings.VoicemailSettingsActivity;
 import com.android.phone.settings.fdn.FdnSetting;
 import com.android.services.telephony.sip.SipUtil;
@@ -274,8 +275,8 @@
         }
 
         if (ImsManager.isVolteEnabledByPlatform(this) &&
-                !mPhone.getContext().getResources().getBoolean(
-                        com.android.internal.R.bool.config_carrier_volte_tty_supported)) {
+                !carrierConfig.getBoolean(
+                        CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
             TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
             /* tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); */
         }
@@ -284,20 +285,10 @@
                 getResources().getString(R.string.wifi_calling_settings_key));
 
         final PhoneAccountHandle simCallManager = mTelecomManager.getSimCallManager();
-        String simCallManagerPackage = simCallManager != null
-                && simCallManager.getComponentName() != null
-                        ? simCallManager.getComponentName().getPackageName()
-                        : null;
-
-        if (!TextUtils.isEmpty(simCallManagerPackage)) {
-            final Intent intent = new Intent(TelecomManager.ACTION_CONNECTION_SERVICE_CONFIGURE)
-                    .addCategory(Intent.CATEGORY_DEFAULT)
-                    .setPackage(simCallManagerPackage);
-
-            // Check whether the configuration intent is supported.
-            PackageManager pm = getPackageManager();
-            List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
-            if (resolutions.size() > 0) {
+        if (simCallManager != null) {
+            Intent intent = PhoneAccountSettingsFragment.buildPhoneAccountConfigureIntent(
+                    this, simCallManager);
+            if (intent != null) {
                 wifiCallingSettings.setTitle(R.string.wifi_calling);
                 wifiCallingSettings.setSummary(null);
                 wifiCallingSettings.setIntent(intent);
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 303a384..780f76b 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -200,10 +200,13 @@
 
         mDialButton = findViewById(R.id.floating_action_button);
 
-        CarrierConfigLoader configLoader = CarrierConfigLoader.init(this);
         // Check whether we should show the onscreen "Dial" button and co.
+        // Read carrier config through the public API because PhoneGlobals is not available when we
+        // run as a secondary user.
+        CarrierConfigManager configMgr =
+                (CarrierConfigManager) getSystemService(Context.CARRIER_CONFIG_SERVICE);
         PersistableBundle carrierConfig =
-                configLoader.getConfigForSubId(SubscriptionManager.getDefaultVoiceSubId());
+                configMgr.getConfigForSubId(SubscriptionManager.getDefaultVoiceSubId());
         if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL)) {
             mDialButton.setOnClickListener(this);
         } else {
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index 96d00ae..bcb627e 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -470,6 +470,12 @@
     }
 
     @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mPhoneChangeReceiver);
+    }
+
+    @Override
     protected void onResume() {
         super.onResume();
         if (DBG) log("onResume:+");
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index e8b6e05..0e82b99 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -2393,7 +2393,8 @@
 
     @Override
     public String getLine1NumberForDisplay(int subId, String callingPackage) {
-        if (!canReadPhoneState(callingPackage, "getLine1NumberForDisplay")) {
+        // This is open to apps with WRITE_SMS.
+        if (!canReadPhoneNumber(callingPackage, "getLine1NumberForDisplay")) {
             return null;
         }
 
@@ -2651,8 +2652,16 @@
     }
 
     private boolean canReadPhoneState(String callingPackage, String message) {
-        mApp.enforceCallingOrSelfPermission(
-                android.Manifest.permission.READ_PHONE_STATE, message);
+        try {
+            mApp.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
+
+            // SKIP checking for run-time permission since self or obtained PRIVILEDGED
+            return true;
+        } catch (SecurityException e) {
+            mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
+                    message);
+        }
 
         if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
@@ -2662,6 +2671,16 @@
         return true;
     }
 
+    /**
+     * Besides READ_PHONE_STATE, WRITE_SMS also allows apps to get phone numbers.
+     */
+    private boolean canReadPhoneNumber(String callingPackage, String message) {
+        // Note canReadPhoneState() may throw, so we need to do the appops check first.
+        return (mAppOps.noteOp(AppOpsManager.OP_WRITE_SMS,
+                        Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED)
+                || canReadPhoneState(callingPackage, message);
+    }
+
     @Override
     public void factoryReset(int subId) {
         enforceConnectivityInternalPermission();
@@ -2719,21 +2738,36 @@
 
         // Try and fetch the locale from the carrier properties or from the SIM language
         // preferences (EF-PL and EF-LI)...
+        final int mcc = info.getMcc();
+        final Locale mccLocale = MccTable.getLocaleFromMcc(mPhone.getContext(), mcc);
         final Phone defaultPhone = getPhone(info.getSubscriptionId());
         if (defaultPhone != null) {
             final Locale localeFromDefaultSim = defaultPhone.getLocaleFromSimAndCarrierPrefs();
             if (localeFromDefaultSim != null) {
-                return localeFromDefaultSim.toLanguageTag();
+                // The SIM language preferences only store a language (e.g. fr = French), not an
+                // exact locale (e.g. fr_FR = French/France). So, if the locale returned from
+                // the SIM and carrier preferences does not include a country we add the country
+                // determined from the SIM MCC to provide an exact locale.
+                // Note this can result in unusual locale combinatons (e.g. en_DE) being returned.
+                if ((localeFromDefaultSim.getCountry().isEmpty()) && (mccLocale != null)) {
+                    final Locale combinedLocale = new Locale (localeFromDefaultSim.getLanguage(),
+                                                              mccLocale.getCountry());
+                    if (DBG) log("Using SIM language and mcc country:" + combinedLocale);
+                    return combinedLocale.toLanguageTag();
+                } else {
+                    if (DBG) log("Using locale from default SIM:" + localeFromDefaultSim);
+                    return localeFromDefaultSim.toLanguageTag();
+                }
             }
         }
 
         // .. if that doesn't work, try and guess the language from the sim MCC.
-        final int mcc = info.getMcc();
-        final Locale locale = MccTable.getLocaleFromMcc(mPhone.getContext(), mcc);
-        if (locale != null) {
-            return locale.toLanguageTag();
+        if (mccLocale != null) {
+            if (DBG) log("No locale from default SIM, using mcc locale:" + mccLocale);
+            return mccLocale.toLanguageTag();
         }
 
+        if (DBG) log("No locale found - returning null");
         return null;
     }
 
diff --git a/src/com/android/phone/settings/AccessibilitySettingsFragment.java b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
index fdb0cd5..d5a613b 100644
--- a/src/com/android/phone/settings/AccessibilitySettingsFragment.java
+++ b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
@@ -25,6 +25,7 @@
 import android.preference.PreferenceScreen;
 import android.provider.Settings;
 import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -98,9 +99,7 @@
     public void onResume() {
         super.onResume();
 
-        if (ImsManager.isVolteEnabledByPlatform(mContext) &&
-                !mContext.getResources().getBoolean(
-                        com.android.internal.R.bool.config_carrier_volte_tty_supported)) {
+        if (ImsManager.isVolteEnabledByPlatform(mContext) && !getVolteTtySupported()) {
             TelephonyManager tm =
                     (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
             tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
@@ -111,9 +110,7 @@
     public void onPause() {
         super.onPause();
 
-        if (ImsManager.isVolteEnabledByPlatform(mContext) &&
-                !mContext.getResources().getBoolean(
-                        com.android.internal.R.bool.config_carrier_volte_tty_supported)) {
+        if (ImsManager.isVolteEnabledByPlatform(mContext) && !getVolteTtySupported()) {
             TelephonyManager tm =
                     (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
             tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
@@ -138,4 +135,11 @@
         }
         return false;
     }
+
+    private boolean getVolteTtySupported() {
+        CarrierConfigManager configManager =
+                (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        return configManager.getConfig().getBoolean(
+                CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL);
+    }
 }
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index dd9c050..c08b32a 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -18,6 +18,8 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.internal.telephony.Phone;
 import com.android.phone.PhoneUtils;
@@ -48,6 +50,9 @@
     private static final String USE_SIP_PREF_KEY = "use_sip_calling_options_key";
     private static final String SIP_RECEIVE_CALLS_PREF_KEY = "sip_receive_calls_key";
 
+    private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT =
+            "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
+
     /**
      * Value to start ordering of phone accounts relative to other preferences. By setting this
      * value on the phone account listings, we ensure that anything that is ordered before
@@ -56,7 +61,7 @@
      */
     private static final int ACCOUNT_ORDERING_START_VALUE = 100;
 
-    private String LOG_TAG = PhoneAccountSettingsFragment.class.getSimpleName();
+    private static final String LOG_TAG = PhoneAccountSettingsFragment.class.getSimpleName();
 
     private TelecomManager mTelecomManager;
     private TelephonyManager mTelephonyManager;
@@ -346,18 +351,7 @@
                     }
                 }
             } else {
-                // Build the settings intent.
-                intent = new Intent(TelecomManager.ACTION_CONNECTION_SERVICE_CONFIGURE);
-                intent.setPackage(handle.getComponentName().getPackageName());
-                intent.addCategory(Intent.CATEGORY_DEFAULT);
-                intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, handle);
-
-                // Check to see that the phone account package can handle the setting intent.
-                PackageManager pm = getActivity().getPackageManager();
-                List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
-                if (resolutions.size() == 0) {
-                    intent = null;  // set no intent if the package cannot handle it.
-                }
+                intent = buildPhoneAccountConfigureIntent(getActivity(), handle);
             }
 
             // Create the preference & add the label
@@ -417,4 +411,43 @@
         return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
                 (Phone) null, "" /* prefix */, true /* isEmergency */);
     }
+
+    public static Intent buildPhoneAccountConfigureIntent(
+            Context context, PhoneAccountHandle accountHandle) {
+        Intent intent = buildConfigureIntent(
+                context, accountHandle, TelecomManager.ACTION_CONFIGURE_PHONE_ACCOUNT);
+
+        if (intent == null) {
+            // If the new configuration didn't work, try the old configuration intent.
+            intent = buildConfigureIntent(
+                    context, accountHandle, LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT);
+            if (intent != null) {
+                Log.w(LOG_TAG, "Phone account using old configuration intent: " + accountHandle);
+            }
+        }
+        return intent;
+    }
+
+    private static Intent buildConfigureIntent(
+            Context context, PhoneAccountHandle accountHandle, String actionStr) {
+        if (accountHandle == null || accountHandle.getComponentName() == null ||
+                TextUtils.isEmpty(accountHandle.getComponentName().getPackageName())) {
+            return null;
+        }
+
+        // Build the settings intent.
+        Intent intent = new Intent(actionStr);
+        intent.setPackage(accountHandle.getComponentName().getPackageName());
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
+
+        // Check to see that the phone account package can handle the setting intent.
+        PackageManager pm = context.getPackageManager();
+        List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
+        if (resolutions.size() == 0) {
+            intent = null;  // set no intent if the package cannot handle it.
+        }
+
+        return intent;
+    }
 }
diff --git a/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java b/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
index d2e797b..45ad1cb 100644
--- a/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
+++ b/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
@@ -40,6 +40,12 @@
     // however, the user can override this setting.
     private static final String IS_USER_SET = "is_user_set";
 
+    // Setting for how often retries should be done.
+    private static final String SYNC_RETRY_INTERVAL = "sync_retry_interval";
+    private static final long MAX_SYNC_RETRY_INTERVAL_MS = 86400000;   // 24 hours
+    private static final long DEFAULT_SYNC_RETRY_INTERVAL_MS = 900000; // 15 minutes
+
+
     public static void setVisualVoicemailEnabled(Phone phone, boolean isEnabled,
             boolean isUserSet) {
         setVisualVoicemailEnabled(phone.getContext(), PhoneUtils.makePstnPhoneAccountHandle(phone),
@@ -73,12 +79,6 @@
                 PhoneUtils.makePstnPhoneAccountHandle(phone));
     }
 
-    public static boolean isEnabledByUserOverride(Context context,
-            PhoneAccountHandle phoneAccount) {
-        return isVisualVoicemailUserSet(context, phoneAccount) &&
-                isVisualVoicemailEnabled(context, phoneAccount);
-    }
-
     /**
      * Differentiate user-enabled/disabled to know whether to ignore automatic enabling and
      * disabling by the system. This is relevant when a carrier vvm app is installed and the user
@@ -121,6 +121,27 @@
         return prefs.getString(getVisualVoicemailSharedPrefsKey(key, phoneAccount), null);
     }
 
+    public static long getVisualVoicemailRetryInterval(Context context,
+            PhoneAccountHandle phoneAccount) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        return prefs.getLong(getVisualVoicemailSharedPrefsKey(SYNC_RETRY_INTERVAL, phoneAccount),
+                DEFAULT_SYNC_RETRY_INTERVAL_MS);
+    }
+
+    public static void resetVisualVoicemailRetryInterval(Context context,
+            PhoneAccountHandle phoneAccount) {
+        setVisualVoicemailRetryInterval(context, phoneAccount, DEFAULT_SYNC_RETRY_INTERVAL_MS);
+    }
+
+    public static void setVisualVoicemailRetryInterval(Context context,
+            PhoneAccountHandle phoneAccount, long interval) {
+        SharedPreferences.Editor editor =
+                PreferenceManager.getDefaultSharedPreferences(context).edit();
+        editor.putLong(getVisualVoicemailSharedPrefsKey(SYNC_RETRY_INTERVAL, phoneAccount),
+                Math.min(interval, MAX_SYNC_RETRY_INTERVAL_MS));
+        editor.commit();
+    }
+
     private static String getVisualVoicemailSharedPrefsKey(String key,
             PhoneAccountHandle phoneAccount) {
         return VISUAL_VOICEMAIL_SHARED_PREFS_KEY_PREFIX + key + "_" + phoneAccount.getId();
diff --git a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
index 0823552..5ffc9ea 100644
--- a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
+++ b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
@@ -21,7 +21,6 @@
 import android.telecom.PhoneAccountHandle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.internal.telephony.IccCardConstants;
@@ -68,20 +67,30 @@
                     PhoneAccountHandle phoneAccount = PhoneUtils.makePstnPhoneAccountHandle(
                             SubscriptionManager.getPhoneId(subId));
 
-                    if (carrierConfigHelper.isEnabledByDefault()) {
-                        VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(
-                                context, phoneAccount, true, false);
+                    boolean isUserSet = VisualVoicemailSettingsUtil.isVisualVoicemailUserSet(
+                            context, phoneAccount);
+                    boolean isEnabledInSettings =
+                            VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(context,
+                            phoneAccount);
+                    boolean isEnabled = isUserSet ? isEnabledInSettings :
+                        carrierConfigHelper.isEnabledByDefault();
+
+                    if (!isUserSet) {
+                        // Preserve the previous setting for "isVisualVoicemailEnabled" if it is
+                        // set by the user, otherwise, set this value for the first time.
+                        VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(context, phoneAccount,
+                                isEnabled, /** isUserSet */ false);
                     }
 
-                    if (carrierConfigHelper.isEnabledByDefault() ||
-                            VisualVoicemailSettingsUtil.isEnabledByUserOverride(
-                                    context, phoneAccount)) {
+                    if (isEnabled) {
+                        // Add a phone state listener so that changes to the communication channels
+                        // can be recorded.
+                        OmtpVvmSourceManager.getInstance(context).addPhoneStateListener(
+                                phoneAccount);
                         carrierConfigHelper.startActivation();
                     } else {
                         // It may be that the source was not registered to begin with but we want
                         // to run through the steps to remove the source just in case.
-                        VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(
-                                context, phoneAccount, false, false);
                         OmtpVvmSourceManager.getInstance(context).removeSource(phoneAccount);
                         carrierConfigHelper.startDeactivation();
                     }
diff --git a/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java b/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java
index 5559feb..0c4eb62 100644
--- a/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java
+++ b/src/com/android/phone/vvm/omtp/VvmPackageInstallReceiver.java
@@ -45,8 +45,8 @@
         OmtpVvmSourceManager vvmSourceManager = OmtpVvmSourceManager.getInstance(context);
         Set<PhoneAccountHandle> phoneAccounts = vvmSourceManager.getOmtpVvmSources();
         for (PhoneAccountHandle phoneAccount : phoneAccounts) {
-            if (VisualVoicemailSettingsUtil.isEnabledByUserOverride(context, phoneAccount)) {
-                // Skip the check if this voicemail source is enabled by the user.
+            if (VisualVoicemailSettingsUtil.isVisualVoicemailUserSet(context, phoneAccount)) {
+                // Skip the check if this voicemail source's setting is overridden by the user.
                 continue;
             }
 
diff --git a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
index de2ccf5..43724e4 100644
--- a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
+++ b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
@@ -53,6 +53,9 @@
     // Timeout used to call ConnectivityManager.requestNetwork
     private static final int NETWORK_REQUEST_TIMEOUT_MILLIS = 60 * 1000;
 
+    // Number of retries
+    private static final int NETWORK_RETRY_COUNT = 3;
+
     private ContentResolver mContentResolver;
     private Uri mUri;
     private NetworkRequest mNetworkRequest;
@@ -61,6 +64,7 @@
     private String mUid;
     private ConnectivityManager mConnectivityManager;
     private PhoneAccountHandle mPhoneAccount;
+    private int mRetryCount = NETWORK_RETRY_COUNT;
 
     @Override
     public void onReceive(final Context context, Intent intent) {
@@ -107,15 +111,14 @@
                     }
 
                     int subId = PhoneUtils.getSubIdForPhoneAccountHandle(mPhoneAccount);
-
                     mNetworkRequest = new NetworkRequest.Builder()
-                            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                            .setNetworkSpecifier(Integer.toString(subId))
-                            .build();
+                    .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                    .setNetworkSpecifier(Integer.toString(subId))
+                    .build();
+
                     mNetworkCallback = new OmtpVvmNetworkRequestCallback();
-                    getConnectivityManager().requestNetwork(
-                            mNetworkRequest, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MILLIS);
+                    requestNetwork();
                 }
             } finally {
                 cursor.close();
@@ -130,9 +133,23 @@
             executor.execute(new Runnable() {
                 @Override
                 public void run() {
-                    new ImapHelper(mContext, mPhoneAccount, network).fetchVoicemailPayload(
-                            new VoicemailFetchedCallback(mContext, mUri), mUid);
-                    releaseNetwork();
+                    while (mRetryCount > 0) {
+                        ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
+                        if (!imapHelper.isSuccessfullyInitialized()) {
+                            Log.w(TAG, "Can't retrieve Imap credentials.");
+                            releaseNetwork();
+                            return;
+                        }
+
+                        boolean success = imapHelper.fetchVoicemailPayload(
+                                new VoicemailFetchedCallback(mContext, mUri), mUid);
+                        if (!success && mRetryCount > 0) {
+                            mRetryCount--;
+                        } else {
+                            releaseNetwork();
+                            return;
+                        }
+                    }
                 }
             });
         }
@@ -140,14 +157,29 @@
         @Override
         public void onLost(Network network) {
             releaseNetwork();
+
+            if (mRetryCount > 0) {
+                mRetryCount--;
+                requestNetwork();
+            }
         }
 
         @Override
         public void onUnavailable() {
             releaseNetwork();
+
+            if (mRetryCount > 0) {
+                mRetryCount--;
+                requestNetwork();
+            }
         }
     }
 
+    private void requestNetwork() {
+        getConnectivityManager().requestNetwork(
+                mNetworkRequest, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MILLIS);
+    }
+
     private void releaseNetwork() {
         getConnectivityManager().unregisterNetworkCallback(mNetworkCallback);
     }
diff --git a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
index e23dd41..1d20dbd 100644
--- a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
+++ b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
@@ -79,10 +79,19 @@
             mImapStore = new ImapStore(
                     context, username, password, port, serverName, ImapStore.FLAG_NONE, network);
         } catch (NumberFormatException e) {
-            LogUtils.e(TAG, e, "Could not parse port number");
+            LogUtils.w(TAG, "Could not parse port number");
         }
     }
 
+    /**
+     * If mImapStore is null, this means that there was a missing or badly formatted port number,
+     * which means there aren't sufficient credentials for login. If mImapStore is succcessfully
+     * initialized, then ImapHelper is ready to go.
+     */
+    public boolean isSuccessfullyInitialized() {
+        return mImapStore != null;
+    }
+
     /** The caller thread will block until the method returns. */
     public boolean markMessagesAsRead(List<Voicemail> voicemails) {
         return setFlags(voicemails, Flag.SEEN);
@@ -175,21 +184,23 @@
     }
 
 
-    public void fetchVoicemailPayload(VoicemailFetchedCallback callback, final String uid) {
+    public boolean fetchVoicemailPayload(VoicemailFetchedCallback callback, final String uid) {
         Message message;
         try {
             mFolder = openImapFolder(ImapFolder.MODE_READ_WRITE);
             if (mFolder == null) {
                 // This means we were unable to successfully open the folder.
-                return;
+                return false;
             }
             message = mFolder.getMessage(uid);
             VoicemailPayload voicemailPayload = fetchVoicemailPayload(message);
             callback.setVoicemailContent(voicemailPayload);
+            return true;
         } catch (MessagingException e) {
         } finally {
             closeImapFolder();
         }
+        return false;
     }
 
     /**
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
index 1467e41..d768e17 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
@@ -104,9 +104,9 @@
                 queryHelper.insertIfUnique(voicemail);
                 break;
             case OmtpConstants.MAILBOX_UPDATE:
-                Intent serviceIntent = new Intent(mContext, OmtpVvmSyncService.class);
-                serviceIntent.setAction(OmtpVvmSyncService.SYNC_DOWNLOAD_ONLY);
-                serviceIntent.putExtra(OmtpVvmSyncService.EXTRA_PHONE_ACCOUNT, mPhoneAccount);
+                Intent serviceIntent = OmtpVvmSyncService.getSyncIntent(
+                        mContext, OmtpVvmSyncService.SYNC_DOWNLOAD_ONLY, mPhoneAccount,
+                        true /* firstAttempt */);
                 mContext.startService(serviceIntent);
                 break;
             case OmtpConstants.GREETINGS_UPDATE:
@@ -121,23 +121,32 @@
     private void updateSource(StatusMessage message) {
         OmtpVvmSourceManager vvmSourceManager =
                 OmtpVvmSourceManager.getInstance(mContext);
-        VoicemailContract.Status.setStatus(mContext, mPhoneAccount,
-                VoicemailContract.Status.CONFIGURATION_STATE_OK,
-                VoicemailContract.Status.DATA_CHANNEL_STATE_OK,
-                VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK);
 
-        // Save the IMAP credentials in preferences so they are persistent and can be retrieved.
-        VisualVoicemailSettingsUtil.setVisualVoicemailCredentialsFromStatusMessage(
-                mContext,
-                mPhoneAccount,
-                message);
+        if (OmtpConstants.SUCCESS.equals(message.getReturnCode())) {
+            VoicemailContract.Status.setStatus(mContext, mPhoneAccount,
+                    VoicemailContract.Status.CONFIGURATION_STATE_OK,
+                    VoicemailContract.Status.DATA_CHANNEL_STATE_OK,
+                    VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK);
 
-        // Add a phone state listener so that changes to the communication channels can be recorded.
-        vvmSourceManager.addPhoneStateListener(mPhoneAccount);
+            // Save the IMAP credentials in preferences so they are persistent and can be retrieved.
+            VisualVoicemailSettingsUtil.setVisualVoicemailCredentialsFromStatusMessage(
+                    mContext,
+                    mPhoneAccount,
+                    message);
 
-        Intent serviceIntent = new Intent(mContext, OmtpVvmSyncService.class);
-        serviceIntent.setAction(OmtpVvmSyncService.SYNC_FULL_SYNC);
-        serviceIntent.putExtra(OmtpVvmSyncService.EXTRA_PHONE_ACCOUNT, mPhoneAccount);
-        mContext.startService(serviceIntent);
+            // Add the source to indicate that it is active.
+            vvmSourceManager.addSource(mPhoneAccount);
+
+            Intent serviceIntent = OmtpVvmSyncService.getSyncIntent(
+                    mContext, OmtpVvmSyncService.SYNC_FULL_SYNC, mPhoneAccount,
+                    true /* firstAttempt */);
+            mContext.startService(serviceIntent);
+        } else {
+            Log.w(TAG, "Visual voicemail not available for subscriber.");
+            // Override default isEnabled setting to false since visual voicemail is unable to
+            // be accessed for some reason.
+            VisualVoicemailSettingsUtil.setVisualVoicemailEnabled(mContext, mPhoneAccount,
+                    /* isEnabled */ false, /* isUserSet */ true);
+        }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java
index 37fb5e4..63e9fee 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java
@@ -24,9 +24,9 @@
 
 import com.android.internal.telephony.Phone;
 import com.android.phone.PhoneUtils;
-import com.android.phone.settings.VisualVoicemailSettingsUtil;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
@@ -45,6 +45,7 @@
     private TelephonyManager mTelephonyManager;
     // Each phone account is associated with a phone state listener for updates to whether the
     // device is able to sync.
+    private Set<PhoneAccountHandle> mActiveVvmSources;
     private Map<PhoneAccountHandle, PhoneStateListener> mPhoneStateListenerMap;
 
     /**
@@ -67,18 +68,22 @@
             mSubscriptionManager = SubscriptionManager.from(context);
             mTelephonyManager = (TelephonyManager)
                     mContext.getSystemService(Context.TELEPHONY_SERVICE);
+            mActiveVvmSources = new HashSet<PhoneAccountHandle>();
             mPhoneStateListenerMap = new HashMap<PhoneAccountHandle, PhoneStateListener>();
         }
     }
 
+    public void addSource(PhoneAccountHandle phoneAccount) {
+        mActiveVvmSources.add(phoneAccount);
+    }
+
     /**
      * When a voicemail source is removed, we don't always know which one was removed. Check the
      * list of registered phone accounts against the active subscriptions list and remove the
      * inactive sources.
      */
     public void removeInactiveSources() {
-        Set<PhoneAccountHandle> phoneAccounts = getOmtpVvmSources();
-        for (PhoneAccountHandle phoneAccount : phoneAccounts) {
+        for (PhoneAccountHandle phoneAccount : mActiveVvmSources) {
             if (!PhoneUtils.isPhoneAccountActive(mSubscriptionManager, phoneAccount)) {
                 removeSource(phoneAccount);
             }
@@ -95,6 +100,8 @@
                 VoicemailContract.Status.DATA_CHANNEL_STATE_NO_CONNECTION,
                 VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
         removePhoneStateListener(phoneAccount);
+        mActiveVvmSources.remove(phoneAccount);
+        OmtpVvmSyncService.cancelAllRetries(mContext, phoneAccount);
     }
 
     public void addPhoneStateListener(PhoneAccountHandle phoneAccount) {
@@ -113,27 +120,21 @@
     }
 
     public Set<PhoneAccountHandle> getOmtpVvmSources() {
-        return mPhoneStateListenerMap.keySet();
+        return mActiveVvmSources;
     }
 
     /**
      * Check if a certain account is registered.
      *
      * @param phoneAccount The account to look for.
-     * @return {@code true} if the account is in the list of registered OMTP voicemail sync
-     * accounts. {@code false} otherwise.
+     * @return {@code true} if the account is in the list of registered OMTP voicemail sources.
+     * {@code false} otherwise.
      */
     public boolean isVvmSourceRegistered(PhoneAccountHandle phoneAccount) {
         if (phoneAccount == null) {
             return false;
         }
 
-        Set<PhoneAccountHandle> sources = getOmtpVvmSources();
-        for (PhoneAccountHandle source : sources) {
-            if (phoneAccount.equals(source)) {
-                return true;
-            }
-        }
-        return false;
+        return mActiveVvmSources.contains(phoneAccount);
     }
 }
\ No newline at end of file
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
index 5af912c..d0c6231 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
@@ -15,7 +15,9 @@
  */
 package com.android.phone.vvm.omtp.sync;
 
+import android.app.AlarmManager;
 import android.app.IntentService;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
@@ -55,14 +57,73 @@
     // Timeout used to call ConnectivityManager.requestNetwork
     private static final int NETWORK_REQUEST_TIMEOUT_MILLIS = 60 * 1000;
 
-    private VoicemailsQueryHelper mQueryHelper;
+    // Number of retries
+    private static final int NETWORK_RETRY_COUNT = 6;
 
+    private VoicemailsQueryHelper mQueryHelper;
     private ConnectivityManager mConnectivityManager;
+    private Map<NetworkCallback, NetworkRequest> mNetworkRequestMap;
 
     public OmtpVvmSyncService() {
         super("OmtpVvmSyncService");
     }
 
+    public static Intent getSyncIntent(Context context, String action,
+            PhoneAccountHandle phoneAccount, boolean firstAttempt) {
+        if (firstAttempt) {
+            if (phoneAccount != null) {
+                VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(context,
+                        phoneAccount);
+            } else {
+                OmtpVvmSourceManager vvmSourceManager =
+                        OmtpVvmSourceManager.getInstance(context);
+                Set<PhoneAccountHandle> sources = vvmSourceManager.getOmtpVvmSources();
+                for (PhoneAccountHandle source : sources) {
+                    VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(context, source);
+                }
+            }
+        }
+
+        Intent serviceIntent = new Intent(context, OmtpVvmSyncService.class);
+        serviceIntent.setAction(action);
+        if (phoneAccount != null) {
+            serviceIntent.putExtra(EXTRA_PHONE_ACCOUNT, phoneAccount);
+        }
+
+        cancelRetriesForIntent(context, serviceIntent);
+        return serviceIntent;
+    }
+
+    /**
+     * Cancel all retry syncs for an account.
+     * @param context The context the service runs in.
+     * @param phoneAccount The phone account for which to cancel syncs.
+     */
+    public static void cancelAllRetries(Context context, PhoneAccountHandle phoneAccount) {
+        cancelRetriesForIntent(context, getSyncIntent(context, SYNC_FULL_SYNC, phoneAccount,
+                false));
+    }
+
+    /**
+     * A helper method to cancel all pending alarms for intents that would be identical to the given
+     * intent.
+     * @param context The context the service runs in.
+     * @param intent The intent to search and cancel.
+     */
+    private static void cancelRetriesForIntent(Context context, Intent intent) {
+        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        alarmManager.cancel(PendingIntent.getService(context, 0, intent, 0));
+
+        Intent copyIntent = new Intent(intent);
+        if (SYNC_FULL_SYNC.equals(copyIntent.getAction())) {
+            // A full sync action should also cancel both of the other types of syncs
+            copyIntent.setAction(SYNC_DOWNLOAD_ONLY);
+            alarmManager.cancel(PendingIntent.getService(context, 0, copyIntent, 0));
+            copyIntent.setAction(SYNC_UPLOAD_ONLY);
+            alarmManager.cancel(PendingIntent.getService(context, 0, copyIntent, 0));
+        }
+    }
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -76,80 +137,151 @@
             return;
         }
 
+        mNetworkRequestMap = new HashMap<NetworkCallback, NetworkRequest>();
+
         String action = intent.getAction();
         PhoneAccountHandle phoneAccount = intent.getParcelableExtra(EXTRA_PHONE_ACCOUNT);
         if (phoneAccount != null) {
             Log.v(TAG, "Sync requested: " + action + " - for account: " + phoneAccount);
-            doSync(phoneAccount, action);
+            setupAndSendNetworkRequest(phoneAccount, action);
         } else {
             Log.v(TAG, "Sync requested: " + action + " - for all accounts");
             OmtpVvmSourceManager vvmSourceManager =
                     OmtpVvmSourceManager.getInstance(this);
             Set<PhoneAccountHandle> sources = vvmSourceManager.getOmtpVvmSources();
             for (PhoneAccountHandle source : sources) {
-                doSync(source, action);
+                setupAndSendNetworkRequest(source, action);
             }
         }
     }
 
-    private void doSync(PhoneAccountHandle phoneAccount, String action) {
+    private void setupAndSendNetworkRequest(PhoneAccountHandle phoneAccount, String action) {
         if (!VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(this, phoneAccount)) {
             Log.v(TAG, "Sync requested for disabled account");
             return;
         }
 
-        int subId = PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccount);
-
         NetworkRequest networkRequest = new NetworkRequest.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .setNetworkSpecifier(Integer.toString(subId))
-                .build();
+        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+        .setNetworkSpecifier(
+                Integer.toString(PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccount)))
+        .build();
+
         NetworkCallback networkCallback = new OmtpVvmNetworkRequestCallback(this, phoneAccount,
                 action);
-        getConnectivityManager().requestNetwork(
-                networkRequest, networkCallback, NETWORK_REQUEST_TIMEOUT_MILLIS);
+
+        mNetworkRequestMap.put(networkCallback, networkRequest);
+
+        requestNetwork(networkCallback);
     }
 
     private class OmtpVvmNetworkRequestCallback extends ConnectivityManager.NetworkCallback {
         Context mContext;
         PhoneAccountHandle mPhoneAccount;
         String mAction;
+        int mRetryCount;
 
         public OmtpVvmNetworkRequestCallback(Context context, PhoneAccountHandle phoneAccount,
                 String action) {
             mContext = context;
             mPhoneAccount = phoneAccount;
             mAction = action;
+            mRetryCount = NETWORK_RETRY_COUNT;
         }
 
         @Override
         public void onAvailable(final Network network) {
-            ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
-            if (SYNC_FULL_SYNC.equals(mAction) || SYNC_UPLOAD_ONLY.equals(mAction)) {
-                upload(imapHelper);
+            boolean uploadSuccess;
+            boolean downloadSuccess;
+
+            while (mRetryCount > 0) {
+                uploadSuccess = true;
+                downloadSuccess = true;
+
+                ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
+
+                if (!imapHelper.isSuccessfullyInitialized()) {
+                    Log.w(TAG, "Can't retrieve Imap credentials.");
+                    releaseNetwork(this);
+                    VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(mContext,
+                            mPhoneAccount);
+                    return;
+                }
+
+                if (SYNC_FULL_SYNC.equals(mAction) || SYNC_UPLOAD_ONLY.equals(mAction)) {
+                    uploadSuccess = upload(imapHelper);
+                }
+                if (SYNC_FULL_SYNC.equals(mAction) || SYNC_DOWNLOAD_ONLY.equals(mAction)) {
+                    downloadSuccess = download(imapHelper);
+                }
+
+                Log.v(TAG, "upload succeeded: ["+  String.valueOf(uploadSuccess)
+                        + "] download succeeded: [" + String.valueOf(downloadSuccess) + "]");
+
+                // Need to check again for whether visual voicemail is enabled because it could have
+                // been disabled while waiting for the response from the network.
+                if (VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(mContext, mPhoneAccount) &&
+                        (!uploadSuccess || !downloadSuccess)) {
+                    mRetryCount--;
+                    // Re-adjust so that only the unsuccessful action needs to be retried.
+                    // No need to re-adjust if both are unsuccessful. It means the full sync
+                    // failed so the action remains unchanged.
+                    if (uploadSuccess) {
+                        mAction = SYNC_DOWNLOAD_ONLY;
+                    } else if (downloadSuccess) {
+                        mAction = SYNC_UPLOAD_ONLY;
+                    }
+
+                    Log.v(TAG, "Retrying " + mAction);
+                } else {
+                    // Nothing more to do here, just exit.
+                    releaseNetwork(this);
+                    VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(mContext,
+                            mPhoneAccount);
+                    return;
+                }
             }
-            if (SYNC_FULL_SYNC.equals(mAction) || SYNC_DOWNLOAD_ONLY.equals(mAction)) {
-                download(imapHelper);
-            }
-            releaseNetwork();
+
+            releaseNetwork(this);
+            setRetryAlarm(mPhoneAccount, mAction);
         }
 
         @Override
         public void onLost(Network network) {
-            releaseNetwork();
+            releaseNetwork(this);
+
+            if (mRetryCount > 0) {
+                mRetryCount--;
+                requestNetwork(this);
+            } else {
+                setRetryAlarm(mPhoneAccount, mAction);
+            }
         }
 
         @Override
         public void onUnavailable() {
-            releaseNetwork();
-        }
+            releaseNetwork(this);
 
-        private void releaseNetwork() {
-            getConnectivityManager().unregisterNetworkCallback(this);
+            if (mRetryCount> 0) {
+                mRetryCount--;
+                requestNetwork(this);
+            } else {
+                setRetryAlarm(mPhoneAccount, mAction);
+            }
         }
     }
 
+    private void requestNetwork(NetworkCallback networkCallback) {
+        NetworkRequest networkRequest = mNetworkRequestMap.get(networkCallback);
+        getConnectivityManager().requestNetwork(
+                networkRequest, networkCallback, NETWORK_REQUEST_TIMEOUT_MILLIS);
+    }
+
+    private void releaseNetwork(NetworkCallback networkCallback) {
+        getConnectivityManager().unregisterNetworkCallback(networkCallback);
+    }
+
     private ConnectivityManager getConnectivityManager() {
         if (mConnectivityManager == null) {
             mConnectivityManager = (ConnectivityManager) this.getSystemService(
@@ -158,36 +290,58 @@
         return mConnectivityManager;
     }
 
-    private void upload(ImapHelper imapHelper) {
+    private void setRetryAlarm(PhoneAccountHandle phoneAccount, String action) {
+        Intent serviceIntent = new Intent(this, OmtpVvmSyncService.class);
+        serviceIntent.setAction(action);
+        serviceIntent.putExtra(OmtpVvmSyncService.EXTRA_PHONE_ACCOUNT, phoneAccount);
+        PendingIntent pendingIntent = PendingIntent.getService(this, 0, serviceIntent, 0);
+        long retryInterval = VisualVoicemailSettingsUtil.getVisualVoicemailRetryInterval(this,
+                phoneAccount);
+
+        Log.v(TAG, "Retrying "+ action + " in " + retryInterval + "ms");
+
+        AlarmManager alarmManager = (AlarmManager)
+                this.getSystemService(Context.ALARM_SERVICE);
+        alarmManager.set(AlarmManager.ELAPSED_REALTIME, retryInterval, pendingIntent);
+
+        VisualVoicemailSettingsUtil.setVisualVoicemailRetryInterval(this, phoneAccount,
+                retryInterval * 2);
+    }
+
+    private boolean upload(ImapHelper imapHelper) {
         List<Voicemail> readVoicemails = mQueryHelper.getReadVoicemails();
         List<Voicemail> deletedVoicemails = mQueryHelper.getDeletedVoicemails();
 
-        if (deletedVoicemails != null) {
+        boolean success = true;
+
+        if (deletedVoicemails.size() > 0) {
             if (imapHelper.markMessagesAsDeleted(deletedVoicemails)) {
                 // We want to delete selectively instead of all the voicemails for this provider
                 // in case the state changed since the IMAP query was completed.
                 mQueryHelper.deleteFromDatabase(deletedVoicemails);
             } else {
-                mQueryHelper.markUndeletedInDatabase(deletedVoicemails);
+                success = false;
             }
         }
 
-        if (readVoicemails != null) {
+        if (readVoicemails.size() > 0) {
             if (imapHelper.markMessagesAsRead(readVoicemails)) {
                 mQueryHelper.markReadInDatabase(readVoicemails);
             } else {
-                mQueryHelper.markUnreadInDatabase(readVoicemails);
+                success = false;
             }
         }
+
+        return success;
     }
 
-    private void download(ImapHelper imapHelper) {
+    private boolean download(ImapHelper imapHelper) {
         List<Voicemail> serverVoicemails = imapHelper.fetchAllVoicemails();
         List<Voicemail> localVoicemails = mQueryHelper.getAllVoicemails();
 
         if (localVoicemails == null || serverVoicemails == null) {
             // Null value means the query failed.
-            return;
+            return false;
         }
 
         Map<String, Voicemail> remoteMap = buildMap(serverVoicemails);
@@ -213,6 +367,8 @@
         for (Voicemail remoteVoicemail : remoteMap.values()) {
             VoicemailContract.Voicemails.insert(this, remoteVoicemail);
         }
+
+        return true;
     }
 
     /**
diff --git a/src/com/android/phone/vvm/omtp/sync/VoicemailProviderChangeReceiver.java b/src/com/android/phone/vvm/omtp/sync/VoicemailProviderChangeReceiver.java
index 0885d65..c2e6178 100644
--- a/src/com/android/phone/vvm/omtp/sync/VoicemailProviderChangeReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sync/VoicemailProviderChangeReceiver.java
@@ -30,8 +30,8 @@
         OmtpVvmSourceManager vvmSourceManager =
                 OmtpVvmSourceManager.getInstance(context);
         if (vvmSourceManager.getOmtpVvmSources().size() > 0 && !isSelfChanged) {
-            Intent serviceIntent = new Intent(context, OmtpVvmSyncService.class);
-            serviceIntent.setAction(OmtpVvmSyncService.SYNC_UPLOAD_ONLY);
+            Intent serviceIntent = OmtpVvmSyncService.getSyncIntent(
+                    context, OmtpVvmSyncService.SYNC_UPLOAD_ONLY, null, true /* firstAttempt */);
             context.startService(serviceIntent);
         }
     }
diff --git a/src/com/android/phone/vvm/omtp/sync/VoicemailStatusQueryHelper.java b/src/com/android/phone/vvm/omtp/sync/VoicemailStatusQueryHelper.java
index fa87a59..66f3e9d 100644
--- a/src/com/android/phone/vvm/omtp/sync/VoicemailStatusQueryHelper.java
+++ b/src/com/android/phone/vvm/omtp/sync/VoicemailStatusQueryHelper.java
@@ -22,23 +22,23 @@
 import android.provider.VoicemailContract;
 import android.provider.VoicemailContract.Status;
 import android.telecom.PhoneAccountHandle;
-import android.util.Log;
 
 /**
  * Construct queries to interact with the voicemail status table.
  */
 public class VoicemailStatusQueryHelper {
-    private static final String TAG = "VoicemailStatusQueryHelper";
 
     final static String[] PROJECTION = new String[] {
             Status._ID,                        // 0
-            Status.NOTIFICATION_CHANNEL_STATE, // 1
-            Status.SOURCE_PACKAGE              // 2
+            Status.CONFIGURATION_STATE,        // 1
+            Status.NOTIFICATION_CHANNEL_STATE, // 2
+            Status.SOURCE_PACKAGE              // 3
    };
 
     public static final int _ID = 0;
-    public static final int NOTIFICATION_CHANNEL_STATE = 1;
-    public static final int SOURCE_PACKAGE = 2;
+    public static final int CONFIGURATION_STATE = 1;
+    public static final int NOTIFICATION_CHANNEL_STATE = 2;
+    public static final int SOURCE_PACKAGE = 3;
 
     private Context mContext;
     private ContentResolver mContentResolver;
@@ -51,12 +51,38 @@
     }
 
     /**
+     * Check if the configuration state for the voicemail source is "ok", meaning that the
+     * source is set up.
+     *
+     * @param phoneAccount The phone account for the voicemail source to check.
+     * @return {@code true} if the voicemail source is configured, {@code} false otherwise,
+     * including if the voicemail source is not registered in the table.
+     */
+    public boolean isVoicemailSourceConfigured(PhoneAccountHandle phoneAccount) {
+        return isFieldEqualTo(phoneAccount, CONFIGURATION_STATE, Status.CONFIGURATION_STATE_OK);
+    }
+
+    /**
      * Check if the notifications channel of a voicemail source is active. That is, when a new
      * voicemail is available, if the server able to notify the device.
      *
      * @return {@code true} if notifications channel is active, {@code false} otherwise.
      */
     public boolean isNotificationsChannelActive(PhoneAccountHandle phoneAccount) {
+        return isFieldEqualTo(phoneAccount, NOTIFICATION_CHANNEL_STATE,
+                Status.NOTIFICATION_CHANNEL_STATE_OK);
+    }
+
+    /**
+     * Check if a field for an entry in the status table is equal to a specific value.
+     *
+     * @param phoneAccount The phone account of the voicemail source to query for.
+     * @param columnIndex The column index of the field in the returned query.
+     * @param value The value to compare against.
+     * @return {@code true} if the stored value is equal to the provided value. {@code false}
+     * otherwise.
+     */
+    private boolean isFieldEqualTo(PhoneAccountHandle phoneAccount, int columnIndex, int value) {
         Cursor cursor = null;
         if (phoneAccount != null) {
             String phoneAccountComponentName = phoneAccount.getComponentName().flattenToString();
@@ -73,8 +99,7 @@
                 cursor = mContentResolver.query(
                         mSourceUri, PROJECTION, whereClause, whereArgs, null);
                 if (cursor != null && cursor.moveToFirst()) {
-                    return cursor.getInt(NOTIFICATION_CHANNEL_STATE) ==
-                            Status.NOTIFICATION_CHANNEL_STATE_OK;
+                    return cursor.getInt(columnIndex) == value;
                 }
             }
             finally {
diff --git a/src/com/android/phone/vvm/omtp/sync/VoicemailsQueryHelper.java b/src/com/android/phone/vvm/omtp/sync/VoicemailsQueryHelper.java
index 2a30216..b86351c 100644
--- a/src/com/android/phone/vvm/omtp/sync/VoicemailsQueryHelper.java
+++ b/src/com/android/phone/vvm/omtp/sync/VoicemailsQueryHelper.java
@@ -104,7 +104,10 @@
             while (cursor.moveToNext()) {
                 final long id = cursor.getLong(_ID);
                 final String sourceData = cursor.getString(SOURCE_DATA);
-                Voicemail voicemail = Voicemail.createForUpdate(id, sourceData).build();
+                final boolean isRead = cursor.getInt(IS_READ) == 1;
+                Voicemail voicemail = Voicemail
+                        .createForUpdate(id, sourceData)
+                        .setIsRead(isRead).build();
                 voicemails.add(voicemail);
             }
             return voicemails;
@@ -166,36 +169,9 @@
      * Utility method to mark single message as read.
      */
     public void markReadInDatabase(Voicemail voicemail) {
-        updateInDatabase(voicemail, Voicemails.IS_READ, "1");
-    }
-
-    /**
-     * Undelete in database. This will be called if sync to server fails.
-     */
-    public void markUndeletedInDatabase(List<Voicemail> voicemails) {
-        int count = voicemails.size();
-        for (int i = 0; i < count; i++) {
-            updateInDatabase(voicemails.get(i), Voicemails.DELETED, "0");
-        }
-    }
-
-    /**
-     * Unread in database. This will be called if sync to server fails.
-     */
-    public void markUnreadInDatabase(List<Voicemail> voicemails) {
-        int count = voicemails.size();
-        for (int i = 0; i < count; i++) {
-            updateInDatabase(voicemails.get(i), Voicemails.IS_READ, "0");
-        }
-    }
-
-    /**
-     * Make an update of the requested field in the database.
-     */
-    private void updateInDatabase(Voicemail voicemail, String field, String value) {
         Uri uri = ContentUris.withAppendedId(mSourceUri, voicemail.getId());
         ContentValues contentValues = new ContentValues();
-        contentValues.put(field, value);
+        contentValues.put(Voicemails.IS_READ, "1");
         mContentResolver.update(uri, contentValues, null, null);
     }
 
diff --git a/src/com/android/phone/vvm/omtp/sync/VvmPhoneStateListener.java b/src/com/android/phone/vvm/omtp/sync/VvmPhoneStateListener.java
index f51a2f0..84449d7 100644
--- a/src/com/android/phone/vvm/omtp/sync/VvmPhoneStateListener.java
+++ b/src/com/android/phone/vvm/omtp/sync/VvmPhoneStateListener.java
@@ -23,11 +23,13 @@
 import android.telephony.ServiceState;
 
 import com.android.phone.PhoneUtils;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
 
 /**
  * Check if service is lost and indicate this in the voicemail status.
  */
 public class VvmPhoneStateListener extends PhoneStateListener {
+
     private PhoneAccountHandle mPhoneAccount;
     private Context mContext;
     public VvmPhoneStateListener(Context context, PhoneAccountHandle accountHandle) {
@@ -41,17 +43,24 @@
         if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
             VoicemailStatusQueryHelper voicemailStatusQueryHelper =
                     new VoicemailStatusQueryHelper(mContext);
-            if (!voicemailStatusQueryHelper.isNotificationsChannelActive(mPhoneAccount)) {
-                VoicemailContract.Status.setStatus(mContext, mPhoneAccount,
-                        VoicemailContract.Status.CONFIGURATION_STATE_OK,
-                        VoicemailContract.Status.DATA_CHANNEL_STATE_OK,
-                        VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK);
+            if (voicemailStatusQueryHelper.isVoicemailSourceConfigured(mPhoneAccount)) {
+                if (!voicemailStatusQueryHelper.isNotificationsChannelActive(mPhoneAccount)) {
+                    VoicemailContract.Status.setStatus(mContext, mPhoneAccount,
+                            VoicemailContract.Status.CONFIGURATION_STATE_OK,
+                            VoicemailContract.Status.DATA_CHANNEL_STATE_OK,
+                            VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK);
+                    // Run a full sync in case something was missed while signal was down.
+                    Intent serviceIntent = OmtpVvmSyncService.getSyncIntent(
+                            mContext, OmtpVvmSyncService.SYNC_FULL_SYNC, mPhoneAccount,
+                            true /* firstAttempt */);
+                    mContext.startService(serviceIntent);
+                }
+            }
 
-                // Run a full sync in case something was missed while signal was down.
-                Intent serviceIntent = new Intent(mContext, OmtpVvmSyncService.class);
-                serviceIntent.setAction(OmtpVvmSyncService.SYNC_FULL_SYNC);
-                serviceIntent.putExtra(OmtpVvmSyncService.EXTRA_PHONE_ACCOUNT, mPhoneAccount);
-                mContext.startService(serviceIntent);
+            if (!OmtpVvmSourceManager.getInstance(mContext).isVvmSourceRegistered(mPhoneAccount)) {
+                OmtpVvmCarrierConfigHelper carrierConfigHelper = new OmtpVvmCarrierConfigHelper(
+                        mContext, PhoneUtils.getSubIdForPhoneAccountHandle(mPhoneAccount));
+                carrierConfigHelper.startActivation();
             }
         } else {
             VoicemailContract.Status.setStatus(mContext, mPhoneAccount,
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index a1729b7..72c9b07 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -27,6 +27,7 @@
 import android.telecom.ConnectionService;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
@@ -152,44 +153,20 @@
                 // Obtain the configuration for the outgoing phone's SIM. If the outgoing number
                 // matches the *228 regex pattern, fail the call. This number is used for OTASP, and
                 // when dialed could lock LTE SIMs to 3G if not prohibited..
-                SubscriptionManager subManager = SubscriptionManager.from(phone.getContext());
-                SubscriptionInfo subInfo = subManager.getActiveSubscriptionInfo(phone.getSubId());
-                if (subInfo != null) {
-                    Configuration config = new Configuration();
-                    config.mcc = subInfo.getMcc();
-                    config.mnc = subInfo.getMnc();
-                    Context subContext = phone.getContext().createConfigurationContext(config);
+                boolean disableActivation = false;
+                CarrierConfigManager cfgManager = (CarrierConfigManager)
+                        phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+                if (cfgManager != null) {
+                    disableActivation = cfgManager.getConfigForSubId(phone.getSubId())
+                            .getBoolean(CarrierConfigManager.KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL);
+                }
 
-                    // Get the resources specific to the subscription in question.
-                    Resources res = subContext.getResources();
-                    if (res != null) {
-                        boolean disableActivation = false;
-                        String configValue =
-                                res.getString(R.string.config_disable_cdma_activation_code);
-
-                        // Set disableActivation based on the configuration value.
-                        if (!TextUtils.isEmpty(configValue)) {
-                            String [] valueArray = configValue.split(";");
-
-                            if (valueArray.length == 1) {
-                                // If the configuration says just "true" disable it.
-                                disableActivation = valueArray[0].equalsIgnoreCase("true");
-                            } else if (valueArray.length == 2) {
-                                // If the configuration is split by a semicolon, make sure the
-                                // second half is equal to the group ID for the phone.
-                                disableActivation = valueArray[0].equalsIgnoreCase("true") &&
-                                        valueArray[1].equalsIgnoreCase(phone.getGroupIdLevel1());
-                            }
-                        }
-
-                        if (disableActivation) {
-                            return Connection.createFailedConnection(
-                                    DisconnectCauseUtil.toTelecomDisconnectCause(
-                                            android.telephony.DisconnectCause
-                                                    .CDMA_ALREADY_ACTIVATED,
-                                            "Tried to dial *228"));
-                        }
-                    }
+                if (disableActivation) {
+                    return Connection.createFailedConnection(
+                            DisconnectCauseUtil.toTelecomDisconnectCause(
+                                    android.telephony.DisconnectCause
+                                            .CDMA_ALREADY_ACTIVATED,
+                                    "Tried to dial *228"));
                 }
             }
         }
@@ -569,6 +546,8 @@
                 Log.d(this, "Adding CDMA connection to conference controller: " + connection);
                 mCdmaConferenceController.add((CdmaConnection)connection);
             }
+            Log.d(this, "Removing connection from IMS conference controller: " + connection);
+            mImsConferenceController.remove(connection);
         }
     }
 }