Initial fixes for MMS retrieval.

1) Parse the APN information based on what the ConnectionManager
tells us.

2) Accept email addresses as a valid Recipient format.
This commit is contained in:
Moxie Marlinspike 2012-09-30 11:46:45 -07:00
parent 59e7226183
commit cf9dc51f31
9 changed files with 224 additions and 217 deletions

View File

@ -421,9 +421,9 @@ public class MmsDatabase extends Database {
database.delete(TABLE_NAME, null, null); database.delete(TABLE_NAME, null, null);
} }
public Cursor getCarrierMmsInformation() { public Cursor getCarrierMmsInformation(String apn) {
Uri uri = Uri.withAppendedPath(Uri.parse("content://telephony/carriers"), "current"); Uri uri = Uri.withAppendedPath(Uri.parse("content://telephony/carriers"), "current");
String selection = "type = 'mms'"; String selection = (apn == null || apn.trim().length() == 0) ? null : String.format("apn = '%s'", apn.trim());
return context.getContentResolver().query(uri, null, selection, null, null); return context.getContentResolver().query(uri, null, selection, null, null);
} }

View File

@ -1,6 +1,6 @@
/** /**
* Copyright (C) 2011 Whisper Systems * Copyright (C) 2011 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
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
@ -10,15 +10,16 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.thoughtcrime.securesms.mms; package org.thoughtcrime.securesms.mms;
import java.io.DataInputStream; import android.content.Context;
import java.io.IOException; import android.database.Cursor;
import java.net.InetAddress; import android.net.ConnectivityManager;
import android.net.Uri;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
@ -42,57 +43,58 @@ import org.thoughtcrime.securesms.util.Conversions;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
import android.content.Context; import java.io.DataInputStream;
import android.database.Cursor; import java.io.IOException;
import android.net.ConnectivityManager; import java.net.InetAddress;
import android.net.Uri;
public class MmsCommunication { public class MmsCommunication {
protected static MmsConnectionParameters getMmsConnectionParameters(Context context) throws MmsException { protected static MmsConnectionParameters getMmsConnectionParameters(Context context, String apn)
Cursor cursor = DatabaseFactory.getMmsDatabase(context).getCarrierMmsInformation(); throws MmsException
{
Cursor cursor = DatabaseFactory.getMmsDatabase(context).getCarrierMmsInformation(apn);
try { try {
if (cursor == null || !cursor.moveToFirst()) if (cursor == null || !cursor.moveToFirst())
throw new MmsException("No carrier MMS information available."); throw new MmsException("No carrier MMS information available.");
do { do {
String mmsc = cursor.getString(cursor.getColumnIndexOrThrow("mmsc")); String mmsc = cursor.getString(cursor.getColumnIndexOrThrow("mmsc"));
String proxy = cursor.getString(cursor.getColumnIndexOrThrow("mmsproxy")); String proxy = cursor.getString(cursor.getColumnIndexOrThrow("mmsproxy"));
String port = cursor.getString(cursor.getColumnIndexOrThrow("mmsport")); String port = cursor.getString(cursor.getColumnIndexOrThrow("mmsport"));
if (mmsc != null && !mmsc.equals("")) if (mmsc != null && !mmsc.equals(""))
return new MmsConnectionParameters(mmsc, proxy, port); return new MmsConnectionParameters(mmsc, proxy, port);
} while (cursor.moveToNext()); } while (cursor.moveToNext());
throw new MmsException("No carrier MMS information available."); throw new MmsException("No carrier MMS information available.");
} finally { } finally {
if (cursor != null) if (cursor != null)
cursor.close(); cursor.close();
} }
} }
protected static void checkRouteToHost(Context context, MmsConnectionParameters parameters, String url) throws IOException { protected static void checkRouteToHost(Context context, MmsConnectionParameters parameters, String url) throws IOException {
if (parameters == null || !parameters.hasProxy()) if (parameters == null || !parameters.hasProxy())
checkRouteToHost(context, Uri.parse(url).getHost()); checkRouteToHost(context, Uri.parse(url).getHost());
else else
checkRouteToHost(context, parameters.getProxy()); checkRouteToHost(context, parameters.getProxy());
} }
private static void checkRouteToHost(Context context, String host) throws IOException { private static void checkRouteToHost(Context context, String host) throws IOException {
InetAddress inetAddress = InetAddress.getByName(host); InetAddress inetAddress = InetAddress.getByName(host);
byte[] ipAddressBytes = inetAddress.getAddress(); byte[] ipAddressBytes = inetAddress.getAddress();
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(MmsDownloader.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.");
// if (!manager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE, ipAddress)) // if (!manager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE, ipAddress))
// throw new IOException("Connection manager could not obtain route to host."); // throw new IOException("Connection manager could not obtain route to host.");
} }
protected static HttpClient constructHttpClient(MmsConnectionParameters mmsConfig) { protected static HttpClient constructHttpClient(MmsConnectionParameters mmsConfig) {
HttpParams params = new BasicHttpParams(); HttpParams params = new BasicHttpParams();
HttpConnectionParams.setStaleCheckingEnabled(params, false); HttpConnectionParams.setStaleCheckingEnabled(params, false);
@ -102,11 +104,11 @@ public class MmsCommunication {
HttpClientParams.setRedirecting(params, false); HttpClientParams.setRedirecting(params, false);
HttpProtocolParams.setUserAgent(params, "TextSecure/0.1"); HttpProtocolParams.setUserAgent(params, "TextSecure/0.1");
HttpProtocolParams.setContentCharset(params, "UTF-8"); HttpProtocolParams.setContentCharset(params, "UTF-8");
if (mmsConfig.hasProxy()) { if (mmsConfig.hasProxy()) {
ConnRouteParams.setDefaultProxy(params, new HttpHost(mmsConfig.getProxy(), mmsConfig.getPort())); ConnRouteParams.setDefaultProxy(params, new HttpHost(mmsConfig.getProxy(), mmsConfig.getPort()));
} }
SchemeRegistry schemeRegistry = new SchemeRegistry(); SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
@ -114,47 +116,47 @@ public class MmsCommunication {
ClientConnectionManager manager = new ThreadSafeClientConnManager(params, schemeRegistry); ClientConnectionManager manager = new ThreadSafeClientConnManager(params, schemeRegistry);
return new DefaultHttpClient(manager, params); return new DefaultHttpClient(manager, params);
} }
protected static byte[] parseResponse(HttpEntity entity) throws IOException { protected static byte[] parseResponse(HttpEntity entity) throws IOException {
if (entity == null || entity.getContentLength() == 0) if (entity == null || entity.getContentLength() == 0)
throw new IOException("Null response"); throw new IOException("Null response");
byte[] responseBytes = new byte[(int)entity.getContentLength()]; byte[] responseBytes = new byte[(int)entity.getContentLength()];
DataInputStream dataInputStream = new DataInputStream(entity.getContent()); DataInputStream dataInputStream = new DataInputStream(entity.getContent());
dataInputStream.readFully(responseBytes); dataInputStream.readFully(responseBytes);
dataInputStream.close(); dataInputStream.close();
entity.consumeContent(); entity.consumeContent();
return responseBytes; return responseBytes;
} }
protected static class MmsConnectionParameters { protected static class MmsConnectionParameters {
private final String mmsc; private final String mmsc;
private final String proxy; private final String proxy;
private final String port; private final String port;
public MmsConnectionParameters(String mmsc, String proxy, String port) { public MmsConnectionParameters(String mmsc, String proxy, String port) {
this.mmsc = mmsc; this.mmsc = mmsc;
this.proxy = proxy; this.proxy = proxy;
this.port = port; this.port = port;
} }
public boolean hasProxy() { public boolean hasProxy() {
return proxy != null && proxy.trim().length() != 0; return proxy != null && proxy.trim().length() != 0;
} }
public String getMmsc() { public String getMmsc() {
return mmsc; return mmsc;
} }
public String getProxy() { public String getProxy() {
return proxy; return proxy;
} }
public int getPort() { public int getPort() {
if (port == null || port.trim().length() == 0) if (port == null || port.trim().length() == 0)
return 80; return 80;
return Integer.parseInt(port); return Integer.parseInt(port);
} }
} }

View File

@ -1,6 +1,6 @@
/** /**
* Copyright (C) 2011 Whisper Systems * Copyright (C) 2011 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
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
@ -10,15 +10,14 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.thoughtcrime.securesms.mms; package org.thoughtcrime.securesms.mms;
import java.io.IOException; import android.content.Context;
import java.net.URI; import android.util.Log;
import java.net.URISyntaxException;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.HttpRequest; import org.apache.http.HttpRequest;
@ -30,38 +29,41 @@ import org.apache.http.client.methods.HttpGet;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
import android.content.Context; import java.io.IOException;
import android.util.Log; import java.net.URI;
import java.net.URISyntaxException;
public class MmsDownloadHelper extends MmsCommunication { public class MmsDownloadHelper extends MmsCommunication {
private static byte[] makeRequest(MmsConnectionParameters connectionParameters, String url) throws ClientProtocolException, IOException { private static byte[] makeRequest(MmsConnectionParameters connectionParameters, String url)
throws ClientProtocolException, IOException
{
try { try {
HttpClient client = constructHttpClient(connectionParameters); HttpClient client = constructHttpClient(connectionParameters);
URI hostUrl = new URI(url); URI hostUrl = new URI(url);
HttpHost target = new HttpHost(hostUrl.getHost(), hostUrl.getPort(), HttpHost.DEFAULT_SCHEME_NAME); HttpHost target = new HttpHost(hostUrl.getHost(), hostUrl.getPort(), HttpHost.DEFAULT_SCHEME_NAME);
HttpRequest request = new HttpGet(url); HttpRequest request = new HttpGet(url);
request.setParams(client.getParams()); request.setParams(client.getParams());
request.addHeader("Accept", "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic"); request.addHeader("Accept", "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic");
HttpResponse response = client.execute(target, request); HttpResponse response = client.execute(target, request);
StatusLine status = response.getStatusLine(); StatusLine status = response.getStatusLine();
if (status.getStatusCode() != 200) if (status.getStatusCode() != 200)
throw new IOException("Non-successful HTTP response: " + status.getReasonPhrase()); throw new IOException("Non-successful HTTP response: " + status.getReasonPhrase());
return parseResponse(response.getEntity()); return parseResponse(response.getEntity());
} catch (URISyntaxException use) { } catch (URISyntaxException use) {
Log.w("MmsDownlader", use); Log.w("MmsDownlader", use);
throw new IOException("Bad URI syntax"); throw new IOException("Bad URI syntax");
} }
} }
public static byte[] retrieveMms(Context context, String url) throws IOException { public static byte[] retrieveMms(Context context, String url, String apn) throws IOException {
try { try {
MmsConnectionParameters connectionParameters = getMmsConnectionParameters(context); MmsConnectionParameters connectionParameters = getMmsConnectionParameters(context, apn);
checkRouteToHost(context, connectionParameters, url); checkRouteToHost(context, connectionParameters, url);
return makeRequest(connectionParameters, url); return makeRequest(connectionParameters, url);
} catch (MmsException me) { } catch (MmsException me) {

View File

@ -1,6 +1,6 @@
/** /**
* Copyright (C) 2011 Whisper Systems * Copyright (C) 2011 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
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
@ -10,15 +10,14 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.thoughtcrime.securesms.mms; package org.thoughtcrime.securesms.mms;
import java.io.IOException; import android.content.Context;
import java.net.URI; import android.util.Log;
import java.net.URISyntaxException;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
@ -27,14 +26,15 @@ import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
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.util.Hex;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
import android.content.Context;
import android.util.Log; import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
public class MmsSendHelper extends MmsCommunication { public class MmsSendHelper extends MmsCommunication {
private static byte[] makePost(MmsConnectionParameters parameters, byte[] mms) throws ClientProtocolException, IOException { private static byte[] makePost(MmsConnectionParameters parameters, byte[] mms) throws ClientProtocolException, IOException {
Log.w("MmsSender", "Sending MMS1 of length: " + mms.length); Log.w("MmsSender", "Sending MMS1 of length: " + mms.length);
try { try {
@ -43,30 +43,30 @@ public class MmsSendHelper extends MmsCommunication {
HttpHost target = new HttpHost(hostUrl.getHost(), hostUrl.getPort(), HttpHost.DEFAULT_SCHEME_NAME); HttpHost target = new HttpHost(hostUrl.getHost(), hostUrl.getPort(), HttpHost.DEFAULT_SCHEME_NAME);
HttpPost request = new HttpPost(parameters.getMmsc()); HttpPost request = new HttpPost(parameters.getMmsc());
ByteArrayEntity entity = new ByteArrayEntity(mms); ByteArrayEntity entity = new ByteArrayEntity(mms);
entity.setContentType("application/vnd.wap.mms-message"); entity.setContentType("application/vnd.wap.mms-message");
request.setEntity(entity); request.setEntity(entity);
request.setParams(client.getParams()); request.setParams(client.getParams());
request.addHeader("Accept", "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic"); request.addHeader("Accept", "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic");
HttpResponse response = client.execute(target, request); HttpResponse response = client.execute(target, request);
StatusLine status = response.getStatusLine(); StatusLine status = response.getStatusLine();
if (status.getStatusCode() != 200) if (status.getStatusCode() != 200)
throw new IOException("Non-successful HTTP response: " + status.getReasonPhrase()); throw new IOException("Non-successful HTTP response: " + status.getReasonPhrase());
return parseResponse(response.getEntity()); return parseResponse(response.getEntity());
} catch (URISyntaxException use) { } catch (URISyntaxException use) {
Log.w("MmsDownlader", use); Log.w("MmsDownlader", use);
throw new IOException("Bad URI syntax"); throw new IOException("Bad URI syntax");
} }
} }
public static byte[] sendMms(Context context, byte[] mms) throws IOException { public static byte[] sendMms(Context context, byte[] mms, String apn) throws IOException {
Log.w("MmsSender", "Sending MMS of length: " + mms.length); Log.w("MmsSender", "Sending MMS of length: " + mms.length);
try { try {
MmsConnectionParameters parameters = getMmsConnectionParameters(context); MmsConnectionParameters parameters = getMmsConnectionParameters(context, apn);
checkRouteToHost(context, parameters, parameters.getMmsc()); checkRouteToHost(context, parameters, parameters.getMmsc());
return makePost(parameters, mms); return makePost(parameters, mms);
} catch (MmsException me) { } catch (MmsException me) {

View File

@ -19,7 +19,6 @@ package org.thoughtcrime.securesms.recipients;
import android.content.Context; import android.content.Context;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.telephony.PhoneNumberUtils;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
@ -146,7 +145,7 @@ public class RecipientFactory {
int end = recipient.indexOf('>'); int end = recipient.indexOf('>');
String value = recipient.substring(begin + 1, end); String value = recipient.substring(begin + 1, end);
if (PhoneNumberUtils.isWellFormedSmsAddress(value)) if (NumberUtil.isValidSmsOrEmail(value))
return value; return value;
else else
throw new RecipientFormattingException("Bracketed value: " + value + " is not valid."); throw new RecipientFormattingException("Bracketed value: " + value + " is not valid.");

View File

@ -1,6 +1,6 @@
/** /**
* Copyright (C) 2011 Whisper Systems * Copyright (C) 2011 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
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
@ -10,14 +10,15 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.thoughtcrime.securesms.service; package org.thoughtcrime.securesms.service;
import java.io.IOException; import android.content.Context;
import java.util.LinkedList; import android.content.Intent;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.DecryptingQueue; import org.thoughtcrime.securesms.crypto.DecryptingQueue;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
@ -29,20 +30,20 @@ import org.thoughtcrime.securesms.protocol.WirePrefix;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.PduParser; import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.RetrieveConf; import ws.com.google.android.mms.pdu.RetrieveConf;
import android.content.Context;
import android.content.Intent; import java.io.IOException;
import android.util.Log; import java.util.LinkedList;
public class MmsDownloader extends MmscProcessor { public class MmsDownloader extends MmscProcessor {
private final LinkedList<DownloadItem> pendingMessages = new LinkedList<DownloadItem>(); private final LinkedList<DownloadItem> pendingMessages = new LinkedList<DownloadItem>();
private final SendReceiveService.ToastHandler toastHandler; private final SendReceiveService.ToastHandler toastHandler;
public MmsDownloader(Context context, SendReceiveService.ToastHandler toastHandler) { public MmsDownloader(Context context, SendReceiveService.ToastHandler toastHandler) {
super(context); super(context);
this.toastHandler = toastHandler; this.toastHandler = toastHandler;
} }
private void handleDownloadMms(DownloadItem item) { private void handleDownloadMms(DownloadItem item) {
if (!isConnectivityPossible()) { if (!isConnectivityPossible()) {
DatabaseFactory.getMmsDatabase(context).markDownloadState(item.getMessageId(), MmsDatabase.Types.DOWNLOAD_NO_CONNECTIVITY); DatabaseFactory.getMmsDatabase(context).markDownloadState(item.getMessageId(), MmsDatabase.Types.DOWNLOAD_NO_CONNECTIVITY);
@ -54,37 +55,38 @@ public class MmsDownloader extends MmscProcessor {
issueConnectivityRequest(); issueConnectivityRequest();
} }
} }
private void handleDownloadMmsContinued(DownloadItem item) { private void handleDownloadMmsContinued(DownloadItem item) {
Log.w("MmsDownloadService", "Handling MMS download continuation..."); Log.w("MmsDownloadService", "Handling MMS download continuation...");
MmsDatabase mmsDatabase; MmsDatabase mmsDatabase;
if (item.getMasterSecret() == null) if (item.getMasterSecret() == null)
mmsDatabase = DatabaseFactory.getMmsDatabase(context); mmsDatabase = DatabaseFactory.getMmsDatabase(context);
else else
mmsDatabase = DatabaseFactory.getEncryptingMmsDatabase(context, item.getMasterSecret()); mmsDatabase = DatabaseFactory.getEncryptingMmsDatabase(context, item.getMasterSecret());
try { try {
byte[] pdu = MmsDownloadHelper.retrieveMms(context, item.getContentLocation()); byte[] pdu = MmsDownloadHelper.retrieveMms(context, item.getContentLocation(),
getApnInformation());
RetrieveConf retrieved = (RetrieveConf)new PduParser(pdu).parse(); RetrieveConf retrieved = (RetrieveConf)new PduParser(pdu).parse();
for (int i=0;i<retrieved.getBody().getPartsNum();i++) { for (int i=0;i<retrieved.getBody().getPartsNum();i++) {
Log.w("MmsDownloader", "Sent MMS part of content-type: " + new String(retrieved.getBody().getPart(i).getContentType())); Log.w("MmsDownloader", "Sent MMS part of content-type: " + new String(retrieved.getBody().getPart(i).getContentType()));
} }
if (retrieved == null) if (retrieved == null)
throw new IOException("Bad retrieved PDU"); throw new IOException("Bad retrieved PDU");
if (retrieved.getSubject() != null && WirePrefix.isEncryptedMmsSubject(retrieved.getSubject().getString())) { if (retrieved.getSubject() != null && WirePrefix.isEncryptedMmsSubject(retrieved.getSubject().getString())) {
long messageId = mmsDatabase.insertSecureMessageReceived(retrieved, item.getContentLocation(), item.getThreadId()); long messageId = mmsDatabase.insertSecureMessageReceived(retrieved, item.getContentLocation(), item.getThreadId());
if (item.getMasterSecret() != null) if (item.getMasterSecret() != null)
DecryptingQueue.scheduleDecryption(context, item.getMasterSecret(), messageId, item.getThreadId(), retrieved); DecryptingQueue.scheduleDecryption(context, item.getMasterSecret(), messageId, item.getThreadId(), retrieved);
} else { } else {
mmsDatabase.insertMessageReceived(retrieved, item.getContentLocation(), item.getThreadId()); mmsDatabase.insertMessageReceived(retrieved, item.getContentLocation(), item.getThreadId());
} }
mmsDatabase.delete(item.getMessageId()); mmsDatabase.delete(item.getMessageId());
// NotifyRespInd notifyResponse = new NotifyRespInd(PduHeaders.CURRENT_MMS_VERSION, item.getTransactionId(), PduHeaders.STATUS_RETRIEVED); // NotifyRespInd notifyResponse = new NotifyRespInd(PduHeaders.CURRENT_MMS_VERSION, item.getTransactionId(), PduHeaders.STATUS_RETRIEVED);
@ -92,7 +94,7 @@ public class MmsDownloader extends MmscProcessor {
if (this.pendingMessages.isEmpty()) if (this.pendingMessages.isEmpty())
finishConnectivity(); finishConnectivity();
} catch (IOException e) { } catch (IOException e) {
DatabaseFactory.getMmsDatabase(context).markDownloadState(item.getMessageId(), MmsDatabase.Types.DOWNLOAD_SOFT_FAILURE); DatabaseFactory.getMmsDatabase(context).markDownloadState(item.getMessageId(), MmsDatabase.Types.DOWNLOAD_SOFT_FAILURE);
toastHandler.makeToast("Error connecting to MMS provider..."); toastHandler.makeToast("Error connecting to MMS provider...");
@ -103,7 +105,7 @@ public class MmsDownloader extends MmscProcessor {
Log.w("MmsDownloader", e); Log.w("MmsDownloader", e);
} }
} }
protected void handleConnectivityChange() { protected void handleConnectivityChange() {
if (!isConnected()) { if (!isConnected()) {
if (!isConnectivityPossible() && !pendingMessages.isEmpty()) { if (!isConnectivityPossible() && !pendingMessages.isEmpty()) {
@ -112,36 +114,36 @@ public class MmsDownloader extends MmscProcessor {
Log.w("MmsDownloadService", "Unable to download MMS, please try again later."); Log.w("MmsDownloadService", "Unable to download MMS, please try again later.");
finishConnectivity(); finishConnectivity();
} }
return; return;
} }
if (!pendingMessages.isEmpty()) handleDownloadMmsContinued(pendingMessages.remove()); if (!pendingMessages.isEmpty()) handleDownloadMmsContinued(pendingMessages.remove());
else finishConnectivity(); else finishConnectivity();
} }
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)) {
DownloadItem item = new DownloadItem(intent.getLongExtra("message_id", -1), DownloadItem item = new DownloadItem(intent.getLongExtra("message_id", -1),
intent.getLongExtra("thread_id", -1), intent.getLongExtra("thread_id", -1),
intent.getStringExtra("content_location"), intent.getStringExtra("content_location"),
intent.getByteArrayExtra("transaction_id"), intent.getByteArrayExtra("transaction_id"),
masterSecret); masterSecret);
handleDownloadMms(item); handleDownloadMms(item);
} else if (intent.getAction().equals(SendReceiveService.DOWNLOAD_MMS_CONNECTIVITY_ACTION)) { } else if (intent.getAction().equals(SendReceiveService.DOWNLOAD_MMS_CONNECTIVITY_ACTION)) {
handleConnectivityChange(); handleConnectivityChange();
} }
} }
private class DownloadItem { private class DownloadItem {
private long threadId; private long threadId;
private long messageId; private long messageId;
private byte[] transactionId; private byte[] transactionId;
private String contentLocation; private String contentLocation;
private MasterSecret masterSecret; private MasterSecret masterSecret;
public DownloadItem(long messageId, long threadId, String contentLocation, byte[] transactionId, MasterSecret masterSecret) { public DownloadItem(long messageId, long threadId, String contentLocation, byte[] transactionId, MasterSecret masterSecret) {
this.threadId = threadId; this.threadId = threadId;
this.messageId = messageId; this.messageId = messageId;
@ -149,23 +151,23 @@ public class MmsDownloader extends MmscProcessor {
this.masterSecret = masterSecret; this.masterSecret = masterSecret;
this.transactionId = transactionId; this.transactionId = transactionId;
} }
public long getThreadId() { public long getThreadId() {
return threadId; return threadId;
} }
public long getMessageId() { public long getMessageId() {
return messageId; return messageId;
} }
public String getContentLocation() { public String getContentLocation() {
return contentLocation; return contentLocation;
} }
public byte[] getTransactionId() { public byte[] getTransactionId() {
return transactionId; return transactionId;
} }
public MasterSecret getMasterSecret() { public MasterSecret getMasterSecret() {
return masterSecret; return masterSecret;
} }

View File

@ -1,6 +1,6 @@
/** /**
* Copyright (C) 2011 Whisper Systems * Copyright (C) 2011 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
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
@ -10,12 +10,16 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.thoughtcrime.securesms.service; package org.thoughtcrime.securesms.service;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
@ -24,18 +28,15 @@ import ws.com.google.android.mms.pdu.GenericPdu;
import ws.com.google.android.mms.pdu.NotificationInd; import ws.com.google.android.mms.pdu.NotificationInd;
import ws.com.google.android.mms.pdu.PduHeaders; import ws.com.google.android.mms.pdu.PduHeaders;
import ws.com.google.android.mms.pdu.PduParser; import ws.com.google.android.mms.pdu.PduParser;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class MmsReceiver { public class MmsReceiver {
private final Context context; private final Context context;
public MmsReceiver(Context context) { public MmsReceiver(Context context) {
this.context = context; this.context = context;
} }
private void scheduleDownload(NotificationInd pdu, long messageId) { private void scheduleDownload(NotificationInd pdu, long messageId) {
Intent intent = new Intent(SendReceiveService.DOWNLOAD_MMS_ACTION, null, context, SendReceiveService.class); Intent intent = new Intent(SendReceiveService.DOWNLOAD_MMS_ACTION, null, context, SendReceiveService.class);
intent.putExtra("content_location", new String(pdu.getContentLocation())); intent.putExtra("content_location", new String(pdu.getContentLocation()));
@ -45,7 +46,7 @@ public class MmsReceiver {
context.startService(intent); context.startService(intent);
} }
public void process(MasterSecret masterSecret, Intent intent) { public void process(MasterSecret masterSecret, Intent intent) {
byte[] mmsData = intent.getByteArrayExtra("data"); byte[] mmsData = intent.getByteArrayExtra("data");
PduParser parser = new PduParser(mmsData); PduParser parser = new PduParser(mmsData);
@ -53,17 +54,17 @@ public class MmsReceiver {
if (pdu.getMessageType() == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) { if (pdu.getMessageType() == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) {
MmsDatabase database; MmsDatabase database;
if (masterSecret != null) if (masterSecret != null)
database = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret); database = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret);
else else
database = DatabaseFactory.getMmsDatabase(context); database = DatabaseFactory.getMmsDatabase(context);
long messageId = database.insertMessageReceived((NotificationInd)pdu); long messageId = database.insertMessageReceived((NotificationInd)pdu);
MessageNotifier.updateNotification(context, true); MessageNotifier.updateNotification(context, true);
scheduleDownload((NotificationInd)pdu, messageId); scheduleDownload((NotificationInd)pdu, messageId);
Log.w("MmsReceiverService", "Inserted received notification..."); Log.w("MmsReceiverService", "Inserted received notification...");
} }
} }
} }

View File

@ -1,6 +1,6 @@
/** /**
* Copyright (C) 2011 Whisper Systems * Copyright (C) 2011 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
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
@ -10,22 +10,22 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.thoughtcrime.securesms.service; package org.thoughtcrime.securesms.service;
import java.io.IOException; import android.content.Context;
import java.util.Arrays; import android.content.Intent;
import java.util.LinkedList; import android.telephony.TelephonyManager;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SessionCipher; 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.mms.MmsSendHelper;
import org.thoughtcrime.securesms.mms.PngTransport;
import org.thoughtcrime.securesms.mms.TextTransport; import org.thoughtcrime.securesms.mms.TextTransport;
import org.thoughtcrime.securesms.protocol.WirePrefix; import org.thoughtcrime.securesms.protocol.WirePrefix;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -33,7 +33,6 @@ import org.thoughtcrime.securesms.util.Hex;
import ws.com.google.android.mms.ContentType; 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.CharacterSets;
import ws.com.google.android.mms.pdu.EncodedStringValue; import ws.com.google.android.mms.pdu.EncodedStringValue;
import ws.com.google.android.mms.pdu.PduBody; import ws.com.google.android.mms.pdu.PduBody;
import ws.com.google.android.mms.pdu.PduComposer; import ws.com.google.android.mms.pdu.PduComposer;
@ -42,51 +41,51 @@ import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.PduPart; import ws.com.google.android.mms.pdu.PduPart;
import ws.com.google.android.mms.pdu.SendConf; 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 android.content.Context;
import android.content.Intent; import java.io.IOException;
import android.telephony.TelephonyManager; import java.util.Arrays;
import android.util.Log; import java.util.LinkedList;
public class MmsSender extends MmscProcessor { public class MmsSender extends MmscProcessor {
private final LinkedList<SendReq[]> pendingMessages = new LinkedList<SendReq[]>(); private final LinkedList<SendReq[]> pendingMessages = new LinkedList<SendReq[]>();
public MmsSender(Context context) { public MmsSender(Context context) {
super(context); super(context);
} }
public void process(MasterSecret masterSecret, Intent intent) { public void process(MasterSecret masterSecret, Intent intent) {
if (intent.getAction().equals(SendReceiveService.SEND_MMS_ACTION)) { if (intent.getAction().equals(SendReceiveService.SEND_MMS_ACTION)) {
long messageId = intent.getLongExtra("message_id", -1); long messageId = intent.getLongExtra("message_id", -1);
MmsDatabase database = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret); MmsDatabase database = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret);
try { try {
SendReq[] sendRequests; SendReq[] sendRequests;
if (messageId == -1) { if (messageId == -1) {
sendRequests = database.getOutgoingMessages(); sendRequests = database.getOutgoingMessages();
} else { } else {
sendRequests = new SendReq[1]; sendRequests = new SendReq[1];
sendRequests[0] = database.getSendRequest(messageId); sendRequests[0] = database.getSendRequest(messageId);
} }
if (sendRequests.length > 0) if (sendRequests.length > 0)
handleSendMms(sendRequests); handleSendMms(sendRequests);
} catch (MmsException me) { } catch (MmsException me) {
Log.w("MmsSender", me); Log.w("MmsSender", me);
if (messageId != -1) if (messageId != -1)
database.markAsSentFailed(messageId); database.markAsSentFailed(messageId);
} }
} else if (intent.getAction().equals(SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION)) { } else if (intent.getAction().equals(SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION)) {
handleConnectivityChange(masterSecret); handleConnectivityChange(masterSecret);
} }
} }
protected void handleConnectivityChange(MasterSecret masterSecret) { protected void handleConnectivityChange(MasterSecret masterSecret) {
if (!isConnected()) if (!isConnected())
return; return;
if (!pendingMessages.isEmpty()) handleSendMmsContinued(masterSecret, pendingMessages.remove()); if (!pendingMessages.isEmpty()) handleSendMmsContinued(masterSecret, pendingMessages.remove());
else finishConnectivity(); else finishConnectivity();
} }
@ -94,13 +93,13 @@ public class MmsSender extends MmscProcessor {
private void handleSendMms(SendReq[] sendRequests) { private void handleSendMms(SendReq[] sendRequests) {
if (!isConnectivityPossible()) { if (!isConnectivityPossible()) {
for (int i=0;i<sendRequests.length;i++) for (int i=0;i<sendRequests.length;i++)
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(sendRequests[i].getDatabaseMessageId()); DatabaseFactory.getMmsDatabase(context).markAsSentFailed(sendRequests[i].getDatabaseMessageId());
} else { } else {
pendingMessages.add(sendRequests); pendingMessages.add(sendRequests);
issueConnectivityRequest(); issueConnectivityRequest();
} }
} }
private boolean isInconsistentResponse(SendReq send, SendConf response) { private boolean isInconsistentResponse(SendReq send, SendConf response) {
Log.w("MmsSenderService", "Comparing: " + Hex.toString(send.getTransactionId())); Log.w("MmsSenderService", "Comparing: " + Hex.toString(send.getTransactionId()));
Log.w("MmsSenderService", "With: " + Hex.toString(response.getTransactionId())); Log.w("MmsSenderService", "With: " + Hex.toString(response.getTransactionId()));
@ -113,7 +112,7 @@ public class MmsSender extends MmscProcessor {
return cipher.encryptMessage(pduBytes); return cipher.encryptMessage(pduBytes);
} }
} }
private SendReq getEncryptedMms(MasterSecret masterSecret, SendReq pdu, long messageId) { private SendReq getEncryptedMms(MasterSecret masterSecret, SendReq pdu, long messageId) {
Log.w("MmsSender", "Sending Secure MMS."); Log.w("MmsSender", "Sending Secure MMS.");
EncodedStringValue[] encodedRecipient = pdu.getTo(); EncodedStringValue[] encodedRecipient = pdu.getTo();
@ -131,51 +130,51 @@ public class MmsSender extends MmscProcessor {
body.addPart(part); body.addPart(part);
pdu.setSubject(new EncodedStringValue(WirePrefix.calculateEncryptedMmsSubject())); pdu.setSubject(new EncodedStringValue(WirePrefix.calculateEncryptedMmsSubject()));
pdu.setBody(body); pdu.setBody(body);
return pdu; return pdu;
} }
private void sendMms(MmsDatabase db, SendReq pdu, String number, long messageId, boolean secure) { private void sendMms(MmsDatabase db, SendReq pdu, String number, long messageId, boolean secure) {
try { try {
if (number != null && number.trim().length() != 0) if (number != null && number.trim().length() != 0)
pdu.setFrom(new EncodedStringValue(number)); pdu.setFrom(new EncodedStringValue(number));
byte[] response = MmsSendHelper.sendMms(context, new PduComposer(context, pdu).make()); byte[] response = MmsSendHelper.sendMms(context, new PduComposer(context, pdu).make(), getApnInformation());
SendConf conf = (SendConf) new PduParser(response).parse(); SendConf conf = (SendConf) new PduParser(response).parse();
for (int i=0;i<pdu.getBody().getPartsNum();i++) { for (int i=0;i<pdu.getBody().getPartsNum();i++) {
Log.w("MmsSender", "Sent MMS part of content-type: " + new String(pdu.getBody().getPart(i).getContentType())); Log.w("MmsSender", "Sent MMS part of content-type: " + new String(pdu.getBody().getPart(i).getContentType()));
} }
if (conf == null) { if (conf == null) {
db.markAsSentFailed(messageId); db.markAsSentFailed(messageId);
Log.w("MmsSender", "No M-Send.conf received in response to send."); Log.w("MmsSender", "No M-Send.conf received in response to send.");
return; return;
} else if (conf.getResponseStatus() != PduHeaders.RESPONSE_STATUS_OK) { } else if (conf.getResponseStatus() != PduHeaders.RESPONSE_STATUS_OK) {
Log.w("MmsSender", "Got bad response: " + conf.getResponseStatus()); Log.w("MmsSender", "Got bad response: " + conf.getResponseStatus());
db.updateResponseStatus(messageId, conf.getResponseStatus()); db.updateResponseStatus(messageId, conf.getResponseStatus());
db.markAsSentFailed(messageId); db.markAsSentFailed(messageId);
return; return;
} else if (isInconsistentResponse(pdu, conf)) { } else if (isInconsistentResponse(pdu, conf)) {
db.markAsSentFailed(messageId); db.markAsSentFailed(messageId);
Log.w("MmsSender", "Got a response for the wrong transaction?"); Log.w("MmsSender", "Got a response for the wrong transaction?");
return; return;
} else { } else {
Log.w("MmsSender", "Successful send! " + messageId); Log.w("MmsSender", "Successful send! " + messageId);
if (!secure) if (!secure)
db.markAsSent(messageId, conf.getMessageId(), conf.getResponseStatus()); db.markAsSent(messageId, conf.getMessageId(), conf.getResponseStatus());
else else
db.markAsSecureSent(messageId, conf.getMessageId(), conf.getResponseStatus()); db.markAsSecureSent(messageId, conf.getMessageId(), conf.getResponseStatus());
} }
} catch (IOException ioe) { } catch (IOException ioe) {
Log.w("MmsSender", ioe); Log.w("MmsSender", ioe);
db.markAsSentFailed(messageId); db.markAsSentFailed(messageId);
} }
} }
private void handleSendMmsContinued(MasterSecret masterSecret, SendReq[] requests) { private void handleSendMmsContinued(MasterSecret masterSecret, SendReq[] requests) {
Log.w("MmsSenderService", "Handling MMS send continuation..."); Log.w("MmsSenderService", "Handling MMS send continuation...");
MmsDatabase db = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret); MmsDatabase db = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret);
String number = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number(); String number = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number();
@ -183,19 +182,19 @@ public class MmsSender extends MmscProcessor {
SendReq request = requests[i]; SendReq request = requests[i];
long messageId = request.getDatabaseMessageId(); long messageId = request.getDatabaseMessageId();
long messageBox = request.getDatabaseMessageBox(); long messageBox = request.getDatabaseMessageBox();
if (MmsDatabase.Types.isSecureMmsBox(messageBox)) if (MmsDatabase.Types.isSecureMmsBox(messageBox))
request = getEncryptedMms(masterSecret, request, messageId); request = getEncryptedMms(masterSecret, request, messageId);
sendMms(db, request, number, messageId, MmsDatabase.Types.isSecureMmsBox(messageBox)); sendMms(db, request, number, messageId, MmsDatabase.Types.isSecureMmsBox(messageBox));
} }
if (this.pendingMessages.isEmpty()) if (this.pendingMessages.isEmpty())
finishConnectivity(); finishConnectivity();
} }
@Override @Override
protected String getConnectivityAction() { protected String getConnectivityAction() {
return SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION; return SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION;
} }

View File

@ -1,6 +1,6 @@
/** /**
* Copyright (C) 2011 Whisper Systems * Copyright (C) 2011 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
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
@ -10,14 +10,12 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.thoughtcrime.securesms.service; package org.thoughtcrime.securesms.service;
import java.util.LinkedList;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -36,34 +34,38 @@ public abstract class MmscProcessor {
private ConnectivityManager connectivityManager; private ConnectivityManager connectivityManager;
private ConnectivityListener connectivityListener; private ConnectivityListener connectivityListener;
private WakeLock wakeLock; private WakeLock wakeLock;
protected final Context context; protected final Context context;
public MmscProcessor(Context context) { public MmscProcessor(Context context) {
this.context = context; this.context = context;
PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
this.connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); this.connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
this.wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MMS Connection"); this.wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MMS Connection");
this.wakeLock.setReferenceCounted(false); this.wakeLock.setReferenceCounted(false);
} }
protected String getApnInformation() {
return connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS).getExtraInfo();
}
protected boolean isConnected() { protected boolean isConnected() {
NetworkInfo info = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS); NetworkInfo info = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);
Log.w("MmsService", "NetworkInfo: " + info); Log.w("MmsService", "NetworkInfo: " + info);
if ((info == null) || (info.getType() != TYPE_MOBILE_MMS) || !info.isConnected()) if ((info == null) || (info.getType() != TYPE_MOBILE_MMS) || !info.isConnected())
return false; return false;
return true; return true;
} }
protected abstract String getConnectivityAction(); protected abstract String getConnectivityAction();
protected void issueConnectivityRequest() { protected void issueConnectivityRequest() {
int status = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS); int status = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS);
if (status == APN_ALREADY_ACTIVE) { if (status == APN_ALREADY_ACTIVE) {
issueConnectivityChange(); issueConnectivityChange();
} else if (connectivityListener == null) { } else if (connectivityListener == null) {
@ -74,7 +76,7 @@ public abstract class MmscProcessor {
wakeLock.acquire(); wakeLock.acquire();
} }
} }
protected boolean isConnectivityPossible() { protected boolean isConnectivityPossible() {
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS); NetworkInfo networkInfo = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);
Log.w("MmsService", "Got network info: " + networkInfo); Log.w("MmsService", "Got network info: " + networkInfo);
@ -84,25 +86,25 @@ public abstract class MmscProcessor {
protected void finishConnectivity() { protected void finishConnectivity() {
Log.w("MmsService", "Calling stop using network feature!"); Log.w("MmsService", "Calling stop using network feature!");
connectivityManager.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS); connectivityManager.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS);
if (connectivityListener != null) { if (connectivityListener != null) {
context.unregisterReceiver(connectivityListener); context.unregisterReceiver(connectivityListener);
connectivityListener = null; connectivityListener = null;
} }
if (this.wakeLock.isHeld()) if (this.wakeLock.isHeld())
this.wakeLock.release(); this.wakeLock.release();
} }
private void issueConnectivityChange() { private void issueConnectivityChange() {
Intent intent = new Intent(context, SendReceiveService.class); Intent intent = new Intent(context, SendReceiveService.class);
intent.setAction(getConnectivityAction()); intent.setAction(getConnectivityAction());
context.startService(intent); context.startService(intent);
} }
private class ConnectivityListener extends BroadcastReceiver { private class ConnectivityListener extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Log.w("MmsService", "Dispatching connectivity change..."); Log.w("MmsService", "Dispatching connectivity change...");
issueConnectivityChange(); issueConnectivityChange();
Log.w("MmsService", "Dispatched..."); Log.w("MmsService", "Dispatched...");