Use new MMS APIs in Lollipop onwards

Fixes #1937
Closes #2727
This commit is contained in:
Jake McGinty
2014-12-29 14:01:02 -08:00
committed by Moxie Marlinspike
parent dfda2f733c
commit 427c9a6b21
22 changed files with 841 additions and 460 deletions

View File

@@ -0,0 +1,143 @@
/**
* Copyright (C) 2015 Open 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.mms;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGetHC4;
import org.apache.http.client.methods.HttpUriRequest;
import java.io.IOException;
import java.util.Arrays;
import ws.com.google.android.mms.InvalidHeaderValueException;
import ws.com.google.android.mms.pdu.NotifyRespInd;
import ws.com.google.android.mms.pdu.PduComposer;
import ws.com.google.android.mms.pdu.PduHeaders;
import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.RetrieveConf;
@SuppressWarnings("deprecation")
public class IncomingLegacyMmsConnection extends LegacyMmsConnection implements IncomingMmsConnection {
private static final String TAG = IncomingLegacyMmsConnection.class.getSimpleName();
public IncomingLegacyMmsConnection(Context context) throws ApnUnavailableException {
super(context);
}
private HttpUriRequest constructRequest(Apn contentApn, boolean useProxy) throws IOException {
HttpGetHC4 request = new HttpGetHC4(contentApn.getMmsc());
for (Header header : getBaseHeaders()) {
request.addHeader(header);
}
if (useProxy) {
HttpHost proxy = new HttpHost(contentApn.getProxy(), contentApn.getPort());
request.setConfig(RequestConfig.custom().setProxy(proxy).build());
}
return request;
}
@Override
public RetrieveConf retrieve(String contentLocation, byte[] transactionId) throws MmsRadioException, ApnUnavailableException, IOException {
MmsRadio radio = MmsRadio.getInstance(context);
Apn contentApn = new Apn(contentLocation, apn.getProxy(), Integer.toString(apn.getPort()), apn.getUsername(), apn.getPassword());
if (isCdmaDevice()) {
Log.w(TAG, "Connecting directly...");
try {
return retrieve(contentApn, transactionId, false, false);
} catch (IOException | ApnUnavailableException e) {
Log.w(TAG, e);
}
}
Log.w(TAG, "Changing radio to MMS mode..");
radio.connect();
try {
Log.w(TAG, "Downloading in MMS mode with proxy...");
try {
return retrieve(contentApn, transactionId, true, true);
} catch (IOException | ApnUnavailableException e) {
Log.w(TAG, e);
}
Log.w(TAG, "Downloading in MMS mode without proxy...");
return retrieve(contentApn, transactionId, true, false);
} finally {
radio.disconnect();
}
}
public RetrieveConf retrieve(Apn contentApn, byte[] transactionId, boolean usingMmsRadio, boolean useProxyIfAvailable)
throws IOException, ApnUnavailableException
{
byte[] pdu = null;
final boolean useProxy = useProxyIfAvailable && contentApn.hasProxy();
final String targetHost = useProxy
? contentApn.getProxy()
: Uri.parse(contentApn.getMmsc()).getHost();
try {
if (checkRouteToHost(context, targetHost, usingMmsRadio)) {
Log.w(TAG, "got successful route to host " + targetHost);
pdu = execute(constructRequest(contentApn, useProxy));
}
} catch (IOException ioe) {
Log.w(TAG, ioe);
}
if (pdu == null) {
throw new IOException("Connection manager could not obtain route to host.");
}
RetrieveConf retrieved = (RetrieveConf)new PduParser(pdu).parse();
if (retrieved == null) {
Log.w(TAG, "Couldn't parse PDU, byte response: " + Arrays.toString(pdu));
Log.w(TAG, "Couldn't parse PDU, ASCII: " + new String(pdu));
throw new IOException("Bad retrieved PDU");
}
sendRetrievedAcknowledgement(transactionId, usingMmsRadio, useProxy);
return retrieved;
}
private void sendRetrievedAcknowledgement(byte[] transactionId,
boolean usingRadio,
boolean useProxy)
throws ApnUnavailableException
{
try {
NotifyRespInd notifyResponse = new NotifyRespInd(PduHeaders.CURRENT_MMS_VERSION,
transactionId,
PduHeaders.STATUS_RETRIEVED);
OutgoingLegacyMmsConnection connection = new OutgoingLegacyMmsConnection(context);
connection.sendNotificationReceived(new PduComposer(context, notifyResponse).make(), usingRadio, useProxy);
} catch (InvalidHeaderValueException | IOException e) {
Log.w(TAG, e);
}
}
}

View File

@@ -0,0 +1,87 @@
/**
* Copyright (C) 2015 Open 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.mms;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.telephony.SmsManager;
import android.util.Log;
import org.thoughtcrime.securesms.providers.MmsBodyProvider;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.Util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.RetrieveConf;
public class IncomingLollipopMmsConnection extends LollipopMmsConnection implements IncomingMmsConnection {
public static final String ACTION = IncomingLollipopMmsConnection.class.getCanonicalName() + "MMS_DOWNLOADED_ACTION";
private static final String TAG = IncomingLollipopMmsConnection.class.getSimpleName();
public IncomingLollipopMmsConnection(Context context) {
super(context, ACTION);
}
@TargetApi(VERSION_CODES.LOLLIPOP)
@Override
public synchronized void onResult(Context context, Intent intent) {
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP_MR1) {
Log.w(TAG, "HTTP status: " + intent.getIntExtra(SmsManager.EXTRA_MMS_HTTP_STATUS, -1));
}
Log.w(TAG, "code: " + getResultCode() + ", result string: " + getResultData());
}
@Override
@TargetApi(VERSION_CODES.LOLLIPOP)
public synchronized RetrieveConf retrieve(String contentLocation, byte[] transactionId) throws MmsException {
beginTransaction();
try {
MmsBodyProvider.Pointer pointer = MmsBodyProvider.makeTemporaryPointer(getContext());
Log.w(TAG, "downloading multimedia from " + contentLocation + " to " + pointer.getUri());
SmsManager.getDefault().downloadMultimediaMessage(getContext(),
contentLocation,
pointer.getUri(),
null,
getPendingIntent());
waitForResult();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Util.copy(pointer.getInputStream(), baos);
pointer.close();
Log.w(TAG, baos.size() + "-byte response: " + Hex.dump(baos.toByteArray()));
return (RetrieveConf) new PduParser(baos.toByteArray()).parse();
} catch (IOException | TimeoutException e) {
Log.w(TAG, e);
throw new MmsException(e);
} finally {
endTransaction();
}
}
}

View File

@@ -1,96 +1,10 @@
/**
* Copyright (C) 2014 Open 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.mms;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGetHC4;
import org.apache.http.client.methods.HttpUriRequest;
import java.io.IOException;
import java.util.Arrays;
import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.RetrieveConf;
public class IncomingMmsConnection extends MmsConnection {
private static final String TAG = IncomingMmsConnection.class.getSimpleName();
public IncomingMmsConnection(Context context, Apn apn) {
super(context, apn);
}
@Override
protected HttpUriRequest constructRequest(boolean useProxy) throws IOException {
HttpGetHC4 request = new HttpGetHC4(apn.getMmsc());
for (Header header : getBaseHeaders()) {
request.addHeader(header);
}
if (useProxy) {
HttpHost proxy = new HttpHost(apn.getProxy(), apn.getPort());
request.setConfig(RequestConfig.custom().setProxy(proxy).build());
}
return request;
}
public static boolean isConnectionPossible(Context context, String apn) {
try {
getApn(context, apn);
return true;
} catch (ApnUnavailableException e) {
return false;
}
}
public RetrieveConf retrieve(boolean usingMmsRadio, boolean useProxyIfAvailable)
throws IOException, ApnUnavailableException
{
byte[] pdu = null;
final boolean useProxy = useProxyIfAvailable && apn.hasProxy();
final String targetHost = useProxy
? apn.getProxy()
: Uri.parse(apn.getMmsc()).getHost();
try {
if (checkRouteToHost(context, targetHost, usingMmsRadio)) {
Log.w(TAG, "got successful route to host " + targetHost);
pdu = makeRequest(useProxy);
}
} catch (IOException ioe) {
Log.w(TAG, ioe);
}
if (pdu == null) {
throw new IOException("Connection manager could not obtain route to host.");
}
RetrieveConf retrieved = (RetrieveConf)new PduParser(pdu).parse();
if (retrieved == null) {
Log.w(TAG, "Couldn't parse PDU, byte response: " + Arrays.toString(pdu));
Log.w(TAG, "Couldn't parse PDU, ASCII: " + new String(pdu));
throw new IOException("Bad retrieved PDU");
}
return retrieved;
}
public interface IncomingMmsConnection {
RetrieveConf retrieve(String contentLocation, byte[] transactionId) throws MmsException, MmsRadioException, ApnUnavailableException, IOException;
}

View File

@@ -18,6 +18,7 @@ package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.net.ConnectivityManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -50,19 +51,19 @@ import java.net.URL;
import java.util.LinkedList;
import java.util.List;
public abstract class MmsConnection {
@SuppressWarnings("deprecation")
public abstract class LegacyMmsConnection {
private static final String TAG = "MmsCommunication";
protected final Context context;
protected final Apn apn;
protected MmsConnection(Context context, Apn apn) {
protected LegacyMmsConnection(Context context) throws ApnUnavailableException {
this.context = context;
this.apn = apn;
this.apn = getApn(context);
}
public static Apn getApn(Context context, String apnName) throws ApnUnavailableException {
Log.w(TAG, "Getting MMSC params for apn " + apnName);
public static Apn getApn(Context context) throws ApnUnavailableException {
try {
Optional<Apn> params = ApnDatabase.getInstance(context)
@@ -79,6 +80,10 @@ public abstract class MmsConnection {
}
}
protected boolean isCdmaDevice() {
return ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA;
}
protected static boolean checkRouteToHost(Context context, String host, boolean usingMmsRadio)
throws IOException
{
@@ -146,14 +151,12 @@ public abstract class MmsConnection {
.build();
}
protected byte[] makeRequest(boolean useProxy) throws IOException {
Log.w(TAG, "connecting to " + apn.getMmsc() + (useProxy ? " using proxy" : ""));
protected byte[] execute(HttpUriRequest request) throws IOException {
Log.w(TAG, "connecting to " + apn.getMmsc());
HttpUriRequest request;
CloseableHttpClient client = null;
CloseableHttpResponse response = null;
try {
request = constructRequest(useProxy);
client = constructHttpClient();
response = client.execute(request);
@@ -170,8 +173,6 @@ public abstract class MmsConnection {
throw new IOException("unhandled response code");
}
protected abstract HttpUriRequest constructRequest(boolean useProxy) throws IOException;
protected List<Header> getBaseHeaders() {
final String number = TelephonyUtil.getManager(context).getLine1Number();
return new LinkedList<Header>() {{

View File

@@ -0,0 +1,86 @@
/**
* Copyright (C) 2015 Open 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.mms;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
import org.thoughtcrime.securesms.util.Util;
import java.util.concurrent.TimeoutException;
public abstract class LollipopMmsConnection extends BroadcastReceiver {
private static final String TAG = LollipopMmsConnection.class.getSimpleName();
private final Context context;
private final String action;
private boolean resultAvailable;
public abstract void onResult(Context context, Intent intent);
protected LollipopMmsConnection(Context context, String action) {
super();
this.context = context;
this.action = action;
}
@Override
public synchronized void onReceive(Context context, Intent intent) {
Log.w(TAG, "onReceive()");
if (!action.equals(intent.getAction())) {
Log.w(TAG, "received broadcast with unexpected action " + intent.getAction());
return;
}
onResult(context, intent);
resultAvailable = true;
notifyAll();
}
protected void beginTransaction() {
getContext().getApplicationContext().registerReceiver(this, new IntentFilter(action));
}
protected void endTransaction() {
getContext().getApplicationContext().unregisterReceiver(this);
resultAvailable = false;
}
protected void waitForResult() throws TimeoutException {
long timeoutExpiration = System.currentTimeMillis() + 30000;
while (!resultAvailable) {
Util.wait(this, Math.max(1, timeoutExpiration - System.currentTimeMillis()));
if (System.currentTimeMillis() >= timeoutExpiration) {
throw new TimeoutException("timeout when waiting for MMS");
}
}
}
protected PendingIntent getPendingIntent() {
return PendingIntent.getBroadcast(getContext(), 1, new Intent(action), PendingIntent.FLAG_ONE_SHOT);
}
protected Context getContext() {
return context;
}
}

View File

@@ -43,10 +43,6 @@ public class MmsRadio {
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();

View File

@@ -0,0 +1,159 @@
/**
* Copyright (C) 2015 Open 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.mms;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.util.Log;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPostHC4;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ByteArrayEntityHC4;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import java.io.IOException;
import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.SendConf;
@SuppressWarnings("deprecation")
public class OutgoingLegacyMmsConnection extends LegacyMmsConnection implements OutgoingMmsConnection {
private final static String TAG = OutgoingLegacyMmsConnection.class.getSimpleName();
public OutgoingLegacyMmsConnection(Context context) throws ApnUnavailableException {
super(context);
}
private HttpUriRequest constructRequest(byte[] pduBytes, boolean useProxy)
throws IOException
{
try {
HttpPostHC4 request = new HttpPostHC4(apn.getMmsc());
for (Header header : getBaseHeaders()) {
request.addHeader(header);
}
request.setEntity(new ByteArrayEntityHC4(pduBytes));
if (useProxy) {
HttpHost proxy = new HttpHost(apn.getProxy(), apn.getPort());
request.setConfig(RequestConfig.custom().setProxy(proxy).build());
}
return request;
} catch (IllegalArgumentException iae) {
throw new IOException(iae);
}
}
public void sendNotificationReceived(byte[] pduBytes, boolean usingMmsRadio, boolean useProxyIfAvailable)
throws IOException
{
sendBytes(pduBytes, usingMmsRadio, useProxyIfAvailable);
}
@Override
public SendConf send(byte[] pduBytes) throws UndeliverableMessageException {
try {
MmsRadio radio = MmsRadio.getInstance(context);
if (isCdmaDevice()) {
Log.w(TAG, "Sending MMS directly without radio change...");
try {
return send(pduBytes, false, false);
} catch (IOException e) {
Log.w(TAG, e);
}
}
Log.w(TAG, "Sending MMS with radio change and proxy...");
radio.connect();
try {
try {
return send(pduBytes, true, true);
} catch (IOException e) {
Log.w(TAG, e);
}
Log.w(TAG, "Sending MMS with radio change and without proxy...");
try {
return send(pduBytes, true, false);
} catch (IOException ioe) {
Log.w(TAG, ioe);
throw new UndeliverableMessageException(ioe);
}
} finally {
radio.disconnect();
}
} catch (MmsRadioException e) {
Log.w(TAG, e);
throw new UndeliverableMessageException(e);
}
}
private SendConf send(byte[] pduBytes, boolean useMmsRadio, boolean useProxyIfAvailable) throws IOException {
byte[] response = sendBytes(pduBytes, useMmsRadio, useProxyIfAvailable);
return (SendConf) new PduParser(response).parse();
}
private byte[] sendBytes(byte[] pduBytes, boolean useMmsRadio, boolean useProxyIfAvailable) throws IOException {
final boolean useProxy = useProxyIfAvailable && apn.hasProxy();
final String targetHost = useProxy
? apn.getProxy()
: Uri.parse(apn.getMmsc()).getHost();
Log.w(TAG, "Sending MMS of length: " + pduBytes.length
+ (useMmsRadio ? ", using mms radio" : "")
+ (useProxy ? ", using proxy" : ""));
try {
if (checkRouteToHost(context, targetHost, useMmsRadio)) {
Log.w(TAG, "got successful route to host " + targetHost);
byte[] response = execute(constructRequest(pduBytes, useProxy));
if (response != null) return response;
}
} catch (IOException ioe) {
Log.w(TAG, ioe);
}
throw new IOException("Connection manager could not obtain route to host.");
}
public static boolean isConnectionPossible(Context context) {
try {
ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(MmsRadio.TYPE_MOBILE_MMS);
if (networkInfo == null) {
Log.w(TAG, "MMS network info was null, unsupported by this device");
return false;
}
getApn(context);
return true;
} catch (ApnUnavailableException e) {
Log.w(TAG, e);
return false;
}
}
}

View File

@@ -0,0 +1,85 @@
/**
* Copyright (C) 2015 Open 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.mms;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.telephony.SmsManager;
import android.util.Log;
import org.thoughtcrime.securesms.providers.MmsBodyProvider;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.Util;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.SendConf;
public class OutgoingLollipopMmsConnection extends LollipopMmsConnection implements OutgoingMmsConnection {
private static final String TAG = OutgoingLollipopMmsConnection.class.getSimpleName();
private static final String ACTION = OutgoingLollipopMmsConnection.class.getCanonicalName() + "MMS_SENT_ACTION";
private byte[] response;
public OutgoingLollipopMmsConnection(Context context) {
super(context, ACTION);
}
@TargetApi(VERSION_CODES.LOLLIPOP_MR1)
@Override
public synchronized void onResult(Context context, Intent intent) {
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP_MR1) {
Log.w(TAG, "HTTP status: " + intent.getIntExtra(SmsManager.EXTRA_MMS_HTTP_STATUS, -1));
}
response = intent.getByteArrayExtra(SmsManager.EXTRA_MMS_DATA);
}
@Override
@TargetApi(VERSION_CODES.LOLLIPOP)
public synchronized SendConf send(byte[] pduBytes) throws UndeliverableMessageException {
beginTransaction();
try {
MmsBodyProvider.Pointer pointer = MmsBodyProvider.makeTemporaryPointer(getContext());
Util.copy(new ByteArrayInputStream(pduBytes), pointer.getOutputStream());
SmsManager.getDefault().sendMultimediaMessage(getContext(),
pointer.getUri(),
null,
null,
getPendingIntent());
waitForResult();
Log.w(TAG, "MMS broadcast received and processed.");
pointer.close();
return (SendConf) new PduParser(response).parse();
} catch (IOException | TimeoutException e) {
throw new UndeliverableMessageException(e);
} finally {
endTransaction();
}
}
}

View File

@@ -1,121 +1,9 @@
/**
* Copyright (C) 2014 Open 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.mms;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPostHC4;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ByteArrayEntityHC4;
import org.thoughtcrime.securesms.util.TelephonyUtil;
import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.SendConf;
public class OutgoingMmsConnection extends MmsConnection {
private final static String TAG = OutgoingMmsConnection.class.getSimpleName();
private final byte[] mms;
public OutgoingMmsConnection(Context context, String apnName, byte[] mms) throws ApnUnavailableException {
super(context, getApn(context, apnName));
this.mms = mms;
}
@Override
protected HttpUriRequest constructRequest(boolean useProxy)
throws IOException
{
try {
HttpPostHC4 request = new HttpPostHC4(apn.getMmsc());
for (Header header : getBaseHeaders()) {
request.addHeader(header);
}
request.setEntity(new ByteArrayEntityHC4(mms));
if (useProxy) {
HttpHost proxy = new HttpHost(apn.getProxy(), apn.getPort());
request.setConfig(RequestConfig.custom().setProxy(proxy).build());
}
return request;
} catch (IllegalArgumentException iae) {
throw new IOException(iae);
}
}
public void sendNotificationReceived(boolean usingMmsRadio, boolean useProxyIfAvailable)
throws IOException
{
sendBytes(usingMmsRadio, useProxyIfAvailable);
}
public SendConf send(boolean useMmsRadio, boolean useProxyIfAvailable) throws IOException {
byte[] response = sendBytes(useMmsRadio, useProxyIfAvailable);
return (SendConf) new PduParser(response).parse();
}
private byte[] sendBytes(boolean useMmsRadio, boolean useProxyIfAvailable) throws IOException {
final boolean useProxy = useProxyIfAvailable && apn.hasProxy();
final String targetHost = useProxy
? apn.getProxy()
: Uri.parse(apn.getMmsc()).getHost();
Log.w(TAG, "Sending MMS of length: " + mms.length
+ (useMmsRadio ? ", using mms radio" : "")
+ (useProxy ? ", using proxy" : ""));
try {
if (checkRouteToHost(context, targetHost, useMmsRadio)) {
Log.w(TAG, "got successful route to host " + targetHost);
byte[] response = makeRequest(useProxy);
if (response != null) return response;
}
} catch (IOException ioe) {
Log.w(TAG, ioe);
}
throw new IOException("Connection manager could not obtain route to host.");
}
public static boolean isConnectionPossible(Context context) {
try {
ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(MmsRadio.TYPE_MOBILE_MMS);
if (networkInfo == null) {
Log.w(TAG, "MMS network info was null, unsupported by this device");
return false;
}
getApn(context, networkInfo.getExtraInfo());
return true;
} catch (ApnUnavailableException e) {
Log.w(TAG, e);
return false;
}
}
public interface OutgoingMmsConnection {
SendConf send(byte[] pduBytes) throws UndeliverableMessageException;
}