mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 20:15:21 +00:00
Refactor MMS send/download to be synchronous.
1) Make the radio change a synchronous action with a timeout. 2) Move the send logic into an MmsTransport, in preparation for UniversalTransport composition. 3) Move the download logic into a synchronous receiver.
This commit is contained in:
parent
53803630d4
commit
fd045f2354
@ -240,6 +240,11 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
|
|||||||
notifyConversationListeners(getThreadIdForMessage(messageId));
|
notifyConversationListeners(getThreadIdForMessage(messageId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void markAsSending(long messageId) {
|
||||||
|
updateMailboxBitmask(messageId, Types.BASE_TYPE_MASK, Types.BASE_SENDING_TYPE);
|
||||||
|
notifyConversationListeners(getThreadIdForMessage(messageId));
|
||||||
|
}
|
||||||
|
|
||||||
public void markAsSent(long messageId, byte[] mmsId, long status) {
|
public void markAsSent(long messageId, byte[] mmsId, long status) {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
ContentValues contentValues = new ContentValues();
|
ContentValues contentValues = new ContentValues();
|
||||||
|
@ -349,7 +349,7 @@ public class ThreadDatabase extends Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipients getRecipientsForThreadId(Context context, long threadId) {
|
public Recipients getRecipientsForThreadId(long threadId) {
|
||||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ public class MmsCommunication {
|
|||||||
int ipAddress = Conversions.byteArrayToIntLittleEndian(ipAddressBytes, 0);
|
int ipAddress = Conversions.byteArrayToIntLittleEndian(ipAddressBytes, 0);
|
||||||
ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
|
||||||
if (!manager.requestRouteToHost(MmsDownloader.TYPE_MOBILE_MMS, ipAddress))
|
if (!manager.requestRouteToHost(MmsRadio.TYPE_MOBILE_MMS, ipAddress))
|
||||||
throw new IOException("Connection manager could not obtain route to host.");
|
throw new IOException("Connection manager could not obtain route to host.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
143
src/org/thoughtcrime/securesms/mms/MmsRadio.java
Normal file
143
src/org/thoughtcrime/securesms/mms/MmsRadio.java
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package org.thoughtcrime.securesms.mms;
|
||||||
|
|
||||||
|
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.os.PowerManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
|
public class MmsRadio {
|
||||||
|
|
||||||
|
private static MmsRadio instance;
|
||||||
|
|
||||||
|
public static synchronized MmsRadio getInstance(Context context) {
|
||||||
|
if (instance == null)
|
||||||
|
instance = new MmsRadio(context);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
private static final String FEATURE_ENABLE_MMS = "enableMMS";
|
||||||
|
private static final int APN_ALREADY_ACTIVE = 0;
|
||||||
|
public static final int TYPE_MOBILE_MMS = 2;
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
private ConnectivityManager connectivityManager;
|
||||||
|
private ConnectivityListener connectivityListener;
|
||||||
|
private PowerManager.WakeLock wakeLock;
|
||||||
|
private int connectedCounter = 0;
|
||||||
|
|
||||||
|
private MmsRadio(Context context) {
|
||||||
|
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||||
|
this.context = context;
|
||||||
|
this.connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
this.wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MMS Connection");
|
||||||
|
this.wakeLock.setReferenceCounted(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApnInformation() {
|
||||||
|
return connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS).getExtraInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void disconnect() {
|
||||||
|
Log.w("MmsRadio", "MMS Radio Disconnect Called...");
|
||||||
|
wakeLock.release();
|
||||||
|
connectedCounter--;
|
||||||
|
|
||||||
|
Log.w("MmsRadio", "Reference count: " + connectedCounter);
|
||||||
|
|
||||||
|
if (connectedCounter == 0) {
|
||||||
|
Log.w("MmsRadio", "Turning off MMS radio...");
|
||||||
|
connectivityManager.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS);
|
||||||
|
|
||||||
|
if (connectivityListener != null) {
|
||||||
|
Log.w("MmsRadio", "Unregistering receiver...");
|
||||||
|
context.unregisterReceiver(connectivityListener);
|
||||||
|
connectivityListener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void connect() throws MmsRadioException {
|
||||||
|
int status = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
|
||||||
|
FEATURE_ENABLE_MMS);
|
||||||
|
|
||||||
|
Log.w("MmsRadio", "startUsingNetworkFeature status: " + status);
|
||||||
|
|
||||||
|
if (status == APN_ALREADY_ACTIVE) {
|
||||||
|
wakeLock.acquire();
|
||||||
|
connectedCounter++;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
wakeLock.acquire();
|
||||||
|
connectedCounter++;
|
||||||
|
|
||||||
|
if (connectivityListener == null) {
|
||||||
|
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||||
|
connectivityListener = new ConnectivityListener();
|
||||||
|
context.registerReceiver(connectivityListener, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.wait(this, 30000);
|
||||||
|
|
||||||
|
if (!isConnected()) {
|
||||||
|
Log.w("MmsRadio", "Got back from connectivity wait, and not connected...");
|
||||||
|
disconnect();
|
||||||
|
throw new MmsRadioException("Unable to successfully enable MMS radio.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isConnected() {
|
||||||
|
NetworkInfo info = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);
|
||||||
|
|
||||||
|
if ((info == null) || (info.getType() != TYPE_MOBILE_MMS) || !info.isConnected())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isConnectivityPossible() {
|
||||||
|
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);
|
||||||
|
|
||||||
|
return networkInfo != null && networkInfo.isAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isConnectivityFailure() {
|
||||||
|
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);
|
||||||
|
|
||||||
|
return networkInfo == null || networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void issueConnectivityChange() {
|
||||||
|
if (isConnected()) {
|
||||||
|
Log.w("MmsRadio", "Notifying connected...");
|
||||||
|
notifyAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isConnected() && (isConnectivityFailure() || !isConnectivityPossible())) {
|
||||||
|
Log.w("MmsRadio", "Notifying not connected...");
|
||||||
|
notifyAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConnectivityListener extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
Log.w("MmsRadio", "Got connectivity change...");
|
||||||
|
issueConnectivityChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package org.thoughtcrime.securesms.mms;
|
||||||
|
|
||||||
|
public class MmsRadioException extends Throwable {
|
||||||
|
public MmsRadioException(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
}
|
@ -24,10 +24,8 @@ import android.util.Log;
|
|||||||
import org.apache.http.HttpHost;
|
import org.apache.http.HttpHost;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.StatusLine;
|
import org.apache.http.StatusLine;
|
||||||
import org.apache.http.client.ClientProtocolException;
|
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.entity.ByteArrayEntity;
|
import org.apache.http.entity.ByteArrayEntity;
|
||||||
import org.thoughtcrime.securesms.service.MmscProcessor;
|
|
||||||
import org.whispersystems.textsecure.util.Util;
|
import org.whispersystems.textsecure.util.Util;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -39,7 +37,9 @@ import ws.com.google.android.mms.pdu.SendConf;
|
|||||||
|
|
||||||
public class MmsSendHelper extends MmsCommunication {
|
public class MmsSendHelper extends MmsCommunication {
|
||||||
|
|
||||||
private static byte[] makePost(Context context, MmsConnectionParameters parameters, byte[] mms) throws ClientProtocolException, IOException {
|
private static byte[] makePost(Context context, MmsConnectionParameters parameters, byte[] mms)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
AndroidHttpClient client = null;
|
AndroidHttpClient client = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -114,7 +114,7 @@ public class MmsSendHelper extends MmsCommunication {
|
|||||||
public static boolean hasNecessaryApnDetails(Context context) {
|
public static boolean hasNecessaryApnDetails(Context context) {
|
||||||
try {
|
try {
|
||||||
ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
String apn = connectivityManager.getNetworkInfo(MmscProcessor.TYPE_MOBILE_MMS).getExtraInfo();
|
String apn = connectivityManager.getNetworkInfo(MmsRadio.TYPE_MOBILE_MMS).getExtraInfo();
|
||||||
|
|
||||||
MmsCommunication.getMmsConnectionParameters(context, apn, true);
|
MmsCommunication.getMmsConnectionParameters(context, apn, true);
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2013 Open Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -30,6 +30,8 @@ import org.thoughtcrime.securesms.database.MmsDatabase;
|
|||||||
import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.mms.ApnUnavailableException;
|
import org.thoughtcrime.securesms.mms.ApnUnavailableException;
|
||||||
import org.thoughtcrime.securesms.mms.MmsDownloadHelper;
|
import org.thoughtcrime.securesms.mms.MmsDownloadHelper;
|
||||||
|
import org.thoughtcrime.securesms.mms.MmsRadio;
|
||||||
|
import org.thoughtcrime.securesms.mms.MmsRadioException;
|
||||||
import org.thoughtcrime.securesms.mms.MmsSendHelper;
|
import org.thoughtcrime.securesms.mms.MmsSendHelper;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
import org.thoughtcrime.securesms.protocol.WirePrefix;
|
import org.thoughtcrime.securesms.protocol.WirePrefix;
|
||||||
@ -45,48 +47,26 @@ import ws.com.google.android.mms.pdu.PduComposer;
|
|||||||
import ws.com.google.android.mms.pdu.PduHeaders;
|
import ws.com.google.android.mms.pdu.PduHeaders;
|
||||||
import ws.com.google.android.mms.pdu.RetrieveConf;
|
import ws.com.google.android.mms.pdu.RetrieveConf;
|
||||||
|
|
||||||
public class MmsDownloader extends MmscProcessor {
|
public class MmsDownloader {
|
||||||
|
|
||||||
private final LinkedList<DownloadItem> pendingMessages = new LinkedList<DownloadItem>();
|
private final Context context;
|
||||||
private final SendReceiveService.ToastHandler toastHandler;
|
private final SendReceiveService.ToastHandler toastHandler;
|
||||||
|
private final MmsRadio radio;
|
||||||
|
|
||||||
public MmsDownloader(Context context, SendReceiveService.ToastHandler toastHandler) {
|
public MmsDownloader(Context context, SendReceiveService.ToastHandler toastHandler) {
|
||||||
super(context);
|
this.context = context;
|
||||||
this.toastHandler = toastHandler;
|
this.toastHandler = toastHandler;
|
||||||
|
this.radio = MmsRadio.getInstance(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(MasterSecret masterSecret, Intent intent) {
|
public void process(MasterSecret masterSecret, Intent intent) {
|
||||||
if (intent.getAction().equals(SendReceiveService.DOWNLOAD_MMS_ACTION)) {
|
if (intent.getAction().equals(SendReceiveService.DOWNLOAD_MMS_ACTION)) {
|
||||||
boolean isCdma = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
|
handleDownloadMms(masterSecret, intent);
|
||||||
DownloadItem item = new DownloadItem(masterSecret, !isCdma, false,
|
|
||||||
intent.getLongExtra("message_id", -1),
|
|
||||||
intent.getLongExtra("thread_id", -1),
|
|
||||||
intent.getBooleanExtra("automatic", false),
|
|
||||||
intent.getStringExtra("content_location"),
|
|
||||||
intent.getByteArrayExtra("transaction_id"));
|
|
||||||
|
|
||||||
handleDownloadMmsAction(item);
|
|
||||||
} else if (intent.getAction().equals(SendReceiveService.DOWNLOAD_MMS_CONNECTIVITY_ACTION)) {
|
|
||||||
handleConnectivityChange();
|
|
||||||
} else if (intent.getAction().equals(SendReceiveService.DOWNLOAD_MMS_PENDING_APN_ACTION)) {
|
} else if (intent.getAction().equals(SendReceiveService.DOWNLOAD_MMS_PENDING_APN_ACTION)) {
|
||||||
handleMmsPendingApnDownloads(masterSecret);
|
handleMmsPendingApnDownloads(masterSecret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDownloadMmsAction(DownloadItem item) {
|
|
||||||
if (!isConnectivityPossible()) {
|
|
||||||
Log.w("MmsDownloader", "No MMS connectivity available!");
|
|
||||||
handleDownloadError(item, MmsDatabase.Status.DOWNLOAD_NO_CONNECTIVITY,
|
|
||||||
context.getString(R.string.MmsDownloader_no_connectivity_available_for_mms_download_try_again_later));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DatabaseFactory.getMmsDatabase(context).markDownloadState(item.getMessageId(), MmsDatabase.Status.DOWNLOAD_CONNECTING);
|
|
||||||
|
|
||||||
if (item.useMmsRadioMode()) downloadMmsWithRadioChange(item);
|
|
||||||
else downloadMms(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleMmsPendingApnDownloads(MasterSecret masterSecret) {
|
private void handleMmsPendingApnDownloads(MasterSecret masterSecret) {
|
||||||
if (!MmsDownloadHelper.isMmsConnectionParametersAvailable(context, null, false))
|
if (!MmsDownloadHelper.isMmsConnectionParametersAvailable(context, null, false))
|
||||||
return;
|
return;
|
||||||
@ -109,86 +89,125 @@ public class MmsDownloader extends MmscProcessor {
|
|||||||
stalledMmsReader.close();
|
stalledMmsReader.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void downloadMmsWithRadioChange(DownloadItem item) {
|
private void handleDownloadMms(MasterSecret masterSecret, Intent intent) {
|
||||||
Log.w("MmsDownloader", "Handling MMS download with radio change...");
|
long messageId = intent.getLongExtra("message_id", -1);
|
||||||
pendingMessages.add(item);
|
long threadId = intent.getLongExtra("thread_id", -1);
|
||||||
issueConnectivityRequest();
|
byte[] transactionId = intent.getByteArrayExtra("transaction_id");
|
||||||
}
|
String contentLocation = intent.getStringExtra("content_location");
|
||||||
|
boolean automatic = intent.getBooleanExtra("automatic", false);
|
||||||
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||||
|
|
||||||
private void downloadMms(DownloadItem item) {
|
database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_CONNECTING);
|
||||||
Log.w("MmsDownloadService", "Handling actual MMS download...");
|
|
||||||
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RetrieveConf retrieved = MmsDownloadHelper.retrieveMms(context, item.getContentLocation(),
|
if (isCdmaNetwork()) {
|
||||||
getApnInformation(),
|
Log.w("MmsDownloader", "Connecting directly...");
|
||||||
item.useMmsRadioMode(),
|
try {
|
||||||
item.proxyRequestIfPossible());
|
retrieveAndStore(masterSecret, messageId, threadId, contentLocation,
|
||||||
|
transactionId, false, false);
|
||||||
for (int i=0;i<retrieved.getBody().getPartsNum();i++) {
|
return;
|
||||||
Log.w("MmsDownloader", "Got MMS part of content-type: " +
|
} catch (IOException e) {
|
||||||
new String(retrieved.getBody().getPart(i).getContentType()));
|
Log.w("MmsDownloader", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
storeRetrievedMms(mmsDatabase, item, retrieved);
|
Log.w("MmsDownloader", "Changing radio to MMS mode..");
|
||||||
sendRetrievedAcknowledgement(item);
|
radio.connect();
|
||||||
|
|
||||||
|
Log.w("MmsDownloader", "Downloading in MMS mode without proxy...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
retrieveAndStore(masterSecret, messageId, threadId, contentLocation,
|
||||||
|
transactionId, true, false);
|
||||||
|
radio.disconnect();
|
||||||
|
return;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w("MmsDownloader", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.w("MmsDownloader", "Downloading in MMS mode with proxy...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
retrieveAndStore(masterSecret, messageId, threadId,
|
||||||
|
contentLocation, transactionId, true, true);
|
||||||
|
radio.disconnect();
|
||||||
|
return;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w("MmsDownloader", e);
|
||||||
|
radio.disconnect();
|
||||||
|
handleDownloadError(masterSecret, messageId, threadId,
|
||||||
|
MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE,
|
||||||
|
context.getString(R.string.MmsDownloader_error_connecting_to_mms_provider),
|
||||||
|
automatic);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (ApnUnavailableException e) {
|
} catch (ApnUnavailableException e) {
|
||||||
Log.w("MmsDownloader", e);
|
Log.w("MmsDownloader", e);
|
||||||
handleDownloadError(item, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE,
|
handleDownloadError(masterSecret, messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE,
|
||||||
context.getString(R.string.MmsDownloader_error_reading_mms_settings));
|
context.getString(R.string.MmsDownloader_error_reading_mms_settings), automatic);
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w("MmsDownloader", e);
|
|
||||||
if (!item.useMmsRadioMode() && !item.proxyRequestIfPossible()) {
|
|
||||||
Log.w("MmsDownloader", "Falling back to just radio mode...");
|
|
||||||
scheduleDownloadWithRadioMode(item);
|
|
||||||
} else if (!item.proxyRequestIfPossible()) {
|
|
||||||
Log.w("MmsDownloadeR", "Falling back to radio mode and proxy...");
|
|
||||||
scheduleDownloadWithRadioModeAndProxy(item);
|
|
||||||
} else {
|
|
||||||
handleDownloadError(item, MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE,
|
|
||||||
context.getString(R.string.MmsDownloader_error_connecting_to_mms_provider));
|
|
||||||
}
|
|
||||||
} catch (MmsException e) {
|
} catch (MmsException e) {
|
||||||
Log.w("MmsDownloader", e);
|
Log.w("MmsDownloader", e);
|
||||||
handleDownloadError(item, MmsDatabase.Status.DOWNLOAD_HARD_FAILURE,
|
handleDownloadError(masterSecret, messageId, threadId,
|
||||||
context.getString(R.string.MmsDownloader_error_storing_mms));
|
MmsDatabase.Status.DOWNLOAD_HARD_FAILURE,
|
||||||
|
context.getString(R.string.MmsDownloader_error_storing_mms),
|
||||||
|
automatic);
|
||||||
|
} catch (MmsRadioException e) {
|
||||||
|
Log.w("MmsDownloader", e);
|
||||||
|
handleDownloadError(masterSecret, messageId, threadId,
|
||||||
|
MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE,
|
||||||
|
context.getString(R.string.MmsDownloader_error_connecting_to_mms_provider),
|
||||||
|
automatic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeRetrievedMms(MmsDatabase mmsDatabase, DownloadItem item, RetrieveConf retrieved)
|
private void retrieveAndStore(MasterSecret masterSecret, long messageId, long threadId,
|
||||||
|
String contentLocation, byte[] transactionId,
|
||||||
|
boolean radioEnabled, boolean useProxy)
|
||||||
|
throws IOException, MmsException
|
||||||
|
{
|
||||||
|
RetrieveConf retrieved = MmsDownloadHelper.retrieveMms(context, contentLocation,
|
||||||
|
radio.getApnInformation(),
|
||||||
|
radioEnabled, useProxy);
|
||||||
|
|
||||||
|
storeRetrievedMms(masterSecret, contentLocation, messageId, threadId, retrieved);
|
||||||
|
sendRetrievedAcknowledgement(transactionId, radioEnabled, useProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeRetrievedMms(MasterSecret masterSecret, String contentLocation,
|
||||||
|
long messageId, long threadId, RetrieveConf retrieved)
|
||||||
throws MmsException
|
throws MmsException
|
||||||
{
|
{
|
||||||
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||||
Pair<Long, Long> messageAndThreadId;
|
Pair<Long, Long> messageAndThreadId;
|
||||||
|
|
||||||
if (retrieved.getSubject() != null && WirePrefix.isEncryptedMmsSubject(retrieved.getSubject().getString())) {
|
if (retrieved.getSubject() != null && WirePrefix.isEncryptedMmsSubject(retrieved.getSubject().getString())) {
|
||||||
messageAndThreadId = mmsDatabase.insertSecureMessageInbox(item.getMasterSecret(), retrieved,
|
messageAndThreadId = database.insertSecureMessageInbox(masterSecret, retrieved,
|
||||||
item.getContentLocation(),
|
contentLocation, threadId);
|
||||||
item.getThreadId());
|
|
||||||
|
|
||||||
if (item.getMasterSecret() != null)
|
if (masterSecret != null)
|
||||||
DecryptingQueue.scheduleDecryption(context, item.getMasterSecret(), messageAndThreadId.first,
|
DecryptingQueue.scheduleDecryption(context, masterSecret, messageAndThreadId.first,
|
||||||
messageAndThreadId.second, retrieved);
|
messageAndThreadId.second, retrieved);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
messageAndThreadId = mmsDatabase.insertMessageInbox(item.getMasterSecret(), retrieved,
|
messageAndThreadId = database.insertMessageInbox(masterSecret, retrieved,
|
||||||
item.getContentLocation(),
|
contentLocation, threadId);
|
||||||
item.getThreadId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mmsDatabase.delete(item.getMessageId());
|
database.delete(messageId);
|
||||||
MessageNotifier.updateNotification(context, item.getMasterSecret(), messageAndThreadId.second);
|
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendRetrievedAcknowledgement(DownloadItem item) {
|
private void sendRetrievedAcknowledgement(byte[] transactionId,
|
||||||
|
boolean usingRadio,
|
||||||
|
boolean useProxy)
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
NotifyRespInd notifyResponse = new NotifyRespInd(PduHeaders.CURRENT_MMS_VERSION,
|
NotifyRespInd notifyResponse = new NotifyRespInd(PduHeaders.CURRENT_MMS_VERSION,
|
||||||
item.getTransactionId(),
|
transactionId,
|
||||||
PduHeaders.STATUS_RETRIEVED);
|
PduHeaders.STATUS_RETRIEVED);
|
||||||
|
|
||||||
MmsSendHelper.sendNotificationReceived(context, new PduComposer(context, notifyResponse).make(),
|
MmsSendHelper.sendNotificationReceived(context, new PduComposer(context, notifyResponse).make(),
|
||||||
getApnInformation(), item.useMmsRadioMode(),
|
radio.getApnInformation(), usingRadio, useProxy);
|
||||||
item.proxyRequestIfPossible());
|
|
||||||
} catch (InvalidHeaderValueException e) {
|
} catch (InvalidHeaderValueException e) {
|
||||||
Log.w("MmsDownloader", e);
|
Log.w("MmsDownloader", e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -196,116 +215,25 @@ public class MmsDownloader extends MmscProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleConnectivityChange() {
|
|
||||||
LinkedList<DownloadItem> downloadItems = (LinkedList<DownloadItem>)pendingMessages.clone();
|
|
||||||
|
|
||||||
if (isConnected()) {
|
private void handleDownloadError(MasterSecret masterSecret, long messageId, long threadId,
|
||||||
pendingMessages.clear();
|
int downloadStatus, String error, boolean automatic)
|
||||||
|
{
|
||||||
for (DownloadItem item : downloadItems) {
|
|
||||||
downloadMms(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pendingMessages.isEmpty())
|
|
||||||
finishConnectivity();
|
|
||||||
|
|
||||||
} else if (!isConnected() && (!isConnectivityPossible() || isConnectivityFailure())) {
|
|
||||||
pendingMessages.clear();
|
|
||||||
handleDownloadError(downloadItems, MmsDatabase.Status.DOWNLOAD_NO_CONNECTIVITY,
|
|
||||||
context.getString(R.string.MmsDownloader_no_connectivity_available_for_mms_download_try_again_later));
|
|
||||||
finishConnectivity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleDownloadError(List<DownloadItem> items, int downloadStatus, String error) {
|
|
||||||
for (DownloadItem item : items) {
|
|
||||||
handleDownloadError(item, downloadStatus, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleDownloadError(DownloadItem item, int downloadStatus, String error) {
|
|
||||||
MmsDatabase db = DatabaseFactory.getMmsDatabase(context);
|
MmsDatabase db = DatabaseFactory.getMmsDatabase(context);
|
||||||
db.markDownloadState(item.getMessageId(), downloadStatus);
|
|
||||||
|
|
||||||
if (item.isAutomatic()) {
|
db.markDownloadState(messageId, downloadStatus);
|
||||||
db.markIncomingNotificationReceived(item.getThreadId());
|
|
||||||
MessageNotifier.updateNotification(context, item.getMasterSecret(), item.getThreadId());
|
if (automatic) {
|
||||||
|
db.markIncomingNotificationReceived(threadId);
|
||||||
|
MessageNotifier.updateNotification(context, masterSecret, threadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
toastHandler.makeToast(error);
|
toastHandler.makeToast(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleDownloadWithRadioMode(DownloadItem item) {
|
private boolean isCdmaNetwork() {
|
||||||
item.mmsRadioMode = true;
|
return ((TelephonyManager)context
|
||||||
handleDownloadMmsAction(item);
|
.getSystemService(Context.TELEPHONY_SERVICE))
|
||||||
}
|
.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
|
||||||
|
|
||||||
private void scheduleDownloadWithRadioModeAndProxy(DownloadItem item) {
|
|
||||||
item.mmsRadioMode = true;
|
|
||||||
item.proxyIfPossible = true;
|
|
||||||
handleDownloadMmsAction(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DownloadItem {
|
|
||||||
private final MasterSecret masterSecret;
|
|
||||||
private boolean mmsRadioMode;
|
|
||||||
private boolean proxyIfPossible;
|
|
||||||
|
|
||||||
private long threadId;
|
|
||||||
private long messageId;
|
|
||||||
private byte[] transactionId;
|
|
||||||
private String contentLocation;
|
|
||||||
private boolean automatic;
|
|
||||||
|
|
||||||
public DownloadItem(MasterSecret masterSecret, boolean mmsRadioMode, boolean proxyIfPossible,
|
|
||||||
long messageId, long threadId, boolean automatic, String contentLocation,
|
|
||||||
byte[] transactionId)
|
|
||||||
{
|
|
||||||
this.masterSecret = masterSecret;
|
|
||||||
this.mmsRadioMode = mmsRadioMode;
|
|
||||||
this.proxyIfPossible = proxyIfPossible;
|
|
||||||
this.threadId = threadId;
|
|
||||||
this.messageId = messageId;
|
|
||||||
this.contentLocation = contentLocation;
|
|
||||||
this.transactionId = transactionId;
|
|
||||||
this.automatic = automatic;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getThreadId() {
|
|
||||||
return threadId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getMessageId() {
|
|
||||||
return messageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContentLocation() {
|
|
||||||
return contentLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getTransactionId() {
|
|
||||||
return transactionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MasterSecret getMasterSecret() {
|
|
||||||
return masterSecret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean proxyRequestIfPossible() {
|
|
||||||
return proxyIfPossible;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean useMmsRadioMode() {
|
|
||||||
return mmsRadioMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAutomatic() {
|
|
||||||
return automatic;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getConnectivityAction() {
|
|
||||||
return SendReceiveService.DOWNLOAD_MMS_CONNECTIVITY_ACTION;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2013 Open Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -18,250 +18,66 @@ package org.thoughtcrime.securesms.service;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.crypto.SessionCipher;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.mms.MmsSendHelper;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.mms.TextTransport;
|
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
import org.thoughtcrime.securesms.protocol.WirePrefix;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
import org.thoughtcrime.securesms.service.SendReceiveService.ToastHandler;
|
import org.thoughtcrime.securesms.service.SendReceiveService.ToastHandler;
|
||||||
import org.thoughtcrime.securesms.util.Hex;
|
import org.thoughtcrime.securesms.transport.MmsTransport;
|
||||||
|
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||||
|
|
||||||
import ws.com.google.android.mms.ContentType;
|
|
||||||
import ws.com.google.android.mms.MmsException;
|
import ws.com.google.android.mms.MmsException;
|
||||||
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
|
||||||
import ws.com.google.android.mms.pdu.PduBody;
|
|
||||||
import ws.com.google.android.mms.pdu.PduComposer;
|
|
||||||
import ws.com.google.android.mms.pdu.PduHeaders;
|
|
||||||
import ws.com.google.android.mms.pdu.PduPart;
|
|
||||||
import ws.com.google.android.mms.pdu.SendConf;
|
|
||||||
import ws.com.google.android.mms.pdu.SendReq;
|
import ws.com.google.android.mms.pdu.SendReq;
|
||||||
|
|
||||||
import java.io.IOException;
|
public class MmsSender {
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class MmsSender extends MmscProcessor {
|
private final Context context;
|
||||||
|
|
||||||
private final LinkedList<SendItem> pendingMessages = new LinkedList<SendItem>();
|
|
||||||
private final ToastHandler toastHandler;
|
private final ToastHandler toastHandler;
|
||||||
|
|
||||||
public MmsSender(Context context, ToastHandler toastHandler) {
|
public MmsSender(Context context, ToastHandler toastHandler) {
|
||||||
super(context);
|
this.context = context;
|
||||||
this.toastHandler = toastHandler;
|
this.toastHandler = toastHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(MasterSecret masterSecret, Intent intent) {
|
public void process(MasterSecret masterSecret, Intent intent) {
|
||||||
|
Log.w("MmsSender", "Got intent action: " + intent.getAction());
|
||||||
if (intent.getAction().equals(SendReceiveService.SEND_MMS_ACTION)) {
|
if (intent.getAction().equals(SendReceiveService.SEND_MMS_ACTION)) {
|
||||||
long messageId = intent.getLongExtra("message_id", -1);
|
handleSendMms(masterSecret, intent);
|
||||||
boolean isCdma = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
|
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
|
|
||||||
try {
|
|
||||||
List<SendReq> sendRequests = getOutgoingMessages(masterSecret, messageId);
|
|
||||||
|
|
||||||
for (SendReq sendRequest : sendRequests) {
|
|
||||||
handleSendMmsAction(new SendItem(masterSecret, sendRequest, messageId != -1, !isCdma, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (MmsException me) {
|
|
||||||
Log.w("MmsSender", me);
|
|
||||||
if (messageId != -1)
|
|
||||||
database.markAsSentFailed(messageId);
|
|
||||||
}
|
|
||||||
} else if (intent.getAction().equals(SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION)) {
|
|
||||||
handleConnectivityChange();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSendMmsAction(SendItem item) {
|
private void handleSendMms(MasterSecret masterSecret, Intent intent) {
|
||||||
if (!isConnectivityPossible()) {
|
long messageId = intent.getLongExtra("message_id", -1);
|
||||||
if (item.targeted) {
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||||
toastHandler
|
ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context);
|
||||||
.obtainMessage(0, context.getString(R.string.MmsSender_currently_unable_to_send_your_mms_message))
|
MmsTransport transport = new MmsTransport(context, masterSecret);
|
||||||
.sendToTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.useMmsRadio) sendMmsMessageWithRadioChange(item);
|
|
||||||
else sendMmsMessage(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendMmsMessageWithRadioChange(SendItem item) {
|
|
||||||
Log.w("MmsSender", "Sending MMS with radio change..");
|
|
||||||
pendingMessages.add(item);
|
|
||||||
issueConnectivityRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendMmsMessage(SendItem item) {
|
|
||||||
Log.w("MmsSender", "Sending MMS SendItem...");
|
|
||||||
MmsDatabase db = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
String number = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number();
|
|
||||||
long messageId = item.request.getDatabaseMessageId();
|
|
||||||
long messageBox = item.request.getDatabaseMessageBox();
|
|
||||||
SendReq request = item.request;
|
|
||||||
|
|
||||||
|
|
||||||
if (MmsDatabase.Types.isSecureType(messageBox)) {
|
|
||||||
request = getEncryptedMms(item.masterSecret, request, messageId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (number != null && number.trim().length() != 0) {
|
|
||||||
request.setFrom(new EncodedStringValue(number));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SendConf conf = MmsSendHelper.sendMms(context, new PduComposer(context, request).make(),
|
SendReq[] messages = database.getOutgoingMessages(masterSecret, messageId);
|
||||||
getApnInformation(), item.useMmsRadio, item.useProxyIfAvailable);
|
|
||||||
|
|
||||||
for (int i=0;i<request.getBody().getPartsNum();i++) {
|
for (SendReq message : messages) {
|
||||||
Log.w("MmsSender", "Sent MMS part of content-type: " + new String(request.getBody().getPart(i).getContentType()));
|
try {
|
||||||
|
Log.w("MmsSender", "Passing to MMS transport: " + message.getDatabaseMessageId());
|
||||||
|
database.markAsSending(message.getDatabaseMessageId());
|
||||||
|
Pair<byte[], Integer> result = transport.deliver(message);
|
||||||
|
database.markAsSent(message.getDatabaseMessageId(), result.first, result.second);
|
||||||
|
} catch (UndeliverableMessageException e) {
|
||||||
|
Log.w("MmsSender", e);
|
||||||
|
database.markAsSentFailed(message.getDatabaseMessageId());
|
||||||
|
long threadId = database.getThreadIdForMessage(messageId);
|
||||||
|
Recipients recipients = threads.getRecipientsForThreadId(threadId);
|
||||||
|
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (MmsException e) {
|
||||||
long threadId = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId);
|
Log.w("MmsSender", e);
|
||||||
Recipients recipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(context, threadId);
|
if (messageId != -1)
|
||||||
|
database.markAsSentFailed(messageId);
|
||||||
if (conf == null) {
|
|
||||||
db.markAsSentFailed(messageId);
|
|
||||||
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
|
|
||||||
Log.w("MmsSender", "No M-Send.conf received in response to send.");
|
|
||||||
return;
|
|
||||||
} else if (conf.getResponseStatus() != PduHeaders.RESPONSE_STATUS_OK) {
|
|
||||||
Log.w("MmsSender", "Got bad response: " + conf.getResponseStatus());
|
|
||||||
db.updateResponseStatus(messageId, conf.getResponseStatus());
|
|
||||||
db.markAsSentFailed(messageId);
|
|
||||||
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
|
|
||||||
return;
|
|
||||||
} else if (isInconsistentResponse(request, conf)) {
|
|
||||||
db.markAsSentFailed(messageId);
|
|
||||||
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
|
|
||||||
Log.w("MmsSender", "Got a response for the wrong transaction?");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
Log.w("MmsSender", "Successful send! " + messageId);
|
|
||||||
db.markAsSent(messageId, conf.getMessageId(), conf.getResponseStatus());
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Log.w("MmsSender", ioe);
|
|
||||||
if (!item.useMmsRadio) scheduleSendWithMmsRadio(item);
|
|
||||||
else if (!item.useProxyIfAvailable) scheduleSendWithMmsRadioAndProxy(item);
|
|
||||||
else db.markAsSentFailed(messageId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SendReq> getOutgoingMessages(MasterSecret masterSecret, long messageId)
|
|
||||||
throws MmsException
|
|
||||||
{
|
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
return Arrays.asList(database.getOutgoingMessages(masterSecret, messageId));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void handleConnectivityChange() {
|
|
||||||
if (!isConnected()) {
|
|
||||||
if ((!isConnectivityPossible() || isConnectivityFailure()) && !pendingMessages.isEmpty()) {
|
|
||||||
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(pendingMessages.remove().request.getDatabaseMessageId());
|
|
||||||
toastHandler.makeToast(context.getString(R.string.MmsSender_currently_unable_to_send_your_mms_message));
|
|
||||||
Log.w("MmsSender", "Unable to send MMS.");
|
|
||||||
finishConnectivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<SendItem> outgoing = (List<SendItem>)pendingMessages.clone();
|
|
||||||
pendingMessages.clear();
|
|
||||||
|
|
||||||
for (SendItem item : outgoing) {
|
|
||||||
sendMmsMessage(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pendingMessages.isEmpty())
|
|
||||||
finishConnectivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isInconsistentResponse(SendReq send, SendConf response) {
|
|
||||||
Log.w("MmsSenderService", "Comparing: " + Hex.toString(send.getTransactionId()));
|
|
||||||
Log.w("MmsSenderService", "With: " + Hex.toString(response.getTransactionId()));
|
|
||||||
return !Arrays.equals(send.getTransactionId(), response.getTransactionId());
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] getEncryptedPdu(MasterSecret masterSecret, String recipient, byte[] pduBytes) {
|
|
||||||
synchronized (SessionCipher.CIPHER_LOCK) {
|
|
||||||
SessionCipher cipher = new SessionCipher(context, masterSecret, new Recipient(null, recipient, null, null), new TextTransport());
|
|
||||||
return cipher.encryptMessage(pduBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SendReq getEncryptedMms(MasterSecret masterSecret, SendReq pdu, long messageId) {
|
|
||||||
Log.w("MmsSender", "Sending Secure MMS.");
|
|
||||||
EncodedStringValue[] encodedRecipient = pdu.getTo();
|
|
||||||
String recipient = encodedRecipient[0].getString();
|
|
||||||
byte[] pduBytes = new PduComposer(context, pdu).make();
|
|
||||||
byte[] encryptedPduBytes = getEncryptedPdu(masterSecret, recipient, pduBytes);
|
|
||||||
|
|
||||||
PduBody body = new PduBody();
|
|
||||||
PduPart part = new PduPart();
|
|
||||||
SendReq encryptedPdu = new SendReq(pdu.getPduHeaders(), body);
|
|
||||||
|
|
||||||
part.setContentId((System.currentTimeMillis()+"").getBytes());
|
|
||||||
part.setContentType(ContentType.TEXT_PLAIN.getBytes());
|
|
||||||
part.setName((System.currentTimeMillis()+"").getBytes());
|
|
||||||
part.setData(encryptedPduBytes);
|
|
||||||
body.addPart(part);
|
|
||||||
encryptedPdu.setSubject(new EncodedStringValue(WirePrefix.calculateEncryptedMmsSubject()));
|
|
||||||
encryptedPdu.setBody(body);
|
|
||||||
|
|
||||||
return encryptedPdu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scheduleSendWithMmsRadioAndProxy(SendItem item) {
|
|
||||||
Log.w("MmsSender", "Falling back to sending MMS with radio and proxy...");
|
|
||||||
item.useMmsRadio = true;
|
|
||||||
item.useProxyIfAvailable = true;
|
|
||||||
handleSendMmsAction(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scheduleSendWithMmsRadio(SendItem item) {
|
|
||||||
Log.w("MmsSender", "Falling back to sending MMS with radio only...");
|
|
||||||
item.useMmsRadio = true;
|
|
||||||
handleSendMmsAction(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getConnectivityAction() {
|
|
||||||
return SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SendItem {
|
|
||||||
private final MasterSecret masterSecret;
|
|
||||||
|
|
||||||
private boolean useMmsRadio;
|
|
||||||
private boolean useProxyIfAvailable;
|
|
||||||
private SendReq request;
|
|
||||||
private boolean targeted;
|
|
||||||
|
|
||||||
public SendItem(MasterSecret masterSecret, SendReq request,
|
|
||||||
boolean targeted, boolean useMmsRadio,
|
|
||||||
boolean useProxyIfAvailable)
|
|
||||||
{
|
|
||||||
this.masterSecret = masterSecret;
|
|
||||||
this.request = request;
|
|
||||||
this.targeted = targeted;
|
|
||||||
this.useMmsRadio = useMmsRadio;
|
|
||||||
this.useProxyIfAvailable = useProxyIfAvailable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,123 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2011 Whisper Systems
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
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.os.PowerManager;
|
|
||||||
import android.os.PowerManager.WakeLock;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
public abstract class MmscProcessor {
|
|
||||||
|
|
||||||
private static final String FEATURE_ENABLE_MMS = "enableMMS";
|
|
||||||
private static final int APN_ALREADY_ACTIVE = 0;
|
|
||||||
public static final int TYPE_MOBILE_MMS = 2;
|
|
||||||
|
|
||||||
private ConnectivityManager connectivityManager;
|
|
||||||
private ConnectivityListener connectivityListener;
|
|
||||||
private WakeLock wakeLock;
|
|
||||||
|
|
||||||
protected final Context context;
|
|
||||||
|
|
||||||
public MmscProcessor(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
|
|
||||||
this.connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
||||||
this.wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MMS Connection");
|
|
||||||
this.wakeLock.setReferenceCounted(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getApnInformation() {
|
|
||||||
return connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS).getExtraInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isConnected() {
|
|
||||||
NetworkInfo info = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);
|
|
||||||
|
|
||||||
Log.w("MmsService", "NetworkInfo: " + info);
|
|
||||||
|
|
||||||
if ((info == null) || (info.getType() != TYPE_MOBILE_MMS) || !info.isConnected())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract String getConnectivityAction();
|
|
||||||
|
|
||||||
protected void issueConnectivityRequest() {
|
|
||||||
int status = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS);
|
|
||||||
|
|
||||||
Log.w("MmscProcessor", "startUsingNetworkFeature status: " + status);
|
|
||||||
|
|
||||||
if (status == APN_ALREADY_ACTIVE) {
|
|
||||||
issueConnectivityChange();
|
|
||||||
} else if (connectivityListener == null) {
|
|
||||||
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
|
|
||||||
connectivityListener = new ConnectivityListener();
|
|
||||||
context.registerReceiver(connectivityListener, filter);
|
|
||||||
|
|
||||||
wakeLock.acquire();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isConnectivityFailure() {
|
|
||||||
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);
|
|
||||||
|
|
||||||
return networkInfo == null || networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected boolean isConnectivityPossible() {
|
|
||||||
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);
|
|
||||||
Log.w("MmsService", "Got network info: " + networkInfo);
|
|
||||||
|
|
||||||
return networkInfo != null && networkInfo.isAvailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void finishConnectivity() {
|
|
||||||
Log.w("MmsService", "Calling stop using network feature!");
|
|
||||||
connectivityManager.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS);
|
|
||||||
|
|
||||||
if (connectivityListener != null) {
|
|
||||||
context.unregisterReceiver(connectivityListener);
|
|
||||||
connectivityListener = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.wakeLock.isHeld())
|
|
||||||
this.wakeLock.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void issueConnectivityChange() {
|
|
||||||
Intent intent = new Intent(context, SendReceiveService.class);
|
|
||||||
intent.setAction(getConnectivityAction());
|
|
||||||
context.startService(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ConnectivityListener extends BroadcastReceiver {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
Log.w("MmsService", "Dispatching connectivity change...");
|
|
||||||
issueConnectivityChange();
|
|
||||||
Log.w("MmsService", "Dispatched...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -50,7 +50,6 @@ public class SendReceiveService extends Service {
|
|||||||
public static final String DELIVERED_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DELIVERED_SMS_ACTION";
|
public static final String DELIVERED_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DELIVERED_SMS_ACTION";
|
||||||
public static final String RECEIVE_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_SMS_ACTION";
|
public static final String RECEIVE_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_SMS_ACTION";
|
||||||
public static final String SEND_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_ACTION";
|
public static final String SEND_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_ACTION";
|
||||||
public static final String SEND_MMS_CONNECTIVITY_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION";
|
|
||||||
public static final String RECEIVE_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_MMS_ACTION";
|
public static final String RECEIVE_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_MMS_ACTION";
|
||||||
public static final String DOWNLOAD_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_MMS_ACTION";
|
public static final String DOWNLOAD_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_MMS_ACTION";
|
||||||
public static final String DOWNLOAD_MMS_CONNECTIVITY_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_MMS_CONNECTIVITY_ACTION";
|
public static final String DOWNLOAD_MMS_CONNECTIVITY_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_MMS_CONNECTIVITY_ACTION";
|
||||||
@ -101,11 +100,11 @@ public class SendReceiveService extends Service {
|
|||||||
scheduleIntent(SEND_SMS, intent);
|
scheduleIntent(SEND_SMS, intent);
|
||||||
else if (intent.getAction().equals(DELIVERED_SMS_ACTION))
|
else if (intent.getAction().equals(DELIVERED_SMS_ACTION))
|
||||||
scheduleIntent(SEND_SMS, intent);
|
scheduleIntent(SEND_SMS, intent);
|
||||||
else if (intent.getAction().equals(SEND_MMS_ACTION) || intent.getAction().equals(SEND_MMS_CONNECTIVITY_ACTION))
|
else if (intent.getAction().equals(SEND_MMS_ACTION))
|
||||||
scheduleSecretRequiredIntent(SEND_MMS, intent);
|
scheduleSecretRequiredIntent(SEND_MMS, intent);
|
||||||
else if (intent.getAction().equals(RECEIVE_MMS_ACTION))
|
else if (intent.getAction().equals(RECEIVE_MMS_ACTION))
|
||||||
scheduleIntent(RECEIVE_MMS, intent);
|
scheduleIntent(RECEIVE_MMS, intent);
|
||||||
else if (intent.getAction().equals(DOWNLOAD_MMS_ACTION) || intent.getAction().equals(DOWNLOAD_MMS_CONNECTIVITY_ACTION))
|
else if (intent.getAction().equals(DOWNLOAD_MMS_ACTION))
|
||||||
scheduleSecretRequiredIntent(DOWNLOAD_MMS, intent);
|
scheduleSecretRequiredIntent(DOWNLOAD_MMS, intent);
|
||||||
else if (intent.getAction().equals(DOWNLOAD_MMS_PENDING_APN_ACTION))
|
else if (intent.getAction().equals(DOWNLOAD_MMS_PENDING_APN_ACTION))
|
||||||
scheduleSecretRequiredIntent(DOWNLOAD_MMS_PENDING, intent);
|
scheduleSecretRequiredIntent(DOWNLOAD_MMS_PENDING, intent);
|
||||||
|
@ -100,7 +100,7 @@ public class SmsSender {
|
|||||||
registerForRadioChanges();
|
registerForRadioChanges();
|
||||||
} else {
|
} else {
|
||||||
long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId);
|
long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId);
|
||||||
Recipients recipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(context, threadId);
|
Recipients recipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(threadId);
|
||||||
|
|
||||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
||||||
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
|
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
|
||||||
|
@ -7,6 +7,8 @@ import android.net.ConnectivityManager;
|
|||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.telephony.ServiceState;
|
import android.telephony.ServiceState;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.mms.MmsRadio;
|
||||||
|
|
||||||
public class SystemStateListener extends BroadcastReceiver {
|
public class SystemStateListener extends BroadcastReceiver {
|
||||||
|
|
||||||
public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
|
public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
|
||||||
@ -42,7 +44,7 @@ public class SystemStateListener extends BroadcastReceiver {
|
|||||||
ConnectivityManager connectivityManager
|
ConnectivityManager connectivityManager
|
||||||
= (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
= (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
|
||||||
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(MmscProcessor.TYPE_MOBILE_MMS);
|
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(MmsRadio.TYPE_MOBILE_MMS);
|
||||||
|
|
||||||
if (networkInfo != null && networkInfo.isAvailable()) {
|
if (networkInfo != null && networkInfo.isAvailable()) {
|
||||||
sendMmsOutbox(context);
|
sendMmsOutbox(context);
|
||||||
|
157
src/org/thoughtcrime/securesms/transport/MmsTransport.java
Normal file
157
src/org/thoughtcrime/securesms/transport/MmsTransport.java
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package org.thoughtcrime.securesms.transport;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
|
import org.thoughtcrime.securesms.crypto.SessionCipher;
|
||||||
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
|
import org.thoughtcrime.securesms.mms.MmsRadio;
|
||||||
|
import org.thoughtcrime.securesms.mms.MmsRadioException;
|
||||||
|
import org.thoughtcrime.securesms.mms.MmsSendHelper;
|
||||||
|
import org.thoughtcrime.securesms.mms.TextTransport;
|
||||||
|
import org.thoughtcrime.securesms.protocol.WirePrefix;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.util.Hex;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import ws.com.google.android.mms.ContentType;
|
||||||
|
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
||||||
|
import ws.com.google.android.mms.pdu.PduBody;
|
||||||
|
import ws.com.google.android.mms.pdu.PduComposer;
|
||||||
|
import ws.com.google.android.mms.pdu.PduHeaders;
|
||||||
|
import ws.com.google.android.mms.pdu.PduPart;
|
||||||
|
import ws.com.google.android.mms.pdu.SendConf;
|
||||||
|
import ws.com.google.android.mms.pdu.SendReq;
|
||||||
|
|
||||||
|
public class MmsTransport {
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final MasterSecret masterSecret;
|
||||||
|
private final MmsRadio radio;
|
||||||
|
|
||||||
|
public MmsTransport(Context context, MasterSecret masterSecret) {
|
||||||
|
this.context = context;
|
||||||
|
this.masterSecret = masterSecret;
|
||||||
|
this.radio = MmsRadio.getInstance(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pair<byte[], Integer> deliver(SendReq message) throws UndeliverableMessageException {
|
||||||
|
try {
|
||||||
|
if (isCdmaDevice()) {
|
||||||
|
Log.w("MmsTransport", "Sending MMS directly without radio change...");
|
||||||
|
try {
|
||||||
|
return sendMms(message, false, false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w("MmsTransport", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.w("MmsTransport", "Sending MMS with radio change...");
|
||||||
|
radio.connect();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Pair<byte[], Integer> result = sendMms(message, true, false);
|
||||||
|
radio.disconnect();
|
||||||
|
return result;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w("MmsTransport", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.w("MmsTransport", "Sending MMS with radio change and proxy...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Pair<byte[], Integer> result = sendMms(message, true, true);
|
||||||
|
radio.disconnect();
|
||||||
|
return result;
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Log.w("MmsTransport", ioe);
|
||||||
|
radio.disconnect();
|
||||||
|
throw new UndeliverableMessageException(ioe);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (MmsRadioException mre) {
|
||||||
|
Log.w("MmsTransport", mre);
|
||||||
|
throw new UndeliverableMessageException(mre);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pair<byte[], Integer> sendMms(SendReq message, boolean usingMmsRadio, boolean useProxy)
|
||||||
|
throws IOException, UndeliverableMessageException
|
||||||
|
{
|
||||||
|
String number = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number();
|
||||||
|
|
||||||
|
if (MmsDatabase.Types.isSecureType(message.getDatabaseMessageBox())) {
|
||||||
|
message = getEncryptedMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (number != null && number.trim().length() != 0) {
|
||||||
|
message.setFrom(new EncodedStringValue(number));
|
||||||
|
}
|
||||||
|
|
||||||
|
SendConf conf = MmsSendHelper.sendMms(context, new PduComposer(context, message).make(),
|
||||||
|
radio.getApnInformation(), usingMmsRadio, useProxy);
|
||||||
|
|
||||||
|
for (int i=0;i<message.getBody().getPartsNum();i++) {
|
||||||
|
Log.w("MmsSender", "Sent MMS part of content-type: " + new String(message.getBody().getPart(i).getContentType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf == null) {
|
||||||
|
throw new UndeliverableMessageException("No M-Send.conf received in response to send.");
|
||||||
|
} else if (conf.getResponseStatus() != PduHeaders.RESPONSE_STATUS_OK) {
|
||||||
|
throw new UndeliverableMessageException("Got bad response: " + conf.getResponseStatus());
|
||||||
|
} else if (isInconsistentResponse(message, conf)) {
|
||||||
|
throw new UndeliverableMessageException("Mismatched response!");
|
||||||
|
} else {
|
||||||
|
return new Pair<byte[], Integer>(conf.getMessageId(), conf.getResponseStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SendReq getEncryptedMessage(SendReq pdu) {
|
||||||
|
EncodedStringValue[] encodedRecipient = pdu.getTo();
|
||||||
|
String recipient = encodedRecipient[0].getString();
|
||||||
|
byte[] pduBytes = new PduComposer(context, pdu).make();
|
||||||
|
byte[] encryptedPduBytes = getEncryptedPdu(masterSecret, recipient, pduBytes);
|
||||||
|
|
||||||
|
PduBody body = new PduBody();
|
||||||
|
PduPart part = new PduPart();
|
||||||
|
SendReq encryptedPdu = new SendReq(pdu.getPduHeaders(), body);
|
||||||
|
|
||||||
|
part.setContentId((System.currentTimeMillis()+"").getBytes());
|
||||||
|
part.setContentType(ContentType.TEXT_PLAIN.getBytes());
|
||||||
|
part.setName((System.currentTimeMillis()+"").getBytes());
|
||||||
|
part.setData(encryptedPduBytes);
|
||||||
|
body.addPart(part);
|
||||||
|
encryptedPdu.setSubject(new EncodedStringValue(WirePrefix.calculateEncryptedMmsSubject()));
|
||||||
|
encryptedPdu.setBody(body);
|
||||||
|
|
||||||
|
return encryptedPdu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getEncryptedPdu(MasterSecret masterSecret, String recipient, byte[] pduBytes) {
|
||||||
|
synchronized (SessionCipher.CIPHER_LOCK) {
|
||||||
|
SessionCipher cipher = new SessionCipher(context, masterSecret,
|
||||||
|
new Recipient(null, recipient, null, null),
|
||||||
|
new TextTransport());
|
||||||
|
|
||||||
|
return cipher.encryptMessage(pduBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInconsistentResponse(SendReq message, SendConf response) {
|
||||||
|
Log.w("MmsTransport", "Comparing: " + Hex.toString(message.getTransactionId()));
|
||||||
|
Log.w("MmsTransport", "With: " + Hex.toString(response.getTransactionId()));
|
||||||
|
return !Arrays.equals(message.getTransactionId(), response.getTransactionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCdmaDevice() {
|
||||||
|
return ((TelephonyManager)context
|
||||||
|
.getSystemService(Context.TELEPHONY_SERVICE))
|
||||||
|
.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -25,6 +25,8 @@ import android.widget.EditText;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.provider.Telephony;
|
import android.provider.Telephony;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.mms.MmsRadio;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
@ -134,6 +136,14 @@ public class Util {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void wait(Object lock, int timeout) {
|
||||||
|
try {
|
||||||
|
lock.wait(timeout);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
throw new AssertionError(ie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static boolean isDefaultSmsProvider(Context context){
|
public static boolean isDefaultSmsProvider(Context context){
|
||||||
return (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) ||
|
return (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) ||
|
||||||
|
Loading…
Reference in New Issue
Block a user