Correct retry logic for SMS JB+ and for push groups.

This commit is contained in:
Moxie Marlinspike 2014-02-15 18:31:25 -08:00
parent b79bc4c234
commit 4e703d5a00
8 changed files with 121 additions and 72 deletions

View File

@ -92,7 +92,7 @@
<activity android:name=".MmsPreferencesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ConversationListActivity"
<activity android:name=".ConversationListActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
@ -251,14 +251,6 @@
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver>
<receiver android:name=".service.SystemStateListener"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.MarkReadReceiver"
android:enabled="true"

View File

@ -247,6 +247,11 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
" WHERE " + ID + " = ?", new String[] {id + ""});
}
public void markAsOutbox(long messageId) {
updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_OUTBOX_TYPE);
notifyConversationListeners(getThreadIdForMessage(messageId));
}
public void markAsSending(long messageId) {
updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE);
notifyConversationListeners(getThreadIdForMessage(messageId));
@ -324,8 +329,8 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
selection = ID_WHERE;
selectionArgs = new String[]{messageId + ""};
} else {
selection = MESSAGE_BOX + " & " + Types.BASE_TYPE_MASK + " = ?";
selectionArgs = new String[]{Types.BASE_OUTBOX_TYPE + ""};
selection = MESSAGE_BOX + " & " + Types.BASE_TYPE_MASK + " = " + Types.BASE_OUTBOX_TYPE;
selectionArgs = null;
}
try {

View File

@ -16,11 +16,13 @@
*/
package org.thoughtcrime.securesms.service;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
@ -28,6 +30,7 @@ import org.thoughtcrime.securesms.mms.MmsSendResult;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.SendReceiveService.ToastHandler;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.transport.UniversalTransport;
import org.whispersystems.textsecure.crypto.MasterSecret;
@ -37,10 +40,14 @@ import ws.com.google.android.mms.pdu.SendReq;
public class MmsSender {
private final Context context;
private final Context context;
private final SystemStateListener systemStateListener;
private final ToastHandler toastHandler;
public MmsSender(Context context, ToastHandler toastHandler) {
this.context = context;
public MmsSender(Context context, SystemStateListener systemStateListener, ToastHandler toastHandler) {
this.context = context;
this.systemStateListener = systemStateListener;
this.toastHandler = toastHandler;
}
public void process(MasterSecret masterSecret, Intent intent) {
@ -73,11 +80,23 @@ public class MmsSender {
database.markAsSent(message.getDatabaseMessageId(), result.getMessageId(),
result.getResponseStatus());
systemStateListener.unregisterForConnectivityChange();
} catch (UndeliverableMessageException e) {
Log.w("MmsSender", e);
database.markAsSentFailed(message.getDatabaseMessageId());
Recipients recipients = threads.getRecipientsForThreadId(threadId);
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
} catch (RetryLaterException e) {
Log.w("MmsSender", e);
database.markAsOutbox(message.getDatabaseMessageId());
if (systemStateListener.isConnected()) scheduleQuickRetryAlarm();
else systemStateListener.registerForConnectivityChange();
toastHandler
.obtainMessage(0, context.getString(R.string.SmsReceiver_currently_unable_to_send_your_sms_message))
.sendToTarget();
}
}
} catch (MmsException e) {
@ -86,4 +105,13 @@ public class MmsSender {
database.markAsSentFailed(messageId);
}
}
private void scheduleQuickRetryAlarm() {
((AlarmManager)context.getSystemService(Context.ALARM_SERVICE))
.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (30 * 1000),
PendingIntent.getService(context, 0,
new Intent(SendReceiveService.SEND_MMS_ACTION,
null, context, SendReceiveService.class),
PendingIntent.FLAG_UPDATE_CURRENT));
}
}

View File

@ -29,9 +29,9 @@ import android.os.Message;
import android.util.Log;
import android.widget.Toast;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.CanonicalSessionMigrator;
import org.thoughtcrime.securesms.util.WorkerThread;
import org.whispersystems.textsecure.crypto.MasterSecret;
import java.util.Iterator;
import java.util.LinkedList;
@ -69,7 +69,8 @@ public class SendReceiveService extends Service {
private static final int DOWNLOAD_PUSH = 7;
private static final int DOWNLOAD_AVATAR = 8;
private ToastHandler toastHandler;
private ToastHandler toastHandler;
private SystemStateListener systemStateListener;
private SmsReceiver smsReceiver;
private SmsSender smsSender;
@ -149,14 +150,15 @@ public class SendReceiveService extends Service {
}
private void initializeHandlers() {
toastHandler = new ToastHandler();
systemStateListener = new SystemStateListener(this);
toastHandler = new ToastHandler();
}
private void initializeProcessors() {
smsReceiver = new SmsReceiver(this);
smsSender = new SmsSender(this, toastHandler);
smsSender = new SmsSender(this, systemStateListener, toastHandler);
mmsReceiver = new MmsReceiver(this);
mmsSender = new MmsSender(this, toastHandler);
mmsSender = new MmsSender(this, systemStateListener, toastHandler);
mmsDownloader = new MmsDownloader(this, toastHandler);
pushReceiver = new PushReceiver(this);
pushDownloader = new PushDownloader(this);

View File

@ -19,13 +19,11 @@ package org.thoughtcrime.securesms.service;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Log;
import org.thoughtcrime.securesms.R;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
@ -34,15 +32,18 @@ import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.SendReceiveService.ToastHandler;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.transport.UniversalTransport;
import org.whispersystems.textsecure.crypto.MasterSecret;
public class SmsSender {
private final Context context;
private final ToastHandler toastHandler;
private final Context context;
private final SystemStateListener systemStateListener;
private final ToastHandler toastHandler;
public SmsSender(Context context, ToastHandler toastHandler) {
this.context = context;
this.toastHandler = toastHandler;
public SmsSender(Context context, SystemStateListener systemStateListener, ToastHandler toastHandler) {
this.context = context;
this.systemStateListener = systemStateListener;
this.toastHandler = toastHandler;
}
public void process(MasterSecret masterSecret, Intent intent) {
@ -127,18 +128,10 @@ public class SmsSender {
}
private void registerForRadioChanges() {
unregisterForRadioChanges();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(SystemStateListener.ACTION_SERVICE_STATE);
context.registerReceiver(SystemStateListener.getInstance(), intentFilter);
systemStateListener.registerForConnectivityChange();
}
private void unregisterForRadioChanges() {
try {
context.unregisterReceiver(SystemStateListener.getInstance());
} catch (IllegalArgumentException iae) {
Log.w("SmsSender", iae);
}
systemStateListener.unregisterForConnectivityChange();
}
}

View File

@ -3,21 +3,48 @@ package org.thoughtcrime.securesms.service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.util.Log;
import org.thoughtcrime.securesms.mms.MmsRadio;
public class SystemStateListener {
public class SystemStateListener extends BroadcastReceiver {
private final TelephonyListener telephonyListener = new TelephonyListener();
private final ConnectivityListener connectivityListener = new ConnectivityListener();
private final Context context;
private final TelephonyManager telephonyManager;
private final ConnectivityManager connectivityManager;
public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
private static final String ACTION_CONNECTIVITY_CHANGE = "android.net.conn.CONNECTIVITY_CHANGE";
public SystemStateListener(Context context) {
this.context = context.getApplicationContext();
this.telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
this.connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
private static final SystemStateListener instance = new SystemStateListener();
public void registerForConnectivityChange() {
unregisterForConnectivityChange();
public static SystemStateListener getInstance() {
return instance;
telephonyManager.listen(telephonyListener, PhoneStateListener.LISTEN_SERVICE_STATE);
context.registerReceiver(connectivityListener, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
public void unregisterForConnectivityChange() {
telephonyManager.listen(telephonyListener, 0);
try {
context.unregisterReceiver(connectivityListener);
} catch (IllegalArgumentException iae) {
Log.w("SystemStateListener", iae);
}
}
public boolean isConnected() {
return
connectivityManager.getActiveNetworkInfo() != null &&
connectivityManager.getActiveNetworkInfo().isConnected();
}
private void sendSmsOutbox(Context context) {
@ -32,34 +59,27 @@ public class SystemStateListener extends BroadcastReceiver {
context.startService(mmsSenderIntent);
}
private void handleRadioServiceStateChange(Context context, Intent intent) {
int state = intent.getIntExtra("state", -31337);
if (state == ServiceState.STATE_IN_SERVICE) {
sendSmsOutbox(context);
private class TelephonyListener extends PhoneStateListener {
@Override
public void onServiceStateChanged(ServiceState state) {
if (state.getState() == ServiceState.STATE_IN_SERVICE) {
sendSmsOutbox(context);
sendMmsOutbox(context);
}
}
}
private void handleDataServiceStateChange(Context context, Intent intent) {
ConnectivityManager connectivityManager
= (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(MmsRadio.TYPE_MOBILE_MMS);
if (networkInfo != null && networkInfo.isAvailable()) {
sendMmsOutbox(context);
private class ConnectivityListener extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
if (connectivityManager.getActiveNetworkInfo() != null &&
connectivityManager.getActiveNetworkInfo().isConnected())
{
sendSmsOutbox(context);
sendMmsOutbox(context);
}
}
}
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null) return;
if (intent.getAction().equals(ACTION_SERVICE_STATE)) {
handleRadioServiceStateChange(context, intent);
} else if (intent.getAction().equals(ACTION_CONNECTIVITY_CHANGE)) {
handleDataServiceStateChange(context, intent);
}
}
}

View File

@ -0,0 +1,4 @@
package org.thoughtcrime.securesms.transport;
public class RetryLaterException extends Exception {
}

View File

@ -18,6 +18,7 @@ package org.thoughtcrime.securesms.transport;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.mms.MmsSendResult;
@ -82,7 +83,7 @@ public class UniversalTransport {
}
public MmsSendResult deliver(SendReq mediaMessage, long threadId)
throws UndeliverableMessageException
throws UndeliverableMessageException, RetryLaterException
{
if (Util.isEmpty(mediaMessage.getTo())) {
throw new UndeliverableMessageException("No destination specified");
@ -103,7 +104,11 @@ public class UniversalTransport {
return new MmsSendResult("push".getBytes("UTF-8"), 0, true);
} catch (IOException ioe) {
Log.w("UniversalTransport", ioe);
return mmsTransport.deliver(mediaMessage);
if (!GroupUtil.isEncodedGroup(mediaMessage.getTo()[0].getString())) {
return mmsTransport.deliver(mediaMessage);
} else {
throw new RetryLaterException();
}
}
} else {
Log.w("UniversalTransport", "Delivering media message with MMS...");