From e4cea3bae3ee3ce00aadb2fc427636c73ec9deed Mon Sep 17 00:00:00 2001 From: Jesse Young Date: Tue, 13 Sep 2011 00:14:35 -0700 Subject: [PATCH] use alarm to schedule retries; fix bug where sms notfications were dropped when multiple messages were sent at once --- AndroidManifest.xml | 6 + src/org/envaya/kalsms/App.java | 178 +++++++++++------- .../kalsms/IncomingMessageForwarder.java | 2 - .../envaya/kalsms/IncomingMessageRetry.java | 16 ++ src/org/envaya/kalsms/Main.java | 2 +- .../envaya/kalsms/MessageStatusNotifier.java | 10 +- .../envaya/kalsms/OutgoingMessagePoller.java | 7 +- .../envaya/kalsms/OutgoingMessageRetry.java | 16 ++ 8 files changed, 160 insertions(+), 77 deletions(-) create mode 100755 src/org/envaya/kalsms/IncomingMessageRetry.java create mode 100755 src/org/envaya/kalsms/OutgoingMessageRetry.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 718ee60..4d98e82 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -35,6 +35,12 @@ + + + + + + diff --git a/src/org/envaya/kalsms/App.java b/src/org/envaya/kalsms/App.java index cf79b3a..98c1ef5 100755 --- a/src/org/envaya/kalsms/App.java +++ b/src/org/envaya/kalsms/App.java @@ -10,16 +10,14 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.net.Uri; import android.os.SystemClock; import android.preference.PreferenceManager; import android.telephony.SmsManager; import android.telephony.SmsMessage; import android.util.Log; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; import org.apache.http.HttpResponse; import org.apache.http.message.BasicNameValuePair; @@ -44,7 +42,7 @@ public class App { public Context context; public SharedPreferences settings; - private class QueuedMessage + private abstract class QueuedMessage { public T sms; public long nextAttemptTime = 0; @@ -52,46 +50,61 @@ public class App { public boolean canAttemptNow() { - return (nextAttemptTime > 0 && nextAttemptTime < System.currentTimeMillis()); + return (nextAttemptTime > 0 && nextAttemptTime < SystemClock.elapsedRealtime()); } - + public boolean scheduleNextAttempt() { - long now = System.currentTimeMillis(); - numAttempts++; + long now = SystemClock.elapsedRealtime(); + numAttempts++; - int sec = 1000; - - if (numAttempts == 1) - { - log("1st failure; retry in 1 minute"); - nextAttemptTime = now + sec * 60; // 1 minute - return true; - } - else if (numAttempts == 2) - { - log("2nd failure; retry in 10 minutes"); - nextAttemptTime = now + sec * 60 * 10; // 10 min - return true; - } - else if (numAttempts == 3) - { - log("3rd failure; retry in 1 hour"); - nextAttemptTime = now + sec * 60 * 60; // 1 hour - return true; - } - else if (numAttempts == 4) - { - log("4th failure: retry in 1 day"); - nextAttemptTime = now + sec * 60 * 60 * 24; // 1 day - return true; - } - else + if (numAttempts > 4) { log("5th failure: giving up"); return false; } + + int second = 1000; + int minute = second * 60; + + if (numAttempts == 1) + { + log("1st failure; retry in 1 minute"); + nextAttemptTime = now + 1 * minute; + } + else if (numAttempts == 2) + { + log("2nd failure; retry in 10 minutes"); + nextAttemptTime = now + 10 * minute; + } + else if (numAttempts == 3) + { + log("3rd failure; retry in 1 hour"); + nextAttemptTime = now + 60 * minute; + } + else + { + log("4th failure: retry in 1 day"); + nextAttemptTime = now + 24 * 60 * minute; + } + + AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, + 0, + getAttemptIntent(), + 0); + + alarm.set( + AlarmManager.ELAPSED_REALTIME_WAKEUP, + nextAttemptTime, + pendingIntent); + + return true; } + + public abstract void attemptNow(); + protected abstract Intent getAttemptIntent(); } private class QueuedIncomingSms extends QueuedMessage { @@ -99,15 +112,42 @@ public class App { { this.sms = sms; } - } - + + public void attemptNow() + { + log("Retrying forwarding SMS from " + sms.getOriginatingAddress()); + trySendMessageToServer(sms); + } + + protected Intent getAttemptIntent() + { + Intent intent = new Intent(context, IncomingMessageRetry.class); + intent.setData(Uri.parse("kalsms://incoming/" + getSmsId(sms))); + return intent; + } + } + private class QueuedOutgoingSms extends QueuedMessage { public QueuedOutgoingSms(OutgoingSmsMessage sms) { this.sms = sms; + } + + public void attemptNow() + { + log("Retrying sending " +sms.getLogName() + " to " + sms.getTo()); + trySendSMS(sms); + } + + protected Intent getAttemptIntent() + { + Intent intent = new Intent(context, OutgoingMessageRetry.class); + intent.setData(Uri.parse("kalsms://outgoing/" + sms.getId())); + log("id=" + sms.getId()); + return intent; } } - + protected App(Context context) { this.context = context; @@ -134,7 +174,7 @@ public class App { Intent broadcast = new Intent(App.LOG_INTENT); broadcast.putExtra("message", msg); - context.sendBroadcast(broadcast); + context.sendBroadcast(broadcast); } public void checkOutgoingMessages() @@ -160,7 +200,7 @@ public class App { PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, OutgoingMessagePoller.class), - 0); + 0); alarm.cancel(pendingIntent); @@ -172,7 +212,7 @@ public class App { AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pollSeconds * 1000, - pendingIntent); + pendingIntent); log("Checking for outgoing messages every " + pollSeconds + " sec"); } else @@ -282,10 +322,10 @@ public class App { } } - public synchronized void retryStuckMessages(boolean retryAll) + public synchronized void retryStuckMessages() { - retryStuckOutgoingMessages(retryAll); - retryStuckIncomingMessages(retryAll); + retryStuckOutgoingMessages(); + retryStuckIncomingMessages(); } public synchronized int getStuckMessageCount() @@ -293,36 +333,19 @@ public class App { return outgoingSmsMap.size() + incomingSmsMap.size(); } - public synchronized void retryStuckOutgoingMessages(boolean retryAll) + public synchronized void retryStuckOutgoingMessages() { - for (Entry entry : outgoingSmsMap.entrySet()) + for (QueuedOutgoingSms queuedSms : outgoingSmsMap.values()) { - QueuedOutgoingSms queuedSms = entry.getValue(); - if (retryAll || queuedSms.canAttemptNow()) - { - queuedSms.nextAttemptTime = 0; - - log("Retrying sending " +queuedSms.sms.getLogName() - + " to " + queuedSms.sms.getTo()); - - trySendSMS(queuedSms.sms); - } + queuedSms.attemptNow(); } } - public synchronized void retryStuckIncomingMessages(boolean retryAll) + public synchronized void retryStuckIncomingMessages() { - for (Entry entry : incomingSmsMap.entrySet()) + for (QueuedIncomingSms queuedSms : incomingSmsMap.values()) { - QueuedIncomingSms queuedSms = entry.getValue(); - if (retryAll || queuedSms.canAttemptNow()) - { - queuedSms.nextAttemptTime = 0; - - log("Retrying forwarding SMS from " + queuedSms.sms.getOriginatingAddress()); - - trySendMessageToServer(queuedSms.sms); - } + queuedSms.attemptNow(); } } @@ -395,7 +418,7 @@ public class App { SmsManager smgr = SmsManager.getDefault(); Intent intent = new Intent(context, MessageStatusNotifier.class); - intent.putExtra("id", sms.getId()); + intent.setData(Uri.parse("kalsms://outgoing/" + sms.getId())); PendingIntent sentIntent = PendingIntent.getBroadcast( this.context, @@ -503,4 +526,23 @@ public class App { } } } + + public synchronized void retryIncomingMessage(String id) + { + QueuedIncomingSms queuedSms = incomingSmsMap.get(id); + if (queuedSms != null) + { + queuedSms.attemptNow(); + } + } + + public synchronized void retryOutgoingMessage(String id) + { + QueuedOutgoingSms queuedSms = outgoingSmsMap.get(id); + if (queuedSms != null) + { + queuedSms.attemptNow(); + } + } + } diff --git a/src/org/envaya/kalsms/IncomingMessageForwarder.java b/src/org/envaya/kalsms/IncomingMessageForwarder.java index 2b13b5e..54c19e9 100755 --- a/src/org/envaya/kalsms/IncomingMessageForwarder.java +++ b/src/org/envaya/kalsms/IncomingMessageForwarder.java @@ -5,9 +5,7 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.telephony.SmsMessage; -import org.apache.http.HttpResponse; -import org.apache.http.message.BasicNameValuePair; public class IncomingMessageForwarder extends BroadcastReceiver { diff --git a/src/org/envaya/kalsms/IncomingMessageRetry.java b/src/org/envaya/kalsms/IncomingMessageRetry.java new file mode 100755 index 0000000..ac2f283 --- /dev/null +++ b/src/org/envaya/kalsms/IncomingMessageRetry.java @@ -0,0 +1,16 @@ + +package org.envaya.kalsms; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class IncomingMessageRetry extends BroadcastReceiver +{ + @Override + public void onReceive(Context context, Intent intent) + { + App app = App.getInstance(context.getApplicationContext()); + app.retryIncomingMessage(intent.getData().getLastPathSegment()); + } +} diff --git a/src/org/envaya/kalsms/Main.java b/src/org/envaya/kalsms/Main.java index 793b159..0fb931f 100755 --- a/src/org/envaya/kalsms/Main.java +++ b/src/org/envaya/kalsms/Main.java @@ -131,7 +131,7 @@ public class Main extends Activity { app.checkOutgoingMessages(); return true; case R.id.retry_now: - app.retryStuckMessages(true); + app.retryStuckMessages(); return true; case R.id.help: startActivity(new Intent(this, Help.class)); diff --git a/src/org/envaya/kalsms/MessageStatusNotifier.java b/src/org/envaya/kalsms/MessageStatusNotifier.java index 13c393e..bc83101 100755 --- a/src/org/envaya/kalsms/MessageStatusNotifier.java +++ b/src/org/envaya/kalsms/MessageStatusNotifier.java @@ -15,10 +15,18 @@ public class MessageStatusNotifier extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { App app = App.getInstance(context.getApplicationContext()); - String id = intent.getExtras().getString("id"); + String id = intent.getData().getLastPathSegment(); int resultCode = getResultCode(); + // uncomment to test retry on outgoing message failure + /* + if (Math.random() > 0.4) + { + resultCode = SmsManager.RESULT_ERROR_NO_SERVICE; + } + */ + app.notifyOutgoingMessageStatus(id, resultCode); } } diff --git a/src/org/envaya/kalsms/OutgoingMessagePoller.java b/src/org/envaya/kalsms/OutgoingMessagePoller.java index 92de238..97af50e 100755 --- a/src/org/envaya/kalsms/OutgoingMessagePoller.java +++ b/src/org/envaya/kalsms/OutgoingMessagePoller.java @@ -6,12 +6,9 @@ import android.content.Intent; public class OutgoingMessagePoller extends BroadcastReceiver { - private App app; - @Override public void onReceive(Context context, Intent intent) { - app = App.getInstance(context.getApplicationContext()); - app.checkOutgoingMessages(); - app.retryStuckMessages(false); + App app = App.getInstance(context.getApplicationContext()); + app.checkOutgoingMessages(); } } diff --git a/src/org/envaya/kalsms/OutgoingMessageRetry.java b/src/org/envaya/kalsms/OutgoingMessageRetry.java new file mode 100755 index 0000000..8ed703b --- /dev/null +++ b/src/org/envaya/kalsms/OutgoingMessageRetry.java @@ -0,0 +1,16 @@ + +package org.envaya.kalsms; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class OutgoingMessageRetry extends BroadcastReceiver +{ + @Override + public void onReceive(Context context, Intent intent) + { + App app = App.getInstance(context.getApplicationContext()); + app.retryOutgoingMessage(intent.getData().getLastPathSegment()); + } +}