From 28e14f47cf267bf94892547e15b16352bf66dc5f Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Fri, 8 Aug 2014 13:57:25 -0700 Subject: [PATCH] use URLConnection for MMS because it's better // FREEBIE --- .../securesms/mms/MmsCommunication.java | 91 ++++++++++--------- .../securesms/mms/MmsDownloadHelper.java | 55 ++++++----- .../securesms/mms/MmsSendHelper.java | 73 ++++++++------- 3 files changed, 118 insertions(+), 101 deletions(-) diff --git a/src/org/thoughtcrime/securesms/mms/MmsCommunication.java b/src/org/thoughtcrime/securesms/mms/MmsCommunication.java index fb3c9f2001..2c94f60a2b 100644 --- a/src/org/thoughtcrime/securesms/mms/MmsCommunication.java +++ b/src/org/thoughtcrime/securesms/mms/MmsCommunication.java @@ -20,28 +20,27 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteException; import android.net.ConnectivityManager; -import android.net.Uri; -import android.net.http.AndroidHttpClient; import android.util.Log; -import org.apache.http.HttpEntity; -import org.apache.http.HttpHost; -import org.apache.http.conn.params.ConnRouteParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.textsecure.util.Conversions; import org.whispersystems.textsecure.util.Util; -import java.io.DataInputStream; +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URL; import java.util.ArrayList; import java.util.List; public class MmsCommunication { + private static final String TAG = "MmsCommunication"; protected static MmsConnectionParameters getLocallyConfiguredMmsConnectionParameters(Context context) throws ApnUnavailableException @@ -83,30 +82,40 @@ public class MmsCommunication { protected static MmsConnectionParameters getMmsConnectionParameters(Context context, String apn) throws ApnUnavailableException { + Log.w(TAG, "Getting MMSC params for apn " + apn); Cursor cursor = null; try { cursor = DatabaseFactory.getMmsDatabase(context).getCarrierMmsInformation(apn); - if (cursor == null || !cursor.moveToFirst()) - return getLocalMmsConnectionParameters(context); + if (cursor == null || !cursor.moveToFirst()) { + MmsConnectionParameters parameters = getLocalMmsConnectionParameters(context); + Log.w(TAG, "Android didn't have a result, using MMSC parameters: " + parameters.get().get(0).getMmsc() + " // " + parameters.get().get(0).getProxy() + " // " + parameters.get().get(0).getPort()); + return parameters; + } do { String mmsc = cursor.getString(cursor.getColumnIndexOrThrow("mmsc")); String proxy = cursor.getString(cursor.getColumnIndexOrThrow("mmsproxy")); String port = cursor.getString(cursor.getColumnIndexOrThrow("mmsport")); - if (!Util.isEmpty(mmsc)) + if (!Util.isEmpty(mmsc)) { + Log.w(TAG, "Using Android-provided MMSC parameters: " + mmsc + " // " + proxy + " // " + port); return new MmsConnectionParameters(mmsc, proxy, port); + } } while (cursor.moveToNext()); + MmsConnectionParameters parameters = getLocalMmsConnectionParameters(context); + Log.w(TAG, "Android didn't have a result, using MMSC parameters: " + parameters.get().get(0).getMmsc() + " // " + parameters.get().get(0).getProxy() + " // " + parameters.get().get(0).getPort()); + return parameters; } catch (SQLiteException sqe) { - Log.w("MmsCommunication", sqe); + Log.w(TAG, sqe); } catch (SecurityException se) { - Log.i("MmsCommunication", "Couldn't write APN settings, expected. msg: " + se.getMessage()); + Log.w(TAG, "Android won't let us query the APN database."); + return getLocalMmsConnectionParameters(context); } catch (IllegalArgumentException iae) { - Log.w("MmsCommunication", iae); + Log.w(TAG, iae); } finally { if (cursor != null) cursor.close(); @@ -127,7 +136,7 @@ public class MmsCommunication { return true; } - Log.w("MmsCommunication", "Checking route to address: " + host + " , " + inetAddress.getHostAddress()); + Log.w(TAG, "Checking route to address: " + host + " , " + inetAddress.getHostAddress()); byte[] ipAddressBytes = inetAddress.getAddress(); @@ -140,33 +149,33 @@ public class MmsCommunication { return true; } - protected static AndroidHttpClient constructHttpClient(Context context, String proxy, int port) { - AndroidHttpClient client = AndroidHttpClient.newInstance("Android-Mms/2.0", context); - HttpParams params = client.getParams(); - HttpProtocolParams.setContentCharset(params, "UTF-8"); - HttpConnectionParams.setSoTimeout(params, 20 * 1000); - - if (proxy != null) { - ConnRouteParams.setDefaultProxy(params, new HttpHost(proxy, port)); - } - - return client; + protected static byte[] parseResponse(InputStream is) throws IOException { + InputStream in = new BufferedInputStream(is); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Util.copy(in, baos); + Log.w(TAG, "received full server response, " + baos.size() + " bytes"); + return baos.toByteArray(); } - protected static byte[] parseResponse(HttpEntity entity) throws IOException { - if (entity == null || entity.getContentLength() == 0) - return null; - - if (entity.getContentLength() < 0) - throw new IOException("Unknown content length!"); - - byte[] responseBytes = new byte[(int)entity.getContentLength()]; - DataInputStream dataInputStream = new DataInputStream(entity.getContent()); - dataInputStream.readFully(responseBytes); - dataInputStream.close(); - - entity.consumeContent(); - return responseBytes; + protected static HttpURLConnection constructHttpClient(String urlString, String proxy, int port) + throws IOException + { + HttpURLConnection urlConnection; + URL url = new URL(urlString); + if (proxy != null) { + Log.w(TAG, "constructing http client using a proxy"); + Proxy proxyRoute = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxy, port)); + urlConnection = (HttpURLConnection) url.openConnection(proxyRoute); + } else { + Log.w(TAG, "constructing http client without proxy"); + urlConnection = (HttpURLConnection) url.openConnection(); + } + urlConnection.setConnectTimeout(20*1000); + urlConnection.setReadTimeout(20*1000); + urlConnection.setUseCaches(false); + urlConnection.setRequestProperty("User-Agent", "Android-Mms/2.0"); + urlConnection.setRequestProperty("Accept-Charset", "UTF-8"); + return urlConnection; } protected static class MmsConnectionParameters { diff --git a/src/org/thoughtcrime/securesms/mms/MmsDownloadHelper.java b/src/org/thoughtcrime/securesms/mms/MmsDownloadHelper.java index aacba4c7b6..cde018d8c8 100644 --- a/src/org/thoughtcrime/securesms/mms/MmsDownloadHelper.java +++ b/src/org/thoughtcrime/securesms/mms/MmsDownloadHelper.java @@ -18,50 +18,49 @@ package org.thoughtcrime.securesms.mms; import android.content.Context; import android.net.Uri; -import android.net.http.AndroidHttpClient; import android.util.Log; -import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; -import org.apache.http.client.methods.HttpGet; - import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; +import java.io.InputStream; +import java.net.HttpURLConnection; import ws.com.google.android.mms.pdu.PduParser; import ws.com.google.android.mms.pdu.RetrieveConf; public class MmsDownloadHelper extends MmsCommunication { + private static final String TAG = MmsDownloadHelper.class.getSimpleName(); - private static byte[] makeRequest(Context context, String url, String proxy, int proxyPort) + private static byte[] makeRequest(String url, String proxy, int proxyPort) throws IOException { - AndroidHttpClient client = null; + HttpURLConnection client = null; try { - client = constructHttpClient(context, proxy, proxyPort); - URI targetUrl = new URI(url.trim()); - HttpHost target = new HttpHost(targetUrl.getHost(), targetUrl.getPort(), HttpHost.DEFAULT_SCHEME_NAME); - HttpGet request = new HttpGet(url.trim()); + client = constructHttpClient(url, proxy, proxyPort); - request.setParams(client.getParams()); - request.addHeader("Accept", "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic"); + client.setDoInput(true); + client.setRequestMethod("GET"); + client.setRequestProperty("Accept", "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic"); - HttpResponse response = client.execute(target, request); - StatusLine status = response.getStatusLine(); + Log.w(TAG, "connecting to " + url); + client.connect(); - if (status.getStatusCode() != 200) - throw new IOException("Non-successful HTTP response: " + status.getReasonPhrase()); + final InputStream is; + try { + is = client.getInputStream(); + } catch (IOException ioe) { + Log.w(TAG, "failed with response code " + client.getResponseCode() + " / " + client.getResponseMessage()); + throw ioe; + } - return parseResponse(response.getEntity()); - } catch (URISyntaxException use) { - Log.w("MmsDownloadHelper", use); - throw new IOException("Couldn't parse URI"); + Log.w(TAG, "response code was " + client.getResponseCode() + "/" + client.getResponseMessage()); + if (client.getResponseCode() != 200) { + throw new IOException("non-200 response"); + } + + return parseResponse(is); } finally { - if (client != null) - client.close(); + if (client != null) client.disconnect(); } } @@ -85,11 +84,11 @@ public class MmsDownloadHelper extends MmsCommunication { try { if (proxyIfPossible && param.hasProxy()) { if (checkRouteToHost(context, param.getProxy(), usingMmsRadio)) { - pdu = makeRequest(context, url, param.getProxy(), param.getPort()); + pdu = makeRequest(url, param.getProxy(), param.getPort()); } } else { if (checkRouteToHost(context, Uri.parse(url).getHost(), usingMmsRadio)) { - pdu = makeRequest(context, url, null, -1); + pdu = makeRequest(url, null, -1); } } diff --git a/src/org/thoughtcrime/securesms/mms/MmsSendHelper.java b/src/org/thoughtcrime/securesms/mms/MmsSendHelper.java index 28ed2932c1..75f0976d38 100644 --- a/src/org/thoughtcrime/securesms/mms/MmsSendHelper.java +++ b/src/org/thoughtcrime/securesms/mms/MmsSendHelper.java @@ -20,17 +20,15 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; -import android.net.http.AndroidHttpClient; import android.util.Log; -import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ByteArrayEntity; import org.whispersystems.textsecure.util.Util; +import java.io.BufferedOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; @@ -40,42 +38,53 @@ import ws.com.google.android.mms.pdu.SendConf; public class MmsSendHelper extends MmsCommunication { private final static String TAG = MmsSendHelper.class.getSimpleName(); - private static byte[] makePost(Context context, String url, String proxy, int proxyPort, byte[] mms) + private static byte[] makePost(String url, String proxy, int proxyPort, byte[] mms) throws IOException { - AndroidHttpClient client = null; + if (mms == null) return null; + + HttpURLConnection client = null; try { - Log.w(TAG, "Sending MMS1 of length: " + (mms != null ? mms.length : "null")); - client = constructHttpClient(context, proxy, proxyPort); - URI targetUrl = new URI(url); + client = constructHttpClient(url, proxy, proxyPort); + client.setFixedLengthStreamingMode(mms.length); + client.setDoInput(true); + client.setDoOutput(true); + client.setRequestMethod("POST"); + URI targetUrl = new URI(url); if (Util.isEmpty(targetUrl.getHost())) throw new IOException("Invalid target host: " + targetUrl.getHost() + " , " + targetUrl); - HttpHost target = new HttpHost(targetUrl.getHost(), targetUrl.getPort(), HttpHost.DEFAULT_SCHEME_NAME); - HttpPost request = new HttpPost(url); - ByteArrayEntity entity = new ByteArrayEntity(mms); + client.setRequestProperty("Content-Type", "application/vnd.wap.mms-message"); + client.setRequestProperty("Accept", "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic"); + client.setRequestProperty("x-wap-profile", "http://www.google.com/oha/rdf/ua-profile-kila.xml"); - entity.setContentType("application/vnd.wap.mms-message"); - - request.setEntity(entity); - request.setParams(client.getParams()); - request.addHeader("Accept", "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic"); - request.addHeader("x-wap-profile", "http://www.google.com/oha/rdf/ua-profile-kila.xml"); - HttpResponse response = client.execute(target, request); - StatusLine status = response.getStatusLine(); - - if (status.getStatusCode() != 200) - throw new IOException("Non-successful HTTP response: " + status.getReasonPhrase()); - - return parseResponse(response.getEntity()); + Log.w(TAG, "connecting to " + targetUrl); + client.connect(); + Log.w(TAG, "writing mms payload, " + mms.length + " bytes"); + OutputStream out = new BufferedOutputStream(client.getOutputStream()); + out.write(mms); + out.flush(); + out.close(); + Log.w(TAG, "payload sent"); + final InputStream is; + try { + is = client.getInputStream(); + } catch (IOException ioe) { + Log.w(TAG, "failed with response code " + client.getResponseCode() + " / " + client.getResponseMessage()); + throw ioe; + } + Log.w(TAG, "response code was " + client.getResponseCode() + "/" + client.getResponseMessage()); + if (client.getResponseCode() != 200) { + throw new IOException("non-200 response"); + } + return parseResponse(is); } catch (URISyntaxException use) { Log.w(TAG, use); throw new IOException("Couldn't parse URI."); } finally { - if (client != null) - client.close(); + if (client != null) client.disconnect(); } } @@ -98,7 +107,7 @@ public class MmsSendHelper extends MmsCommunication { boolean usingMmsRadio, boolean useProxyIfAvailable) throws IOException { - Log.w(TAG, "Sending MMS of length: " + mms.length); + Log.w(TAG, "Sending MMS of length: " + mms.length + "." + (usingMmsRadio ? " using mms radio" : "")); try { MmsConnectionParameters parameters = getMmsConnectionParameters(context, apn); @@ -106,12 +115,12 @@ public class MmsSendHelper extends MmsCommunication { try { if (useProxyIfAvailable && param.hasProxy()) { if (checkRouteToHost(context, param.getProxy(), usingMmsRadio)) { - byte[] response = makePost(context, param.getMmsc(), param.getProxy(), param.getPort(), mms); + byte[] response = makePost(param.getMmsc(), param.getProxy(), param.getPort(), mms); if (response != null) return response; } } else { if (checkRouteToHost(context, Uri.parse(param.getMmsc()).getHost(), usingMmsRadio)) { - byte[] response = makePost(context, param.getMmsc(), null, -1, mms); + byte[] response = makePost(param.getMmsc(), null, -1, mms); if (response != null) return response; } }