Initial client support for GCM message send/receive

This commit is contained in:
Moxie Marlinspike
2013-03-31 19:16:06 -07:00
parent 2f39283da3
commit 303d1acd45
38 changed files with 964 additions and 179 deletions

View File

@@ -17,7 +17,7 @@ import com.google.android.gcm.GCMRegistrar;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.gcm.GcmIntentService;
import org.thoughtcrime.securesms.gcm.GcmRegistrationTimeoutException;
import org.thoughtcrime.securesms.gcm.RegistrationSocket;
import org.thoughtcrime.securesms.gcm.GcmSocket;
import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
@@ -127,14 +127,14 @@ public class RegistrationService extends Service {
registerReceiver(gcmRegistrationReceiver, filter);
}
private void shutdownChallengeListener() {
private synchronized void shutdownChallengeListener() {
if (challengeReceiver != null) {
unregisterReceiver(challengeReceiver);
challengeReceiver = null;
}
}
private void shutdownGcmRegistrationListener() {
private synchronized void shutdownGcmRegistrationListener() {
if (gcmRegistrationReceiver != null) {
unregisterReceiver(gcmRegistrationReceiver);
gcmRegistrationReceiver = null;
@@ -144,7 +144,7 @@ public class RegistrationService extends Service {
private void handleRegistrationIntent(Intent intent) {
markAsVerifying(true);
RegistrationSocket socket;
GcmSocket socket;
String number = intent.getStringExtra("e164number");
try {
@@ -153,7 +153,7 @@ public class RegistrationService extends Service {
initializeGcmRegistrationListener();
setState(new RegistrationState(RegistrationState.STATE_CONNECTING, number));
socket = new RegistrationSocket(this, number, password);
socket = new GcmSocket(this, number, password);
socket.createAccount();
setState(new RegistrationState(RegistrationState.STATE_VERIFYING, number));
@@ -165,6 +165,9 @@ public class RegistrationService extends Service {
String gcmRegistrationId = waitForGcmRegistrationId();
socket.registerGcmId(gcmRegistrationId);
setState(new RegistrationState(RegistrationState.STATE_RETRIEVING_DIRECTORY, number));
socket.retrieveDirectory(this);
markAsVerified(number, password);
setState(new RegistrationState(RegistrationState.STATE_COMPLETE, number));
@@ -315,6 +318,8 @@ public class RegistrationService extends Service {
public static final int STATE_GCM_REGISTERING = 9;
public static final int STATE_GCM_TIMEOUT = 10;
public static final int STATE_RETRIEVING_DIRECTORY = 11;
public final int state;
public final String number;

View File

@@ -63,11 +63,11 @@ public class SendReceiveService extends Service {
private ToastHandler toastHandler;
private SmsReceiver smsReceiver;
private SmsSender smsSender;
private MmsReceiver mmsReceiver;
private MmsSender mmsSender;
private MmsDownloader mmsDownloader;
private SmsReceiver smsReceiver;
private SmsSender smsSender;
private MmsReceiver mmsReceiver;
private MmsSender mmsSender;
private MmsDownloader mmsDownloader;
private MasterSecret masterSecret;
private boolean hasSecret;

View File

@@ -27,6 +27,9 @@ import android.util.Log;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.protocol.WirePrefix;
import org.thoughtcrime.securesms.sms.TextMessage;
import java.util.ArrayList;
public class SmsListener extends BroadcastReceiver {
@@ -74,6 +77,16 @@ public class SmsListener extends BroadcastReceiver {
return bodyBuilder.toString();
}
private ArrayList<TextMessage> getAsTextMessages(Intent intent) {
Object[] pdus = (Object[])intent.getExtras().get("pdus");
ArrayList<TextMessage> messages = new ArrayList<TextMessage>(pdus.length);
for (int i=0;i<pdus.length;i++)
messages.add(new TextMessage(SmsMessage.createFromPdu((byte[])pdus[i])));
return messages;
}
private boolean isRelevant(Context context, Intent intent) {
SmsMessage message = getSmsMessageFromIntent(intent);
String messageBody = getSmsMessageBodyFromIntent(intent);
@@ -131,10 +144,11 @@ public class SmsListener extends BroadcastReceiver {
abortBroadcast();
} else if (intent.getAction().equals(SMS_RECEIVED_ACTION) && isRelevant(context, intent)) {
intent.setAction(SendReceiveService.RECEIVE_SMS_ACTION);
intent.putExtra("ResultCode", this.getResultCode());
intent.setClass(context, SendReceiveService.class);
context.startService(intent);
Intent receivedIntent = new Intent(context, SendReceiveService.class);
receivedIntent.setAction(SendReceiveService.RECEIVE_SMS_ACTION);
receivedIntent.putExtra("ResultCode", this.getResultCode());
receivedIntent.putParcelableArrayListExtra("text_messages",getAsTextMessages(intent));
context.startService(receivedIntent);
abortBroadcast();
} else if (intent.getAction().equals(SendReceiveService.SENT_SMS_ACTION)) {

View File

@@ -18,9 +18,7 @@ package org.thoughtcrime.securesms.service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.telephony.SmsMessage;
import android.util.Log;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
@@ -37,6 +35,9 @@ import org.thoughtcrime.securesms.protocol.Prefix;
import org.thoughtcrime.securesms.protocol.WirePrefix;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.MultipartMessageHandler;
import org.thoughtcrime.securesms.sms.TextMessage;
import java.util.ArrayList;
public class SmsReceiver {
@@ -70,41 +71,42 @@ public class SmsReceiver {
}
private String assembleMessageFragments(SmsMessage[] messages) {
private String assembleMessageFragments(TextMessage[] messages) {
StringBuilder body = new StringBuilder();
for (SmsMessage message : messages) {
body.append(message.getDisplayMessageBody());
for (TextMessage message : messages) {
body.append(message.getMessage());
}
String messageBody = body.toString();
if (WirePrefix.isEncryptedMessage(messageBody) || WirePrefix.isKeyExchange(messageBody)) {
return assembleSecureMessageFragments(messages[0].getDisplayOriginatingAddress(), messageBody);
return assembleSecureMessageFragments(messages[0].getSender(), messageBody);
} else {
return messageBody;
}
}
private long storeSecureMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
private long storeSecureMessage(MasterSecret masterSecret, TextMessage message, String messageBody) {
long messageId = DatabaseFactory.getSmsDatabase(context).insertSecureMessageReceived(message, messageBody);
Log.w("SmsReceiver", "Inserted secure message received: " + messageId);
if (masterSecret != null)
DecryptingQueue.scheduleDecryption(context, masterSecret, messageId, message.getDisplayOriginatingAddress(), messageBody);
DecryptingQueue.scheduleDecryption(context, masterSecret, messageId, message.getSender(), messageBody);
return messageId;
}
private long storeStandardMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
private long storeStandardMessage(MasterSecret masterSecret, TextMessage message, String messageBody) {
if (masterSecret != null) return DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageReceived(masterSecret, message, messageBody);
else if (MasterSecretUtil.hasAsymmericMasterSecret(context)) return DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageReceived(MasterSecretUtil.getAsymmetricMasterSecret(context, null), message, messageBody);
else return DatabaseFactory.getSmsDatabase(context).insertMessageReceived(message, messageBody);
}
private long storeKeyExchangeMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
private long storeKeyExchangeMessage(MasterSecret masterSecret, TextMessage message, String messageBody) {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(ApplicationPreferencesActivity.AUTO_KEY_EXCHANGE_PREF, true)) {
try {
Recipient recipient = new Recipient(null, message.getDisplayOriginatingAddress(), null, null);
Recipient recipient = new Recipient(null, message.getSender(), null, null);
KeyExchangeMessage keyExchangeMessage = new KeyExchangeMessage(messageBody);
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipient);
@@ -132,7 +134,7 @@ public class SmsReceiver {
return storeStandardMessage(masterSecret, message, messageBody);
}
private long storeMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
private long storeMessage(MasterSecret masterSecret, TextMessage message, String messageBody) {
if (messageBody.startsWith(Prefix.ASYMMETRIC_ENCRYPT)) {
return storeSecureMessage(masterSecret, message, messageBody);
} else if (messageBody.startsWith(Prefix.KEY_EXCHANGE)) {
@@ -142,19 +144,21 @@ public class SmsReceiver {
}
}
private SmsMessage[] parseMessages(Bundle bundle) {
Object[] pdus = (Object[])bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdus.length];
for (int i=0;i<pdus.length;i++)
messages[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
return messages;
}
// private SmsMessage[] parseMessages(Bundle bundle) {
// Object[] pdus = (Object[])bundle.get("pdus");
// SmsMessage[] messages = new SmsMessage[pdus.length];
//
// for (int i=0;i<pdus.length;i++)
// messages[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
//
// return messages;
// }
private void handleReceiveMessage(MasterSecret masterSecret, Intent intent) {
Bundle bundle = intent.getExtras();
SmsMessage[] messages = parseMessages(bundle);
ArrayList<TextMessage> messagesList = intent.getExtras().getParcelableArrayList("text_messages");
TextMessage[] messages = messagesList.toArray(new TextMessage[0]);
// Bundle bundle = intent.getExtras();
// SmsMessage[] messages = parseMessages(bundle);
String message = assembleMessageFragments(messages);
if (message != null) {

View File

@@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SessionCipher;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.gcm.OptimizingTransport;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.protocol.KeyExchangeWirePrefix;
import org.thoughtcrime.securesms.protocol.Prefix;
@@ -224,7 +225,9 @@ public class SmsSender {
// the message as a failure. That way at least it doesn't repeatedly crash every time you start
// the app.
try {
SmsManager.getDefault().sendMultipartTextMessage(recipient, null, messages, sentIntents, deliveredIntents);
OptimizingTransport.sendMultipartTextMessage(context, recipient, messages, sentIntents, deliveredIntents);
//
// SmsManager.getDefault().sendMultipartTextMessage(recipient, null, messages, sentIntents, deliveredIntents);
} catch (NullPointerException npe) {
Log.w("SmsSender", npe);
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
@@ -257,8 +260,10 @@ public class SmsSender {
// the message as a failure. That way at least it doesn't repeatedly crash every time you start
// the app.
try {
SmsManager.getDefault().sendTextMessage(recipient, null, messages.get(i), sentIntents.get(i),
deliveredIntents == null ? null : deliveredIntents.get(i));
OptimizingTransport.sendTextMessage(context, recipient, messages.get(i), sentIntents.get(i),
deliveredIntents == null ? null : deliveredIntents.get(i));
// SmsManager.getDefault().sendTextMessage(recipient, null, messages.get(i), sentIntents.get(i),
// deliveredIntents == null ? null : deliveredIntents.get(i));
} catch (NullPointerException npe) {
Log.w("SmsSender", npe);
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);