Set alarm for retries with exponential backoff.
If the first retries don't work, we want to retry later using the
AlarmManager, which will schedule the retry when convenient.
Currently it just retries every 5 seconds (that's bad). This
change includes:
- Add key to preferences to save the current retry interval
- Add helper methods to construct proper pending intent and cancel
existing retries
- Remove the "undelete"/"unread" since we will be retrying uploads as
well as downloads.
Also we don't want to make a new network request if the first one
succeeded, we just want to retry the IMAP connection.
There's also a fix here for a bug where the local voicemails were not
indicated as "read" so there would be unnecessary updates to the
voicemail provider.
Bug: 21978479
Change-Id: I18c5e2d0b7920540ded74b09f63eb94fdd959f43
diff --git a/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java b/src/com/android/phone/settings/VisualVoicemailSettingsUtil.java
index 95dded8..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),
@@ -115,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/fetch/FetchVoicemailReceiver.java b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
index 197c754..83e9633 100644
--- a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
+++ b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
@@ -133,15 +133,16 @@
executor.execute(new Runnable() {
@Override
public void run() {
- ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
- boolean success = imapHelper.fetchVoicemailPayload(
- new VoicemailFetchedCallback(mContext, mUri), mUid);
-
- releaseNetwork();
-
- if (!success && mRetryCount > 0) {
- mRetryCount--;
- requestNetwork();
+ while (mRetryCount > 0) {
+ ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
+ boolean success = imapHelper.fetchVoicemailPayload(
+ new VoicemailFetchedCallback(mContext, mUri), mUid);
+ if (!success && mRetryCount > 0) {
+ mRetryCount--;
+ } else {
+ releaseNetwork();
+ return;
+ }
}
}
});
diff --git a/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java b/src/com/android/phone/vvm/omtp/sms/OmtpMessageReceiver.java
index fe4cf11..6c8106b 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:
@@ -135,9 +135,9 @@
// 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);
- serviceIntent.putExtra(OmtpVvmSyncService.EXTRA_PHONE_ACCOUNT, mPhoneAccount);
+ Intent serviceIntent = OmtpVvmSyncService.getSyncIntent(
+ mContext, OmtpVvmSyncService.SYNC_FULL_SYNC, mPhoneAccount,
+ true /* firstAttempt */);
mContext.startService(serviceIntent);
}
}
\ 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 59230cc..63e9fee 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSourceManager.java
@@ -101,6 +101,7 @@
VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
removePhoneStateListener(phoneAccount);
mActiveVvmSources.remove(phoneAccount);
+ OmtpVvmSyncService.cancelAllRetries(mContext, phoneAccount);
}
public void addPhoneStateListener(PhoneAccountHandle phoneAccount) {
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
index 1b61cfe..d96f058 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
@@ -58,7 +58,7 @@
private static final int NETWORK_REQUEST_TIMEOUT_MILLIS = 60 * 1000;
// Number of retries
- private static final int NETWORK_RETRY_COUNT = 3;
+ private static final int NETWORK_RETRY_COUNT = 6;
private VoicemailsQueryHelper mQueryHelper;
private ConnectivityManager mConnectivityManager;
@@ -68,6 +68,62 @@
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();
@@ -87,19 +143,19 @@
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;
@@ -136,28 +192,32 @@
@Override
public void onAvailable(final Network network) {
- boolean uploadSuccess = true;
- boolean downloadSuccess = true;
+ boolean uploadSuccess;
+ boolean downloadSuccess;
- ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
- 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);
- }
+ while (mRetryCount > 0) {
+ uploadSuccess = true;
+ downloadSuccess = true;
- releaseNetwork(this);
+ ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
+ 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) + "]");
+ Log.v(TAG, "upload succeeded: ["+ String.valueOf(uploadSuccess)
+ + "] download succeeded: [" + String.valueOf(downloadSuccess) + "]");
- if ((!uploadSuccess || !downloadSuccess)) {
- if (mRetryCount > 0) {
+ // 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.
+ // 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) {
@@ -165,12 +225,17 @@
}
Log.v(TAG, "Retrying " + mAction);
-
- requestNetwork(this);
} else {
- setRetryAlarm(mPhoneAccount);
+ // Nothing more to do here, just exit.
+ releaseNetwork(this);
+ VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(mContext,
+ mPhoneAccount);
+ return;
}
}
+
+ releaseNetwork(this);
+ setRetryAlarm(mPhoneAccount, mAction);
}
@Override
@@ -181,7 +246,7 @@
mRetryCount--;
requestNetwork(this);
} else {
- setRetryAlarm(mPhoneAccount);
+ setRetryAlarm(mPhoneAccount, mAction);
}
}
@@ -193,7 +258,7 @@
mRetryCount--;
requestNetwork(this);
} else {
- setRetryAlarm(mPhoneAccount);
+ setRetryAlarm(mPhoneAccount, mAction);
}
}
}
@@ -216,15 +281,22 @@
return mConnectivityManager;
}
- private void setRetryAlarm(PhoneAccountHandle phoneAccount) {
+ private void setRetryAlarm(PhoneAccountHandle phoneAccount, String action) {
Intent serviceIntent = new Intent(this, OmtpVvmSyncService.class);
- serviceIntent.setAction(OmtpVvmSyncService.SYNC_FULL_SYNC);
+ 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, 5000, pendingIntent);
+ alarmManager.set(AlarmManager.ELAPSED_REALTIME, retryInterval, pendingIntent);
+
+ VisualVoicemailSettingsUtil.setVisualVoicemailRetryInterval(this, phoneAccount,
+ retryInterval * 2);
}
private boolean upload(ImapHelper imapHelper) {
@@ -240,7 +312,6 @@
mQueryHelper.deleteFromDatabase(deletedVoicemails);
} else {
success = false;
- mQueryHelper.markUndeletedInDatabase(deletedVoicemails);
}
}
@@ -249,7 +320,6 @@
mQueryHelper.markReadInDatabase(readVoicemails);
} else {
success = false;
- mQueryHelper.markUnreadInDatabase(readVoicemails);
}
}
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/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 30f5a45..d4b84d4 100644
--- a/src/com/android/phone/vvm/omtp/sync/VvmPhoneStateListener.java
+++ b/src/com/android/phone/vvm/omtp/sync/VvmPhoneStateListener.java
@@ -50,9 +50,9 @@
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);
+ Intent serviceIntent = OmtpVvmSyncService.getSyncIntent(
+ mContext, OmtpVvmSyncService.SYNC_FULL_SYNC, mPhoneAccount,
+ true /* firstAttempt */);
mContext.startService(serviceIntent);
}
} else {