diff --git a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java index 81d3ac9252..f5d9b397a2 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsDownloadJob.java @@ -12,6 +12,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.mms.ApnUnavailableException; +import org.thoughtcrime.securesms.mms.CompatMmsConnection; import org.thoughtcrime.securesms.mms.IncomingLollipopMmsConnection; import org.thoughtcrime.securesms.mms.IncomingMediaMessage; import org.thoughtcrime.securesms.mms.IncomingLegacyMmsConnection; @@ -89,7 +90,7 @@ public class MmsDownloadJob extends MasterSecretJob { Log.w(TAG, "Downloading mms at " + Uri.parse(contentLocation).getHost()); - RetrieveConf retrieveConf = getMmsConnection(context).retrieve(contentLocation, transactionId); + RetrieveConf retrieveConf = new CompatMmsConnection(context).retrieve(contentLocation, transactionId); if (retrieveConf == null) { throw new MmsException("RetrieveConf was null"); } @@ -123,16 +124,6 @@ public class MmsDownloadJob extends MasterSecretJob { } } - private IncomingMmsConnection getMmsConnection(Context context) - throws ApnUnavailableException - { - if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - return new IncomingLollipopMmsConnection(context); - } else { - return new IncomingLegacyMmsConnection(context); - } - } - @Override public void onCanceled() { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); diff --git a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java index 332c6e56f6..ff9e962eb0 100644 --- a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java @@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.mms.ApnUnavailableException; +import org.thoughtcrime.securesms.mms.CompatMmsConnection; import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.MmsSendResult; import org.thoughtcrime.securesms.mms.OutgoingLegacyMmsConnection; @@ -68,11 +69,11 @@ public class MmsSendJob extends SendJob { validateDestinations(message); final byte[] pduBytes = getPduBytes(masterSecret, message); - final SendConf sendConf = getMmsConnection(context).send(pduBytes); + final SendConf sendConf = new CompatMmsConnection(context).send(pduBytes); final MmsSendResult result = getSendResult(sendConf, message); database.markAsSent(messageId, result.getMessageId(), result.getResponseStatus()); - } catch (UndeliverableMessageException | IOException | ApnUnavailableException e) { + } catch (UndeliverableMessageException | IOException e) { Log.w(TAG, e); database.markAsSentFailed(messageId); notifyMediaMessageDeliveryFailed(context, messageId); @@ -94,16 +95,6 @@ public class MmsSendJob extends SendJob { notifyMediaMessageDeliveryFailed(context, messageId); } - private OutgoingMmsConnection getMmsConnection(Context context) - throws ApnUnavailableException - { - if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - return new OutgoingLollipopMmsConnection(context); - } else { - return new OutgoingLegacyMmsConnection(context); - } - } - private byte[] getPduBytes(MasterSecret masterSecret, SendReq message) throws IOException, UndeliverableMessageException, InsecureFallbackApprovalException { diff --git a/src/org/thoughtcrime/securesms/mms/CompatMmsConnection.java b/src/org/thoughtcrime/securesms/mms/CompatMmsConnection.java new file mode 100644 index 0000000000..4bd3aae014 --- /dev/null +++ b/src/org/thoughtcrime/securesms/mms/CompatMmsConnection.java @@ -0,0 +1,66 @@ +package org.thoughtcrime.securesms.mms; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import org.thoughtcrime.securesms.transport.UndeliverableMessageException; + +import java.io.IOException; +import java.net.Inet6Address; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + +import ws.com.google.android.mms.MmsException; +import ws.com.google.android.mms.pdu.RetrieveConf; +import ws.com.google.android.mms.pdu.SendConf; + +public class CompatMmsConnection implements OutgoingMmsConnection, IncomingMmsConnection { + private static final String TAG = CompatMmsConnection.class.getSimpleName(); + + private Context context; + + public CompatMmsConnection(Context context) { + this.context = context; + } + + @Nullable @Override public SendConf send(@NonNull byte[] pduBytes) + throws UndeliverableMessageException + { + try { + Log.w(TAG, "Sending via legacy connection"); + return new OutgoingLegacyMmsConnection(context).send(pduBytes); + } catch (UndeliverableMessageException | ApnUnavailableException e) { + if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + Log.w(TAG, "Falling back to try sending via Lollipop API"); + return new OutgoingLollipopMmsConnection(context).send(pduBytes); + } else { + throw new UndeliverableMessageException(e); + } + } + } + + @Nullable @Override public RetrieveConf retrieve(@NonNull String contentLocation, + byte[] transactionId) + throws MmsException, MmsRadioException, ApnUnavailableException, IOException + { + try { + Log.w(TAG, "Receiving via legacy connection"); + return new IncomingLegacyMmsConnection(context).retrieve(contentLocation, transactionId); + } catch (MmsRadioException | IOException | ApnUnavailableException e) { + if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + Log.w(TAG, "Falling back to try receiving via Lollipop API"); + return new IncomingLollipopMmsConnection(context).retrieve(contentLocation, transactionId); + } else { + throw e; + } + } + } +} diff --git a/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java b/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java index 505e6f03bb..ddee0a2fa9 100644 --- a/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java +++ b/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java @@ -104,13 +104,9 @@ public class IncomingLegacyMmsConnection extends LegacyMmsConnection implements 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 (checkRouteToHost(context, targetHost, usingMmsRadio)) { + Log.w(TAG, "got successful route to host " + targetHost); + pdu = execute(constructRequest(contentApn, useProxy)); } if (pdu == null) { diff --git a/src/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java b/src/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java index a413d822a4..ef3bc6ea2f 100644 --- a/src/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java +++ b/src/org/thoughtcrime/securesms/mms/LegacyMmsConnection.java @@ -18,6 +18,7 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -47,6 +48,8 @@ import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.InetAddress; import java.net.URL; import java.util.LinkedList; @@ -57,7 +60,7 @@ public abstract class LegacyMmsConnection { public static final String USER_AGENT = "Android-Mms/2.0"; - private static final String TAG = "MmsCommunication"; + private static final String TAG = LegacyMmsConnection.class.getSimpleName(); protected final Context context; protected final Apn apn; @@ -88,6 +91,7 @@ public abstract class LegacyMmsConnection { return ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA; } + @SuppressWarnings("TryWithIdenticalCatches") protected static boolean checkRouteToHost(Context context, String host, boolean usingMmsRadio) throws IOException { @@ -105,16 +109,29 @@ public abstract class LegacyMmsConnection { } byte[] ipAddressBytes = inetAddress.getAddress(); - if (ipAddressBytes == null || ipAddressBytes.length != 4) { - Log.w(TAG, "returning vacuous success since android.net package doesn't support IPv6"); + if (ipAddressBytes == null) { + Log.w(TAG, "resolved IP address bytes are null, returning true to attempt a connection anyway."); return true; } Log.w(TAG, "Checking route to address: " + host + ", " + inetAddress.getHostAddress()); ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); - int ipAddress = Conversions.byteArrayToIntLittleEndian(ipAddressBytes, 0); - boolean routeToHostObtained = manager.requestRouteToHost(MmsRadio.TYPE_MOBILE_MMS, ipAddress); - Log.w(TAG, "requestRouteToHost result: " + routeToHostObtained); + try { + final Method requestRouteMethod = manager.getClass().getMethod("requestRouteToHostAddress", Integer.TYPE, InetAddress.class); + final boolean routeToHostObtained = (Boolean) requestRouteMethod.invoke(manager, MmsRadio.TYPE_MOBILE_MMS, inetAddress); + Log.w(TAG, "requestRouteToHostAddress(" + inetAddress + ") -> " + routeToHostObtained); + return routeToHostObtained; + } catch (NoSuchMethodException nsme) { + Log.w(TAG, nsme); + } catch (IllegalAccessException iae) { + Log.w(TAG, iae); + } catch (InvocationTargetException ite) { + Log.w(TAG, ite); + } + + final int ipAddress = Conversions.byteArrayToIntLittleEndian(ipAddressBytes, 0); + final boolean routeToHostObtained = manager.requestRouteToHost(MmsRadio.TYPE_MOBILE_MMS, ipAddress); + Log.w(TAG, "requestRouteToHost(" + ipAddress + ") -> " + routeToHostObtained); return routeToHostObtained; } @@ -188,8 +205,6 @@ public abstract class LegacyMmsConnection { add(new BasicHeader("X-MDN", number)); } }}; - - } public static class Apn { diff --git a/src/org/thoughtcrime/securesms/mms/LollipopMmsConnection.java b/src/org/thoughtcrime/securesms/mms/LollipopMmsConnection.java index 0e5c83edd2..d38b6ae142 100644 --- a/src/org/thoughtcrime/securesms/mms/LollipopMmsConnection.java +++ b/src/org/thoughtcrime/securesms/mms/LollipopMmsConnection.java @@ -67,7 +67,7 @@ public abstract class LollipopMmsConnection extends BroadcastReceiver { } protected void waitForResult() throws TimeoutException { - long timeoutExpiration = System.currentTimeMillis() + 30000; + long timeoutExpiration = System.currentTimeMillis() + 60000; while (!resultAvailable) { Util.wait(this, Math.max(1, timeoutExpiration - System.currentTimeMillis())); if (System.currentTimeMillis() >= timeoutExpiration) {