Merge "Update to using CONFIGURE_PHONE_ACCOUNT" into mnc-dev
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/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index e8b6e05..359535e 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -2651,8 +2651,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) {
diff --git a/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java b/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
index d2e797b..95dded8 100644
--- a/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
+++ b/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
@@ -73,12 +73,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
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..197c754 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,16 @@
             executor.execute(new Runnable() {
                 @Override
                 public void run() {
-                    new ImapHelper(mContext, mPhoneAccount, network).fetchVoicemailPayload(
+                    ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
+                    boolean success = imapHelper.fetchVoicemailPayload(
                             new VoicemailFetchedCallback(mContext, mUri), mUid);
+
                     releaseNetwork();
+
+                    if (!success && mRetryCount > 0) {
+                        mRetryCount--;
+                        requestNetwork();
+                    }
                 }
             });
         }
@@ -140,14 +150,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..b40cf1e 100644
--- a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
+++ b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
@@ -175,21 +175,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..fe4cf11 100644
--- a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
+++ b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
@@ -132,8 +132,8 @@
                 mPhoneAccount,
                 message);
 
-        // Add a phone state listener so that changes to the communication channels can be recorded.
-        vvmSourceManager.addPhoneStateListener(mPhoneAccount);
+        // Add the source to indicate that it is active.
+        vvmSourceManager.addSource(mPhoneAccount);
 
         Intent serviceIntent = new Intent(mContext, OmtpVvmSyncService.class);
         serviceIntent.setAction(OmtpVvmSyncService.SYNC_FULL_SYNC);
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java
index 37fb5e4..59230cc 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,7 @@
                 VoicemailContract.Status.DATA_CHANNEL_STATE_NO_CONNECTION,
                 VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
         removePhoneStateListener(phoneAccount);
+        mActiveVvmSources.remove(phoneAccount);
     }
 
     public void addPhoneStateListener(PhoneAccountHandle phoneAccount) {
@@ -113,27 +119,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..1b61cfe 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,9 +57,12 @@
     // 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 = 3;
 
+    private VoicemailsQueryHelper mQueryHelper;
     private ConnectivityManager mConnectivityManager;
+    private Map<NetworkCallback, NetworkRequest> mNetworkRequestMap;
 
     public OmtpVvmSyncService() {
         super("OmtpVvmSyncService");
@@ -76,6 +81,8 @@
             return;
         }
 
+        mNetworkRequestMap = new HashMap<NetworkCallback, NetworkRequest>();
+
         String action = intent.getAction();
         PhoneAccountHandle phoneAccount = intent.getParcelableExtra(EXTRA_PHONE_ACCOUNT);
         if (phoneAccount != null) {
@@ -98,58 +105,109 @@
             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) {
+            boolean uploadSuccess = true;
+            boolean downloadSuccess = true;
+
             ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
             if (SYNC_FULL_SYNC.equals(mAction) || SYNC_UPLOAD_ONLY.equals(mAction)) {
-                upload(imapHelper);
+                uploadSuccess = upload(imapHelper);
             }
             if (SYNC_FULL_SYNC.equals(mAction) || SYNC_DOWNLOAD_ONLY.equals(mAction)) {
-                download(imapHelper);
+                downloadSuccess = download(imapHelper);
             }
-            releaseNetwork();
+
+            releaseNetwork(this);
+
+            Log.v(TAG, "upload succeeded: ["+  String.valueOf(uploadSuccess)
+                    + "] download succeeded: [" + String.valueOf(downloadSuccess) + "]");
+
+            if ((!uploadSuccess || !downloadSuccess)) {
+                if (mRetryCount > 0) {
+                    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);
+
+                    requestNetwork(this);
+                } else {
+                    setRetryAlarm(mPhoneAccount);
+                }
+            }
         }
 
         @Override
         public void onLost(Network network) {
-            releaseNetwork();
+            releaseNetwork(this);
+
+            if (mRetryCount > 0) {
+                mRetryCount--;
+                requestNetwork(this);
+            } else {
+                setRetryAlarm(mPhoneAccount);
+            }
         }
 
         @Override
         public void onUnavailable() {
-            releaseNetwork();
-        }
+            releaseNetwork(this);
 
-        private void releaseNetwork() {
-            getConnectivityManager().unregisterNetworkCallback(this);
+            if (mRetryCount> 0) {
+                mRetryCount--;
+                requestNetwork(this);
+            } else {
+                setRetryAlarm(mPhoneAccount);
+            }
         }
     }
 
+    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 +216,53 @@
         return mConnectivityManager;
     }
 
-    private void upload(ImapHelper imapHelper) {
+    private void setRetryAlarm(PhoneAccountHandle phoneAccount) {
+        Intent serviceIntent = new Intent(this, OmtpVvmSyncService.class);
+        serviceIntent.setAction(OmtpVvmSyncService.SYNC_FULL_SYNC);
+        serviceIntent.putExtra(OmtpVvmSyncService.EXTRA_PHONE_ACCOUNT, phoneAccount);
+        PendingIntent pendingIntent = PendingIntent.getService(this, 0, serviceIntent, 0);
+
+        AlarmManager alarmManager = (AlarmManager)
+                this.getSystemService(Context.ALARM_SERVICE);
+        alarmManager.set(AlarmManager.ELAPSED_REALTIME, 5000, pendingIntent);
+    }
+
+    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 {
+                success = false;
                 mQueryHelper.markUndeletedInDatabase(deletedVoicemails);
             }
         }
 
-        if (readVoicemails != null) {
+        if (readVoicemails.size() > 0) {
             if (imapHelper.markMessagesAsRead(readVoicemails)) {
                 mQueryHelper.markReadInDatabase(readVoicemails);
             } else {
+                success = false;
                 mQueryHelper.markUnreadInDatabase(readVoicemails);
             }
         }
+
+        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 +288,8 @@
         for (Voicemail remoteVoicemail : remoteMap.values()) {
             VoicemailContract.Voicemails.insert(this, remoteVoicemail);
         }
+
+        return true;
     }
 
     /**
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/VvmPhoneStateListener.java b/src/com/android/phone/vvm/omtp/sync/VvmPhoneStateListener.java
index f51a2f0..30f5a45 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,22 @@
         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);
-
-                // 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 (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 = new Intent(mContext, OmtpVvmSyncService.class);
+                    serviceIntent.setAction(OmtpVvmSyncService.SYNC_FULL_SYNC);
+                    serviceIntent.putExtra(OmtpVvmSyncService.EXTRA_PHONE_ACCOUNT, mPhoneAccount);
+                    mContext.startService(serviceIntent);
+                }
+            } else {
+                OmtpVvmCarrierConfigHelper carrierConfigHelper = new OmtpVvmCarrierConfigHelper(
+                        mContext, PhoneUtils.getSubIdForPhoneAccountHandle(mPhoneAccount));
+                carrierConfigHelper.startActivation();
             }
         } else {
             VoicemailContract.Status.setStatus(mContext, mPhoneAccount,