Merge "Immediately retry visual voicemail server calls." into mnc-dev
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/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;
     }
 
     /**