summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--src/com/android/mms/service/DownloadRequest.java36
-rw-r--r--src/com/android/mms/service/HttpUtils.java418
-rw-r--r--src/com/android/mms/service/MmsHttpClient.java377
-rw-r--r--src/com/android/mms/service/MmsNetworkManager.java67
-rw-r--r--src/com/android/mms/service/MmsRequest.java172
-rw-r--r--src/com/android/mms/service/SendRequest.java34
-rw-r--r--src/com/android/mms/service/http/NameResolver.java27
-rw-r--r--src/com/android/mms/service/http/NetworkAwareClientConnectionOperator.java185
-rw-r--r--src/com/android/mms/service/http/NetworkAwareHttpClient.java504
-rw-r--r--src/com/android/mms/service/http/NetworkAwareThreadSafeClientConnManager.java40
11 files changed, 478 insertions, 1384 deletions
diff --git a/Android.mk b/Android.mk
index 20d7357..e78401a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -22,7 +22,7 @@ include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := MmsService
LOCAL_PRIVILEGED_MODULE := true
-LOCAL_JAVA_LIBRARIES := telephony-common
+LOCAL_JAVA_LIBRARIES := telephony-common okhttp
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/src/com/android/mms/service/DownloadRequest.java b/src/com/android/mms/service/DownloadRequest.java
index 9c5ac14..3805d1c 100644
--- a/src/com/android/mms/service/DownloadRequest.java
+++ b/src/com/android/mms/service/DownloadRequest.java
@@ -16,16 +16,6 @@
package com.android.mms.service;
-import com.google.android.mms.MmsException;
-import com.google.android.mms.pdu.GenericPdu;
-import com.google.android.mms.pdu.PduHeaders;
-import com.google.android.mms.pdu.PduParser;
-import com.google.android.mms.pdu.PduPersister;
-import com.google.android.mms.pdu.RetrieveConf;
-import com.google.android.mms.util.SqliteWrapper;
-
-import com.android.mms.service.exception.MmsHttpException;
-
import android.app.Activity;
import android.app.AppOpsManager;
import android.app.PendingIntent;
@@ -42,6 +32,15 @@ import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import com.android.mms.service.exception.MmsHttpException;
+import com.google.android.mms.MmsException;
+import com.google.android.mms.pdu.GenericPdu;
+import com.google.android.mms.pdu.PduHeaders;
+import com.google.android.mms.pdu.PduParser;
+import com.google.android.mms.pdu.PduPersister;
+import com.google.android.mms.pdu.RetrieveConf;
+import com.google.android.mms.util.SqliteWrapper;
+
import java.util.List;
/**
@@ -67,12 +66,19 @@ public class DownloadRequest extends MmsRequest {
@Override
protected byte[] doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn)
throws MmsHttpException {
- return doHttpForResolvedAddresses(context,
- netMgr,
+ final MmsHttpClient mmsHttpClient = netMgr.getOrCreateHttpClient();
+ if (mmsHttpClient == null) {
+ Log.e(MmsService.TAG, "MMS network is not ready!");
+ throw new MmsHttpException(0/*statusCode*/, "MMS network is not ready");
+ }
+ return mmsHttpClient.execute(
mLocationUrl,
- null/*pdu*/,
- HttpUtils.HTTP_GET_METHOD,
- apn);
+ null/*pud*/,
+ MmsHttpClient.METHOD_GET,
+ apn.isProxySet(),
+ apn.getProxyAddress(),
+ apn.getProxyPort(),
+ mMmsConfig);
}
@Override
diff --git a/src/com/android/mms/service/HttpUtils.java b/src/com/android/mms/service/HttpUtils.java
deleted file mode 100644
index a58af39..0000000
--- a/src/com/android/mms/service/HttpUtils.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mms.service;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.mms.service.exception.MmsHttpException;
-import com.android.mms.service.http.NameResolver;
-import com.android.mms.service.http.NetworkAwareHttpClient;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.StatusLine;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.conn.params.ConnRouteParams;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
-import org.apache.http.params.HttpProtocolParams;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * HTTP utils to make HTTP request to MMSC
- */
-public class HttpUtils {
- private static final String TAG = MmsService.TAG;
-
- public static final int HTTP_POST_METHOD = 1;
- public static final int HTTP_GET_METHOD = 2;
-
- // Definition for necessary HTTP headers.
- private static final String HDR_KEY_ACCEPT = "Accept";
- private static final String HDR_KEY_ACCEPT_LANGUAGE = "Accept-Language";
-
- private static final String HDR_VALUE_ACCEPT =
- "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic";
-
- private HttpUtils() {
- // To forbidden instantiate this class.
- }
-
- /**
- * A helper method to send or retrieve data through HTTP protocol.
- *
- * @param url The URL used in a GET request. Null when the method is
- * HTTP_POST_METHOD.
- * @param pdu The data to be POST. Null when the method is HTTP_GET_METHOD.
- * @param method HTTP_POST_METHOD or HTTP_GET_METHOD.
- * @param isProxySet If proxy is set
- * @param proxyHost The host of the proxy
- * @param proxyPort The port of the proxy
- * @param resolver The custom name resolver to use
- * @param useIpv6 If we should use IPv6 address when the HTTP client resolves the host name
- * @param mmsConfig The MmsConfig to use
- * @return A byte array which contains the response data.
- * If an HTTP error code is returned, an IOException will be thrown.
- * @throws com.android.mms.service.exception.MmsHttpException if HTTP request gets error response (>=400)
- */
- public static byte[] httpConnection(Context context, String url, byte[] pdu, int method,
- boolean isProxySet, String proxyHost, int proxyPort, NameResolver resolver,
- boolean useIpv6, MmsConfig.Overridden mmsConfig) throws MmsHttpException {
- final String methodString = getMethodString(method);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "HttpUtils: request param list\n"
- + "url=" + url + "\n"
- + "method=" + methodString + "\n"
- + "isProxySet=" + isProxySet + "\n"
- + "proxyHost=" + proxyHost + "\n"
- + "proxyPort=" + proxyPort + "\n"
- + "size=" + (pdu != null ? pdu.length : 0));
- } else {
- Log.d(TAG, "HttpUtils: " + methodString + " " + url);
- }
-
- NetworkAwareHttpClient client = null;
- try {
- // Make sure to use a proxy which supports CONNECT.
- URI hostUrl = new URI(url);
- HttpHost target = new HttpHost(hostUrl.getHost(), hostUrl.getPort(),
- HttpHost.DEFAULT_SCHEME_NAME);
- client = createHttpClient(context, resolver, useIpv6, mmsConfig);
- HttpRequest req = null;
-
- switch (method) {
- case HTTP_POST_METHOD:
- ByteArrayEntity entity = new ByteArrayEntity(pdu);
- // Set request content type.
- entity.setContentType("application/vnd.wap.mms-message");
- HttpPost post = new HttpPost(url);
- post.setEntity(entity);
- req = post;
- break;
- case HTTP_GET_METHOD:
- req = new HttpGet(url);
- break;
- }
-
- // Set route parameters for the request.
- HttpParams params = client.getParams();
- if (isProxySet) {
- ConnRouteParams.setDefaultProxy(params, new HttpHost(proxyHost, proxyPort));
- }
- req.setParams(params);
-
- // Set necessary HTTP headers for MMS transmission.
- req.addHeader(HDR_KEY_ACCEPT, HDR_VALUE_ACCEPT);
-
- // UA Profile URL header
- String xWapProfileTagName = mmsConfig.getUaProfTagName();
- String xWapProfileUrl = mmsConfig.getUaProfUrl();
- if (xWapProfileUrl != null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "HttpUtils: xWapProfUrl=" + xWapProfileUrl);
- }
- req.addHeader(xWapProfileTagName, xWapProfileUrl);
- }
-
- // Extra http parameters. Split by '|' to get a list of value pairs.
- // Separate each pair by the first occurrence of ':' to obtain a name and
- // value. Replace the occurrence of the string returned by
- // MmsConfig.getHttpParamsLine1Key() with the users telephone number inside
- // the value. And replace the occurrence of the string returned by
- // MmsConfig.getHttpParamsNaiKey() with the users NAI(Network Access Identifier)
- // inside the value.
- String extraHttpParams = mmsConfig.getHttpParams();
-
- if (!TextUtils.isEmpty(extraHttpParams)) {
- // Parse the parameter list
- String paramList[] = extraHttpParams.split("\\|");
- for (String paramPair : paramList) {
- String splitPair[] = paramPair.split(":", 2);
- if (splitPair.length == 2) {
- final String name = splitPair[0].trim();
- final String value = resolveMacro(context, splitPair[1].trim(), mmsConfig);
- if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(value)) {
- req.addHeader(name, value);
- }
- }
- }
- }
- req.addHeader(HDR_KEY_ACCEPT_LANGUAGE, getCurrentAcceptLanguage(Locale.getDefault()));
-
- final HttpResponse response = client.execute(target, req);
- final StatusLine status = response.getStatusLine();
- final HttpEntity entity = response.getEntity();
- Log.d(TAG, "HttpUtils: status=" + status + " size="
- + (entity != null ? entity.getContentLength() : -1));
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- for (Header header : req.getAllHeaders()) {
- if (header != null) {
- Log.v(TAG, "HttpUtils: header "
- + header.getName() + "=" + header.getValue());
- }
- }
- }
- byte[] body = null;
- if (entity != null) {
- try {
- if (entity.getContentLength() > 0) {
- body = new byte[(int) entity.getContentLength()];
- DataInputStream dis = new DataInputStream(entity.getContent());
- try {
- dis.readFully(body);
- } finally {
- try {
- dis.close();
- } catch (IOException e) {
- Log.e(TAG, "HttpUtils: Error closing input stream: "
- + e.getMessage());
- }
- }
- }
- if (entity.isChunked()) {
- Log.d(TAG, "HttpUtils: transfer encoding is chunked");
- int bytesTobeRead = mmsConfig.getMaxMessageSize();
- byte[] tempBody = new byte[bytesTobeRead];
- DataInputStream dis = new DataInputStream(entity.getContent());
- try {
- int bytesRead = 0;
- int offset = 0;
- boolean readError = false;
- do {
- try {
- bytesRead = dis.read(tempBody, offset, bytesTobeRead);
- } catch (IOException e) {
- readError = true;
- Log.e(TAG, "HttpUtils: error reading input stream", e);
- break;
- }
- if (bytesRead > 0) {
- bytesTobeRead -= bytesRead;
- offset += bytesRead;
- }
- } while (bytesRead >= 0 && bytesTobeRead > 0);
- if (bytesRead == -1 && offset > 0 && !readError) {
- // offset is same as total number of bytes read
- // bytesRead will be -1 if the data was read till the eof
- body = new byte[offset];
- System.arraycopy(tempBody, 0, body, 0, offset);
- Log.d(TAG, "HttpUtils: Chunked response length " + offset);
- } else {
- Log.e(TAG, "HttpUtils: Response entity too large or empty");
- }
- } finally {
- try {
- dis.close();
- } catch (IOException e) {
- Log.e(TAG, "HttpUtils: Error closing input stream", e);
- }
- }
- }
- } finally {
- if (entity != null) {
- entity.consumeContent();
- }
- }
- }
- final int statusCode = status.getStatusCode();
- if (statusCode != 200) { // HTTP 200 is success.
- StringBuilder sb = new StringBuilder();
- if (body != null) {
- sb.append("response: text=").append(new String(body)).append('\n');
- }
- for (Header header : req.getAllHeaders()) {
- if (header != null) {
- sb.append("req header: ")
- .append(header.getName())
- .append('=')
- .append(header.getValue())
- .append('\n');
- }
- }
- for (Header header : response.getAllHeaders()) {
- if (header != null) {
- sb.append("resp header: ")
- .append(header.getName())
- .append('=')
- .append(header.getValue())
- .append('\n');
- }
- }
- Log.e(TAG, "HttpUtils: error response -- \n"
- + "mStatusCode=" + statusCode + "\n"
- + "reason=" + status.getReasonPhrase() + "\n"
- + "url=" + url + "\n"
- + "method=" + methodString + "\n"
- + "isProxySet=" + isProxySet + "\n"
- + "proxyHost=" + proxyHost + "\n"
- + "proxyPort=" + proxyPort
- + (sb != null ? "\n" + sb.toString() : ""));
- throw new MmsHttpException(statusCode, status.getReasonPhrase());
- }
- return body;
- } catch (IOException e) {
- Log.e(TAG, "HttpUtils: IO failure", e);
- throw new MmsHttpException(0/*statusCode*/, e);
- } catch (URISyntaxException e) {
- Log.e(TAG, "HttpUtils: invalid url " + url);
- throw new MmsHttpException(0/*statusCode*/, "Invalid url " + url);
- } finally {
- if (client != null) {
- client.close();
- }
- }
- }
-
- private static String getMethodString(int method) {
- return ((method == HTTP_POST_METHOD) ?
- "POST" : ((method == HTTP_GET_METHOD) ? "GET" : "UNKNOWN"));
- }
-
- /**
- * Create an HTTP client
- *
- * @param context
- * @return {@link android.net.http.AndroidHttpClient}
- */
- private static NetworkAwareHttpClient createHttpClient(Context context, NameResolver resolver,
- boolean useIpv6, MmsConfig.Overridden mmsConfig) {
- final String userAgent = mmsConfig.getUserAgent();
- final NetworkAwareHttpClient client = NetworkAwareHttpClient.newInstance(userAgent, context,
- resolver, useIpv6);
- final HttpParams params = client.getParams();
- HttpProtocolParams.setContentCharset(params, "UTF-8");
-
- // set the socket timeout
- int soTimeout = mmsConfig.getHttpSocketTimeout();
-
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "HttpUtils: createHttpClient w/ socket timeout "
- + soTimeout + " ms, UA=" + userAgent);
- }
- HttpConnectionParams.setSoTimeout(params, soTimeout);
- return client;
- }
-
- private static final String ACCEPT_LANG_FOR_US_LOCALE = "en-US";
-
- /**
- * Return the Accept-Language header. Use the current locale plus
- * US if we are in a different locale than US.
- * This code copied from the browser's WebSettings.java
- *
- * @return Current AcceptLanguage String.
- */
- public static String getCurrentAcceptLanguage(Locale locale) {
- final StringBuilder buffer = new StringBuilder();
- addLocaleToHttpAcceptLanguage(buffer, locale);
-
- if (!Locale.US.equals(locale)) {
- if (buffer.length() > 0) {
- buffer.append(", ");
- }
- buffer.append(ACCEPT_LANG_FOR_US_LOCALE);
- }
-
- return buffer.toString();
- }
-
- /**
- * Convert obsolete language codes, including Hebrew/Indonesian/Yiddish,
- * to new standard.
- */
- private static String convertObsoleteLanguageCodeToNew(String langCode) {
- if (langCode == null) {
- return null;
- }
- if ("iw".equals(langCode)) {
- // Hebrew
- return "he";
- } else if ("in".equals(langCode)) {
- // Indonesian
- return "id";
- } else if ("ji".equals(langCode)) {
- // Yiddish
- return "yi";
- }
- return langCode;
- }
-
- private static void addLocaleToHttpAcceptLanguage(StringBuilder builder, Locale locale) {
- final String language = convertObsoleteLanguageCodeToNew(locale.getLanguage());
- if (language != null) {
- builder.append(language);
- final String country = locale.getCountry();
- if (country != null) {
- builder.append("-");
- builder.append(country);
- }
- }
- }
-
- private static final Pattern MACRO_P = Pattern.compile("##(\\S+)##");
- /**
- * Resolve the macro in HTTP param value text
- * For example, "something##LINE1##something" is resolved to "something9139531419something"
- *
- * @param value The HTTP param value possibly containing macros
- * @return The HTTP param with macro resolved to real value
- */
- private static String resolveMacro(Context context, String value,
- MmsConfig.Overridden mmsConfig) {
- if (TextUtils.isEmpty(value)) {
- return value;
- }
- final Matcher matcher = MACRO_P.matcher(value);
- int nextStart = 0;
- StringBuilder replaced = null;
- while (matcher.find()) {
- if (replaced == null) {
- replaced = new StringBuilder();
- }
- final int matchedStart = matcher.start();
- if (matchedStart > nextStart) {
- replaced.append(value.substring(nextStart, matchedStart));
- }
- final String macro = matcher.group(1);
- final String macroValue = mmsConfig.getHttpParamMacro(context, macro);
- if (macroValue != null) {
- replaced.append(macroValue);
- } else {
- Log.w(TAG, "HttpUtils: invalid macro " + macro);
- }
- nextStart = matcher.end();
- }
- if (replaced != null && nextStart < value.length()) {
- replaced.append(value.substring(nextStart));
- }
- return replaced == null ? value : replaced.toString();
- }
-}
diff --git a/src/com/android/mms/service/MmsHttpClient.java b/src/com/android/mms/service/MmsHttpClient.java
new file mode 100644
index 0000000..8e1b4f1
--- /dev/null
+++ b/src/com/android/mms/service/MmsHttpClient.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mms.service;
+
+import javax.net.SocketFactory;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.mms.service.exception.MmsHttpException;
+import com.android.okhttp.ConnectionPool;
+import com.android.okhttp.HostResolver;
+import com.android.okhttp.HttpHandler;
+import com.android.okhttp.HttpsHandler;
+import com.android.okhttp.OkHttpClient;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.URL;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * MMS HTTP client for sending and downloading MMS messages
+ */
+public class MmsHttpClient {
+ public static final String METHOD_POST = "POST";
+ public static final String METHOD_GET = "GET";
+
+ private static final String HEADER_CONTENT_TYPE = "Content-Type";
+ private static final String HEADER_ACCEPT = "Accept";
+ private static final String HEADER_ACCEPT_LANGUAGE = "Accept-Language";
+ private static final String HEADER_USER_AGENT = "User-Agent";
+
+ // The "Accept" header value
+ private static final String HEADER_VALUE_ACCEPT =
+ "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic";
+ // The "Content-Type" header value
+ private static final String HEADER_VALUE_CONTENT_TYPE =
+ "application/vnd.wap.mms-message; charset=utf-8";
+
+ private final Context mContext;
+ private final SocketFactory mSocketFactory;
+ private final HostResolver mHostResolver;
+ private final ConnectionPool mConnectionPool;
+
+ /**
+ * Constructor
+ *
+ * @param context The Context object
+ * @param socketFactory The socket factory for creating an OKHttp client
+ * @param hostResolver The host name resolver for creating an OKHttp client
+ * @param connectionPool The connection pool for creating an OKHttp client
+ */
+ public MmsHttpClient(Context context, SocketFactory socketFactory, HostResolver hostResolver,
+ ConnectionPool connectionPool) {
+ mContext = context;
+ mSocketFactory = socketFactory;
+ mHostResolver = hostResolver;
+ mConnectionPool = connectionPool;
+ }
+
+ /**
+ * Execute an MMS HTTP request, either a POST (sending) or a GET (downloading)
+ *
+ * @param urlString The request URL, for sending it is usually the MMSC, and for downloading
+ * it is the message URL
+ * @param pdu For POST (sending) only, the PDU to send
+ * @param method HTTP method, POST for sending and GET for downloading
+ * @param isProxySet Is there a proxy for the MMSC
+ * @param proxyHost The proxy host
+ * @param proxyPort The proxy port
+ * @param mmsConfig The MMS config to use
+ * @return The HTTP response body
+ * @throws MmsHttpException For any failures
+ */
+ public byte[] execute(String urlString, byte[] pdu, String method, boolean isProxySet,
+ String proxyHost, int proxyPort, MmsConfig.Overridden mmsConfig)
+ throws MmsHttpException {
+ Log.d(MmsService.TAG, "HTTP: " + method + " " + urlString
+ + (isProxySet ? (", proxy=" + proxyHost + ":" + proxyPort) : "")
+ + ", PDU size=" + (pdu != null ? pdu.length : 0));
+ checkMethod(method);
+ HttpURLConnection connection = null;
+ try {
+ Proxy proxy = null;
+ if (isProxySet) {
+ proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
+ }
+ final URL url = new URL(urlString);
+ // Now get the connection
+ connection = openConnection(url, proxy);
+ connection.setDoInput(true);
+ connection.setConnectTimeout(mmsConfig.getHttpSocketTimeout());
+ // ------- COMMON HEADERS ---------
+ // Header: Accept
+ connection.setRequestProperty(HEADER_ACCEPT, HEADER_VALUE_ACCEPT);
+ // Header: Accept-Language
+ connection.setRequestProperty(
+ HEADER_ACCEPT_LANGUAGE, getCurrentAcceptLanguage(Locale.getDefault()));
+ // Header: User-Agent
+ connection.setRequestProperty(HEADER_USER_AGENT, mmsConfig.getUserAgent());
+ // Header: x-wap-profile
+ final String uaProfUrlTagName = mmsConfig.getUaProfTagName();
+ final String uaProfUrl = mmsConfig.getUaProfUrl();
+ if (uaProfUrl != null) {
+ if (Log.isLoggable(MmsService.TAG, Log.VERBOSE)) {
+ Log.v(MmsService.TAG, "HTTP: UaProfUrl=" + uaProfUrl);
+ }
+ connection.setRequestProperty(uaProfUrlTagName, uaProfUrl);
+ }
+ // Add extra headers specified by mms_config.xml's httpparams
+ addExtraHeaders(connection, mmsConfig);
+ // Different stuff for GET and POST
+ if (METHOD_POST.equals(method)) {
+ if (pdu == null || pdu.length < 1) {
+ Log.e(MmsService.TAG, "HTTP: empty pdu");
+ throw new MmsHttpException(0/*statusCode*/, "Sending empty PDU");
+ }
+ connection.setDoOutput(true);
+ connection.setRequestMethod(METHOD_POST);
+ connection.setRequestProperty(HEADER_CONTENT_TYPE, HEADER_VALUE_CONTENT_TYPE);
+ if (Log.isLoggable(MmsService.TAG, Log.VERBOSE)) {
+ logHttpHeaders(connection.getRequestProperties());
+ }
+ connection.setFixedLengthStreamingMode(pdu.length);
+ // Sending request body
+ final OutputStream out =
+ new BufferedOutputStream(connection.getOutputStream());
+ out.write(pdu);
+ out.flush();
+ out.close();
+ } else if (METHOD_GET.equals(method)) {
+ if (Log.isLoggable(MmsService.TAG, Log.VERBOSE)) {
+ logHttpHeaders(connection.getRequestProperties());
+ }
+ connection.setRequestMethod(METHOD_GET);
+ }
+ // Get response
+ final InputStream in = new BufferedInputStream(connection.getInputStream());
+ final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ final byte[] buf = new byte[4096];
+ int count = 0;
+ while ((count = in.read(buf)) > 0) {
+ byteOut.write(buf, 0, count);
+ }
+ in.close();
+ final byte[] responseBody = byteOut.toByteArray();
+ final int responseCode = connection.getResponseCode();
+ final String responseMessage = connection.getResponseMessage();
+ Log.d(MmsService.TAG, "HTTP: " + responseCode + " " + responseMessage);
+ if (Log.isLoggable(MmsService.TAG, Log.VERBOSE)) {
+ logHttpHeaders(connection.getHeaderFields());
+ }
+ if (responseCode / 100 != 2) {
+ Log.e(MmsService.TAG, "HTTP: response="
+ + (responseBody != null ? " " + new String(responseBody, "UTF=8") : ""));
+ throw new MmsHttpException(responseCode, responseMessage);
+ }
+ Log.d(MmsService.TAG, "HTTP: response size="
+ + (responseBody != null ? responseBody.length : 0));
+ return responseBody;
+ } catch (MalformedURLException e) {
+ Log.e(MmsService.TAG, "HTTP: invalid URL " + urlString, e);
+ throw new MmsHttpException(0/*statusCode*/, "Invalid URL " + urlString, e);
+ } catch (ProtocolException e) {
+ Log.e(MmsService.TAG, "HTTP: invalid URL protocol " + urlString, e);
+ throw new MmsHttpException(0/*statusCode*/, "Invalid URL protocol " + urlString, e);
+ } catch (IOException e) {
+ Log.e(MmsService.TAG, "HTTP: IO failure", e);
+ throw new MmsHttpException(0/*statusCode*/, e);
+ } finally {
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ }
+
+ /**
+ * Open an HTTP connection
+ *
+ * TODO: The following code is borrowed from android.net.Network.openConnection
+ * Once that method supports proxy, we should use that instead
+ * Also we should remove the associated HostResolver and ConnectionPool from
+ * MmsNetworkManager
+ *
+ * @param url The URL to connect to
+ * @param proxy The proxy to use
+ * @return The opened HttpURLConnection
+ * @throws MalformedURLException If URL is malformed
+ */
+ private HttpURLConnection openConnection(URL url, Proxy proxy) throws MalformedURLException {
+ final String protocol = url.getProtocol();
+ OkHttpClient okHttpClient;
+ if (protocol.equals("http")) {
+ okHttpClient = HttpHandler.createHttpOkHttpClient(proxy);
+ } else if (protocol.equals("https")) {
+ okHttpClient = HttpsHandler.createHttpsOkHttpClient(proxy);
+ } else {
+ throw new MalformedURLException("Invalid URL or unrecognized protocol " + protocol);
+ }
+ return okHttpClient.setSocketFactory(mSocketFactory)
+ .setHostResolver(mHostResolver)
+ .setConnectionPool(mConnectionPool)
+ .open(url);
+ }
+
+ private static void logHttpHeaders(Map<String, List<String>> headers) {
+ final StringBuilder sb = new StringBuilder();
+ if (headers != null) {
+ for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
+ final String key = entry.getKey();
+ final List<String> values = entry.getValue();
+ if (values != null) {
+ for (String value : values) {
+ sb.append(key).append('=').append(value).append('\n');
+ }
+ }
+ }
+ Log.v(MmsService.TAG, "HTTP: headers\n" + sb.toString());
+ }
+ }
+
+ private static void checkMethod(String method) throws MmsHttpException {
+ if (!METHOD_GET.equals(method) && !METHOD_POST.equals(method)) {
+ throw new MmsHttpException(0/*statusCode*/, "Invalid method " + method);
+ }
+ }
+
+ private static final String ACCEPT_LANG_FOR_US_LOCALE = "en-US";
+
+ /**
+ * Return the Accept-Language header. Use the current locale plus
+ * US if we are in a different locale than US.
+ * This code copied from the browser's WebSettings.java
+ *
+ * @return Current AcceptLanguage String.
+ */
+ public static String getCurrentAcceptLanguage(Locale locale) {
+ final StringBuilder buffer = new StringBuilder();
+ addLocaleToHttpAcceptLanguage(buffer, locale);
+
+ if (!Locale.US.equals(locale)) {
+ if (buffer.length() > 0) {
+ buffer.append(", ");
+ }
+ buffer.append(ACCEPT_LANG_FOR_US_LOCALE);
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * Convert obsolete language codes, including Hebrew/Indonesian/Yiddish,
+ * to new standard.
+ */
+ private static String convertObsoleteLanguageCodeToNew(String langCode) {
+ if (langCode == null) {
+ return null;
+ }
+ if ("iw".equals(langCode)) {
+ // Hebrew
+ return "he";
+ } else if ("in".equals(langCode)) {
+ // Indonesian
+ return "id";
+ } else if ("ji".equals(langCode)) {
+ // Yiddish
+ return "yi";
+ }
+ return langCode;
+ }
+
+ private static void addLocaleToHttpAcceptLanguage(StringBuilder builder, Locale locale) {
+ final String language = convertObsoleteLanguageCodeToNew(locale.getLanguage());
+ if (language != null) {
+ builder.append(language);
+ final String country = locale.getCountry();
+ if (country != null) {
+ builder.append("-");
+ builder.append(country);
+ }
+ }
+ }
+
+ private static final Pattern MACRO_P = Pattern.compile("##(\\S+)##");
+ /**
+ * Resolve the macro in HTTP param value text
+ * For example, "something##LINE1##something" is resolved to "something9139531419something"
+ *
+ * @param value The HTTP param value possibly containing macros
+ * @return The HTTP param with macro resolved to real value
+ */
+ private static String resolveMacro(Context context, String value,
+ MmsConfig.Overridden mmsConfig) {
+ if (TextUtils.isEmpty(value)) {
+ return value;
+ }
+ final Matcher matcher = MACRO_P.matcher(value);
+ int nextStart = 0;
+ StringBuilder replaced = null;
+ while (matcher.find()) {
+ if (replaced == null) {
+ replaced = new StringBuilder();
+ }
+ final int matchedStart = matcher.start();
+ if (matchedStart > nextStart) {
+ replaced.append(value.substring(nextStart, matchedStart));
+ }
+ final String macro = matcher.group(1);
+ final String macroValue = mmsConfig.getHttpParamMacro(context, macro);
+ if (macroValue != null) {
+ replaced.append(macroValue);
+ } else {
+ Log.w(MmsService.TAG, "HTTP: invalid macro " + macro);
+ }
+ nextStart = matcher.end();
+ }
+ if (replaced != null && nextStart < value.length()) {
+ replaced.append(value.substring(nextStart));
+ }
+ return replaced == null ? value : replaced.toString();
+ }
+
+ /**
+ * Add extra HTTP headers from mms_config.xml's httpParams, which is a list of key/value
+ * pairs separated by "|". Each key/value pair is separated by ":". Value may contain
+ * macros like "##LINE1##" or "##NAI##" which is resolved with methods in this class
+ *
+ * @param connection The HttpURLConnection that we add headers to
+ * @param mmsConfig The MmsConfig object
+ */
+ private void addExtraHeaders(HttpURLConnection connection, MmsConfig.Overridden mmsConfig) {
+ final String extraHttpParams = mmsConfig.getHttpParams();
+ if (!TextUtils.isEmpty(extraHttpParams)) {
+ // Parse the parameter list
+ String paramList[] = extraHttpParams.split("\\|");
+ for (String paramPair : paramList) {
+ String splitPair[] = paramPair.split(":", 2);
+ if (splitPair.length == 2) {
+ final String name = splitPair[0].trim();
+ final String value = resolveMacro(mContext, splitPair[1].trim(), mmsConfig);
+ if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(value)) {
+ // Add the header if the param is valid
+ connection.setRequestProperty(name, value);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/mms/service/MmsNetworkManager.java b/src/com/android/mms/service/MmsNetworkManager.java
index cefafc7..a9c5eca 100644
--- a/src/com/android/mms/service/MmsNetworkManager.java
+++ b/src/com/android/mms/service/MmsNetworkManager.java
@@ -16,9 +16,6 @@
package com.android.mms.service;
-import com.android.mms.service.exception.MmsNetworkException;
-import com.android.mms.service.http.NameResolver;
-
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
@@ -28,13 +25,17 @@ import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
+import com.android.mms.service.exception.MmsNetworkException;
+import com.android.okhttp.ConnectionPool;
+import com.android.okhttp.HostResolver;
+
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Manages the MMS network connectivity
*/
-public class MmsNetworkManager implements NameResolver {
+public class MmsNetworkManager implements HostResolver {
// Timeout used to call ConnectivityManager.requestNetwork
private static final int NETWORK_REQUEST_TIMEOUT_MILLIS = 60 * 1000;
// Wait timeout for this class, a little bit longer than the above timeout
@@ -42,6 +43,14 @@ public class MmsNetworkManager implements NameResolver {
private static final int NETWORK_ACQUIRE_TIMEOUT_MILLIS =
NETWORK_REQUEST_TIMEOUT_MILLIS + (5 * 1000);
+ // Borrowed from {@link android.net.Network}
+ private static final boolean httpKeepAlive =
+ Boolean.parseBoolean(System.getProperty("http.keepAlive", "true"));
+ private static final int httpMaxConnections =
+ httpKeepAlive ? Integer.parseInt(System.getProperty("http.maxConnections", "5")) : 0;
+ private static final long httpKeepAliveDurationMs =
+ Long.parseLong(System.getProperty("http.keepAliveDuration", "300000")); // 5 minutes.
+
private Context mContext;
// The requested MMS {@link android.net.Network} we are holding
// We need this when we unbind from it. This is also used to indicate if the
@@ -60,7 +69,13 @@ public class MmsNetworkManager implements NameResolver {
// The callback to register when we request MMS network
private ConnectivityManager.NetworkCallback mNetworkCallback;
- private ConnectivityManager mConnectivityManager;
+ private volatile ConnectivityManager mConnectivityManager;
+
+ // The OkHttp's ConnectionPool used by the HTTP client associated with this network manager
+ private ConnectionPool mConnectionPool;
+
+ // The MMS HTTP client for this network
+ private MmsHttpClient mMmsHttpClient;
// TODO: we need to re-architect this when we support MSIM, like maybe one manager for each SIM?
public MmsNetworkManager(Context context) {
@@ -69,12 +84,8 @@ public class MmsNetworkManager implements NameResolver {
mNetwork = null;
mMmsRequestCount = 0;
mConnectivityManager = null;
- }
-
- public Network getNetwork() {
- synchronized (this) {
- return mNetwork;
- }
+ mConnectionPool = null;
+ mMmsHttpClient = null;
}
/**
@@ -201,6 +212,12 @@ public class MmsNetworkManager implements NameResolver {
mNetworkCallback = null;
mNetwork = null;
mMmsRequestCount = 0;
+ // Currently we follow what android.net.Network does with ConnectionPool,
+ // which is per Network object. So if Network changes, we should clear
+ // out the ConnectionPool and thus the MmsHttpClient (since it is linked
+ // to a specific ConnectionPool).
+ mConnectionPool = null;
+ mMmsHttpClient = null;
}
@Override
@@ -225,4 +242,32 @@ public class MmsNetworkManager implements NameResolver {
return Settings.System.getInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
}
+
+ private ConnectionPool getOrCreateConnectionPoolLocked() {
+ if (mConnectionPool == null) {
+ mConnectionPool = new ConnectionPool(httpMaxConnections, httpKeepAliveDurationMs);
+ }
+ return mConnectionPool;
+ }
+
+ /**
+ * Get an MmsHttpClient for the current network
+ *
+ * @return The MmsHttpClient instance
+ */
+ public MmsHttpClient getOrCreateHttpClient() {
+ synchronized (this) {
+ if (mMmsHttpClient == null) {
+ if (mNetwork != null) {
+ // Create new MmsHttpClient for the current Network
+ mMmsHttpClient = new MmsHttpClient(
+ mContext,
+ mNetwork.getSocketFactory(),
+ MmsNetworkManager.this,
+ getOrCreateConnectionPoolLocked());
+ }
+ }
+ return mMmsHttpClient;
+ }
+ }
}
diff --git a/src/com/android/mms/service/MmsRequest.java b/src/com/android/mms/service/MmsRequest.java
index 04b9ff9..adaf083 100644
--- a/src/com/android/mms/service/MmsRequest.java
+++ b/src/com/android/mms/service/MmsRequest.java
@@ -16,30 +16,20 @@
package com.android.mms.service;
-import com.android.mms.service.exception.ApnException;
-import com.android.mms.service.exception.MmsHttpException;
-import com.android.mms.service.exception.MmsNetworkException;
-
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.Network;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Telephony;
import android.telephony.SmsManager;
import android.util.Log;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
+import com.android.mms.service.exception.ApnException;
+import com.android.mms.service.exception.MmsHttpException;
+import com.android.mms.service.exception.MmsNetworkException;
/**
* Base class for MMS requests. This has the common logic of sending/downloading MMS.
@@ -216,162 +206,6 @@ public abstract class MmsRequest {
}
/**
- * Try running MMS HTTP request for all the addresses that we can resolve to
- *
- * @param context The context
- * @param netMgr The {@link com.android.mms.service.MmsNetworkManager}
- * @param url The HTTP URL
- * @param pdu The PDU to send
- * @param method The HTTP method to use
- * @param apn The APN setting to use
- * @return The response data
- * @throws MmsHttpException If there is any HTTP/network failure
- */
- protected byte[] doHttpForResolvedAddresses(Context context, MmsNetworkManager netMgr,
- String url, byte[] pdu, int method, ApnSettings apn) throws MmsHttpException {
- MmsHttpException lastException = null;
- final ConnectivityManager connMgr =
- (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- // Do HTTP on all the addresses we can resolve to
- for (final InetAddress address : resolveDestination(connMgr, netMgr, url, apn)) {
- try {
- // TODO: we have to use a deprecated API here because with the new
- // ConnectivityManager APIs in LMP, we need to either use a bound process
- // or a bound socket. The former can not be used since we share the
- // phone process with others. The latter is not supported by any HTTP
- // library yet. We have to rely on this API to get things work. Once
- // a multinet aware HTTP lib is ready, we should switch to that and
- // remove all the unnecessary code.
- if (!connMgr.requestRouteToHostAddress(
- ConnectivityManager.TYPE_MOBILE_MMS, address)) {
- throw new MmsHttpException(0/*statusCode*/,
- "MmsRequest: can not request a route for host " + address);
- }
- return HttpUtils.httpConnection(
- context,
- url,
- pdu,
- method,
- apn.isProxySet(),
- apn.getProxyAddress(),
- apn.getProxyPort(),
- netMgr,
- address instanceof Inet6Address,
- mMmsConfig);
- } catch (MmsHttpException e) {
- lastException = e;
- Log.e(MmsService.TAG, "MmsRequest: failure in trying address " + address, e);
- }
- }
- if (lastException != null) {
- throw lastException;
- } else {
- // Should not reach here
- throw new MmsHttpException(0/*statusCode*/, "MmsRequest: unknown failure");
- }
- }
-
- /**
- * Resolve the name of the host we are about to connect to, which can be the URL host or
- * the proxy host. We only resolve to the supported address types (IPv4 or IPv6 or both)
- * based on the MMS network interface's address type, i.e. we only need addresses that
- * match the link address type.
- *
- * @param connMgr The connectivity manager
- * @param netMgr The current {@link MmsNetworkManager}
- * @param url The HTTP URL
- * @param apn The APN setting to use
- * @return A list of matching resolved addresses
- * @throws MmsHttpException For any network failure
- */
- private static List<InetAddress> resolveDestination(ConnectivityManager connMgr,
- MmsNetworkManager netMgr, String url, ApnSettings apn) throws MmsHttpException {
- Log.d(MmsService.TAG, "MmsRequest: resolve url " + url);
- // Find the real host to connect to
- String host = null;
- if (apn.isProxySet()) {
- host = apn.getProxyAddress();
- } else {
- final Uri uri = Uri.parse(url);
- host = uri.getHost();
- }
- // Find out the link address types: ipv4 or ipv6 or both
- final int addressTypes = getMmsLinkAddressTypes(connMgr, netMgr.getNetwork());
- Log.d(MmsService.TAG, "MmsRequest: addressTypes=" + addressTypes);
- // Resolve the host to a list of addresses based on supported address types
- return resolveHostName(netMgr, host, addressTypes);
- }
-
- // Address type masks
- private static final int ADDRESS_TYPE_IPV4 = 1;
- private static final int ADDRESS_TYPE_IPV6 = 1 << 1;
-
- /**
- * Try to find out if we should use IPv6 or IPv4 for MMS. Basically we check if the MMS
- * network interface has IPv6 address or not. If so, we will use IPv6. Otherwise, use
- * IPv4.
- *
- * @param connMgr The connectivity manager
- * @return A bit mask indicating what address types we have
- */
- private static int getMmsLinkAddressTypes(ConnectivityManager connMgr, Network network) {
- int result = 0;
- // Return none if network is not available
- if (network == null) {
- return result;
- }
- final LinkProperties linkProperties = connMgr.getLinkProperties(network);
- if (linkProperties != null) {
- for (InetAddress addr : linkProperties.getAddresses()) {
- if (addr instanceof Inet4Address) {
- result |= ADDRESS_TYPE_IPV4;
- } else if (addr instanceof Inet6Address) {
- result |= ADDRESS_TYPE_IPV6;
- }
- }
- }
- return result;
- }
-
- /**
- * Resolve host name to address by specified address types.
- *
- * @param netMgr The current {@link MmsNetworkManager}
- * @param host The host name
- * @param addressTypes The required address type in a bit mask
- * (0x01: IPv4, 0x10: IPv6, 0x11: both)
- * @return
- * @throws MmsHttpException
- */
- private static List<InetAddress> resolveHostName(MmsNetworkManager netMgr, String host,
- int addressTypes) throws MmsHttpException {
- final List<InetAddress> resolved = new ArrayList<InetAddress>();
- try {
- if (addressTypes != 0) {
- for (final InetAddress addr : netMgr.getAllByName(host)) {
- if ((addressTypes & ADDRESS_TYPE_IPV6) != 0
- && addr instanceof Inet6Address) {
- // Should use IPv6 and this is IPv6 address, add it
- resolved.add(addr);
- } else if ((addressTypes & ADDRESS_TYPE_IPV4) != 0
- && addr instanceof Inet4Address) {
- // Should use IPv4 and this is IPv4 address, add it
- resolved.add(addr);
- }
- }
- }
- if (resolved.size() < 1) {
- throw new MmsHttpException(0/*statusCode*/,
- "Failed to resolve " + host
- + " for allowed address types: " + addressTypes);
- }
- return resolved;
- } catch (final UnknownHostException e) {
- throw new MmsHttpException(0/*statusCode*/, "Failed to resolve " + host, e);
- }
- }
-
- /**
* Process the result of the completed request, including updating the message status
* in database and sending back the result via pending intents.
* @param context The context
diff --git a/src/com/android/mms/service/SendRequest.java b/src/com/android/mms/service/SendRequest.java
index 6d016ae..64c47f1 100644
--- a/src/com/android/mms/service/SendRequest.java
+++ b/src/com/android/mms/service/SendRequest.java
@@ -16,16 +16,6 @@
package com.android.mms.service;
-import com.google.android.mms.MmsException;
-import com.google.android.mms.pdu.GenericPdu;
-import com.google.android.mms.pdu.PduParser;
-import com.google.android.mms.pdu.PduPersister;
-import com.google.android.mms.pdu.SendConf;
-import com.google.android.mms.pdu.SendReq;
-import com.google.android.mms.util.SqliteWrapper;
-
-import com.android.mms.service.exception.MmsHttpException;
-
import android.app.Activity;
import android.app.AppOpsManager;
import android.app.PendingIntent;
@@ -43,6 +33,15 @@ import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import com.android.mms.service.exception.MmsHttpException;
+import com.google.android.mms.MmsException;
+import com.google.android.mms.pdu.GenericPdu;
+import com.google.android.mms.pdu.PduParser;
+import com.google.android.mms.pdu.PduPersister;
+import com.google.android.mms.pdu.SendConf;
+import com.google.android.mms.pdu.SendReq;
+import com.google.android.mms.util.SqliteWrapper;
+
import java.util.List;
/**
@@ -67,12 +66,19 @@ public class SendRequest extends MmsRequest {
@Override
protected byte[] doHttp(Context context, MmsNetworkManager netMgr, ApnSettings apn)
throws MmsHttpException {
- return doHttpForResolvedAddresses(context,
- netMgr,
+ final MmsHttpClient mmsHttpClient = netMgr.getOrCreateHttpClient();
+ if (mmsHttpClient == null) {
+ Log.e(MmsService.TAG, "MMS network is not ready!");
+ throw new MmsHttpException(0/*statusCode*/, "MMS network is not ready");
+ }
+ return mmsHttpClient.execute(
mLocationUrl != null ? mLocationUrl : apn.getMmscUrl(),
mPduData,
- HttpUtils.HTTP_POST_METHOD,
- apn);
+ MmsHttpClient.METHOD_POST,
+ apn.isProxySet(),
+ apn.getProxyAddress(),
+ apn.getProxyPort(),
+ mMmsConfig);
}
@Override
diff --git a/src/com/android/mms/service/http/NameResolver.java b/src/com/android/mms/service/http/NameResolver.java
deleted file mode 100644
index 13b4046..0000000
--- a/src/com/android/mms/service/http/NameResolver.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mms.service.http;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * An interface for DNS name resolver
- */
-public interface NameResolver {
- public InetAddress[] getAllByName(String host) throws UnknownHostException;
-}
diff --git a/src/com/android/mms/service/http/NetworkAwareClientConnectionOperator.java b/src/com/android/mms/service/http/NetworkAwareClientConnectionOperator.java
deleted file mode 100644
index ec5fa59..0000000
--- a/src/com/android/mms/service/http/NetworkAwareClientConnectionOperator.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mms.service.http;
-
-import com.android.mms.service.MmsService;
-
-import org.apache.http.HttpHost;
-import org.apache.http.conn.ConnectTimeoutException;
-import org.apache.http.conn.HttpHostConnectException;
-import org.apache.http.conn.OperatedClientConnection;
-import org.apache.http.conn.scheme.LayeredSocketFactory;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.conn.scheme.SocketFactory;
-import org.apache.http.impl.conn.DefaultClientConnectionOperator;
-import org.apache.http.params.HttpParams;
-import org.apache.http.protocol.HttpContext;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.net.ConnectException;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.SocketException;
-import java.util.ArrayList;
-
-/**
- * This is a subclass of {@link org.apache.http.impl.conn.DefaultClientConnectionOperator}
- * which allows us to use a custom name resolver and pick the address type when we resolve
- * the host name to connect.
- */
-public class NetworkAwareClientConnectionOperator extends DefaultClientConnectionOperator {
- private static final PlainSocketFactory staticPlainSocketFactory = new PlainSocketFactory();
-
- private NameResolver mResolver;
- private boolean mShouldUseIpv6;
-
- public NetworkAwareClientConnectionOperator(SchemeRegistry schemes) {
- super(schemes);
- }
-
- public void setNameResolver(NameResolver resolver) {
- mResolver = resolver;
- }
-
- public void setShouldUseIpv6(boolean value) {
- mShouldUseIpv6 = value;
- }
-
- /**
- * Resolve name by address type. Only returns IPv6 addresses if required, or IPv4 if not.
- *
- * @param hostName
- * @return The list addresses resolved
- * @throws java.io.IOException
- */
- private ArrayList<InetAddress> resolveHostName(final String hostName) throws IOException {
- final ArrayList<InetAddress> addresses = new ArrayList<InetAddress>();
- for (final InetAddress address : mResolver.getAllByName(hostName)) {
- if (mShouldUseIpv6 && address instanceof Inet6Address) {
- addresses.add(address);
- } else if (!mShouldUseIpv6 && address instanceof Inet4Address){
- addresses.add(address);
- }
- }
- return addresses;
- }
-
- /**
- * This method is mostly copied from the overridden one in parent. The only change
- * is how we resolve host name.
- */
- @Override
- public void openConnection(OperatedClientConnection conn, HttpHost target, InetAddress local,
- HttpContext context, HttpParams params) throws IOException {
- if (conn == null) {
- throw new IllegalArgumentException
- ("Connection must not be null.");
- }
- if (target == null) {
- throw new IllegalArgumentException
- ("Target host must not be null.");
- }
- // local address may be null
- //@@@ is context allowed to be null?
- if (params == null) {
- throw new IllegalArgumentException
- ("Parameters must not be null.");
- }
- if (conn.isOpen()) {
- throw new IllegalArgumentException
- ("Connection must not be open.");
- }
-
- final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
- final SocketFactory sf = schm.getSocketFactory();
- final SocketFactory plain_sf;
- final LayeredSocketFactory layered_sf;
- if (sf instanceof LayeredSocketFactory) {
- plain_sf = staticPlainSocketFactory;
- layered_sf = (LayeredSocketFactory)sf;
- } else {
- plain_sf = sf;
- layered_sf = null;
- }
- // CHANGE FOR MmsService
- ArrayList<InetAddress> addresses = resolveHostName(target.getHostName());
-
- for (int i = 0; i < addresses.size(); ++i) {
- Log.d(MmsService.TAG, "NetworkAwareClientConnectionOperator: connecting "
- + addresses.get(i));
- Socket sock = plain_sf.createSocket();
- conn.opening(sock, target);
-
- try {
- Socket connsock = plain_sf.connectSocket(sock,
- addresses.get(i).getHostAddress(),
- schm.resolvePort(target.getPort()),
- local, 0, params);
- if (sock != connsock) {
- sock = connsock;
- conn.opening(sock, target);
- }
- /*
- * prepareSocket is called on the just connected
- * socket before the creation of the layered socket to
- * ensure that desired socket options such as
- * TCP_NODELAY, SO_RCVTIMEO, SO_LINGER will be set
- * before any I/O is performed on the socket. This
- * happens in the common case as
- * SSLSocketFactory.createSocket performs hostname
- * verification which requires that SSL handshaking be
- * performed.
- */
- prepareSocket(sock, context, params);
- if (layered_sf != null) {
- Socket layeredsock = layered_sf.createSocket(sock,
- target.getHostName(),
- schm.resolvePort(target.getPort()),
- true);
- if (layeredsock != sock) {
- conn.opening(layeredsock, target);
- }
- conn.openCompleted(sf.isSecure(layeredsock), params);
- } else {
- conn.openCompleted(sf.isSecure(sock), params);
- }
- break;
- // BEGIN android-changed
- // catch SocketException to cover any kind of connect failure
- } catch (SocketException ex) {
- if (i == addresses.size() - 1) {
- ConnectException cause = ex instanceof ConnectException
- ? (ConnectException) ex :
- (ConnectException) new ConnectException(
- ex.getMessage()).initCause(ex);
- throw new HttpHostConnectException(target, cause);
- }
- // END android-changed
- } catch (ConnectTimeoutException ex) {
- if (i == addresses.size() - 1) {
- throw ex;
- }
- }
- }
- }
-}
diff --git a/src/com/android/mms/service/http/NetworkAwareHttpClient.java b/src/com/android/mms/service/http/NetworkAwareHttpClient.java
deleted file mode 100644
index 646670d..0000000
--- a/src/com/android/mms/service/http/NetworkAwareHttpClient.java
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mms.service.http;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpEntityEnclosingRequest;
-import org.apache.http.HttpException;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpRequestInterceptor;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.ResponseHandler;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.client.params.HttpClientParams;
-import org.apache.http.client.protocol.ClientContext;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.entity.AbstractHttpEntity;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.client.RequestWrapper;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
-import org.apache.http.params.HttpProtocolParams;
-import org.apache.http.protocol.BasicHttpContext;
-import org.apache.http.protocol.BasicHttpProcessor;
-import org.apache.http.protocol.HttpContext;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.net.SSLCertificateSocketFactory;
-import android.net.SSLSessionCache;
-import android.os.Looper;
-import android.util.Base64;
-import android.util.Log;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URI;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.GZIPOutputStream;
-
-/**
- * This is a copy of {@link android.net.http.AndroidHttpClient} with changes
- * to allow us using a different {@org.apache.http.conn.ClientConnectionManager}
- */
-
-/**
- * Implementation of the Apache {@link org.apache.http.impl.client.DefaultHttpClient} that is configured with
- * reasonable default settings and registered schemes for Android.
- * Don't create this directly, use the {@link #newInstance} factory method.
- *
- * <p>This client processes cookies but does not retain them by default.
- * To retain cookies, simply add a cookie store to the HttpContext:</p>
- *
- * <pre>context.setAttribute(ClientContext.COOKIE_STORE, cookieStore);</pre>
- */
-public final class NetworkAwareHttpClient implements HttpClient {
-
- // Gzip of data shorter than this probably won't be worthwhile
- public static long DEFAULT_SYNC_MIN_GZIP_BYTES = 256;
-
- // Default connection and socket timeout of 60 seconds. Tweak to taste.
- private static final int SOCKET_OPERATION_TIMEOUT = 60 * 1000;
-
- private static final String TAG = "MmsHttpClient";
-
- private static String[] textContentTypes = new String[] {
- "text/",
- "application/xml",
- "application/json"
- };
-
- /** Interceptor throws an exception if the executing thread is blocked */
- private static final HttpRequestInterceptor sThreadCheckInterceptor =
- new HttpRequestInterceptor() {
- @Override
- public void process(HttpRequest request, HttpContext context) {
- // Prevent the HttpRequest from being sent on the main thread
- if (Looper.myLooper() != null && Looper.myLooper() == Looper.getMainLooper() ) {
- throw new RuntimeException("This thread forbids HTTP requests");
- }
- }
- };
-
- /**
- * Create a new HttpClient with reasonable defaults (which you can update).
- *
- * @param userAgent to report in your HTTP requests
- * @param context to use for caching SSL sessions (may be null for no caching)
- * @return AndroidHttpClient for you to use for all your requests.
- */
- public static NetworkAwareHttpClient newInstance(String userAgent, Context context,
- NameResolver resolver, boolean shouldUseIpv6) {
- HttpParams params = new BasicHttpParams();
-
- // Turn off stale checking. Our connections break all the time anyway,
- // and it's not worth it to pay the penalty of checking every time.
- HttpConnectionParams.setStaleCheckingEnabled(params, false);
-
- HttpConnectionParams.setConnectionTimeout(params, SOCKET_OPERATION_TIMEOUT);
- HttpConnectionParams.setSoTimeout(params, SOCKET_OPERATION_TIMEOUT);
- HttpConnectionParams.setSocketBufferSize(params, 8192);
-
- // Don't handle redirects -- return them to the caller. Our code
- // often wants to re-POST after a redirect, which we must do ourselves.
- HttpClientParams.setRedirecting(params, false);
-
- // Use a session cache for SSL sockets
- SSLSessionCache sessionCache = context == null ? null : new SSLSessionCache(context);
-
- // Set the specified user agent and register standard protocols.
- HttpProtocolParams.setUserAgent(params, userAgent);
- SchemeRegistry schemeRegistry = new SchemeRegistry();
- schemeRegistry.register(new Scheme("http",
- PlainSocketFactory.getSocketFactory(), 80));
- schemeRegistry.register(new Scheme("https",
- SSLCertificateSocketFactory.getHttpSocketFactory(
- SOCKET_OPERATION_TIMEOUT, sessionCache), 443));
-
- /*
- * CHANGE FOR MmsService: using a different ClientConnectionManager which
- * uses a custom name resolver and can specify address type
- */
- ClientConnectionManager manager = new NetworkAwareThreadSafeClientConnManager(
- params, schemeRegistry, resolver, shouldUseIpv6);
-
- // We use a factory method to modify superclass initialization
- // parameters without the funny call-a-static-method dance.
- return new NetworkAwareHttpClient(manager, params);
- }
-
- private final HttpClient delegate;
-
- private RuntimeException mLeakedException = new IllegalStateException(
- "AndroidHttpClient created and never closed");
-
- private NetworkAwareHttpClient(ClientConnectionManager ccm, HttpParams params) {
- this.delegate = new DefaultHttpClient(ccm, params) {
- @Override
- protected BasicHttpProcessor createHttpProcessor() {
- // Add interceptor to prevent making requests from main thread.
- BasicHttpProcessor processor = super.createHttpProcessor();
- processor.addRequestInterceptor(sThreadCheckInterceptor);
- processor.addRequestInterceptor(new CurlLogger());
-
- return processor;
- }
-
- @Override
- protected HttpContext createHttpContext() {
- // Same as DefaultHttpClient.createHttpContext() minus the
- // cookie store.
- HttpContext context = new BasicHttpContext();
- context.setAttribute(
- ClientContext.AUTHSCHEME_REGISTRY,
- getAuthSchemes());
- context.setAttribute(
- ClientContext.COOKIESPEC_REGISTRY,
- getCookieSpecs());
- context.setAttribute(
- ClientContext.CREDS_PROVIDER,
- getCredentialsProvider());
- return context;
- }
- };
- }
-
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- if (mLeakedException != null) {
- Log.e(TAG, "Leak found", mLeakedException);
- mLeakedException = null;
- }
- }
-
- /**
- * Modifies a request to indicate to the server that we would like a
- * gzipped response. (Uses the "Accept-Encoding" HTTP header.)
- * @param request the request to modify
- * @see #getUngzippedContent
- */
- public static void modifyRequestToAcceptGzipResponse(HttpRequest request) {
- request.addHeader("Accept-Encoding", "gzip");
- }
-
- /**
- * Gets the input stream from a response entity. If the entity is gzipped
- * then this will get a stream over the uncompressed data.
- *
- * @param entity the entity whose content should be read
- * @return the input stream to read from
- * @throws java.io.IOException
- */
- public static InputStream getUngzippedContent(HttpEntity entity)
- throws IOException {
- InputStream responseStream = entity.getContent();
- if (responseStream == null) return responseStream;
- Header header = entity.getContentEncoding();
- if (header == null) return responseStream;
- String contentEncoding = header.getValue();
- if (contentEncoding == null) return responseStream;
- if (contentEncoding.contains("gzip")) responseStream
- = new GZIPInputStream(responseStream);
- return responseStream;
- }
-
- /**
- * Release resources associated with this client. You must call this,
- * or significant resources (sockets and memory) may be leaked.
- */
- public void close() {
- if (mLeakedException != null) {
- getConnectionManager().shutdown();
- mLeakedException = null;
- }
- }
-
- @Override
- public HttpParams getParams() {
- return delegate.getParams();
- }
-
- @Override
- public ClientConnectionManager getConnectionManager() {
- return delegate.getConnectionManager();
- }
-
- @Override
- public HttpResponse execute(HttpUriRequest request) throws IOException {
- return delegate.execute(request);
- }
-
- @Override
- public HttpResponse execute(HttpUriRequest request, HttpContext context)
- throws IOException {
- return delegate.execute(request, context);
- }
-
- @Override
- public HttpResponse execute(HttpHost target, HttpRequest request)
- throws IOException {
- return delegate.execute(target, request);
- }
-
- @Override
- public HttpResponse execute(HttpHost target, HttpRequest request,
- HttpContext context) throws IOException {
- return delegate.execute(target, request, context);
- }
-
- @Override
- public <T> T execute(HttpUriRequest request,
- ResponseHandler<? extends T> responseHandler)
- throws IOException, ClientProtocolException {
- return delegate.execute(request, responseHandler);
- }
-
- @Override
- public <T> T execute(HttpUriRequest request,
- ResponseHandler<? extends T> responseHandler, HttpContext context)
- throws IOException, ClientProtocolException {
- return delegate.execute(request, responseHandler, context);
- }
-
- @Override
- public <T> T execute(HttpHost target, HttpRequest request,
- ResponseHandler<? extends T> responseHandler) throws IOException,
- ClientProtocolException {
- return delegate.execute(target, request, responseHandler);
- }
-
- @Override
- public <T> T execute(HttpHost target, HttpRequest request,
- ResponseHandler<? extends T> responseHandler, HttpContext context)
- throws IOException, ClientProtocolException {
- return delegate.execute(target, request, responseHandler, context);
- }
-
- /**
- * Compress data to send to server.
- * Creates a Http Entity holding the gzipped data.
- * The data will not be compressed if it is too short.
- * @param data The bytes to compress
- * @return Entity holding the data
- */
- public static AbstractHttpEntity getCompressedEntity(byte data[], ContentResolver resolver)
- throws IOException {
- AbstractHttpEntity entity;
- if (data.length < getMinGzipSize(resolver)) {
- entity = new ByteArrayEntity(data);
- } else {
- ByteArrayOutputStream arr = new ByteArrayOutputStream();
- OutputStream zipper = new GZIPOutputStream(arr);
- zipper.write(data);
- zipper.close();
- entity = new ByteArrayEntity(arr.toByteArray());
- entity.setContentEncoding("gzip");
- }
- return entity;
- }
-
- /**
- * Retrieves the minimum size for compressing data.
- * Shorter data will not be compressed.
- */
- public static long getMinGzipSize(ContentResolver resolver) {
- return DEFAULT_SYNC_MIN_GZIP_BYTES; // For now, this is just a constant.
- }
-
- /* cURL logging support. */
-
- /**
- * Logging tag and level.
- */
- private static class LoggingConfiguration {
-
- private final String tag;
- private final int level;
-
- private LoggingConfiguration(String tag, int level) {
- this.tag = tag;
- this.level = level;
- }
-
- /**
- * Returns true if logging is turned on for this configuration.
- */
- private boolean isLoggable() {
- return Log.isLoggable(tag, level);
- }
-
- /**
- * Prints a message using this configuration.
- */
- private void println(String message) {
- Log.println(level, tag, message);
- }
- }
-
- /** cURL logging configuration. */
- private volatile LoggingConfiguration curlConfiguration;
-
- /**
- * Enables cURL request logging for this client.
- *
- * @param name to log messages with
- * @param level at which to log messages (see {@link android.util.Log})
- */
- public void enableCurlLogging(String name, int level) {
- if (name == null) {
- throw new NullPointerException("name");
- }
- if (level < Log.VERBOSE || level > Log.ASSERT) {
- throw new IllegalArgumentException("Level is out of range ["
- + Log.VERBOSE + ".." + Log.ASSERT + "]");
- }
-
- curlConfiguration = new LoggingConfiguration(name, level);
- }
-
- /**
- * Disables cURL logging for this client.
- */
- public void disableCurlLogging() {
- curlConfiguration = null;
- }
-
- /**
- * Logs cURL commands equivalent to requests.
- */
- private class CurlLogger implements HttpRequestInterceptor {
- @Override
- public void process(HttpRequest request, HttpContext context)
- throws HttpException, IOException {
- LoggingConfiguration configuration = curlConfiguration;
- if (configuration != null
- && configuration.isLoggable()
- && request instanceof HttpUriRequest) {
- // Never print auth token -- we used to check ro.secure=0 to
- // enable that, but can't do that in unbundled code.
- configuration.println(toCurl((HttpUriRequest) request, false));
- }
- }
- }
-
- /**
- * Generates a cURL command equivalent to the given request.
- */
- private static String toCurl(HttpUriRequest request, boolean logAuthToken) throws IOException {
- StringBuilder builder = new StringBuilder();
-
- builder.append("curl ");
-
- // add in the method
- builder.append("-X ");
- builder.append(request.getMethod());
- builder.append(" ");
-
- for (Header header: request.getAllHeaders()) {
- if (!logAuthToken
- && (header.getName().equals("Authorization") ||
- header.getName().equals("Cookie"))) {
- continue;
- }
- builder.append("--header \"");
- builder.append(header.toString().trim());
- builder.append("\" ");
- }
-
- URI uri = request.getURI();
-
- // If this is a wrapped request, use the URI from the original
- // request instead. getURI() on the wrapper seems to return a
- // relative URI. We want an absolute URI.
- if (request instanceof RequestWrapper) {
- HttpRequest original = ((RequestWrapper) request).getOriginal();
- if (original instanceof HttpUriRequest) {
- uri = ((HttpUriRequest) original).getURI();
- }
- }
-
- builder.append("\"");
- builder.append(uri);
- builder.append("\"");
-
- if (request instanceof HttpEntityEnclosingRequest) {
- HttpEntityEnclosingRequest entityRequest =
- (HttpEntityEnclosingRequest) request;
- HttpEntity entity = entityRequest.getEntity();
- if (entity != null && entity.isRepeatable()) {
- if (entity.getContentLength() < 1024) {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- entity.writeTo(stream);
-
- if (isBinaryContent(request)) {
- String base64 = Base64.encodeToString(stream.toByteArray(), Base64.NO_WRAP);
- builder.insert(0, "echo '" + base64 + "' | base64 -d > /tmp/$$.bin; ");
- builder.append(" --data-binary @/tmp/$$.bin");
- } else {
- String entityString = stream.toString();
- builder.append(" --data-ascii \"")
- .append(entityString)
- .append("\"");
- }
- } else {
- builder.append(" [TOO MUCH DATA TO INCLUDE]");
- }
- }
- }
-
- return builder.toString();
- }
-
- public final static String CONTENT_ENCODING = "content-encoding";
- public final static String CONTENT_TYPE = "content-type";
-
- private static boolean isBinaryContent(HttpUriRequest request) {
- Header[] headers;
- headers = request.getHeaders(CONTENT_ENCODING);
- if (headers != null) {
- for (Header header : headers) {
- if ("gzip".equalsIgnoreCase(header.getValue())) {
- return true;
- }
- }
- }
-
- headers = request.getHeaders(CONTENT_TYPE);
- if (headers != null) {
- for (Header header : headers) {
- for (String contentType : textContentTypes) {
- if (header.getValue().startsWith(contentType)) {
- return false;
- }
- }
- }
- }
- return true;
- }
-}
diff --git a/src/com/android/mms/service/http/NetworkAwareThreadSafeClientConnManager.java b/src/com/android/mms/service/http/NetworkAwareThreadSafeClientConnManager.java
deleted file mode 100644
index a9175cd..0000000
--- a/src/com/android/mms/service/http/NetworkAwareThreadSafeClientConnManager.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mms.service.http;
-
-import org.apache.http.conn.ClientConnectionOperator;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
-import org.apache.http.params.HttpParams;
-
-/**
- * This is a subclass of {@link org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager}
- * which allows us to specify a custom name resolver and the address type
- */
-public class NetworkAwareThreadSafeClientConnManager extends ThreadSafeClientConnManager {
- public NetworkAwareThreadSafeClientConnManager(HttpParams params,
- SchemeRegistry schreg, NameResolver resolver, boolean shouldUseIpv6) {
- super(params, schreg);
- ((NetworkAwareClientConnectionOperator)connOperator).setNameResolver(resolver);
- ((NetworkAwareClientConnectionOperator)connOperator).setShouldUseIpv6(shouldUseIpv6);
- }
-
- @Override
- protected ClientConnectionOperator createConnectionOperator(SchemeRegistry schreg) {
- return new NetworkAwareClientConnectionOperator(schreg);
- }
-}