2014-11-03 23:16:04 +00:00
|
|
|
package org.thoughtcrime.securesms.jobs;
|
2011-12-20 18:20:44 +00:00
|
|
|
|
2012-09-30 18:46:45 +00:00
|
|
|
import android.content.Context;
|
2014-07-29 22:31:20 +00:00
|
|
|
import android.net.Uri;
|
2013-03-04 02:44:58 +00:00
|
|
|
import android.telephony.TelephonyManager;
|
2012-09-30 18:46:45 +00:00
|
|
|
import android.util.Log;
|
2013-04-26 18:23:43 +00:00
|
|
|
import android.util.Pair;
|
2011-12-20 18:20:44 +00:00
|
|
|
|
2013-02-21 02:10:33 +00:00
|
|
|
import org.thoughtcrime.securesms.R;
|
2014-11-03 23:16:04 +00:00
|
|
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
|
|
|
import org.thoughtcrime.securesms.crypto.MmsCipher;
|
|
|
|
import org.thoughtcrime.securesms.crypto.storage.TextSecureAxolotlStore;
|
2011-12-20 18:20:44 +00:00
|
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
|
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
2014-11-03 23:16:04 +00:00
|
|
|
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
|
2013-09-16 07:55:01 +00:00
|
|
|
import org.thoughtcrime.securesms.mms.ApnUnavailableException;
|
2013-07-19 00:42:45 +00:00
|
|
|
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
2014-11-03 23:16:04 +00:00
|
|
|
import org.thoughtcrime.securesms.mms.IncomingMmsConnection;
|
|
|
|
import org.thoughtcrime.securesms.mms.MmsConnection;
|
2013-07-17 02:52:02 +00:00
|
|
|
import org.thoughtcrime.securesms.mms.MmsRadio;
|
|
|
|
import org.thoughtcrime.securesms.mms.MmsRadioException;
|
2014-11-03 23:16:04 +00:00
|
|
|
import org.thoughtcrime.securesms.mms.OutgoingMmsConnection;
|
2013-04-26 18:23:43 +00:00
|
|
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
2011-12-20 18:20:44 +00:00
|
|
|
import org.thoughtcrime.securesms.protocol.WirePrefix;
|
2014-11-03 23:16:04 +00:00
|
|
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
|
|
|
import org.whispersystems.jobqueue.JobParameters;
|
|
|
|
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
|
|
|
import org.whispersystems.libaxolotl.DuplicateMessageException;
|
|
|
|
import org.whispersystems.libaxolotl.InvalidMessageException;
|
|
|
|
import org.whispersystems.libaxolotl.LegacyMessageException;
|
|
|
|
import org.whispersystems.libaxolotl.NoSessionException;
|
|
|
|
import org.whispersystems.libaxolotl.util.guava.Optional;
|
2011-12-20 18:20:44 +00:00
|
|
|
|
2013-09-16 07:55:01 +00:00
|
|
|
import java.io.IOException;
|
|
|
|
|
2013-05-07 02:09:06 +00:00
|
|
|
import ws.com.google.android.mms.InvalidHeaderValueException;
|
2011-12-20 18:20:44 +00:00
|
|
|
import ws.com.google.android.mms.MmsException;
|
2014-11-03 23:16:04 +00:00
|
|
|
import ws.com.google.android.mms.pdu.MultimediaMessagePdu;
|
|
|
|
import ws.com.google.android.mms.pdu.NotificationInd;
|
2013-05-07 02:09:06 +00:00
|
|
|
import ws.com.google.android.mms.pdu.NotifyRespInd;
|
|
|
|
import ws.com.google.android.mms.pdu.PduComposer;
|
|
|
|
import ws.com.google.android.mms.pdu.PduHeaders;
|
2011-12-20 18:20:44 +00:00
|
|
|
import ws.com.google.android.mms.pdu.RetrieveConf;
|
2012-09-30 18:46:45 +00:00
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
import static org.thoughtcrime.securesms.mms.MmsConnection.Apn;
|
2011-12-20 18:20:44 +00:00
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
public class MmsDownloadJob extends MasterSecretJob {
|
2012-09-30 18:46:45 +00:00
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
private static final String TAG = MmsDownloadJob.class.getSimpleName();
|
|
|
|
|
|
|
|
private final long messageId;
|
|
|
|
private final long threadId;
|
|
|
|
private final boolean automatic;
|
|
|
|
|
|
|
|
public MmsDownloadJob(Context context, long messageId, long threadId, boolean automatic) {
|
|
|
|
super(context, JobParameters.newBuilder()
|
|
|
|
.withPersistence()
|
|
|
|
.withRequirement(new MasterSecretRequirement(context))
|
|
|
|
.withRequirement(new NetworkRequirement(context))
|
|
|
|
.withGroupId("mms-download")
|
|
|
|
.create());
|
|
|
|
|
|
|
|
this.messageId = messageId;
|
|
|
|
this.threadId = threadId;
|
|
|
|
this.automatic = automatic;
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|
2012-09-30 18:46:45 +00:00
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
@Override
|
|
|
|
public void onAdded() {
|
|
|
|
if (automatic && KeyCachingService.getMasterSecret(context) == null) {
|
|
|
|
DatabaseFactory.getMmsDatabase(context).markIncomingNotificationReceived(threadId);
|
|
|
|
MessageNotifier.updateNotification(context, null);
|
2013-02-21 02:10:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
@Override
|
|
|
|
public void onRun() throws RequirementNotMetException {
|
|
|
|
Log.w(TAG, "MmsDownloadJob:onRun()");
|
|
|
|
|
|
|
|
MasterSecret masterSecret = getMasterSecret();
|
|
|
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
|
|
Optional<NotificationInd> notification = database.getNotification(messageId);
|
2013-09-16 07:55:01 +00:00
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
if (!notification.isPresent()) {
|
|
|
|
Log.w(TAG, "No notification for ID: " + messageId);
|
|
|
|
return;
|
2013-09-16 07:55:01 +00:00
|
|
|
}
|
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_CONNECTING);
|
2013-09-16 07:55:01 +00:00
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
String contentLocation = new String(notification.get().getContentLocation());
|
|
|
|
byte[] transactionId = notification.get().getTransactionId();
|
|
|
|
MmsRadio radio = MmsRadio.getInstance(context);
|
2012-09-30 18:46:45 +00:00
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, "About to parse URL...");
|
2012-09-30 18:46:45 +00:00
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, "Downloading mms at " + Uri.parse(contentLocation).getHost());
|
2014-07-29 22:31:20 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
try {
|
2013-07-17 02:52:02 +00:00
|
|
|
if (isCdmaNetwork()) {
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, "Connecting directly...");
|
2013-07-17 02:52:02 +00:00
|
|
|
try {
|
2014-11-03 23:16:04 +00:00
|
|
|
retrieveAndStore(masterSecret, radio, messageId, threadId, contentLocation,
|
2013-07-17 02:52:02 +00:00
|
|
|
transactionId, false, false);
|
|
|
|
return;
|
|
|
|
} catch (IOException e) {
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, e);
|
2013-07-17 02:52:02 +00:00
|
|
|
}
|
2013-01-01 20:54:12 +00:00
|
|
|
}
|
2012-09-30 18:46:45 +00:00
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, "Changing radio to MMS mode..");
|
2013-07-17 02:52:02 +00:00
|
|
|
radio.connect();
|
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, "Downloading in MMS mode with proxy...");
|
2013-07-17 02:52:02 +00:00
|
|
|
|
|
|
|
try {
|
2014-11-03 23:16:04 +00:00
|
|
|
retrieveAndStore(masterSecret, radio, messageId, threadId, contentLocation,
|
2014-07-29 22:31:20 +00:00
|
|
|
transactionId, true, true);
|
2013-07-17 02:52:02 +00:00
|
|
|
radio.disconnect();
|
|
|
|
return;
|
|
|
|
} catch (IOException e) {
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, e);
|
2013-07-17 02:52:02 +00:00
|
|
|
}
|
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, "Downloading in MMS mode without proxy...");
|
2013-07-17 02:52:02 +00:00
|
|
|
|
|
|
|
try {
|
2014-11-03 23:16:04 +00:00
|
|
|
retrieveAndStore(masterSecret, radio, messageId, threadId,
|
2014-07-29 22:31:20 +00:00
|
|
|
contentLocation, transactionId, true, false);
|
2013-07-17 02:52:02 +00:00
|
|
|
radio.disconnect();
|
|
|
|
} catch (IOException e) {
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, e);
|
2013-07-17 02:52:02 +00:00
|
|
|
radio.disconnect();
|
|
|
|
handleDownloadError(masterSecret, messageId, threadId,
|
|
|
|
MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE,
|
|
|
|
context.getString(R.string.MmsDownloader_error_connecting_to_mms_provider),
|
|
|
|
automatic);
|
|
|
|
}
|
2011-12-20 18:20:44 +00:00
|
|
|
|
2013-09-16 07:55:01 +00:00
|
|
|
} catch (ApnUnavailableException e) {
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, e);
|
2013-07-17 02:52:02 +00:00
|
|
|
handleDownloadError(masterSecret, messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE,
|
|
|
|
context.getString(R.string.MmsDownloader_error_reading_mms_settings), automatic);
|
2011-12-20 18:20:44 +00:00
|
|
|
} catch (MmsException e) {
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, e);
|
2013-07-17 02:52:02 +00:00
|
|
|
handleDownloadError(masterSecret, messageId, threadId,
|
|
|
|
MmsDatabase.Status.DOWNLOAD_HARD_FAILURE,
|
|
|
|
context.getString(R.string.MmsDownloader_error_storing_mms),
|
|
|
|
automatic);
|
|
|
|
} catch (MmsRadioException e) {
|
2014-11-03 23:16:04 +00:00
|
|
|
Log.w(TAG, e);
|
2013-07-17 02:52:02 +00:00
|
|
|
handleDownloadError(masterSecret, messageId, threadId,
|
|
|
|
MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE,
|
|
|
|
context.getString(R.string.MmsDownloader_error_connecting_to_mms_provider),
|
|
|
|
automatic);
|
2014-11-03 23:16:04 +00:00
|
|
|
} catch (DuplicateMessageException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
database.markAsDecryptDuplicate(messageId, threadId);
|
|
|
|
} catch (LegacyMessageException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
database.markAsLegacyVersion(messageId, threadId);
|
|
|
|
} catch (NoSessionException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
database.markAsNoSession(messageId, threadId);
|
|
|
|
} catch (InvalidMessageException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
database.markAsDecryptFailed(messageId, threadId);
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|
|
|
|
}
|
2012-09-30 18:46:45 +00:00
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
@Override
|
|
|
|
public void onCanceled() {
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onShouldRetry(Throwable throwable) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void retrieveAndStore(MasterSecret masterSecret, MmsRadio radio,
|
|
|
|
long messageId, long threadId,
|
2013-07-17 02:52:02 +00:00
|
|
|
String contentLocation, byte[] transactionId,
|
|
|
|
boolean radioEnabled, boolean useProxy)
|
2014-11-03 23:16:04 +00:00
|
|
|
throws IOException, MmsException, ApnUnavailableException,
|
|
|
|
DuplicateMessageException, NoSessionException,
|
|
|
|
InvalidMessageException, LegacyMessageException
|
2013-07-17 02:52:02 +00:00
|
|
|
{
|
2014-09-16 23:21:41 +00:00
|
|
|
Apn dbApn = MmsConnection.getApn(context, radio.getApnInformation());
|
|
|
|
Apn contentApn = new Apn(contentLocation, dbApn.getProxy(), Integer.toString(dbApn.getPort()));
|
|
|
|
IncomingMmsConnection connection = new IncomingMmsConnection(context, contentApn);
|
|
|
|
RetrieveConf retrieved = connection.retrieve(radioEnabled, useProxy);
|
2013-07-17 02:52:02 +00:00
|
|
|
|
|
|
|
storeRetrievedMms(masterSecret, contentLocation, messageId, threadId, retrieved);
|
2014-11-03 23:16:04 +00:00
|
|
|
sendRetrievedAcknowledgement(radio, transactionId, radioEnabled, useProxy);
|
2013-07-17 02:52:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void storeRetrievedMms(MasterSecret masterSecret, String contentLocation,
|
|
|
|
long messageId, long threadId, RetrieveConf retrieved)
|
2014-11-03 23:16:04 +00:00
|
|
|
throws MmsException, NoSessionException, DuplicateMessageException, InvalidMessageException,
|
|
|
|
LegacyMessageException
|
2013-02-21 02:10:33 +00:00
|
|
|
{
|
2013-07-19 00:42:45 +00:00
|
|
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
|
|
IncomingMediaMessage message = new IncomingMediaMessage(retrieved);
|
|
|
|
|
2013-04-26 18:23:43 +00:00
|
|
|
Pair<Long, Long> messageAndThreadId;
|
|
|
|
|
2013-02-21 02:10:33 +00:00
|
|
|
if (retrieved.getSubject() != null && WirePrefix.isEncryptedMmsSubject(retrieved.getSubject().getString())) {
|
2014-11-03 23:16:04 +00:00
|
|
|
MmsCipher mmsCipher = new MmsCipher(new TextSecureAxolotlStore(context, masterSecret));
|
|
|
|
MultimediaMessagePdu plaintextPdu = mmsCipher.decrypt(context, retrieved);
|
|
|
|
RetrieveConf plaintextRetrieved = new RetrieveConf(plaintextPdu.getPduHeaders(), plaintextPdu.getBody());
|
|
|
|
IncomingMediaMessage plaintextMessage = new IncomingMediaMessage(plaintextRetrieved);
|
|
|
|
|
|
|
|
messageAndThreadId = database.insertSecureDecryptedMessageInbox(masterSecret, plaintextMessage,
|
|
|
|
threadId);
|
2013-02-21 02:10:33 +00:00
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
// if (masterSecret != null)
|
|
|
|
// DecryptingQueue.scheduleDecryption(context, masterSecret, messageAndThreadId.first,
|
|
|
|
// messageAndThreadId.second, retrieved);
|
2013-02-21 02:10:33 +00:00
|
|
|
|
|
|
|
} else {
|
2013-07-19 00:42:45 +00:00
|
|
|
messageAndThreadId = database.insertMessageInbox(masterSecret, message,
|
2013-07-17 02:52:02 +00:00
|
|
|
contentLocation, threadId);
|
2013-02-21 02:10:33 +00:00
|
|
|
}
|
|
|
|
|
2013-07-17 02:52:02 +00:00
|
|
|
database.delete(messageId);
|
|
|
|
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
2013-02-21 02:10:33 +00:00
|
|
|
}
|
|
|
|
|
2014-11-03 23:16:04 +00:00
|
|
|
private void sendRetrievedAcknowledgement(MmsRadio radio,
|
|
|
|
byte[] transactionId,
|
2013-07-17 02:52:02 +00:00
|
|
|
boolean usingRadio,
|
|
|
|
boolean useProxy)
|
2014-09-16 23:21:41 +00:00
|
|
|
throws ApnUnavailableException
|
2013-07-17 02:52:02 +00:00
|
|
|
{
|
2013-05-07 02:09:06 +00:00
|
|
|
try {
|
|
|
|
NotifyRespInd notifyResponse = new NotifyRespInd(PduHeaders.CURRENT_MMS_VERSION,
|
2013-07-17 02:52:02 +00:00
|
|
|
transactionId,
|
2013-05-07 02:09:06 +00:00
|
|
|
PduHeaders.STATUS_RETRIEVED);
|
|
|
|
|
2014-09-16 23:21:41 +00:00
|
|
|
OutgoingMmsConnection connection = new OutgoingMmsConnection(context, radio.getApnInformation(), new PduComposer(context, notifyResponse).make());
|
|
|
|
connection.sendNotificationReceived(usingRadio, useProxy);
|
2014-11-03 23:16:04 +00:00
|
|
|
} catch (InvalidHeaderValueException | IOException e) {
|
|
|
|
Log.w(TAG, e);
|
2013-05-07 02:09:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-17 02:52:02 +00:00
|
|
|
private void handleDownloadError(MasterSecret masterSecret, long messageId, long threadId,
|
|
|
|
int downloadStatus, String error, boolean automatic)
|
|
|
|
{
|
2013-04-26 18:23:43 +00:00
|
|
|
MmsDatabase db = DatabaseFactory.getMmsDatabase(context);
|
2012-09-30 18:46:45 +00:00
|
|
|
|
2013-07-17 02:52:02 +00:00
|
|
|
db.markDownloadState(messageId, downloadStatus);
|
2013-02-21 02:10:33 +00:00
|
|
|
|
2013-07-17 02:52:02 +00:00
|
|
|
if (automatic) {
|
|
|
|
db.markIncomingNotificationReceived(threadId);
|
|
|
|
MessageNotifier.updateNotification(context, masterSecret, threadId);
|
2013-02-21 02:10:33 +00:00
|
|
|
}
|
2014-11-03 23:16:04 +00:00
|
|
|
//
|
|
|
|
// toastHandler.makeToast(error);
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|
|
|
|
|
2013-07-17 02:52:02 +00:00
|
|
|
private boolean isCdmaNetwork() {
|
|
|
|
return ((TelephonyManager)context
|
|
|
|
.getSystemService(Context.TELEPHONY_SERVICE))
|
|
|
|
.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|
2014-11-03 23:16:04 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|