am 72b40431: (-s ours) Merge "Start handshake before calling hostname verifier"

* commit '72b40431c28ad3fff177813d3bbd68e779302924':
  Start handshake before calling hostname verifier
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..9dffd55
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,116 @@
+# 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.
+#
+LOCAL_PATH:= $(call my-dir)
+
+apache_http_src_files := \
+    $(call all-java-files-under,src) \
+    $(call all-java-files-under,android)
+
+apache_http_java_libs := conscrypt
+
+apache_http_packages := $(strip \
+  com.android.internal.http.multipart \
+  org.apache.commons.logging \
+  org.apache.commons.logging.impl \
+  org.apache.commons.codec \
+  org.apache.commons.codec.net \
+  org.apache.commons.codec.language \
+  org.apache.commons.codec.binary \
+  org.apache.http.params \
+  org.apache.http \
+  org.apache.http.client.params \
+  org.apache.http.client \
+  org.apache.http.client.utils \
+  org.apache.http.client.protocol \
+  org.apache.http.client.methods \
+  org.apache.http.client.entity \
+  org.apache.http.protocol \
+  org.apache.http.impl \
+  org.apache.http.impl.client \
+  org.apache.http.impl.auth \
+  org.apache.http.impl.cookie \
+  org.apache.http.impl.entity \
+  org.apache.http.impl.io \
+  org.apache.http.impl.conn \
+  org.apache.http.impl.conn.tsccm \
+  org.apache.http.message \
+  org.apache.http.auth.params \
+  org.apache.http.auth \
+  org.apache.http.cookie.params \
+  org.apache.http.cookie \
+  org.apache.http.util \
+  org.apache.http.entity \
+  org.apache.http.io \
+  org.apache.http.conn.params \
+  org.apache.http.conn \
+  org.apache.http.conn.routing \
+  org.apache.http.conn.scheme \
+  org.apache.http.conn.util \
+  android.net.compatibility \
+  android.net.http \
+)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := org.apache.http.legacy.boot
+LOCAL_MODULE_TAGS := optional
+LOCAL_JAVA_LIBRARIES := $(apache_http_java_libs)
+LOCAL_SRC_FILES := $(apache_http_src_files)
+LOCAL_SDK_VERSION := 21
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_JAVA_LIBRARY)
+
+##############################################
+# Generate the stub source files
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(apache_http_src_files)
+LOCAL_JAVA_LIBRARIES := $(apache_http_java_libs)
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src \
+  $(LOCAL_PATH)/android
+
+LOCAL_DROIDDOC_OPTIONS:= \
+    -stubpackages $(subst $(space),:,$(apache_http_packages)) \
+    -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/org.apache.http.legacy_intermediates/src \
+    -nodocs
+
+LOCAL_SDK_VERSION := 21
+LOCAL_UNINSTALLABLE_MODULE := true
+LOCAL_MODULE := apache-http-stubs-gen
+
+include $(BUILD_DROIDDOC)
+apache_http_stubs_gen_stamp := $(full_target)
+
+# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
+ifeq (,$(TARGET_BUILD_APPS))
+###############################################
+# Build the stub source files into a jar.
+include $(CLEAR_VARS)
+LOCAL_MODULE := org.apache.http.legacy
+LOCAL_SOURCE_FILES_ALL_GENERATED := true
+LOCAL_SDK_VERSION := 21
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Make sure to run droiddoc first to generate the stub source files.
+$(full_classes_compiled_jar) : $(apache_http_stubs_gen_stamp)
+$(full_classes_jack) : $(apache_http_stubs_gen_stamp)
+
+# Archive a copy of the classes.jar in SDK build.
+$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):org.apache.http.legacy.jar)
+endif  # not TARGET_BUILD_APPS
+
+apache_http_src_files :=
+apache_http_java_libs :=
+apache_http_packages :=
+apache_http_stubs_gen_stamp :=
diff --git a/CleanSpec.mk b/CleanSpec.mk
index b84e1b6..694f8d2 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -47,3 +47,5 @@
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/org.apache.http.legacy_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/apache-http-stubs_intermediates)
diff --git a/android/src/android/net/compatibility/WebAddress.java b/android/src/android/net/compatibility/WebAddress.java
new file mode 100644
index 0000000..ca3081f
--- /dev/null
+++ b/android/src/android/net/compatibility/WebAddress.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2006 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 android.net.compatibility;
+
+import static android.util.Patterns.GOOD_IRI_CHAR;
+
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Web Address Parser
+ *
+ * This is called WebAddress, rather than URL or URI, because it
+ * attempts to parse the stuff that a user will actually type into a
+ * browser address widget.
+ *
+ * Unlike java.net.uri, this parser will not choke on URIs missing
+ * schemes.  It will only throw a IllegalArgumentException if the input is
+ * really hosed.
+ *
+ * If given an https scheme but no port, fills in port
+ *
+ */
+public class WebAddress {
+
+    private String mScheme;
+    private String mHost;
+    private int mPort;
+    private String mPath;
+    private String mAuthInfo;
+
+    static final int MATCH_GROUP_SCHEME = 1;
+    static final int MATCH_GROUP_AUTHORITY = 2;
+    static final int MATCH_GROUP_HOST = 3;
+    static final int MATCH_GROUP_PORT = 4;
+    static final int MATCH_GROUP_PATH = 5;
+
+    static Pattern sAddressPattern = Pattern.compile(
+            /* scheme    */ "(?:(http|https|file)\\:\\/\\/)?" +
+            /* authority */ "(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" +
+            /* host      */ "([" + GOOD_IRI_CHAR + "%_-][" + GOOD_IRI_CHAR + "%_\\.-]*|\\[[0-9a-fA-F:\\.]+\\])?" +
+            /* port      */ "(?:\\:([0-9]*))?" +
+            /* path      */ "(\\/?[^#]*)?" +
+            /* anchor    */ ".*", Pattern.CASE_INSENSITIVE);
+
+    /** parses given uriString. */
+    public WebAddress(String address) throws IllegalArgumentException {
+        if (address == null) {
+            throw new NullPointerException();
+        }
+
+        // android.util.Log.d(LOGTAG, "WebAddress: " + address);
+
+        mScheme = "";
+        mHost = "";
+        mPort = -1;
+        mPath = "/";
+        mAuthInfo = "";
+
+        Matcher m = sAddressPattern.matcher(address);
+        String t;
+        if (m.matches()) {
+            t = m.group(MATCH_GROUP_SCHEME);
+            if (t != null) mScheme = t.toLowerCase(Locale.ROOT);
+            t = m.group(MATCH_GROUP_AUTHORITY);
+            if (t != null) mAuthInfo = t;
+            t = m.group(MATCH_GROUP_HOST);
+            if (t != null) mHost = t;
+            t = m.group(MATCH_GROUP_PORT);
+            if (t != null && t.length() > 0) {
+                // The ':' character is not returned by the regex.
+                try {
+                    mPort = Integer.parseInt(t);
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException("Bad port");
+                }
+            }
+            t = m.group(MATCH_GROUP_PATH);
+            if (t != null && t.length() > 0) {
+                /* handle busted myspace frontpage redirect with
+                   missing initial "/" */
+                if (t.charAt(0) == '/') {
+                    mPath = t;
+                } else {
+                    mPath = "/" + t;
+                }
+            }
+
+        } else {
+            // nothing found... outa here
+            throw new IllegalArgumentException("Bad address");
+        }
+
+        /* Get port from scheme or scheme from port, if necessary and
+           possible */
+        if (mPort == 443 && mScheme.equals("")) {
+            mScheme = "https";
+        } else if (mPort == -1) {
+            if (mScheme.equals("https"))
+                mPort = 443;
+            else
+                mPort = 80; // default
+        }
+        if (mScheme.equals("")) mScheme = "http";
+    }
+
+    @Override
+    public String toString() {
+        String port = "";
+        if ((mPort != 443 && mScheme.equals("https")) ||
+            (mPort != 80 && mScheme.equals("http"))) {
+            port = ":" + Integer.toString(mPort);
+        }
+        String authInfo = "";
+        if (mAuthInfo.length() > 0) {
+            authInfo = mAuthInfo + "@";
+        }
+
+        return mScheme + "://" + authInfo + mHost + port + mPath;
+    }
+
+    public void setScheme(String scheme) {
+      mScheme = scheme;
+    }
+
+    public String getScheme() {
+      return mScheme;
+    }
+
+    public void setHost(String host) {
+      mHost = host;
+    }
+
+    public String getHost() {
+      return mHost;
+    }
+
+    public void setPort(int port) {
+      mPort = port;
+    }
+
+    public int getPort() {
+      return mPort;
+    }
+
+    public void setPath(String path) {
+      mPath = path;
+    }
+
+    public String getPath() {
+      return mPath;
+    }
+
+    public void setAuthInfo(String authInfo) {
+      mAuthInfo = authInfo;
+    }
+
+    public String getAuthInfo() {
+      return mAuthInfo;
+    }
+}
diff --git a/android/src/android/net/http/AndroidHttpClient.java b/android/src/android/net/http/AndroidHttpClient.java
new file mode 100644
index 0000000..c2e5999
--- /dev/null
+++ b/android/src/android/net/http/AndroidHttpClient.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2007 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 android.net.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.impl.conn.tsccm.ThreadSafeClientConnManager;
+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;
+
+/**
+ * Implementation of the Apache {@link 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 AndroidHttpClient 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 = "AndroidHttpClient";
+
+    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() {
+        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 AndroidHttpClient newInstance(String userAgent, Context context) {
+        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));
+
+        ClientConnectionManager manager =
+                new ThreadSafeClientConnManager(params, schemeRegistry);
+
+        // We use a factory method to modify superclass initialization
+        // parameters without the funny call-a-static-method dance.
+        return new AndroidHttpClient(manager, params);
+    }
+
+    /**
+     * Create a new HttpClient with reasonable defaults (which you can update).
+     * @param userAgent to report in your HTTP requests.
+     * @return AndroidHttpClient for you to use for all your requests.
+     */
+    public static AndroidHttpClient newInstance(String userAgent) {
+        return newInstance(userAgent, null /* session cache */);
+    }
+
+    private final HttpClient delegate;
+
+    private RuntimeException mLeakedException = new IllegalStateException(
+            "AndroidHttpClient created and never closed");
+
+    private AndroidHttpClient(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 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;
+        }
+    }
+
+    public HttpParams getParams() {
+        return delegate.getParams();
+    }
+
+    public ClientConnectionManager getConnectionManager() {
+        return delegate.getConnectionManager();
+    }
+
+    public HttpResponse execute(HttpUriRequest request) throws IOException {
+        return delegate.execute(request);
+    }
+
+    public HttpResponse execute(HttpUriRequest request, HttpContext context)
+            throws IOException {
+        return delegate.execute(request, context);
+    }
+
+    public HttpResponse execute(HttpHost target, HttpRequest request)
+            throws IOException {
+        return delegate.execute(target, request);
+    }
+
+    public HttpResponse execute(HttpHost target, HttpRequest request,
+            HttpContext context) throws IOException {
+        return delegate.execute(target, request, context);
+    }
+
+    public <T> T execute(HttpUriRequest request,
+            ResponseHandler<? extends T> responseHandler)
+            throws IOException, ClientProtocolException {
+        return delegate.execute(request, responseHandler);
+    }
+
+    public <T> T execute(HttpUriRequest request,
+            ResponseHandler<? extends T> responseHandler, HttpContext context)
+            throws IOException, ClientProtocolException {
+        return delegate.execute(request, responseHandler, context);
+    }
+
+    public <T> T execute(HttpHost target, HttpRequest request,
+            ResponseHandler<? extends T> responseHandler) throws IOException,
+            ClientProtocolException {
+        return delegate.execute(target, request, responseHandler);
+    }
+
+    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 {
+        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();
+    }
+
+    private static boolean isBinaryContent(HttpUriRequest request) {
+        Header[] headers;
+        headers = request.getHeaders(Headers.CONTENT_ENCODING);
+        if (headers != null) {
+            for (Header header : headers) {
+                if ("gzip".equalsIgnoreCase(header.getValue())) {
+                    return true;
+                }
+            }
+        }
+
+        headers = request.getHeaders(Headers.CONTENT_TYPE);
+        if (headers != null) {
+            for (Header header : headers) {
+                for (String contentType : textContentTypes) {
+                    if (header.getValue().startsWith(contentType)) {
+                        return false;
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns the date of the given HTTP date string. This method can identify
+     * and parse the date formats emitted by common HTTP servers, such as
+     * <a href="http://www.ietf.org/rfc/rfc0822.txt">RFC 822</a>,
+     * <a href="http://www.ietf.org/rfc/rfc0850.txt">RFC 850</a>,
+     * <a href="http://www.ietf.org/rfc/rfc1036.txt">RFC 1036</a>,
+     * <a href="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</a> and
+     * <a href="http://www.opengroup.org/onlinepubs/007908799/xsh/asctime.html">ANSI
+     * C's asctime()</a>.
+     *
+     * @return the number of milliseconds since Jan. 1, 1970, midnight GMT.
+     * @throws IllegalArgumentException if {@code dateString} is not a date or
+     *     of an unsupported format.
+     */
+    public static long parseDate(String dateString) {
+        return LegacyHttpDateTime.parse(dateString);
+    }
+}
diff --git a/android/src/android/net/http/AndroidHttpClientConnection.java b/android/src/android/net/http/AndroidHttpClientConnection.java
new file mode 100644
index 0000000..a90da62
--- /dev/null
+++ b/android/src/android/net/http/AndroidHttpClientConnection.java
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2008 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 android.net.http;
+
+import org.apache.http.HttpConnection;
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.StatusLine;
+import org.apache.http.entity.BasicHttpEntity;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.impl.HttpConnectionMetricsImpl;
+import org.apache.http.impl.entity.EntitySerializer;
+import org.apache.http.impl.entity.StrictContentLengthStrategy;
+import org.apache.http.impl.io.ChunkedInputStream;
+import org.apache.http.impl.io.ContentLengthInputStream;
+import org.apache.http.impl.io.HttpRequestWriter;
+import org.apache.http.impl.io.IdentityInputStream;
+import org.apache.http.impl.io.SocketInputBuffer;
+import org.apache.http.impl.io.SocketOutputBuffer;
+import org.apache.http.io.HttpMessageWriter;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.io.SessionOutputBuffer;
+import org.apache.http.message.BasicLineParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.ParseException;
+import org.apache.http.util.CharArrayBuffer;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+
+/**
+ * A alternate class for (@link DefaultHttpClientConnection).
+ * It has better performance than DefaultHttpClientConnection
+ */
+public class AndroidHttpClientConnection
+        implements HttpInetConnection, HttpConnection {
+
+    private SessionInputBuffer inbuffer = null;
+    private SessionOutputBuffer outbuffer = null;
+    private int maxHeaderCount;
+    // store CoreConnectionPNames.MAX_LINE_LENGTH for performance
+    private int maxLineLength;
+
+    private final EntitySerializer entityserializer;
+
+    private HttpMessageWriter requestWriter = null;
+    private HttpConnectionMetricsImpl metrics = null;
+    private volatile boolean open;
+    private Socket socket = null;
+
+    public AndroidHttpClientConnection() {
+        this.entityserializer =  new EntitySerializer(
+                new StrictContentLengthStrategy());
+    }
+
+    /**
+     * Bind socket and set HttpParams to AndroidHttpClientConnection
+     * @param socket outgoing socket
+     * @param params HttpParams
+     * @throws IOException
+      */
+    public void bind(
+            final Socket socket,
+            final HttpParams params) throws IOException {
+        if (socket == null) {
+            throw new IllegalArgumentException("Socket may not be null");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        assertNotOpen();
+        socket.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params));
+        socket.setSoTimeout(HttpConnectionParams.getSoTimeout(params));
+
+        int linger = HttpConnectionParams.getLinger(params);
+        if (linger >= 0) {
+            socket.setSoLinger(linger > 0, linger);
+        }
+        this.socket = socket;
+
+        int buffersize = HttpConnectionParams.getSocketBufferSize(params);
+        this.inbuffer = new SocketInputBuffer(socket, buffersize, params);
+        this.outbuffer = new SocketOutputBuffer(socket, buffersize, params);
+
+        maxHeaderCount = params.getIntParameter(
+                CoreConnectionPNames.MAX_HEADER_COUNT, -1);
+        maxLineLength = params.getIntParameter(
+                CoreConnectionPNames.MAX_LINE_LENGTH, -1);
+
+        this.requestWriter = new HttpRequestWriter(outbuffer, null, params);
+
+        this.metrics = new HttpConnectionMetricsImpl(
+                inbuffer.getMetrics(),
+                outbuffer.getMetrics());
+
+        this.open = true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append(getClass().getSimpleName()).append("[");
+        if (isOpen()) {
+            buffer.append(getRemotePort());
+        } else {
+            buffer.append("closed");
+        }
+        buffer.append("]");
+        return buffer.toString();
+    }
+
+
+    private void assertNotOpen() {
+        if (this.open) {
+            throw new IllegalStateException("Connection is already open");
+        }
+    }
+
+    private void assertOpen() {
+        if (!this.open) {
+            throw new IllegalStateException("Connection is not open");
+        }
+    }
+
+    public boolean isOpen() {
+        // to make this method useful, we want to check if the socket is connected
+        return (this.open && this.socket != null && this.socket.isConnected());
+    }
+
+    public InetAddress getLocalAddress() {
+        if (this.socket != null) {
+            return this.socket.getLocalAddress();
+        } else {
+            return null;
+        }
+    }
+
+    public int getLocalPort() {
+        if (this.socket != null) {
+            return this.socket.getLocalPort();
+        } else {
+            return -1;
+        }
+    }
+
+    public InetAddress getRemoteAddress() {
+        if (this.socket != null) {
+            return this.socket.getInetAddress();
+        } else {
+            return null;
+        }
+    }
+
+    public int getRemotePort() {
+        if (this.socket != null) {
+            return this.socket.getPort();
+        } else {
+            return -1;
+        }
+    }
+
+    public void setSocketTimeout(int timeout) {
+        assertOpen();
+        if (this.socket != null) {
+            try {
+                this.socket.setSoTimeout(timeout);
+            } catch (SocketException ignore) {
+                // It is not quite clear from the original documentation if there are any
+                // other legitimate cases for a socket exception to be thrown when setting
+                // SO_TIMEOUT besides the socket being already closed
+            }
+        }
+    }
+
+    public int getSocketTimeout() {
+        if (this.socket != null) {
+            try {
+                return this.socket.getSoTimeout();
+            } catch (SocketException ignore) {
+                return -1;
+            }
+        } else {
+            return -1;
+        }
+    }
+
+    public void shutdown() throws IOException {
+        this.open = false;
+        Socket tmpsocket = this.socket;
+        if (tmpsocket != null) {
+            tmpsocket.close();
+        }
+    }
+
+    public void close() throws IOException {
+        if (!this.open) {
+            return;
+        }
+        this.open = false;
+        doFlush();
+        try {
+            try {
+                this.socket.shutdownOutput();
+            } catch (IOException ignore) {
+            }
+            try {
+                this.socket.shutdownInput();
+            } catch (IOException ignore) {
+            }
+        } catch (UnsupportedOperationException ignore) {
+            // if one isn't supported, the other one isn't either
+        }
+        this.socket.close();
+    }
+
+    /**
+     * Sends the request line and all headers over the connection.
+     * @param request the request whose headers to send.
+     * @throws HttpException
+     * @throws IOException
+     */
+    public void sendRequestHeader(final HttpRequest request)
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        assertOpen();
+        this.requestWriter.write(request);
+        this.metrics.incrementRequestCount();
+    }
+
+    /**
+     * Sends the request entity over the connection.
+     * @param request the request whose entity to send.
+     * @throws HttpException
+     * @throws IOException
+     */
+    public void sendRequestEntity(final HttpEntityEnclosingRequest request)
+            throws HttpException, IOException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        assertOpen();
+        if (request.getEntity() == null) {
+            return;
+        }
+        this.entityserializer.serialize(
+                this.outbuffer,
+                request,
+                request.getEntity());
+    }
+
+    protected void doFlush() throws IOException {
+        this.outbuffer.flush();
+    }
+
+    public void flush() throws IOException {
+        assertOpen();
+        doFlush();
+    }
+
+    /**
+     * Parses the response headers and adds them to the
+     * given {@code headers} object, and returns the response StatusLine
+     * @param headers store parsed header to headers.
+     * @throws IOException
+     * @return StatusLine
+     * @see HttpClientConnection#receiveResponseHeader()
+      */
+    public StatusLine parseResponseHeader(Headers headers)
+            throws IOException, ParseException {
+        assertOpen();
+
+        CharArrayBuffer current = new CharArrayBuffer(64);
+
+        if (inbuffer.readLine(current) == -1) {
+            throw new NoHttpResponseException("The target server failed to respond");
+        }
+
+        // Create the status line from the status string
+        StatusLine statusline = BasicLineParser.DEFAULT.parseStatusLine(
+                current, new ParserCursor(0, current.length()));
+        
+        if (HttpLog.LOGV) HttpLog.v("read: " + statusline);
+        int statusCode = statusline.getStatusCode();
+
+        // Parse header body
+        CharArrayBuffer previous = null;
+        int headerNumber = 0;
+        while(true) {
+            if (current == null) {
+                current = new CharArrayBuffer(64);
+            } else {
+                // This must be he buffer used to parse the status
+                current.clear();
+            }
+            int l = inbuffer.readLine(current);
+            if (l == -1 || current.length() < 1) {
+                break;
+            }
+            // Parse the header name and value
+            // Check for folded headers first
+            // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2
+            // discussion on folded headers
+            char first = current.charAt(0);
+            if ((first == ' ' || first == '\t') && previous != null) {
+                // we have continuation folded header
+                // so append value
+                int start = 0;
+                int length = current.length();
+                while (start < length) {
+                    char ch = current.charAt(start);
+                    if (ch != ' ' && ch != '\t') {
+                        break;
+                    }
+                    start++;
+                }
+                if (maxLineLength > 0 &&
+                        previous.length() + 1 + current.length() - start >
+                            maxLineLength) {
+                    throw new IOException("Maximum line length limit exceeded");
+                }
+                previous.append(' ');
+                previous.append(current, start, current.length() - start);
+            } else {
+                if (previous != null) {
+                    headers.parseHeader(previous);
+                }
+                headerNumber++;
+                previous = current;
+                current = null;
+            }
+            if (maxHeaderCount > 0 && headerNumber >= maxHeaderCount) {
+                throw new IOException("Maximum header count exceeded");
+            }
+        }
+
+        if (previous != null) {
+            headers.parseHeader(previous);
+        }
+
+        if (statusCode >= 200) {
+            this.metrics.incrementResponseCount();
+        }
+        return statusline;
+    }
+
+    /**
+     * Return the next response entity.
+     * @param headers contains values for parsing entity
+     * @see HttpClientConnection#receiveResponseEntity(HttpResponse response)
+     */
+    public HttpEntity receiveResponseEntity(final Headers headers) {
+        assertOpen();
+        BasicHttpEntity entity = new BasicHttpEntity();
+
+        long len = determineLength(headers);
+        if (len == ContentLengthStrategy.CHUNKED) {
+            entity.setChunked(true);
+            entity.setContentLength(-1);
+            entity.setContent(new ChunkedInputStream(inbuffer));
+        } else if (len == ContentLengthStrategy.IDENTITY) {
+            entity.setChunked(false);
+            entity.setContentLength(-1);
+            entity.setContent(new IdentityInputStream(inbuffer));
+        } else {
+            entity.setChunked(false);
+            entity.setContentLength(len);
+            entity.setContent(new ContentLengthInputStream(inbuffer, len));
+        }
+
+        String contentTypeHeader = headers.getContentType();
+        if (contentTypeHeader != null) {
+            entity.setContentType(contentTypeHeader);
+        }
+        String contentEncodingHeader = headers.getContentEncoding();
+        if (contentEncodingHeader != null) {
+            entity.setContentEncoding(contentEncodingHeader);
+        }
+
+       return entity;
+    }
+
+    private long determineLength(final Headers headers) {
+        long transferEncoding = headers.getTransferEncoding();
+        // We use Transfer-Encoding if present and ignore Content-Length.
+        // RFC2616, 4.4 item number 3
+        if (transferEncoding < Headers.NO_TRANSFER_ENCODING) {
+            return transferEncoding;
+        } else {
+            long contentlen = headers.getContentLength();
+            if (contentlen > Headers.NO_CONTENT_LENGTH) {
+                return contentlen;
+            } else {
+                return ContentLengthStrategy.IDENTITY;
+            }
+        }
+    }
+
+    /**
+     * Checks whether this connection has gone down.
+     * Network connections may get closed during some time of inactivity
+     * for several reasons. The next time a read is attempted on such a
+     * connection it will throw an IOException.
+     * This method tries to alleviate this inconvenience by trying to
+     * find out if a connection is still usable. Implementations may do
+     * that by attempting a read with a very small timeout. Thus this
+     * method may block for a small amount of time before returning a result.
+     * It is therefore an <i>expensive</i> operation.
+     *
+     * @return  <code>true</code> if attempts to use this connection are
+     *          likely to succeed, or <code>false</code> if they are likely
+     *          to fail and this connection should be closed
+     */
+    public boolean isStale() {
+        assertOpen();
+        try {
+            this.inbuffer.isDataAvailable(1);
+            return false;
+        } catch (IOException ex) {
+            return true;
+        }
+    }
+
+    /**
+     * Returns a collection of connection metrcis
+     * @return HttpConnectionMetrics
+     */
+    public HttpConnectionMetrics getMetrics() {
+        return this.metrics;
+    }
+}
diff --git a/android/src/android/net/http/CertificateChainValidator.java b/android/src/android/net/http/CertificateChainValidator.java
new file mode 100644
index 0000000..d45e83f
--- /dev/null
+++ b/android/src/android/net/http/CertificateChainValidator.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2008 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 android.net.http;
+
+import com.android.org.conscrypt.SSLParametersImpl;
+import com.android.org.conscrypt.TrustManagerImpl;
+
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * Class responsible for all server certificate validation functionality
+ */
+public class CertificateChainValidator {
+    private static final String TAG = "CertificateChainValidator";
+
+    private static class NoPreloadHolder {
+        /**
+         * The singleton instance of the certificate chain validator.
+         */
+        private static final CertificateChainValidator sInstance = new CertificateChainValidator();
+
+        /**
+         * The singleton instance of the hostname verifier.
+         */
+        private static final HostnameVerifier sVerifier = HttpsURLConnection
+                .getDefaultHostnameVerifier();
+    }
+
+    private X509TrustManager mTrustManager;
+
+    /**
+     * @return The singleton instance of the certificates chain validator
+     */
+    public static CertificateChainValidator getInstance() {
+        return NoPreloadHolder.sInstance;
+    }
+
+    /**
+     * Creates a new certificate chain validator. This is a private constructor.
+     * If you need a Certificate chain validator, call getInstance().
+     */
+    private CertificateChainValidator() {
+        try {
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509");
+            tmf.init((KeyStore) null);
+            for (TrustManager tm : tmf.getTrustManagers()) {
+                if (tm instanceof X509TrustManager) {
+                    mTrustManager = (X509TrustManager) tm;
+                }
+            }
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("X.509 TrustManagerFactory must be available", e);
+        } catch (KeyStoreException e) {
+            throw new RuntimeException("X.509 TrustManagerFactory cannot be initialized", e);
+        }
+
+        if (mTrustManager == null) {
+            throw new RuntimeException(
+                    "None of the X.509 TrustManagers are X509TrustManager");
+        }
+    }
+
+    /**
+     * Performs the handshake and server certificates validation
+     * Notice a new chain will be rebuilt by tracing the issuer and subject
+     * before calling checkServerTrusted().
+     * And if the last traced certificate is self issued and it is expired, it
+     * will be dropped.
+     * @param sslSocket The secure connection socket
+     * @param domain The website domain
+     * @return An SSL error object if there is an error and null otherwise
+     */
+    public SslError doHandshakeAndValidateServerCertificates(
+            HttpsConnection connection, SSLSocket sslSocket, String domain)
+            throws IOException {
+        // get a valid SSLSession, close the socket if we fail
+        SSLSession sslSession = sslSocket.getSession();
+        if (!sslSession.isValid()) {
+            closeSocketThrowException(sslSocket, "failed to perform SSL handshake");
+        }
+
+        // retrieve the chain of the server peer certificates
+        Certificate[] peerCertificates =
+            sslSocket.getSession().getPeerCertificates();
+
+        if (peerCertificates == null || peerCertificates.length == 0) {
+            closeSocketThrowException(
+                sslSocket, "failed to retrieve peer certificates");
+        } else {
+            // update the SSL certificate associated with the connection
+            if (connection != null) {
+                if (peerCertificates[0] != null) {
+                    connection.setCertificate(
+                        new SslCertificate((X509Certificate)peerCertificates[0]));
+                }
+            }
+        }
+
+        return verifyServerDomainAndCertificates((X509Certificate[]) peerCertificates, domain, "RSA");
+    }
+
+    /**
+     * Similar to doHandshakeAndValidateServerCertificates but exposed to JNI for use
+     * by Chromium HTTPS stack to validate the cert chain.
+     * @param certChain The bytes for certificates in ASN.1 DER encoded certificates format.
+     * @param domain The full website hostname and domain
+     * @param authType The authentication type for the cert chain
+     * @return An SSL error object if there is an error and null otherwise
+     */
+    public static SslError verifyServerCertificates(
+        byte[][] certChain, String domain, String authType)
+        throws IOException {
+
+        if (certChain == null || certChain.length == 0) {
+            throw new IllegalArgumentException("bad certificate chain");
+        }
+
+        X509Certificate[] serverCertificates = new X509Certificate[certChain.length];
+
+        try {
+            CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            for (int i = 0; i < certChain.length; ++i) {
+                serverCertificates[i] = (X509Certificate) cf.generateCertificate(
+                        new ByteArrayInputStream(certChain[i]));
+            }
+        } catch (CertificateException e) {
+            throw new IOException("can't read certificate", e);
+        }
+
+        return verifyServerDomainAndCertificates(serverCertificates, domain, authType);
+    }
+
+    /**
+     * Handles updates to credential storage.
+     */
+    public static void handleTrustStorageUpdate() {
+        TrustManagerFactory tmf;
+        try {
+            tmf = TrustManagerFactory.getInstance("X.509");
+            tmf.init((KeyStore) null);
+        } catch (NoSuchAlgorithmException e) {
+            Log.w(TAG, "Couldn't find default X.509 TrustManagerFactory");
+            return;
+        } catch (KeyStoreException e) {
+            Log.w(TAG, "Couldn't initialize default X.509 TrustManagerFactory", e);
+            return;
+        }
+
+        TrustManager[] tms = tmf.getTrustManagers();
+        boolean sentUpdate = false;
+        for (TrustManager tm : tms) {
+            try {
+                Method updateMethod = tm.getClass().getDeclaredMethod("handleTrustStorageUpdate");
+                updateMethod.setAccessible(true);
+                updateMethod.invoke(tm);
+                sentUpdate = true;
+            } catch (Exception e) {
+            }
+        }
+        if (!sentUpdate) {
+            Log.w(TAG, "Didn't find a TrustManager to handle CA list update");
+        }
+    }
+
+    /**
+     * Common code of doHandshakeAndValidateServerCertificates and verifyServerCertificates.
+     * Calls DomainNamevalidator to verify the domain, and TrustManager to verify the certs.
+     * @param chain the cert chain in X509 cert format.
+     * @param domain The full website hostname and domain
+     * @param authType The authentication type for the cert chain
+     * @return An SSL error object if there is an error and null otherwise
+     */
+    private static SslError verifyServerDomainAndCertificates(
+            X509Certificate[] chain, String domain, String authType)
+            throws IOException {
+        // check if the first certificate in the chain is for this site
+        X509Certificate currCertificate = chain[0];
+        if (currCertificate == null) {
+            throw new IllegalArgumentException("certificate for this site is null");
+        }
+
+        boolean valid = domain != null
+                && !domain.isEmpty()
+                && NoPreloadHolder.sVerifier.verify(domain,
+                        new DelegatingSSLSession.CertificateWrap(currCertificate));
+        if (!valid) {
+            if (HttpLog.LOGV) {
+                HttpLog.v("certificate not for this host: " + domain);
+            }
+            return new SslError(SslError.SSL_IDMISMATCH, currCertificate);
+        }
+
+        try {
+            X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager();
+            if (x509TrustManager instanceof TrustManagerImpl) {
+                TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
+                trustManager.checkServerTrusted(chain, authType, domain);
+            } else {
+                x509TrustManager.checkServerTrusted(chain, authType);
+            }
+            return null;  // No errors.
+        } catch (GeneralSecurityException e) {
+            if (HttpLog.LOGV) {
+                HttpLog.v("failed to validate the certificate chain, error: " +
+                    e.getMessage());
+            }
+            return new SslError(SslError.SSL_UNTRUSTED, currCertificate);
+        }
+    }
+
+    /**
+     * Returns the platform default {@link X509TrustManager}.
+     */
+    private X509TrustManager getTrustManager() {
+        return mTrustManager;
+    }
+
+    private void closeSocketThrowException(
+            SSLSocket socket, String errorMessage, String defaultErrorMessage)
+            throws IOException {
+        closeSocketThrowException(
+            socket, errorMessage != null ? errorMessage : defaultErrorMessage);
+    }
+
+    private void closeSocketThrowException(SSLSocket socket,
+            String errorMessage) throws IOException {
+        if (HttpLog.LOGV) {
+            HttpLog.v("validation error: " + errorMessage);
+        }
+
+        if (socket != null) {
+            SSLSession session = socket.getSession();
+            if (session != null) {
+                session.invalidate();
+            }
+
+            socket.close();
+        }
+
+        throw new SSLHandshakeException(errorMessage);
+    }
+}
diff --git a/android/src/android/net/http/Connection.java b/android/src/android/net/http/Connection.java
new file mode 100644
index 0000000..4a49d22
--- /dev/null
+++ b/android/src/android/net/http/Connection.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2007 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 android.net.http;
+
+import android.content.Context;
+import android.os.SystemClock;
+
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.LinkedList;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpVersion;
+import org.apache.http.ParseException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.BasicHttpContext;
+
+abstract class Connection {
+
+    /**
+     * Allow a TCP connection 60 idle seconds before erroring out
+     */
+    static final int SOCKET_TIMEOUT = 60000;
+
+    private static final int SEND = 0;
+    private static final int READ = 1;
+    private static final int DRAIN = 2;
+    private static final int DONE = 3;
+    private static final String[] states = {"SEND",  "READ", "DRAIN", "DONE"};
+
+    Context mContext;
+
+    /** The low level connection */
+    protected AndroidHttpClientConnection mHttpClientConnection = null;
+
+    /**
+     * The server SSL certificate associated with this connection
+     * (null if the connection is not secure)
+     * It would be nice to store the whole certificate chain, but
+     * we want to keep things as light-weight as possible
+     */
+    protected SslCertificate mCertificate = null;
+
+    /**
+     * The host this connection is connected to.  If using proxy,
+     * this is set to the proxy address
+     */
+    HttpHost mHost;
+
+    /** true if the connection can be reused for sending more requests */
+    private boolean mCanPersist;
+
+    /** context required by ConnectionReuseStrategy. */
+    private HttpContext mHttpContext;
+
+    /** set when cancelled */
+    private static int STATE_NORMAL = 0;
+    private static int STATE_CANCEL_REQUESTED = 1;
+    private int mActive = STATE_NORMAL;
+
+    /** The number of times to try to re-connect (if connect fails). */
+    private final static int RETRY_REQUEST_LIMIT = 2;
+
+    private static final int MIN_PIPE = 2;
+    private static final int MAX_PIPE = 3;
+
+    /**
+     * Doesn't seem to exist anymore in the new HTTP client, so copied here.
+     */
+    private static final String HTTP_CONNECTION = "http.connection";
+
+    RequestFeeder mRequestFeeder;
+
+    /**
+     * Buffer for feeding response blocks to webkit.  One block per
+     * connection reduces memory churn.
+     */
+    private byte[] mBuf;
+
+    protected Connection(Context context, HttpHost host,
+                         RequestFeeder requestFeeder) {
+        mContext = context;
+        mHost = host;
+        mRequestFeeder = requestFeeder;
+
+        mCanPersist = false;
+        mHttpContext = new BasicHttpContext(null);
+    }
+
+    HttpHost getHost() {
+        return mHost;
+    }
+
+    /**
+     * connection factory: returns an HTTP or HTTPS connection as
+     * necessary
+     */
+    static Connection getConnection(
+            Context context, HttpHost host, HttpHost proxy,
+            RequestFeeder requestFeeder) {
+
+        if (host.getSchemeName().equals("http")) {
+            return new HttpConnection(context, host, requestFeeder);
+        }
+
+        // Otherwise, default to https
+        return new HttpsConnection(context, host, proxy, requestFeeder);
+    }
+
+    /**
+     * @return The server SSL certificate associated with this
+     * connection (null if the connection is not secure)
+     */
+    /* package */ SslCertificate getCertificate() {
+        return mCertificate;
+    }
+
+    /**
+     * Close current network connection
+     * Note: this runs in non-network thread
+     */
+    void cancel() {
+        mActive = STATE_CANCEL_REQUESTED;
+        closeConnection();
+        if (HttpLog.LOGV) HttpLog.v(
+            "Connection.cancel(): connection closed " + mHost);
+    }
+
+    /**
+     * Process requests in queue
+     * pipelines requests
+     */
+    void processRequests(Request firstRequest) {
+        Request req = null;
+        boolean empty;
+        int error = EventHandler.OK;
+        Exception exception = null;
+
+        LinkedList<Request> pipe = new LinkedList<Request>();
+
+        int minPipe = MIN_PIPE, maxPipe = MAX_PIPE;
+        int state = SEND;
+
+        while (state != DONE) {
+            if (HttpLog.LOGV) HttpLog.v(
+                    states[state] + " pipe " + pipe.size());
+
+            /* If a request was cancelled, give other cancel requests
+               some time to go through so we don't uselessly restart
+               connections */
+            if (mActive == STATE_CANCEL_REQUESTED) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException x) { /* ignore */ }
+                mActive = STATE_NORMAL;
+            }
+
+            switch (state) {
+                case SEND: {
+                    if (pipe.size() == maxPipe) {
+                        state = READ;
+                        break;
+                    }
+                    /* get a request */
+                    if (firstRequest == null) {
+                        req = mRequestFeeder.getRequest(mHost);
+                    } else {
+                        req = firstRequest;
+                        firstRequest = null;
+                    }
+                    if (req == null) {
+                        state = DRAIN;
+                        break;
+                    }
+                    req.setConnection(this);
+
+                    /* Don't work on cancelled requests. */
+                    if (req.mCancelled) {
+                        if (HttpLog.LOGV) HttpLog.v(
+                                "processRequests(): skipping cancelled request "
+                                + req);
+                        req.complete();
+                        break;
+                    }
+
+                    if (mHttpClientConnection == null ||
+                        !mHttpClientConnection.isOpen()) {
+                        /* If this call fails, the address is bad or
+                           the net is down.  Punt for now.
+
+                           FIXME: blow out entire queue here on
+                           connection failure if net up? */
+
+                        if (!openHttpConnection(req)) {
+                            state = DONE;
+                            break;
+                        }
+                    }
+
+                    /* we have a connection, let the event handler
+                     * know of any associated certificate,
+                     * potentially none.
+                     */
+                    req.mEventHandler.certificate(mCertificate);
+
+                    try {
+                        /* FIXME: don't increment failure count if old
+                           connection?  There should not be a penalty for
+                           attempting to reuse an old connection */
+                        req.sendRequest(mHttpClientConnection);
+                    } catch (HttpException e) {
+                        exception = e;
+                        error = EventHandler.ERROR;
+                    } catch (IOException e) {
+                        exception = e;
+                        error = EventHandler.ERROR_IO;
+                    } catch (IllegalStateException e) {
+                        exception = e;
+                        error = EventHandler.ERROR_IO;
+                    }
+                    if (exception != null) {
+                        if (httpFailure(req, error, exception) &&
+                            !req.mCancelled) {
+                            /* retry request if not permanent failure
+                               or cancelled */
+                            pipe.addLast(req);
+                        }
+                        exception = null;
+                        state = clearPipe(pipe) ? DONE : SEND;
+                        minPipe = maxPipe = 1;
+                        break;
+                    }
+
+                    pipe.addLast(req);
+                    if (!mCanPersist) state = READ;
+                    break;
+
+                }
+                case DRAIN:
+                case READ: {
+                    empty = !mRequestFeeder.haveRequest(mHost);
+                    int pipeSize = pipe.size();
+                    if (state != DRAIN && pipeSize < minPipe &&
+                        !empty && mCanPersist) {
+                        state = SEND;
+                        break;
+                    } else if (pipeSize == 0) {
+                        /* Done if no other work to do */
+                        state = empty ? DONE : SEND;
+                        break;
+                    }
+
+                    req = (Request)pipe.removeFirst();
+                    if (HttpLog.LOGV) HttpLog.v(
+                            "processRequests() reading " + req);
+
+                    try {
+                        req.readResponse(mHttpClientConnection);
+                    } catch (ParseException e) {
+                        exception = e;
+                        error = EventHandler.ERROR_IO;
+                    } catch (IOException e) {
+                        exception = e;
+                        error = EventHandler.ERROR_IO;
+                    } catch (IllegalStateException e) {
+                        exception = e;
+                        error = EventHandler.ERROR_IO;
+                    }
+                    if (exception != null) {
+                        if (httpFailure(req, error, exception) &&
+                            !req.mCancelled) {
+                            /* retry request if not permanent failure
+                               or cancelled */
+                            req.reset();
+                            pipe.addFirst(req);
+                        }
+                        exception = null;
+                        mCanPersist = false;
+                    }
+                    if (!mCanPersist) {
+                        if (HttpLog.LOGV) HttpLog.v(
+                                "processRequests(): no persist, closing " +
+                                mHost);
+
+                        closeConnection();
+
+                        mHttpContext.removeAttribute(HTTP_CONNECTION);
+                        clearPipe(pipe);
+                        minPipe = maxPipe = 1;
+                        state = SEND;
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * After a send/receive failure, any pipelined requests must be
+     * cleared back to the mRequest queue
+     * @return true if mRequests is empty after pipe cleared
+     */
+    private boolean clearPipe(LinkedList<Request> pipe) {
+        boolean empty = true;
+        if (HttpLog.LOGV) HttpLog.v(
+                "Connection.clearPipe(): clearing pipe " + pipe.size());
+        synchronized (mRequestFeeder) {
+            Request tReq;
+            while (!pipe.isEmpty()) {
+                tReq = (Request)pipe.removeLast();
+                if (HttpLog.LOGV) HttpLog.v(
+                        "clearPipe() adding back " + mHost + " " + tReq);
+                mRequestFeeder.requeueRequest(tReq);
+                empty = false;
+            }
+            if (empty) empty = !mRequestFeeder.haveRequest(mHost);
+        }
+        return empty;
+    }
+
+    /**
+     * @return true on success
+     */
+    private boolean openHttpConnection(Request req) {
+
+        long now = SystemClock.uptimeMillis();
+        int error = EventHandler.OK;
+        Exception exception = null;
+
+        try {
+            // reset the certificate to null before opening a connection
+            mCertificate = null;
+            mHttpClientConnection = openConnection(req);
+            if (mHttpClientConnection != null) {
+                mHttpClientConnection.setSocketTimeout(SOCKET_TIMEOUT);
+                mHttpContext.setAttribute(HTTP_CONNECTION,
+                                          mHttpClientConnection);
+            } else {
+                // we tried to do SSL tunneling, failed,
+                // and need to drop the request;
+                // we have already informed the handler
+                req.mFailCount = RETRY_REQUEST_LIMIT;
+                return false;
+            }
+        } catch (UnknownHostException e) {
+            if (HttpLog.LOGV) HttpLog.v("Failed to open connection");
+            error = EventHandler.ERROR_LOOKUP;
+            exception = e;
+        } catch (IllegalArgumentException e) {
+            if (HttpLog.LOGV) HttpLog.v("Illegal argument exception");
+            error = EventHandler.ERROR_CONNECT;
+            req.mFailCount = RETRY_REQUEST_LIMIT;
+            exception = e;
+        } catch (SSLConnectionClosedByUserException e) {
+            // hack: if we have an SSL connection failure,
+            // we don't want to reconnect
+            req.mFailCount = RETRY_REQUEST_LIMIT;
+            // no error message
+            return false;
+        } catch (SSLHandshakeException e) {
+            // hack: if we have an SSL connection failure,
+            // we don't want to reconnect
+            req.mFailCount = RETRY_REQUEST_LIMIT;
+            if (HttpLog.LOGV) HttpLog.v(
+                    "SSL exception performing handshake");
+            error = EventHandler.ERROR_FAILED_SSL_HANDSHAKE;
+            exception = e;
+        } catch (IOException e) {
+            error = EventHandler.ERROR_CONNECT;
+            exception = e;
+        }
+
+        if (HttpLog.LOGV) {
+            long now2 = SystemClock.uptimeMillis();
+            HttpLog.v("Connection.openHttpConnection() " +
+                      (now2 - now) + " " + mHost);
+        }
+
+        if (error == EventHandler.OK) {
+            return true;
+        } else {
+            if (req.mFailCount < RETRY_REQUEST_LIMIT) {
+                // requeue
+                mRequestFeeder.requeueRequest(req);
+                req.mFailCount++;
+            } else {
+                httpFailure(req, error, exception);
+            }
+            return error == EventHandler.OK;
+        }
+    }
+
+    /**
+     * Helper.  Calls the mEventHandler's error() method only if
+     * request failed permanently.  Increments mFailcount on failure.
+     *
+     * Increments failcount only if the network is believed to be
+     * connected
+     *
+     * @return true if request can be retried (less than
+     * RETRY_REQUEST_LIMIT failures have occurred).
+     */
+    private boolean httpFailure(Request req, int errorId, Exception e) {
+        boolean ret = true;
+
+        // e.printStackTrace();
+        if (HttpLog.LOGV) HttpLog.v(
+                "httpFailure() ******* " + e + " count " + req.mFailCount +
+                " " + mHost + " " + req.getUri());
+
+        if (++req.mFailCount >= RETRY_REQUEST_LIMIT) {
+            ret = false;
+            String error;
+            if (errorId < 0) {
+                error = getEventHandlerErrorString(errorId);
+            } else {
+                Throwable cause = e.getCause();
+                error = cause != null ? cause.toString() : e.getMessage();
+            }
+            req.mEventHandler.error(errorId, error);
+            req.complete();
+        }
+
+        closeConnection();
+        mHttpContext.removeAttribute(HTTP_CONNECTION);
+
+        return ret;
+    }
+
+    private static String getEventHandlerErrorString(int errorId) {
+        switch (errorId) {
+            case EventHandler.OK:
+                return "OK";
+
+            case EventHandler.ERROR:
+                return "ERROR";
+
+            case EventHandler.ERROR_LOOKUP:
+                return "ERROR_LOOKUP";
+
+            case EventHandler.ERROR_UNSUPPORTED_AUTH_SCHEME:
+                return "ERROR_UNSUPPORTED_AUTH_SCHEME";
+
+            case EventHandler.ERROR_AUTH:
+                return "ERROR_AUTH";
+
+            case EventHandler.ERROR_PROXYAUTH:
+                return "ERROR_PROXYAUTH";
+
+            case EventHandler.ERROR_CONNECT:
+                return "ERROR_CONNECT";
+
+            case EventHandler.ERROR_IO:
+                return "ERROR_IO";
+
+            case EventHandler.ERROR_TIMEOUT:
+                return "ERROR_TIMEOUT";
+
+            case EventHandler.ERROR_REDIRECT_LOOP:
+                return "ERROR_REDIRECT_LOOP";
+
+            case EventHandler.ERROR_UNSUPPORTED_SCHEME:
+                return "ERROR_UNSUPPORTED_SCHEME";
+
+            case EventHandler.ERROR_FAILED_SSL_HANDSHAKE:
+                return "ERROR_FAILED_SSL_HANDSHAKE";
+
+            case EventHandler.ERROR_BAD_URL:
+                return "ERROR_BAD_URL";
+
+            case EventHandler.FILE_ERROR:
+                return "FILE_ERROR";
+
+            case EventHandler.FILE_NOT_FOUND_ERROR:
+                return "FILE_NOT_FOUND_ERROR";
+
+            case EventHandler.TOO_MANY_REQUESTS_ERROR:
+                return "TOO_MANY_REQUESTS_ERROR";
+
+            default:
+                return "UNKNOWN_ERROR";
+        }
+    }
+
+    HttpContext getHttpContext() {
+        return mHttpContext;
+    }
+
+    /**
+     * Use same logic as ConnectionReuseStrategy
+     * @see ConnectionReuseStrategy
+     */
+    private boolean keepAlive(HttpEntity entity,
+            ProtocolVersion ver, int connType, final HttpContext context) {
+        org.apache.http.HttpConnection conn = (org.apache.http.HttpConnection)
+            context.getAttribute(ExecutionContext.HTTP_CONNECTION);
+
+        if (conn != null && !conn.isOpen())
+            return false;
+        // do NOT check for stale connection, that is an expensive operation
+
+        if (entity != null) {
+            if (entity.getContentLength() < 0) {
+                if (!entity.isChunked() || ver.lessEquals(HttpVersion.HTTP_1_0)) {
+                    // if the content length is not known and is not chunk
+                    // encoded, the connection cannot be reused
+                    return false;
+                }
+            }
+        }
+        // Check for 'Connection' directive
+        if (connType == Headers.CONN_CLOSE) {
+            return false;
+        } else if (connType == Headers.CONN_KEEP_ALIVE) {
+            return true;
+        }
+        // Resorting to protocol version default close connection policy
+        return !ver.lessEquals(HttpVersion.HTTP_1_0);
+    }
+
+    void setCanPersist(HttpEntity entity, ProtocolVersion ver, int connType) {
+        mCanPersist = keepAlive(entity, ver, connType, mHttpContext);
+    }
+
+    void setCanPersist(boolean canPersist) {
+        mCanPersist = canPersist;
+    }
+
+    boolean getCanPersist() {
+        return mCanPersist;
+    }
+
+    /** typically http or https... set by subclass */
+    abstract String getScheme();
+    abstract void closeConnection();
+    abstract AndroidHttpClientConnection openConnection(Request req) throws IOException;
+
+    /**
+     * Prints request queue to log, for debugging.
+     * returns request count
+     */
+    public synchronized String toString() {
+        return mHost.toString();
+    }
+
+    byte[] getBuf() {
+        if (mBuf == null) mBuf = new byte[8192];
+        return mBuf;
+    }
+
+}
diff --git a/android/src/android/net/http/ConnectionThread.java b/android/src/android/net/http/ConnectionThread.java
new file mode 100644
index 0000000..3a8cd05
--- /dev/null
+++ b/android/src/android/net/http/ConnectionThread.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2008 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 android.net.http;
+
+import android.content.Context;
+import android.os.SystemClock;
+
+import java.lang.Thread;
+
+class ConnectionThread extends Thread {
+
+    static final int WAIT_TIMEOUT = 5000;
+    static final int WAIT_TICK = 1000;
+
+    // Performance probe
+    long mCurrentThreadTime;
+    long mTotalThreadTime;
+
+    private boolean mWaiting;
+    private volatile boolean mRunning = true;
+    private Context mContext;
+    private RequestQueue.ConnectionManager mConnectionManager;
+    private RequestFeeder mRequestFeeder;
+
+    private int mId;
+    Connection mConnection;
+
+    ConnectionThread(Context context,
+                     int id,
+                     RequestQueue.ConnectionManager connectionManager,
+                     RequestFeeder requestFeeder) {
+        super();
+        mContext = context;
+        setName("http" + id);
+        mId = id;
+        mConnectionManager = connectionManager;
+        mRequestFeeder = requestFeeder;
+    }
+
+    void requestStop() {
+        synchronized (mRequestFeeder) {
+            mRunning = false;
+            mRequestFeeder.notify();
+        }
+    }
+
+    /**
+     * Loop until app shutdown. Runs connections in priority
+     * order.
+     */
+    public void run() {
+        android.os.Process.setThreadPriority(
+                android.os.Process.THREAD_PRIORITY_DEFAULT +
+                android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
+
+        // these are used to get performance data. When it is not in the timing,
+        // mCurrentThreadTime is 0. When it starts timing, mCurrentThreadTime is
+        // first set to -1, it will be set to the current thread time when the
+        // next request starts.
+        mCurrentThreadTime = 0;
+        mTotalThreadTime = 0;
+
+        while (mRunning) {
+            if (mCurrentThreadTime == -1) {
+                mCurrentThreadTime = SystemClock.currentThreadTimeMillis();
+            }
+
+            Request request;
+
+            /* Get a request to process */
+            request = mRequestFeeder.getRequest();
+
+            /* wait for work */
+            if (request == null) {
+                synchronized(mRequestFeeder) {
+                    if (HttpLog.LOGV) HttpLog.v("ConnectionThread: Waiting for work");
+                    mWaiting = true;
+                    try {
+                        mRequestFeeder.wait();
+                    } catch (InterruptedException e) {
+                    }
+                    mWaiting = false;
+                    if (mCurrentThreadTime != 0) {
+                        mCurrentThreadTime = SystemClock
+                                .currentThreadTimeMillis();
+                    }
+                }
+            } else {
+                if (HttpLog.LOGV) HttpLog.v("ConnectionThread: new request " +
+                                            request.mHost + " " + request );
+
+                mConnection = mConnectionManager.getConnection(mContext,
+                        request.mHost);
+                mConnection.processRequests(request);
+                if (mConnection.getCanPersist()) {
+                    if (!mConnectionManager.recycleConnection(mConnection)) {
+                        mConnection.closeConnection();
+                    }
+                } else {
+                    mConnection.closeConnection();
+                }
+                mConnection = null;
+
+                if (mCurrentThreadTime > 0) {
+                    long start = mCurrentThreadTime;
+                    mCurrentThreadTime = SystemClock.currentThreadTimeMillis();
+                    mTotalThreadTime += mCurrentThreadTime - start;
+                }
+            }
+
+        }
+    }
+
+    public synchronized String toString() {
+        String con = mConnection == null ? "" : mConnection.toString();
+        String active = mWaiting ? "w" : "a";
+        return "cid " + mId + " " + active + " "  + con;
+    }
+
+}
diff --git a/android/src/android/net/http/DelegatingSSLSession.java b/android/src/android/net/http/DelegatingSSLSession.java
new file mode 100644
index 0000000..48692d7
--- /dev/null
+++ b/android/src/android/net/http/DelegatingSSLSession.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 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 android.net.http;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * This is only used when a {@code certificate} is available but usage
+ * requires a {@link SSLSession}.
+ */
+public class DelegatingSSLSession implements SSLSession {
+    protected DelegatingSSLSession() {
+    }
+
+    public static class CertificateWrap extends DelegatingSSLSession {
+        private final Certificate mCertificate;
+
+        public CertificateWrap(Certificate certificate) {
+            mCertificate = certificate;
+        }
+
+        @Override
+        public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+            return new Certificate[] { mCertificate };
+        }
+    }
+
+
+    @Override
+    public int getApplicationBufferSize() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getCipherSuite() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public long getCreationTime() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public byte[] getId() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public long getLastAccessedTime() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Certificate[] getLocalCertificates() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Principal getLocalPrincipal() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getPacketBufferSize() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+            throws SSLPeerUnverifiedException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getPeerHost() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getPeerPort() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getProtocol() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public SSLSessionContext getSessionContext() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Object getValue(String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String[] getValueNames() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void invalidate() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isValid() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void putValue(String name, Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void removeValue(String name) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/android/src/android/net/http/EventHandler.java b/android/src/android/net/http/EventHandler.java
new file mode 100644
index 0000000..df9a5d6
--- /dev/null
+++ b/android/src/android/net/http/EventHandler.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2006 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 android.net.http;
+
+
+/**
+ * Callbacks in this interface are made as an HTTP request is
+ * processed. The normal order of callbacks is status(), headers(),
+ * then multiple data() then endData().  handleSslErrorRequest(), if
+ * there is an SSL certificate error. error() can occur anywhere
+ * in the transaction.
+ */
+
+public interface EventHandler {
+
+    /**
+     * Error codes used in the error() callback.  Positive error codes
+     * are reserved for codes sent by http servers.  Negative error
+     * codes are connection/parsing failures, etc.
+     */
+
+    /** Success */
+    public static final int OK = 0;
+    /** Generic error */
+    public static final int ERROR = -1;
+    /** Server or proxy hostname lookup failed */
+    public static final int ERROR_LOOKUP = -2;
+    /** Unsupported authentication scheme (ie, not basic or digest) */
+    public static final int ERROR_UNSUPPORTED_AUTH_SCHEME = -3;
+    /** User authentication failed on server */
+    public static final int ERROR_AUTH = -4;
+    /** User authentication failed on proxy */
+    public static final int ERROR_PROXYAUTH = -5;
+    /** Could not connect to server */
+    public static final int ERROR_CONNECT = -6;
+    /** Failed to write to or read from server */
+    public static final int ERROR_IO = -7;
+    /** Connection timed out */
+    public static final int ERROR_TIMEOUT = -8;
+    /** Too many redirects */
+    public static final int ERROR_REDIRECT_LOOP = -9;
+    /** Unsupported URI scheme (ie, not http, https, etc) */
+    public static final int ERROR_UNSUPPORTED_SCHEME = -10;
+    /** Failed to perform SSL handshake */
+    public static final int ERROR_FAILED_SSL_HANDSHAKE = -11;
+    /** Bad URL */
+    public static final int ERROR_BAD_URL = -12;
+    /** Generic file error for file:/// loads */
+    public static final int FILE_ERROR = -13;
+    /** File not found error for file:/// loads */
+    public static final int FILE_NOT_FOUND_ERROR = -14;
+    /** Too many requests queued */
+    public static final int TOO_MANY_REQUESTS_ERROR = -15;
+
+    /**
+     * Called after status line has been sucessfully processed.
+     * @param major_version HTTP version advertised by server.  major
+     * is the part before the "."
+     * @param minor_version HTTP version advertised by server.  minor
+     * is the part after the "."
+     * @param code HTTP Status code.  See RFC 2616.
+     * @param reason_phrase Textual explanation sent by server
+     */
+    public void status(int major_version,
+                       int minor_version,
+                       int code,
+                       String reason_phrase);
+
+    /**
+     * Called after all headers are successfully processed.
+     */
+    public void headers(Headers headers);
+
+    /**
+     * An array containing all or part of the http body as read from
+     * the server.
+     * @param data A byte array containing the content
+     * @param len The length of valid content in data
+     *
+     * Note: chunked and compressed encodings are handled within
+     * android.net.http.  Decoded data is passed through this
+     * interface.
+     */
+    public void data(byte[] data, int len);
+
+    /**
+     * Called when the document is completely read.  No more data()
+     * callbacks will be made after this call
+     */
+    public void endData();
+
+    /**
+     * SSL certificate callback called before resource request is
+     * made, which will be null for insecure connection.
+     */
+    public void certificate(SslCertificate certificate);
+
+    /**
+     * There was trouble.
+     * @param id One of the error codes defined below
+     * @param description of error
+     */
+    public void error(int id, String description);
+
+    /**
+     * SSL certificate error callback. Handles SSL error(s) on the way
+     * up to the user. The callback has to make sure that restartConnection() is called,
+     * otherwise the connection will be suspended indefinitely.
+     * @return True if the callback can handle the error, which means it will
+     *              call restartConnection() to unblock the thread later,
+     *              otherwise return false.
+     */
+    public boolean handleSslErrorRequest(SslError error);
+
+}
diff --git a/android/src/android/net/http/Headers.java b/android/src/android/net/http/Headers.java
new file mode 100644
index 0000000..f5b1044
--- /dev/null
+++ b/android/src/android/net/http/Headers.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2006 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 android.net.http;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+
+import org.apache.http.HeaderElement;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.message.BasicHeaderValueParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Manages received headers
+ */
+public final class Headers {
+    private static final String LOGTAG = "Http";
+
+    // header parsing constant
+    /**
+     * indicate HTTP 1.0 connection close after the response
+     */
+    public final static int CONN_CLOSE = 1;
+    /**
+     * indicate HTTP 1.1 connection keep alive
+     */
+    public final static int CONN_KEEP_ALIVE = 2;
+
+    // initial values.
+    public final static int NO_CONN_TYPE = 0;
+    public final static long NO_TRANSFER_ENCODING = 0;
+    public final static long NO_CONTENT_LENGTH = -1;
+
+    // header strings
+    public final static String TRANSFER_ENCODING = "transfer-encoding";
+    public final static String CONTENT_LEN = "content-length";
+    public final static String CONTENT_TYPE = "content-type";
+    public final static String CONTENT_ENCODING = "content-encoding";
+    public final static String CONN_DIRECTIVE = "connection";
+
+    public final static String LOCATION = "location";
+    public final static String PROXY_CONNECTION = "proxy-connection";
+
+    public final static String WWW_AUTHENTICATE = "www-authenticate";
+    public final static String PROXY_AUTHENTICATE = "proxy-authenticate";
+    public final static String CONTENT_DISPOSITION = "content-disposition";
+    public final static String ACCEPT_RANGES = "accept-ranges";
+    public final static String EXPIRES = "expires";
+    public final static String CACHE_CONTROL = "cache-control";
+    public final static String LAST_MODIFIED = "last-modified";
+    public final static String ETAG = "etag";
+    public final static String SET_COOKIE = "set-cookie";
+    public final static String PRAGMA = "pragma";
+    public final static String REFRESH = "refresh";
+    public final static String X_PERMITTED_CROSS_DOMAIN_POLICIES = "x-permitted-cross-domain-policies";
+
+    // following hash are generated by String.hashCode()
+    private final static int HASH_TRANSFER_ENCODING = 1274458357;
+    private final static int HASH_CONTENT_LEN = -1132779846;
+    private final static int HASH_CONTENT_TYPE = 785670158;
+    private final static int HASH_CONTENT_ENCODING = 2095084583;
+    private final static int HASH_CONN_DIRECTIVE = -775651618;
+    private final static int HASH_LOCATION = 1901043637;
+    private final static int HASH_PROXY_CONNECTION = 285929373;
+    private final static int HASH_WWW_AUTHENTICATE = -243037365;
+    private final static int HASH_PROXY_AUTHENTICATE = -301767724;
+    private final static int HASH_CONTENT_DISPOSITION = -1267267485;
+    private final static int HASH_ACCEPT_RANGES = 1397189435;
+    private final static int HASH_EXPIRES = -1309235404;
+    private final static int HASH_CACHE_CONTROL = -208775662;
+    private final static int HASH_LAST_MODIFIED = 150043680;
+    private final static int HASH_ETAG = 3123477;
+    private final static int HASH_SET_COOKIE = 1237214767;
+    private final static int HASH_PRAGMA = -980228804;
+    private final static int HASH_REFRESH = 1085444827;
+    private final static int HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES = -1345594014;
+
+    // keep any headers that require direct access in a presized
+    // string array
+    private final static int IDX_TRANSFER_ENCODING = 0;
+    private final static int IDX_CONTENT_LEN = 1;
+    private final static int IDX_CONTENT_TYPE = 2;
+    private final static int IDX_CONTENT_ENCODING = 3;
+    private final static int IDX_CONN_DIRECTIVE = 4;
+    private final static int IDX_LOCATION = 5;
+    private final static int IDX_PROXY_CONNECTION = 6;
+    private final static int IDX_WWW_AUTHENTICATE = 7;
+    private final static int IDX_PROXY_AUTHENTICATE = 8;
+    private final static int IDX_CONTENT_DISPOSITION = 9;
+    private final static int IDX_ACCEPT_RANGES = 10;
+    private final static int IDX_EXPIRES = 11;
+    private final static int IDX_CACHE_CONTROL = 12;
+    private final static int IDX_LAST_MODIFIED = 13;
+    private final static int IDX_ETAG = 14;
+    private final static int IDX_SET_COOKIE = 15;
+    private final static int IDX_PRAGMA = 16;
+    private final static int IDX_REFRESH = 17;
+    private final static int IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES = 18;
+
+    private final static int HEADER_COUNT = 19;
+
+    /* parsed values */
+    private long transferEncoding;
+    private long contentLength; // Content length of the incoming data
+    private int connectionType;
+    private ArrayList<String> cookies = new ArrayList<String>(2);
+
+    private String[] mHeaders = new String[HEADER_COUNT];
+    private final static String[] sHeaderNames = {
+        TRANSFER_ENCODING,
+        CONTENT_LEN,
+        CONTENT_TYPE,
+        CONTENT_ENCODING,
+        CONN_DIRECTIVE,
+        LOCATION,
+        PROXY_CONNECTION,
+        WWW_AUTHENTICATE,
+        PROXY_AUTHENTICATE,
+        CONTENT_DISPOSITION,
+        ACCEPT_RANGES,
+        EXPIRES,
+        CACHE_CONTROL,
+        LAST_MODIFIED,
+        ETAG,
+        SET_COOKIE,
+        PRAGMA,
+        REFRESH,
+        X_PERMITTED_CROSS_DOMAIN_POLICIES
+    };
+
+    // Catch-all for headers not explicitly handled
+    private ArrayList<String> mExtraHeaderNames = new ArrayList<String>(4);
+    private ArrayList<String> mExtraHeaderValues = new ArrayList<String>(4);
+
+    public Headers() {
+        transferEncoding = NO_TRANSFER_ENCODING;
+        contentLength = NO_CONTENT_LENGTH;
+        connectionType = NO_CONN_TYPE;
+    }
+
+    public void parseHeader(CharArrayBuffer buffer) {
+        int pos = setLowercaseIndexOf(buffer, ':');
+        if (pos == -1) {
+            return;
+        }
+        String name = buffer.substringTrimmed(0, pos);
+        if (name.length() == 0) {
+            return;
+        }
+        pos++;
+
+        String val = buffer.substringTrimmed(pos, buffer.length());
+        if (HttpLog.LOGV) {
+            HttpLog.v("hdr " + buffer.length() + " " + buffer);
+        }
+
+        switch (name.hashCode()) {
+        case HASH_TRANSFER_ENCODING:
+            if (name.equals(TRANSFER_ENCODING)) {
+                mHeaders[IDX_TRANSFER_ENCODING] = val;
+                HeaderElement[] encodings = BasicHeaderValueParser.DEFAULT
+                        .parseElements(buffer, new ParserCursor(pos,
+                                buffer.length()));
+                // The chunked encoding must be the last one applied RFC2616,
+                // 14.41
+                int len = encodings.length;
+                if (HTTP.IDENTITY_CODING.equalsIgnoreCase(val)) {
+                    transferEncoding = ContentLengthStrategy.IDENTITY;
+                } else if ((len > 0)
+                        && (HTTP.CHUNK_CODING
+                                .equalsIgnoreCase(encodings[len - 1].getName()))) {
+                    transferEncoding = ContentLengthStrategy.CHUNKED;
+                } else {
+                    transferEncoding = ContentLengthStrategy.IDENTITY;
+                }
+            }
+            break;
+        case HASH_CONTENT_LEN:
+            if (name.equals(CONTENT_LEN)) {
+                mHeaders[IDX_CONTENT_LEN] = val;
+                try {
+                    contentLength = Long.parseLong(val);
+                } catch (NumberFormatException e) {
+                    if (false) {
+                        Log.v(LOGTAG, "Headers.headers(): error parsing"
+                                + " content length: " + buffer.toString());
+                    }
+                }
+            }
+            break;
+        case HASH_CONTENT_TYPE:
+            if (name.equals(CONTENT_TYPE)) {
+                mHeaders[IDX_CONTENT_TYPE] = val;
+            }
+            break;
+        case HASH_CONTENT_ENCODING:
+            if (name.equals(CONTENT_ENCODING)) {
+                mHeaders[IDX_CONTENT_ENCODING] = val;
+            }
+            break;
+        case HASH_CONN_DIRECTIVE:
+            if (name.equals(CONN_DIRECTIVE)) {
+                mHeaders[IDX_CONN_DIRECTIVE] = val;
+                setConnectionType(buffer, pos);
+            }
+            break;
+        case HASH_LOCATION:
+            if (name.equals(LOCATION)) {
+                mHeaders[IDX_LOCATION] = val;
+            }
+            break;
+        case HASH_PROXY_CONNECTION:
+            if (name.equals(PROXY_CONNECTION)) {
+                mHeaders[IDX_PROXY_CONNECTION] = val;
+                setConnectionType(buffer, pos);
+            }
+            break;
+        case HASH_WWW_AUTHENTICATE:
+            if (name.equals(WWW_AUTHENTICATE)) {
+                mHeaders[IDX_WWW_AUTHENTICATE] = val;
+            }
+            break;
+        case HASH_PROXY_AUTHENTICATE:
+            if (name.equals(PROXY_AUTHENTICATE)) {
+                mHeaders[IDX_PROXY_AUTHENTICATE] = val;
+            }
+            break;
+        case HASH_CONTENT_DISPOSITION:
+            if (name.equals(CONTENT_DISPOSITION)) {
+                mHeaders[IDX_CONTENT_DISPOSITION] = val;
+            }
+            break;
+        case HASH_ACCEPT_RANGES:
+            if (name.equals(ACCEPT_RANGES)) {
+                mHeaders[IDX_ACCEPT_RANGES] = val;
+            }
+            break;
+        case HASH_EXPIRES:
+            if (name.equals(EXPIRES)) {
+                mHeaders[IDX_EXPIRES] = val;
+            }
+            break;
+        case HASH_CACHE_CONTROL:
+            if (name.equals(CACHE_CONTROL)) {
+                // In case where we receive more than one header, create a ',' separated list.
+                // This should be ok, according to RFC 2616 chapter 4.2
+                if (mHeaders[IDX_CACHE_CONTROL] != null &&
+                    mHeaders[IDX_CACHE_CONTROL].length() > 0) {
+                    mHeaders[IDX_CACHE_CONTROL] += (',' + val);
+                } else {
+                    mHeaders[IDX_CACHE_CONTROL] = val;
+                }
+            }
+            break;
+        case HASH_LAST_MODIFIED:
+            if (name.equals(LAST_MODIFIED)) {
+                mHeaders[IDX_LAST_MODIFIED] = val;
+            }
+            break;
+        case HASH_ETAG:
+            if (name.equals(ETAG)) {
+                mHeaders[IDX_ETAG] = val;
+            }
+            break;
+        case HASH_SET_COOKIE:
+            if (name.equals(SET_COOKIE)) {
+                mHeaders[IDX_SET_COOKIE] = val;
+                cookies.add(val);
+            }
+            break;
+        case HASH_PRAGMA:
+            if (name.equals(PRAGMA)) {
+                mHeaders[IDX_PRAGMA] = val;
+            }
+            break;
+        case HASH_REFRESH:
+            if (name.equals(REFRESH)) {
+                mHeaders[IDX_REFRESH] = val;
+            }
+            break;
+        case HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES:
+            if (name.equals(X_PERMITTED_CROSS_DOMAIN_POLICIES)) {
+                mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = val;
+            }
+            break;
+        default:
+            mExtraHeaderNames.add(name);
+            mExtraHeaderValues.add(val);
+        }
+    }
+
+    public long getTransferEncoding() {
+        return transferEncoding;
+    }
+
+    public long getContentLength() {
+        return contentLength;
+    }
+
+    public int getConnectionType() {
+        return connectionType;
+    }
+
+    public String getContentType() {
+        return mHeaders[IDX_CONTENT_TYPE];
+    }
+
+    public String getContentEncoding() {
+        return mHeaders[IDX_CONTENT_ENCODING];
+    }
+
+    public String getLocation() {
+        return mHeaders[IDX_LOCATION];
+    }
+
+    public String getWwwAuthenticate() {
+        return mHeaders[IDX_WWW_AUTHENTICATE];
+    }
+
+    public String getProxyAuthenticate() {
+        return mHeaders[IDX_PROXY_AUTHENTICATE];
+    }
+
+    public String getContentDisposition() {
+        return mHeaders[IDX_CONTENT_DISPOSITION];
+    }
+
+    public String getAcceptRanges() {
+        return mHeaders[IDX_ACCEPT_RANGES];
+    }
+
+    public String getExpires() {
+        return mHeaders[IDX_EXPIRES];
+    }
+
+    public String getCacheControl() {
+        return mHeaders[IDX_CACHE_CONTROL];
+    }
+
+    public String getLastModified() {
+        return mHeaders[IDX_LAST_MODIFIED];
+    }
+
+    public String getEtag() {
+        return mHeaders[IDX_ETAG];
+    }
+
+    public ArrayList<String> getSetCookie() {
+        return this.cookies;
+    }
+
+    public String getPragma() {
+        return mHeaders[IDX_PRAGMA];
+    }
+
+    public String getRefresh() {
+        return mHeaders[IDX_REFRESH];
+    }
+
+    public String getXPermittedCrossDomainPolicies() {
+        return mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES];
+    }
+
+    public void setContentLength(long value) {
+        this.contentLength = value;
+    }
+
+    public void setContentType(String value) {
+        mHeaders[IDX_CONTENT_TYPE] = value;
+    }
+
+    public void setContentEncoding(String value) {
+        mHeaders[IDX_CONTENT_ENCODING] = value;
+    }
+
+    public void setLocation(String value) {
+        mHeaders[IDX_LOCATION] = value;
+    }
+
+    public void setWwwAuthenticate(String value) {
+        mHeaders[IDX_WWW_AUTHENTICATE] = value;
+    }
+
+    public void setProxyAuthenticate(String value) {
+        mHeaders[IDX_PROXY_AUTHENTICATE] = value;
+    }
+
+    public void setContentDisposition(String value) {
+        mHeaders[IDX_CONTENT_DISPOSITION] = value;
+    }
+
+    public void setAcceptRanges(String value) {
+        mHeaders[IDX_ACCEPT_RANGES] = value;
+    }
+
+    public void setExpires(String value) {
+        mHeaders[IDX_EXPIRES] = value;
+    }
+
+    public void setCacheControl(String value) {
+        mHeaders[IDX_CACHE_CONTROL] = value;
+    }
+
+    public void setLastModified(String value) {
+        mHeaders[IDX_LAST_MODIFIED] = value;
+    }
+
+    public void setEtag(String value) {
+        mHeaders[IDX_ETAG] = value;
+    }
+
+    public void setXPermittedCrossDomainPolicies(String value) {
+        mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = value;
+    }
+
+    public interface HeaderCallback {
+        public void header(String name, String value);
+    }
+
+    /**
+     * Reports all non-null headers to the callback
+     */
+    public void getHeaders(HeaderCallback hcb) {
+        for (int i = 0; i < HEADER_COUNT; i++) {
+            String h = mHeaders[i];
+            if (h != null) {
+                hcb.header(sHeaderNames[i], h);
+            }
+        }
+        int extraLen = mExtraHeaderNames.size();
+        for (int i = 0; i < extraLen; i++) {
+            if (false) {
+                HttpLog.v("Headers.getHeaders() extra: " + i + " " +
+                          mExtraHeaderNames.get(i) + " " + mExtraHeaderValues.get(i));
+            }
+            hcb.header(mExtraHeaderNames.get(i),
+                       mExtraHeaderValues.get(i));
+        }
+
+    }
+
+    private void setConnectionType(CharArrayBuffer buffer, int pos) {
+        if (containsIgnoreCaseTrimmed(buffer, pos, HTTP.CONN_CLOSE)) {
+            connectionType = CONN_CLOSE;
+        } else if (containsIgnoreCaseTrimmed(
+                buffer, pos, HTTP.CONN_KEEP_ALIVE)) {
+            connectionType = CONN_KEEP_ALIVE;
+        }
+    }
+
+
+    /**
+     * Returns true if the buffer contains the given string. Ignores leading
+     * whitespace and case.
+     *
+     * @param buffer to search
+     * @param beginIndex index at which we should start
+     * @param str to search for
+     */
+    static boolean containsIgnoreCaseTrimmed(CharArrayBuffer buffer,
+            int beginIndex, final String str) {
+        int len = buffer.length();
+        char[] chars = buffer.buffer();
+        while (beginIndex < len && HTTP.isWhitespace(chars[beginIndex])) {
+            beginIndex++;
+        }
+        int size = str.length();
+        boolean ok = len >= (beginIndex + size);
+        for (int j=0; ok && (j < size); j++) {
+            char a = chars[beginIndex + j];
+            char b = str.charAt(j);
+            if (a != b) {
+                a = Character.toLowerCase(a);
+                b = Character.toLowerCase(b);
+                ok = a == b;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns index of first occurence ch. Lower cases characters leading up
+     * to first occurrence of ch.
+     */
+    static int setLowercaseIndexOf(CharArrayBuffer buffer, final int ch) {
+
+        int beginIndex = 0;
+        int endIndex = buffer.length();
+        char[] chars = buffer.buffer();
+
+        for (int i = beginIndex; i < endIndex; i++) {
+            char current = chars[i];
+            if (current == ch) {
+                return i;
+            } else {
+                chars[i] = Character.toLowerCase(current);
+            }
+        }
+        return -1;
+    }
+}
diff --git a/android/src/android/net/http/HttpAuthHeader.java b/android/src/android/net/http/HttpAuthHeader.java
new file mode 100644
index 0000000..2a1ee98
--- /dev/null
+++ b/android/src/android/net/http/HttpAuthHeader.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2007 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 android.net.http;
+
+import java.util.Locale;
+
+/**
+ * HttpAuthHeader: a class to store HTTP authentication-header parameters.
+ * For more information, see: RFC 2617: HTTP Authentication.
+ */
+public class HttpAuthHeader {
+    /**
+     * Possible HTTP-authentication header tokens to search for:
+     */
+    public final static String BASIC_TOKEN = "Basic";
+    public final static String DIGEST_TOKEN = "Digest";
+
+    private final static String REALM_TOKEN = "realm";
+    private final static String NONCE_TOKEN = "nonce";
+    private final static String STALE_TOKEN = "stale";
+    private final static String OPAQUE_TOKEN = "opaque";
+    private final static String QOP_TOKEN = "qop";
+    private final static String ALGORITHM_TOKEN = "algorithm";
+
+    /**
+     * An authentication scheme. We currently support two different schemes:
+     * HttpAuthHeader.BASIC  - basic, and
+     * HttpAuthHeader.DIGEST - digest (algorithm=MD5, QOP="auth").
+     */
+    private int mScheme;
+
+    public static final int UNKNOWN = 0;
+    public static final int BASIC = 1;
+    public static final int DIGEST = 2;
+
+    /**
+     * A flag, indicating that the previous request from the client was
+     * rejected because the nonce value was stale. If stale is TRUE
+     * (case-insensitive), the client may wish to simply retry the request
+     * with a new encrypted response, without reprompting the user for a
+     * new username and password.
+     */
+    private boolean mStale;
+
+    /**
+     * A string to be displayed to users so they know which username and
+     * password to use.
+     */
+    private String mRealm;
+
+    /**
+     * A server-specified data string which should be uniquely generated
+     * each time a 401 response is made.
+     */
+    private String mNonce;
+
+    /**
+     * A string of data, specified by the server, which should be returned
+     *  by the client unchanged in the Authorization header of subsequent
+     * requests with URIs in the same protection space.
+     */
+    private String mOpaque;
+
+    /**
+     * This directive is optional, but is made so only for backward
+     * compatibility with RFC 2069 [6]; it SHOULD be used by all
+     * implementations compliant with this version of the Digest scheme.
+     * If present, it is a quoted string of one or more tokens indicating
+     * the "quality of protection" values supported by the server.  The
+     * value "auth" indicates authentication; the value "auth-int"
+     * indicates authentication with integrity protection.
+     */
+    private String mQop;
+
+    /**
+     * A string indicating a pair of algorithms used to produce the digest
+     * and a checksum. If this is not present it is assumed to be "MD5".
+     */
+    private String mAlgorithm;
+
+    /**
+     * Is this authentication request a proxy authentication request?
+     */
+    private boolean mIsProxy;
+
+    /**
+     * Username string we get from the user.
+     */
+    private String mUsername;
+
+    /**
+     * Password string we get from the user.
+     */
+    private String mPassword;
+
+    /**
+     * Creates a new HTTP-authentication header object from the
+     * input header string.
+     * The header string is assumed to contain parameters of at
+     * most one authentication-scheme (ensured by the caller).
+     */
+    public HttpAuthHeader(String header) {
+        if (header != null) {
+            parseHeader(header);
+        }
+    }
+
+    /**
+     * @return True iff this is a proxy authentication header.
+     */
+    public boolean isProxy() {
+        return mIsProxy;
+    }
+
+    /**
+     * Marks this header as a proxy authentication header.
+     */
+    public void setProxy() {
+        mIsProxy = true;
+    }
+
+    /**
+     * @return The username string.
+     */
+    public String getUsername() {
+        return mUsername;
+    }
+
+    /**
+     * Sets the username string.
+     */
+    public void setUsername(String username) {
+        mUsername = username;
+    }
+
+    /**
+     * @return The password string.
+     */
+    public String getPassword() {
+        return mPassword;
+    }
+
+    /**
+     * Sets the password string.
+     */
+    public void setPassword(String password) {
+        mPassword = password;
+    }
+
+    /**
+     * @return True iff this is the  BASIC-authentication request.
+     */
+    public boolean isBasic () {
+        return mScheme == BASIC;
+    }
+
+    /**
+     * @return True iff this is the DIGEST-authentication request.
+     */
+    public boolean isDigest() {
+        return mScheme == DIGEST;
+    }
+
+    /**
+     * @return The authentication scheme requested. We currently
+     * support two schemes:
+     * HttpAuthHeader.BASIC  - basic, and
+     * HttpAuthHeader.DIGEST - digest (algorithm=MD5, QOP="auth").
+     */
+    public int getScheme() {
+        return mScheme;
+    }
+
+    /**
+     * @return True if indicating that the previous request from
+     * the client was rejected because the nonce value was stale.
+     */
+    public boolean getStale() {
+        return mStale;
+    }
+
+    /**
+     * @return The realm value or null if there is none.
+     */
+    public String getRealm() {
+        return mRealm;
+    }
+
+    /**
+     * @return The nonce value or null if there is none.
+     */
+    public String getNonce() {
+        return mNonce;
+    }
+
+    /**
+     * @return The opaque value or null if there is none.
+     */
+    public String getOpaque() {
+        return mOpaque;
+    }
+
+    /**
+     * @return The QOP ("quality-of_protection") value or null if
+     * there is none. The QOP value is always lower-case.
+     */
+    public String getQop() {
+        return mQop;
+    }
+
+    /**
+     * @return The name of the algorithm used or null if there is
+     * none. By default, MD5 is used.
+     */
+    public String getAlgorithm() {
+        return mAlgorithm;
+    }
+
+    /**
+     * @return True iff the authentication scheme requested by the
+     * server is supported; currently supported schemes:
+     * BASIC,
+     * DIGEST (only algorithm="md5", no qop or qop="auth).
+     */
+    public boolean isSupportedScheme() {
+        // it is a good idea to enforce non-null realms!
+        if (mRealm != null) {
+            if (mScheme == BASIC) {
+                return true;
+            } else {
+                if (mScheme == DIGEST) {
+                    return
+                        mAlgorithm.equals("md5") &&
+                        (mQop == null || mQop.equals("auth"));
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Parses the header scheme name and then scheme parameters if
+     * the scheme is supported.
+     */
+    private void parseHeader(String header) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("HttpAuthHeader.parseHeader(): header: " + header);
+        }
+
+        if (header != null) {
+            String parameters = parseScheme(header);
+            if (parameters != null) {
+                // if we have a supported scheme
+                if (mScheme != UNKNOWN) {
+                    parseParameters(parameters);
+                }
+            }
+        }
+    }
+
+    /**
+     * Parses the authentication scheme name. If we have a Digest
+     * scheme, sets the algorithm value to the default of MD5.
+     * @return The authentication scheme parameters string to be
+     * parsed later (if the scheme is supported) or null if failed
+     * to parse the scheme (the header value is null?).
+     */
+    private String parseScheme(String header) {
+        if (header != null) {
+            int i = header.indexOf(' ');
+            if (i >= 0) {
+                String scheme = header.substring(0, i).trim();
+                if (scheme.equalsIgnoreCase(DIGEST_TOKEN)) {
+                    mScheme = DIGEST;
+
+                    // md5 is the default algorithm!!!
+                    mAlgorithm = "md5";
+                } else {
+                    if (scheme.equalsIgnoreCase(BASIC_TOKEN)) {
+                        mScheme = BASIC;
+                    }
+                }
+
+                return header.substring(i + 1);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Parses a comma-separated list of authentification scheme
+     * parameters.
+     */
+    private void parseParameters(String parameters) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("HttpAuthHeader.parseParameters():" +
+                      " parameters: " + parameters);
+        }
+
+        if (parameters != null) {
+            int i;
+            do {
+                i = parameters.indexOf(',');
+                if (i < 0) {
+                    // have only one parameter
+                    parseParameter(parameters);
+                } else {
+                    parseParameter(parameters.substring(0, i));
+                    parameters = parameters.substring(i + 1);
+                }
+            } while (i >= 0);
+        }
+    }
+
+    /**
+     * Parses a single authentication scheme parameter. The parameter
+     * string is expected to follow the format: PARAMETER=VALUE.
+     */
+    private void parseParameter(String parameter) {
+        if (parameter != null) {
+            // here, we are looking for the 1st occurence of '=' only!!!
+            int i = parameter.indexOf('=');
+            if (i >= 0) {
+                String token = parameter.substring(0, i).trim();
+                String value =
+                    trimDoubleQuotesIfAny(parameter.substring(i + 1).trim());
+
+                if (HttpLog.LOGV) {
+                    HttpLog.v("HttpAuthHeader.parseParameter():" +
+                              " token: " + token +
+                              " value: " + value);
+                }
+
+                if (token.equalsIgnoreCase(REALM_TOKEN)) {
+                    mRealm = value;
+                } else {
+                    if (mScheme == DIGEST) {
+                        parseParameter(token, value);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * If the token is a known parameter name, parses and initializes
+     * the token value.
+     */
+    private void parseParameter(String token, String value) {
+        if (token != null && value != null) {
+            if (token.equalsIgnoreCase(NONCE_TOKEN)) {
+                mNonce = value;
+                return;
+            }
+
+            if (token.equalsIgnoreCase(STALE_TOKEN)) {
+                parseStale(value);
+                return;
+            }
+
+            if (token.equalsIgnoreCase(OPAQUE_TOKEN)) {
+                mOpaque = value;
+                return;
+            }
+
+            if (token.equalsIgnoreCase(QOP_TOKEN)) {
+                mQop = value.toLowerCase(Locale.ROOT);
+                return;
+            }
+
+            if (token.equalsIgnoreCase(ALGORITHM_TOKEN)) {
+                mAlgorithm = value.toLowerCase(Locale.ROOT);
+                return;
+            }
+        }
+    }
+
+    /**
+     * Parses and initializes the 'stale' paramer value. Any value
+     * different from case-insensitive "true" is considered "false".
+     */
+    private void parseStale(String value) {
+        if (value != null) {
+            if (value.equalsIgnoreCase("true")) {
+                mStale = true;
+            }
+        }
+    }
+
+    /**
+     * Trims double-quotes around a parameter value if there are any.
+     * @return The string value without the outermost pair of double-
+     * quotes or null if the original value is null.
+     */
+    static private String trimDoubleQuotesIfAny(String value) {
+        if (value != null) {
+            int len = value.length();
+            if (len > 2 &&
+                value.charAt(0) == '\"' && value.charAt(len - 1) == '\"') {
+                return value.substring(1, len - 1);
+            }
+        }
+
+        return value;
+    }
+}
diff --git a/android/src/android/net/http/HttpConnection.java b/android/src/android/net/http/HttpConnection.java
new file mode 100644
index 0000000..316c1a8
--- /dev/null
+++ b/android/src/android/net/http/HttpConnection.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 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 android.net.http;
+
+import android.content.Context;
+
+import java.net.Socket;
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+
+/**
+ * A requestConnection connecting to a normal (non secure) http server
+ */
+class HttpConnection extends Connection {
+
+    HttpConnection(Context context, HttpHost host,
+                   RequestFeeder requestFeeder) {
+        super(context, host, requestFeeder);
+    }
+
+    /**
+     * Opens the connection to a http server
+     *
+     * @return the opened low level connection
+     * @throws IOException if the connection fails for any reason.
+     */
+    @Override
+    AndroidHttpClientConnection openConnection(Request req) throws IOException {
+
+        // Update the certificate info (connection not secure - set to null)
+        EventHandler eventHandler = req.getEventHandler();
+        mCertificate = null;
+        eventHandler.certificate(mCertificate);
+
+        AndroidHttpClientConnection conn = new AndroidHttpClientConnection();
+        BasicHttpParams params = new BasicHttpParams();
+        Socket sock = new Socket(mHost.getHostName(), mHost.getPort());
+        params.setIntParameter(HttpConnectionParams.SOCKET_BUFFER_SIZE, 8192);
+        conn.bind(sock, params);
+        return conn;
+    }
+
+    /**
+     * Closes the low level connection.
+     *
+     * If an exception is thrown then it is assumed that the
+     * connection will have been closed (to the extent possible)
+     * anyway and the caller does not need to take any further action.
+     *
+     */
+    void closeConnection() {
+        try {
+            if (mHttpClientConnection != null && mHttpClientConnection.isOpen()) {
+                mHttpClientConnection.close();
+            }
+        } catch (IOException e) {
+            if (HttpLog.LOGV) HttpLog.v(
+                    "closeConnection(): failed closing connection " +
+                    mHost);
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Restart a secure connection suspended waiting for user interaction.
+     */
+    void restartConnection(boolean abort) {
+        // not required for plain http connections
+    }
+
+    String getScheme() {
+        return "http";
+    }
+}
diff --git a/android/src/android/net/http/HttpLog.java b/android/src/android/net/http/HttpLog.java
new file mode 100644
index 0000000..60b7673
--- /dev/null
+++ b/android/src/android/net/http/HttpLog.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2007 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-level logging flag
+ */
+
+package android.net.http;
+
+import android.os.SystemClock;
+
+import android.util.Log;
+
+class HttpLog {
+    private final static String LOGTAG = "http";
+
+    private static final boolean DEBUG = false;
+    static final boolean LOGV = false;
+
+    static void v(String logMe) {
+        Log.v(LOGTAG, SystemClock.uptimeMillis() + " " + Thread.currentThread().getName() + " " + logMe);
+    }
+
+    static void e(String logMe) {
+        Log.e(LOGTAG, logMe);
+    }
+}
diff --git a/android/src/android/net/http/HttpsConnection.java b/android/src/android/net/http/HttpsConnection.java
new file mode 100644
index 0000000..213537b
--- /dev/null
+++ b/android/src/android/net/http/HttpsConnection.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2007 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 android.net.http;
+
+import android.content.Context;
+import android.util.Log;
+import com.android.org.conscrypt.FileClientSessionCache;
+import com.android.org.conscrypt.OpenSSLContextImpl;
+import com.android.org.conscrypt.SSLClientSessionCache;
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpStatus;
+import org.apache.http.ParseException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.File;
+import java.io.IOException;
+import java.net.Socket;
+import java.security.KeyManagementException;
+import java.security.cert.X509Certificate;
+import java.util.Locale;
+
+/**
+ * A Connection connecting to a secure http server or tunneling through
+ * a http proxy server to a https server.
+ */
+public class HttpsConnection extends Connection {
+
+    /**
+     * SSL socket factory
+     */
+    private static SSLSocketFactory mSslSocketFactory = null;
+
+    static {
+        // This initialization happens in the zygote. It triggers some
+        // lazy initialization that can will benefit later invocations of
+        // initializeEngine().
+        initializeEngine(null);
+    }
+
+    /**
+     * @param sessionDir directory to cache SSL sessions
+     */
+    public static void initializeEngine(File sessionDir) {
+        try {
+            SSLClientSessionCache cache = null;
+            if (sessionDir != null) {
+                Log.d("HttpsConnection", "Caching SSL sessions in "
+                        + sessionDir + ".");
+                cache = FileClientSessionCache.usingDirectory(sessionDir);
+            }
+
+            OpenSSLContextImpl sslContext = OpenSSLContextImpl.getPreferred();
+
+            // here, trust managers is a single trust-all manager
+            TrustManager[] trustManagers = new TrustManager[] {
+                new X509TrustManager() {
+                    public X509Certificate[] getAcceptedIssuers() {
+                        return null;
+                    }
+
+                    public void checkClientTrusted(
+                        X509Certificate[] certs, String authType) {
+                    }
+
+                    public void checkServerTrusted(
+                        X509Certificate[] certs, String authType) {
+                    }
+                }
+            };
+
+            sslContext.engineInit(null, trustManagers, null);
+            sslContext.engineGetClientSessionContext().setPersistentCache(cache);
+
+            synchronized (HttpsConnection.class) {
+                mSslSocketFactory = sslContext.engineGetSocketFactory();
+            }
+        } catch (KeyManagementException e) {
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private synchronized static SSLSocketFactory getSocketFactory() {
+        return mSslSocketFactory;
+    }
+
+    /**
+     * Object to wait on when suspending the SSL connection
+     */
+    private Object mSuspendLock = new Object();
+
+    /**
+     * True if the connection is suspended pending the result of asking the
+     * user about an error.
+     */
+    private boolean mSuspended = false;
+
+    /**
+     * True if the connection attempt should be aborted due to an ssl
+     * error.
+     */
+    private boolean mAborted = false;
+
+    // Used when connecting through a proxy.
+    private HttpHost mProxyHost;
+
+    /**
+     * Contructor for a https connection.
+     */
+    HttpsConnection(Context context, HttpHost host, HttpHost proxy,
+                    RequestFeeder requestFeeder) {
+        super(context, host, requestFeeder);
+        mProxyHost = proxy;
+    }
+
+    /**
+     * Sets the server SSL certificate associated with this
+     * connection.
+     * @param certificate The SSL certificate
+     */
+    /* package */ void setCertificate(SslCertificate certificate) {
+        mCertificate = certificate;
+    }
+
+    /**
+     * Opens the connection to a http server or proxy.
+     *
+     * @return the opened low level connection
+     * @throws IOException if the connection fails for any reason.
+     */
+    @Override
+    AndroidHttpClientConnection openConnection(Request req) throws IOException {
+        SSLSocket sslSock = null;
+
+        if (mProxyHost != null) {
+            // If we have a proxy set, we first send a CONNECT request
+            // to the proxy; if the proxy returns 200 OK, we negotiate
+            // a secure connection to the target server via the proxy.
+            // If the request fails, we drop it, but provide the event
+            // handler with the response status and headers. The event
+            // handler is then responsible for cancelling the load or
+            // issueing a new request.
+            AndroidHttpClientConnection proxyConnection = null;
+            Socket proxySock = null;
+            try {
+                proxySock = new Socket
+                    (mProxyHost.getHostName(), mProxyHost.getPort());
+
+                proxySock.setSoTimeout(60 * 1000);
+
+                proxyConnection = new AndroidHttpClientConnection();
+                HttpParams params = new BasicHttpParams();
+                HttpConnectionParams.setSocketBufferSize(params, 8192);
+
+                proxyConnection.bind(proxySock, params);
+            } catch(IOException e) {
+                if (proxyConnection != null) {
+                    proxyConnection.close();
+                }
+
+                String errorMessage = e.getMessage();
+                if (errorMessage == null) {
+                    errorMessage =
+                        "failed to establish a connection to the proxy";
+                }
+
+                throw new IOException(errorMessage);
+            }
+
+            StatusLine statusLine = null;
+            int statusCode = 0;
+            Headers headers = new Headers();
+            try {
+                BasicHttpRequest proxyReq = new BasicHttpRequest
+                    ("CONNECT", mHost.toHostString());
+
+                // add all 'proxy' headers from the original request, we also need
+                // to add 'host' header unless we want proxy to answer us with a
+                // 400 Bad Request
+                for (Header h : req.mHttpRequest.getAllHeaders()) {
+                    String headerName = h.getName().toLowerCase(Locale.ROOT);
+                    if (headerName.startsWith("proxy") || headerName.equals("keep-alive")
+                            || headerName.equals("host")) {
+                        proxyReq.addHeader(h);
+                    }
+                }
+
+                proxyConnection.sendRequestHeader(proxyReq);
+                proxyConnection.flush();
+
+                // it is possible to receive informational status
+                // codes prior to receiving actual headers;
+                // all those status codes are smaller than OK 200
+                // a loop is a standard way of dealing with them
+                do {
+                    statusLine = proxyConnection.parseResponseHeader(headers);
+                    statusCode = statusLine.getStatusCode();
+                } while (statusCode < HttpStatus.SC_OK);
+            } catch (ParseException e) {
+                String errorMessage = e.getMessage();
+                if (errorMessage == null) {
+                    errorMessage =
+                        "failed to send a CONNECT request";
+                }
+
+                throw new IOException(errorMessage);
+            } catch (HttpException e) {
+                String errorMessage = e.getMessage();
+                if (errorMessage == null) {
+                    errorMessage =
+                        "failed to send a CONNECT request";
+                }
+
+                throw new IOException(errorMessage);
+            } catch (IOException e) {
+                String errorMessage = e.getMessage();
+                if (errorMessage == null) {
+                    errorMessage =
+                        "failed to send a CONNECT request";
+                }
+
+                throw new IOException(errorMessage);
+            }
+
+            if (statusCode == HttpStatus.SC_OK) {
+                try {
+                    sslSock = (SSLSocket) getSocketFactory().createSocket(
+                            proxySock, mHost.getHostName(), mHost.getPort(), true);
+                } catch(IOException e) {
+                    if (sslSock != null) {
+                        sslSock.close();
+                    }
+
+                    String errorMessage = e.getMessage();
+                    if (errorMessage == null) {
+                        errorMessage =
+                            "failed to create an SSL socket";
+                    }
+                    throw new IOException(errorMessage);
+                }
+            } else {
+                // if the code is not OK, inform the event handler
+                ProtocolVersion version = statusLine.getProtocolVersion();
+
+                req.mEventHandler.status(version.getMajor(),
+                                         version.getMinor(),
+                                         statusCode,
+                                         statusLine.getReasonPhrase());
+                req.mEventHandler.headers(headers);
+                req.mEventHandler.endData();
+
+                proxyConnection.close();
+
+                // here, we return null to indicate that the original
+                // request needs to be dropped
+                return null;
+            }
+        } else {
+            // if we do not have a proxy, we simply connect to the host
+            try {
+                sslSock = (SSLSocket) getSocketFactory().createSocket(
+                        mHost.getHostName(), mHost.getPort());
+                sslSock.setSoTimeout(SOCKET_TIMEOUT);
+            } catch(IOException e) {
+                if (sslSock != null) {
+                    sslSock.close();
+                }
+
+                String errorMessage = e.getMessage();
+                if (errorMessage == null) {
+                    errorMessage = "failed to create an SSL socket";
+                }
+
+                throw new IOException(errorMessage);
+            }
+        }
+
+        // do handshake and validate server certificates
+        SslError error = CertificateChainValidator.getInstance().
+            doHandshakeAndValidateServerCertificates(this, sslSock, mHost.getHostName());
+
+        // Inform the user if there is a problem
+        if (error != null) {
+            // handleSslErrorRequest may immediately unsuspend if it wants to
+            // allow the certificate anyway.
+            // So we mark the connection as suspended, call handleSslErrorRequest
+            // then check if we're still suspended and only wait if we actually
+            // need to.
+            synchronized (mSuspendLock) {
+                mSuspended = true;
+            }
+            // don't hold the lock while calling out to the event handler
+            boolean canHandle = req.getEventHandler().handleSslErrorRequest(error);
+            if(!canHandle) {
+                throw new IOException("failed to handle "+ error);
+            }
+            synchronized (mSuspendLock) {
+                if (mSuspended) {
+                    try {
+                        // Put a limit on how long we are waiting; if the timeout
+                        // expires (which should never happen unless you choose
+                        // to ignore the SSL error dialog for a very long time),
+                        // we wake up the thread and abort the request. This is
+                        // to prevent us from stalling the network if things go
+                        // very bad.
+                        mSuspendLock.wait(10 * 60 * 1000);
+                        if (mSuspended) {
+                            // mSuspended is true if we have not had a chance to
+                            // restart the connection yet (ie, the wait timeout
+                            // has expired)
+                            mSuspended = false;
+                            mAborted = true;
+                            if (HttpLog.LOGV) {
+                                HttpLog.v("HttpsConnection.openConnection():" +
+                                          " SSL timeout expired and request was cancelled!!!");
+                            }
+                        }
+                    } catch (InterruptedException e) {
+                        // ignore
+                    }
+                }
+                if (mAborted) {
+                    // The user decided not to use this unverified connection
+                    // so close it immediately.
+                    sslSock.close();
+                    throw new SSLConnectionClosedByUserException("connection closed by the user");
+                }
+            }
+        }
+
+        // All went well, we have an open, verified connection.
+        AndroidHttpClientConnection conn = new AndroidHttpClientConnection();
+        BasicHttpParams params = new BasicHttpParams();
+        params.setIntParameter(HttpConnectionParams.SOCKET_BUFFER_SIZE, 8192);
+        conn.bind(sslSock, params);
+
+        return conn;
+    }
+
+    /**
+     * Closes the low level connection.
+     *
+     * If an exception is thrown then it is assumed that the connection will
+     * have been closed (to the extent possible) anyway and the caller does not
+     * need to take any further action.
+     *
+     */
+    @Override
+    void closeConnection() {
+        // if the connection has been suspended due to an SSL error
+        if (mSuspended) {
+            // wake up the network thread
+            restartConnection(false);
+        }
+
+        try {
+            if (mHttpClientConnection != null && mHttpClientConnection.isOpen()) {
+                mHttpClientConnection.close();
+            }
+        } catch (IOException e) {
+            if (HttpLog.LOGV)
+                HttpLog.v("HttpsConnection.closeConnection():" +
+                          " failed closing connection " + mHost);
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Restart a secure connection suspended waiting for user interaction.
+     */
+    void restartConnection(boolean proceed) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("HttpsConnection.restartConnection():" +
+                      " proceed: " + proceed);
+        }
+
+        synchronized (mSuspendLock) {
+            if (mSuspended) {
+                mSuspended = false;
+                mAborted = !proceed;
+                mSuspendLock.notify();
+            }
+        }
+    }
+
+    @Override
+    String getScheme() {
+        return "https";
+    }
+}
+
+/**
+ * Simple exception we throw if the SSL connection is closed by the user.
+ *
+ */
+class SSLConnectionClosedByUserException extends SSLException {
+
+    public SSLConnectionClosedByUserException(String reason) {
+        super(reason);
+    }
+}
diff --git a/android/src/android/net/http/IdleCache.java b/android/src/android/net/http/IdleCache.java
new file mode 100644
index 0000000..023dff1
--- /dev/null
+++ b/android/src/android/net/http/IdleCache.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/**
+ * Hangs onto idle live connections for a little while
+ */
+
+package android.net.http;
+
+import org.apache.http.HttpHost;
+
+import android.os.SystemClock;
+
+class IdleCache {
+
+    class Entry {
+        HttpHost mHost;
+        Connection mConnection;
+        long mTimeout;
+    };
+
+    private final static int IDLE_CACHE_MAX = 8;
+
+    /* Allow five consecutive empty queue checks before shutdown */
+    private final static int EMPTY_CHECK_MAX = 5;
+
+    /* six second timeout for connections */
+    private final static int TIMEOUT = 6 * 1000;
+    private final static int CHECK_INTERVAL = 2 * 1000;
+    private Entry[] mEntries = new Entry[IDLE_CACHE_MAX];
+
+    private int mCount = 0;
+
+    private IdleReaper mThread = null;
+
+    /* stats */
+    private int mCached = 0;
+    private int mReused = 0;
+
+    IdleCache() {
+        for (int i = 0; i < IDLE_CACHE_MAX; i++) {
+            mEntries[i] = new Entry();
+        }
+    }
+
+    /**
+     * Caches connection, if there is room.
+     * @return true if connection cached
+     */
+    synchronized boolean cacheConnection(
+            HttpHost host, Connection connection) {
+
+        boolean ret = false;
+
+        if (HttpLog.LOGV) {
+            HttpLog.v("IdleCache size " + mCount + " host "  + host);
+        }
+
+        if (mCount < IDLE_CACHE_MAX) {
+            long time = SystemClock.uptimeMillis();
+            for (int i = 0; i < IDLE_CACHE_MAX; i++) {
+                Entry entry = mEntries[i];
+                if (entry.mHost == null) {
+                    entry.mHost = host;
+                    entry.mConnection = connection;
+                    entry.mTimeout = time + TIMEOUT;
+                    mCount++;
+                    if (HttpLog.LOGV) mCached++;
+                    ret = true;
+                    if (mThread == null) {
+                        mThread = new IdleReaper();
+                        mThread.start();
+                    }
+                    break;
+                }
+            }
+        }
+        return ret;
+    }
+
+    synchronized Connection getConnection(HttpHost host) {
+        Connection ret = null;
+
+        if (mCount > 0) {
+            for (int i = 0; i < IDLE_CACHE_MAX; i++) {
+                Entry entry = mEntries[i];
+                HttpHost eHost = entry.mHost;
+                if (eHost != null && eHost.equals(host)) {
+                    ret = entry.mConnection;
+                    entry.mHost = null;
+                    entry.mConnection = null;
+                    mCount--;
+                    if (HttpLog.LOGV) mReused++;
+                    break;
+                }
+            }
+        }
+        return ret;
+    }
+
+    synchronized void clear() {
+        for (int i = 0; mCount > 0 && i < IDLE_CACHE_MAX; i++) {
+            Entry entry = mEntries[i];
+            if (entry.mHost != null) {
+                entry.mHost = null;
+                entry.mConnection.closeConnection();
+                entry.mConnection = null;
+                mCount--;
+            }
+        }
+    }
+
+    private synchronized void clearIdle() {
+        if (mCount > 0) {
+            long time = SystemClock.uptimeMillis();
+            for (int i = 0; i < IDLE_CACHE_MAX; i++) {
+                Entry entry = mEntries[i];
+                if (entry.mHost != null && time > entry.mTimeout) {
+                    entry.mHost = null;
+                    entry.mConnection.closeConnection();
+                    entry.mConnection = null;
+                    mCount--;
+                }
+            }
+        }
+    }
+
+    private class IdleReaper extends Thread {
+
+        public void run() {
+            int check = 0;
+
+            setName("IdleReaper");
+            android.os.Process.setThreadPriority(
+                    android.os.Process.THREAD_PRIORITY_BACKGROUND);
+            synchronized (IdleCache.this) {
+                while (check < EMPTY_CHECK_MAX) {
+                    try {
+                        IdleCache.this.wait(CHECK_INTERVAL);
+                    } catch (InterruptedException ex) {
+                    }
+                    if (mCount == 0) {
+                        check++;
+                    } else {
+                        check = 0;
+                        clearIdle();
+                    }
+                }
+                mThread = null;
+            }
+            if (HttpLog.LOGV) {
+                HttpLog.v("IdleCache IdleReaper shutdown: cached " + mCached +
+                          " reused " + mReused);
+                mCached = 0;
+                mReused = 0;
+            }
+        }
+    }
+}
diff --git a/android/src/android/net/http/LegacyHttpDateTime.java b/android/src/android/net/http/LegacyHttpDateTime.java
new file mode 100644
index 0000000..f8d0529
--- /dev/null
+++ b/android/src/android/net/http/LegacyHttpDateTime.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2007 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 android.net.http;
+
+import android.text.format.Time;
+
+import java.util.Calendar;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Helper for parsing an HTTP date.
+ */
+final class LegacyHttpDateTime {
+
+    /*
+     * Regular expression for parsing HTTP-date.
+     *
+     * Wdy, DD Mon YYYY HH:MM:SS GMT
+     * RFC 822, updated by RFC 1123
+     *
+     * Weekday, DD-Mon-YY HH:MM:SS GMT
+     * RFC 850, obsoleted by RFC 1036
+     *
+     * Wdy Mon DD HH:MM:SS YYYY
+     * ANSI C's asctime() format
+     *
+     * with following variations
+     *
+     * Wdy, DD-Mon-YYYY HH:MM:SS GMT
+     * Wdy, (SP)D Mon YYYY HH:MM:SS GMT
+     * Wdy,DD Mon YYYY HH:MM:SS GMT
+     * Wdy, DD-Mon-YY HH:MM:SS GMT
+     * Wdy, DD Mon YYYY HH:MM:SS -HHMM
+     * Wdy, DD Mon YYYY HH:MM:SS
+     * Wdy Mon (SP)D HH:MM:SS YYYY
+     * Wdy Mon DD HH:MM:SS YYYY GMT
+     * 
+     * HH can be H if the first digit is zero.
+     * 
+     * Mon can be the full name of the month.
+     */
+    private static final String HTTP_DATE_RFC_REGEXP =
+            "([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]"
+            + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])";
+
+    private static final String HTTP_DATE_ANSIC_REGEXP =
+            "[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]"
+            + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})";
+
+    /**
+     * The compiled version of the HTTP-date regular expressions.
+     */
+    private static final Pattern HTTP_DATE_RFC_PATTERN =
+            Pattern.compile(HTTP_DATE_RFC_REGEXP);
+    private static final Pattern HTTP_DATE_ANSIC_PATTERN =
+            Pattern.compile(HTTP_DATE_ANSIC_REGEXP);
+
+    private static class TimeOfDay {
+        TimeOfDay(int h, int m, int s) {
+            this.hour = h;
+            this.minute = m;
+            this.second = s;
+        }
+        
+        int hour;
+        int minute;
+        int second;
+    }
+
+    public static long parse(String timeString)
+            throws IllegalArgumentException {
+
+        int date = 1;
+        int month = Calendar.JANUARY;
+        int year = 1970;
+        TimeOfDay timeOfDay;
+
+        Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString);
+        if (rfcMatcher.find()) {
+            date = getDate(rfcMatcher.group(1));
+            month = getMonth(rfcMatcher.group(2));
+            year = getYear(rfcMatcher.group(3));
+            timeOfDay = getTime(rfcMatcher.group(4));
+        } else {
+            Matcher ansicMatcher = HTTP_DATE_ANSIC_PATTERN.matcher(timeString);
+            if (ansicMatcher.find()) {
+                month = getMonth(ansicMatcher.group(1));
+                date = getDate(ansicMatcher.group(2));
+                timeOfDay = getTime(ansicMatcher.group(3));
+                year = getYear(ansicMatcher.group(4));
+            } else {
+                throw new IllegalArgumentException();
+            }
+        }
+
+        // FIXME: Y2038 BUG!
+        if (year >= 2038) {
+            year = 2038;
+            month = Calendar.JANUARY;
+            date = 1;
+        }
+
+        Time time = new Time(Time.TIMEZONE_UTC);
+        time.set(timeOfDay.second, timeOfDay.minute, timeOfDay.hour, date,
+                month, year);
+        return time.toMillis(false /* use isDst */);
+    }
+
+    private static int getDate(String dateString) {
+        if (dateString.length() == 2) {
+            return (dateString.charAt(0) - '0') * 10
+                    + (dateString.charAt(1) - '0');
+        } else {
+            return (dateString.charAt(0) - '0');
+        }
+    }
+
+    /*
+     * jan = 9 + 0 + 13 = 22
+     * feb = 5 + 4 + 1 = 10
+     * mar = 12 + 0 + 17 = 29
+     * apr = 0 + 15 + 17 = 32
+     * may = 12 + 0 + 24 = 36
+     * jun = 9 + 20 + 13 = 42
+     * jul = 9 + 20 + 11 = 40
+     * aug = 0 + 20 + 6 = 26
+     * sep = 18 + 4 + 15 = 37
+     * oct = 14 + 2 + 19 = 35
+     * nov = 13 + 14 + 21 = 48
+     * dec = 3 + 4 + 2 = 9
+     */
+    private static int getMonth(String monthString) {
+        int hash = Character.toLowerCase(monthString.charAt(0)) +
+                Character.toLowerCase(monthString.charAt(1)) +
+                Character.toLowerCase(monthString.charAt(2)) - 3 * 'a';
+        switch (hash) {
+            case 22:
+                return Calendar.JANUARY;
+            case 10:
+                return Calendar.FEBRUARY;
+            case 29:
+                return Calendar.MARCH;
+            case 32:
+                return Calendar.APRIL;
+            case 36:
+                return Calendar.MAY;
+            case 42:
+                return Calendar.JUNE;
+            case 40:
+                return Calendar.JULY;
+            case 26:
+                return Calendar.AUGUST;
+            case 37:
+                return Calendar.SEPTEMBER;
+            case 35:
+                return Calendar.OCTOBER;
+            case 48:
+                return Calendar.NOVEMBER;
+            case 9:
+                return Calendar.DECEMBER;
+            default:
+                throw new IllegalArgumentException();
+        }
+    }
+
+    private static int getYear(String yearString) {
+        if (yearString.length() == 2) {
+            int year = (yearString.charAt(0) - '0') * 10
+                    + (yearString.charAt(1) - '0');
+            if (year >= 70) {
+                return year + 1900;
+            } else {
+                return year + 2000;
+            }
+        } else if (yearString.length() == 3) {
+            // According to RFC 2822, three digit years should be added to 1900.
+            int year = (yearString.charAt(0) - '0') * 100
+                    + (yearString.charAt(1) - '0') * 10
+                    + (yearString.charAt(2) - '0');
+            return year + 1900;
+        } else if (yearString.length() == 4) {
+             return (yearString.charAt(0) - '0') * 1000
+                    + (yearString.charAt(1) - '0') * 100
+                    + (yearString.charAt(2) - '0') * 10
+                    + (yearString.charAt(3) - '0');
+        } else {
+             return 1970;
+        }
+    }
+
+    private static TimeOfDay getTime(String timeString) {
+        // HH might be H
+        int i = 0;
+        int hour = timeString.charAt(i++) - '0';
+        if (timeString.charAt(i) != ':')
+            hour = hour * 10 + (timeString.charAt(i++) - '0');
+        // Skip ':'
+        i++;
+        
+        int minute = (timeString.charAt(i++) - '0') * 10
+                    + (timeString.charAt(i++) - '0');
+        // Skip ':'
+        i++;
+        
+        int second = (timeString.charAt(i++) - '0') * 10
+                  + (timeString.charAt(i++) - '0');
+
+        return new TimeOfDay(hour, minute, second);        
+    }
+}
diff --git a/android/src/android/net/http/LoggingEventHandler.java b/android/src/android/net/http/LoggingEventHandler.java
new file mode 100644
index 0000000..4af1e8b
--- /dev/null
+++ b/android/src/android/net/http/LoggingEventHandler.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/**
+ * A test EventHandler: Logs everything received
+ */
+
+package android.net.http;
+
+import android.net.http.Headers;
+
+public class LoggingEventHandler implements EventHandler {
+
+    public void requestSent() {
+        HttpLog.v("LoggingEventHandler:requestSent()");
+    }
+
+    public void status(int major_version,
+                       int minor_version,
+                       int code, /* Status-Code value */
+                       String reason_phrase) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("LoggingEventHandler:status() major: " + major_version +
+                  " minor: " + minor_version +
+                  " code: " + code +
+                  " reason: " + reason_phrase);
+        }
+    }
+
+    public void headers(Headers headers) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("LoggingEventHandler:headers()");
+            HttpLog.v(headers.toString());
+        }
+    }
+
+    public void locationChanged(String newLocation, boolean permanent) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("LoggingEventHandler: locationChanged() " + newLocation +
+                      " permanent " + permanent);
+        }
+    }
+
+    public void data(byte[] data, int len) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("LoggingEventHandler: data() " + len + " bytes");
+        }
+        // HttpLog.v(new String(data, 0, len));
+    }
+    public void endData() {
+        if (HttpLog.LOGV) {
+            HttpLog.v("LoggingEventHandler: endData() called");
+        }
+    }
+
+    public void certificate(SslCertificate certificate) {
+         if (HttpLog.LOGV) {
+             HttpLog.v("LoggingEventHandler: certificate(): " + certificate);
+         }
+    }
+
+    public void error(int id, String description) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("LoggingEventHandler: error() called Id:" + id +
+                      " description " + description);
+        }
+    }
+
+    public boolean handleSslErrorRequest(SslError error) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("LoggingEventHandler: handleSslErrorRequest():" + error);
+        }
+        // return false so that the caller thread won't wait forever
+        return false;
+    }
+}
diff --git a/android/src/android/net/http/Request.java b/android/src/android/net/http/Request.java
new file mode 100644
index 0000000..d8eba11
--- /dev/null
+++ b/android/src/android/net/http/Request.java
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2006 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 android.net.http;
+
+import java.io.EOFException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.http.entity.InputStreamEntity;
+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.HttpStatus;
+import org.apache.http.ParseException;
+import org.apache.http.ProtocolVersion;
+
+import org.apache.http.StatusLine;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.message.BasicHttpEntityEnclosingRequest;
+import org.apache.http.protocol.RequestContent;
+
+/**
+ * Represents an HTTP request for a given host.
+ */
+
+class Request {
+
+    /** The eventhandler to call as the request progresses */
+    EventHandler mEventHandler;
+
+    private Connection mConnection;
+
+    /** The Apache http request */
+    BasicHttpRequest mHttpRequest;
+
+    /** The path component of this request */
+    String mPath;
+
+    /** Host serving this request */
+    HttpHost mHost;
+
+    /** Set if I'm using a proxy server */
+    HttpHost mProxyHost;
+
+    /** True if request has been cancelled */
+    volatile boolean mCancelled = false;
+
+    int mFailCount = 0;
+
+    // This will be used to set the Range field if we retry a connection. This
+    // is http/1.1 feature.
+    private int mReceivedBytes = 0;
+
+    private InputStream mBodyProvider;
+    private int mBodyLength;
+
+    private final static String HOST_HEADER = "Host";
+    private final static String ACCEPT_ENCODING_HEADER = "Accept-Encoding";
+    private final static String CONTENT_LENGTH_HEADER = "content-length";
+
+    /* Used to synchronize waitUntilComplete() requests */
+    private final Object mClientResource = new Object();
+
+    /** True if loading should be paused **/
+    private boolean mLoadingPaused = false;
+
+    /**
+     * Processor used to set content-length and transfer-encoding
+     * headers.
+     */
+    private static RequestContent requestContentProcessor =
+            new RequestContent();
+
+    /**
+     * Instantiates a new Request.
+     * @param method GET/POST/PUT
+     * @param host The server that will handle this request
+     * @param path path part of URI
+     * @param bodyProvider InputStream providing HTTP body, null if none
+     * @param bodyLength length of body, must be 0 if bodyProvider is null
+     * @param eventHandler request will make progress callbacks on
+     * this interface
+     * @param headers reqeust headers
+     */
+    Request(String method, HttpHost host, HttpHost proxyHost, String path,
+            InputStream bodyProvider, int bodyLength,
+            EventHandler eventHandler,
+            Map<String, String> headers) {
+        mEventHandler = eventHandler;
+        mHost = host;
+        mProxyHost = proxyHost;
+        mPath = path;
+        mBodyProvider = bodyProvider;
+        mBodyLength = bodyLength;
+
+        if (bodyProvider == null && !"POST".equalsIgnoreCase(method)) {
+            mHttpRequest = new BasicHttpRequest(method, getUri());
+        } else {
+            mHttpRequest = new BasicHttpEntityEnclosingRequest(
+                    method, getUri());
+            // it is ok to have null entity for BasicHttpEntityEnclosingRequest.
+            // By using BasicHttpEntityEnclosingRequest, it will set up the
+            // correct content-length, content-type and content-encoding.
+            if (bodyProvider != null) {
+                setBodyProvider(bodyProvider, bodyLength);
+            }
+        }
+        addHeader(HOST_HEADER, getHostPort());
+
+        /* FIXME: if webcore will make the root document a
+           high-priority request, we can ask for gzip encoding only on
+           high priority reqs (saving the trouble for images, etc) */
+        addHeader(ACCEPT_ENCODING_HEADER, "gzip");
+        addHeaders(headers);
+    }
+
+    /**
+     * @param pause True if the load should be paused.
+     */
+    synchronized void setLoadingPaused(boolean pause) {
+        mLoadingPaused = pause;
+
+        // Wake up the paused thread if we're unpausing the load.
+        if (!mLoadingPaused) {
+            notify();
+        }
+    }
+
+    /**
+     * @param connection Request served by this connection
+     */
+    void setConnection(Connection connection) {
+        mConnection = connection;
+    }
+
+    /* package */ EventHandler getEventHandler() {
+        return mEventHandler;
+    }
+
+    /**
+     * Add header represented by given pair to request.  Header will
+     * be formatted in request as "name: value\r\n".
+     * @param name of header
+     * @param value of header
+     */
+    void addHeader(String name, String value) {
+        if (name == null) {
+            String damage = "Null http header name";
+            HttpLog.e(damage);
+            throw new NullPointerException(damage);
+        }
+        if (value == null || value.length() == 0) {
+            String damage = "Null or empty value for header \"" + name + "\"";
+            HttpLog.e(damage);
+            throw new RuntimeException(damage);
+        }
+        mHttpRequest.addHeader(name, value);
+    }
+
+    /**
+     * Add all headers in given map to this request.  This is a helper
+     * method: it calls addHeader for each pair in the map.
+     */
+    void addHeaders(Map<String, String> headers) {
+        if (headers == null) {
+            return;
+        }
+
+        Entry<String, String> entry;
+        Iterator<Entry<String, String>> i = headers.entrySet().iterator();
+        while (i.hasNext()) {
+            entry = i.next();
+            addHeader(entry.getKey(), entry.getValue());
+        }
+    }
+
+    /**
+     * Send the request line and headers
+     */
+    void sendRequest(AndroidHttpClientConnection httpClientConnection)
+            throws HttpException, IOException {
+
+        if (mCancelled) return; // don't send cancelled requests
+
+        if (HttpLog.LOGV) {
+            HttpLog.v("Request.sendRequest() " + mHost.getSchemeName() + "://" + getHostPort());
+            // HttpLog.v(mHttpRequest.getRequestLine().toString());
+            if (false) {
+                Iterator i = mHttpRequest.headerIterator();
+                while (i.hasNext()) {
+                    Header header = (Header)i.next();
+                    HttpLog.v(header.getName() + ": " + header.getValue());
+                }
+            }
+        }
+
+        requestContentProcessor.process(mHttpRequest,
+                                        mConnection.getHttpContext());
+        httpClientConnection.sendRequestHeader(mHttpRequest);
+        if (mHttpRequest instanceof HttpEntityEnclosingRequest) {
+            httpClientConnection.sendRequestEntity(
+                    (HttpEntityEnclosingRequest) mHttpRequest);
+        }
+
+        if (HttpLog.LOGV) {
+            HttpLog.v("Request.requestSent() " + mHost.getSchemeName() + "://" + getHostPort() + mPath);
+        }
+    }
+
+
+    /**
+     * Receive a single http response.
+     *
+     * @param httpClientConnection the request to receive the response for.
+     */
+    void readResponse(AndroidHttpClientConnection httpClientConnection)
+            throws IOException, ParseException {
+
+        if (mCancelled) return; // don't send cancelled requests
+
+        StatusLine statusLine = null;
+        boolean hasBody = false;
+        httpClientConnection.flush();
+        int statusCode = 0;
+
+        Headers header = new Headers();
+        do {
+            statusLine = httpClientConnection.parseResponseHeader(header);
+            statusCode = statusLine.getStatusCode();
+        } while (statusCode < HttpStatus.SC_OK);
+        if (HttpLog.LOGV) HttpLog.v(
+                "Request.readResponseStatus() " +
+                statusLine.toString().length() + " " + statusLine);
+
+        ProtocolVersion v = statusLine.getProtocolVersion();
+        mEventHandler.status(v.getMajor(), v.getMinor(),
+                statusCode, statusLine.getReasonPhrase());
+        mEventHandler.headers(header);
+        HttpEntity entity = null;
+        hasBody = canResponseHaveBody(mHttpRequest, statusCode);
+
+        if (hasBody)
+            entity = httpClientConnection.receiveResponseEntity(header);
+
+        // restrict the range request to the servers claiming that they are
+        // accepting ranges in bytes
+        boolean supportPartialContent = "bytes".equalsIgnoreCase(header
+                .getAcceptRanges());
+
+        if (entity != null) {
+            InputStream is = entity.getContent();
+
+            // process gzip content encoding
+            Header contentEncoding = entity.getContentEncoding();
+            InputStream nis = null;
+            byte[] buf = null;
+            int count = 0;
+            try {
+                if (contentEncoding != null &&
+                    contentEncoding.getValue().equals("gzip")) {
+                    nis = new GZIPInputStream(is);
+                } else {
+                    nis = is;
+                }
+
+                /* accumulate enough data to make it worth pushing it
+                 * up the stack */
+                buf = mConnection.getBuf();
+                int len = 0;
+                int lowWater = buf.length / 2;
+                while (len != -1) {
+                    synchronized(this) {
+                        while (mLoadingPaused) {
+                            // Put this (network loading) thread to sleep if WebCore
+                            // has asked us to. This can happen with plugins for
+                            // example, if we are streaming data but the plugin has
+                            // filled its internal buffers.
+                            try {
+                                wait();
+                            } catch (InterruptedException e) {
+                                HttpLog.e("Interrupted exception whilst "
+                                    + "network thread paused at WebCore's request."
+                                    + " " + e.getMessage());
+                            }
+                        }
+                    }
+
+                    len = nis.read(buf, count, buf.length - count);
+
+                    if (len != -1) {
+                        count += len;
+                        if (supportPartialContent) mReceivedBytes += len;
+                    }
+                    if (len == -1 || count >= lowWater) {
+                        if (HttpLog.LOGV) HttpLog.v("Request.readResponse() " + count);
+                        mEventHandler.data(buf, count);
+                        count = 0;
+                    }
+                }
+            } catch (EOFException e) {
+                /* InflaterInputStream throws an EOFException when the
+                   server truncates gzipped content.  Handle this case
+                   as we do truncated non-gzipped content: no error */
+                if (count > 0) {
+                    // if there is uncommited content, we should commit them
+                    mEventHandler.data(buf, count);
+                }
+                if (HttpLog.LOGV) HttpLog.v( "readResponse() handling " + e);
+            } catch(IOException e) {
+                // don't throw if we have a non-OK status code
+                if (statusCode == HttpStatus.SC_OK
+                        || statusCode == HttpStatus.SC_PARTIAL_CONTENT) {
+                    if (supportPartialContent && count > 0) {
+                        // if there is uncommited content, we should commit them
+                        // as we will continue the request
+                        mEventHandler.data(buf, count);
+                    }
+                    throw e;
+                }
+            } finally {
+                if (nis != null) {
+                    nis.close();
+                }
+            }
+        }
+        mConnection.setCanPersist(entity, statusLine.getProtocolVersion(),
+                header.getConnectionType());
+        mEventHandler.endData();
+        complete();
+
+        if (HttpLog.LOGV) HttpLog.v("Request.readResponse(): done " +
+                                    mHost.getSchemeName() + "://" + getHostPort() + mPath);
+    }
+
+    /**
+     * Data will not be sent to or received from server after cancel()
+     * call.  Does not close connection--use close() below for that.
+     *
+     * Called by RequestHandle from non-network thread
+     */
+    synchronized void cancel() {
+        if (HttpLog.LOGV) {
+            HttpLog.v("Request.cancel(): " + getUri());
+        }
+
+        // Ensure that the network thread is not blocked by a hanging request from WebCore to
+        // pause the load.
+        mLoadingPaused = false;
+        notify();
+
+        mCancelled = true;
+        if (mConnection != null) {
+            mConnection.cancel();
+        }
+    }
+
+    String getHostPort() {
+        String myScheme = mHost.getSchemeName();
+        int myPort = mHost.getPort();
+
+        // Only send port when we must... many servers can't deal with it
+        if (myPort != 80 && myScheme.equals("http") ||
+            myPort != 443 && myScheme.equals("https")) {
+            return mHost.toHostString();
+        } else {
+            return mHost.getHostName();
+        }
+    }
+
+    String getUri() {
+        if (mProxyHost == null ||
+            mHost.getSchemeName().equals("https")) {
+            return mPath;
+        }
+        return mHost.getSchemeName() + "://" + getHostPort() + mPath;
+    }
+
+    /**
+     * for debugging
+     */
+    public String toString() {
+        return mPath;
+    }
+
+
+    /**
+     * If this request has been sent once and failed, it must be reset
+     * before it can be sent again.
+     */
+    void reset() {
+        /* clear content-length header */
+        mHttpRequest.removeHeaders(CONTENT_LENGTH_HEADER);
+
+        if (mBodyProvider != null) {
+            try {
+                mBodyProvider.reset();
+            } catch (IOException ex) {
+                if (HttpLog.LOGV) HttpLog.v(
+                        "failed to reset body provider " +
+                        getUri());
+            }
+            setBodyProvider(mBodyProvider, mBodyLength);
+        }
+
+        if (mReceivedBytes > 0) {
+            // reset the fail count as we continue the request
+            mFailCount = 0;
+            // set the "Range" header to indicate that the retry will continue
+            // instead of restarting the request
+            HttpLog.v("*** Request.reset() to range:" + mReceivedBytes);
+            mHttpRequest.setHeader("Range", "bytes=" + mReceivedBytes + "-");
+        }
+    }
+
+    /**
+     * Pause thread request completes.  Used for synchronous requests,
+     * and testing
+     */
+    void waitUntilComplete() {
+        synchronized (mClientResource) {
+            try {
+                if (HttpLog.LOGV) HttpLog.v("Request.waitUntilComplete()");
+                mClientResource.wait();
+                if (HttpLog.LOGV) HttpLog.v("Request.waitUntilComplete() done waiting");
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    void complete() {
+        synchronized (mClientResource) {
+            mClientResource.notifyAll();
+        }
+    }
+
+    /**
+     * Decide whether a response comes with an entity.
+     * The implementation in this class is based on RFC 2616.
+     * Unknown methods and response codes are supposed to
+     * indicate responses with an entity.
+     * <br/>
+     * Derived executors can override this method to handle
+     * methods and response codes not specified in RFC 2616.
+     *
+     * @param request   the request, to obtain the executed method
+     * @param response  the response, to obtain the status code
+     */
+
+    private static boolean canResponseHaveBody(final HttpRequest request,
+                                               final int status) {
+
+        if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
+            return false;
+        }
+        return status >= HttpStatus.SC_OK
+            && status != HttpStatus.SC_NO_CONTENT
+            && status != HttpStatus.SC_NOT_MODIFIED;
+    }
+
+    /**
+     * Supply an InputStream that provides the body of a request.  It's
+     * not great that the caller must also provide the length of the data
+     * returned by that InputStream, but the client needs to know up
+     * front, and I'm not sure how to get this out of the InputStream
+     * itself without a costly readthrough.  I'm not sure skip() would
+     * do what we want.  If you know a better way, please let me know.
+     */
+    private void setBodyProvider(InputStream bodyProvider, int bodyLength) {
+        if (!bodyProvider.markSupported()) {
+            throw new IllegalArgumentException(
+                    "bodyProvider must support mark()");
+        }
+        // Mark beginning of stream
+        bodyProvider.mark(Integer.MAX_VALUE);
+
+        ((BasicHttpEntityEnclosingRequest)mHttpRequest).setEntity(
+                new InputStreamEntity(bodyProvider, bodyLength));
+    }
+
+
+    /**
+     * Handles SSL error(s) on the way down from the user (the user
+     * has already provided their feedback).
+     */
+    public void handleSslErrorResponse(boolean proceed) {
+        HttpsConnection connection = (HttpsConnection)(mConnection);
+        if (connection != null) {
+            connection.restartConnection(proceed);
+        }
+    }
+
+    /**
+     * Helper: calls error() on eventhandler with appropriate message
+     * This should not be called before the mConnection is set.
+     */
+    void error(int errorId, String errorMessage) {
+        mEventHandler.error(errorId, errorMessage);
+    }
+
+}
diff --git a/android/src/android/net/http/RequestFeeder.java b/android/src/android/net/http/RequestFeeder.java
new file mode 100644
index 0000000..a6322ae
--- /dev/null
+++ b/android/src/android/net/http/RequestFeeder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/**
+ * Supplies Requests to a Connection
+ */
+
+package android.net.http;
+
+import org.apache.http.HttpHost;
+
+interface RequestFeeder {
+
+    Request getRequest();
+    Request getRequest(HttpHost host);
+
+    /**
+     * @return true if a request for this host is available
+     */
+    boolean haveRequest(HttpHost host);
+
+    /**
+     * Put request back on head of queue
+     */
+    void requeueRequest(Request request);
+}
diff --git a/android/src/android/net/http/RequestHandle.java b/android/src/android/net/http/RequestHandle.java
new file mode 100644
index 0000000..bc40df1
--- /dev/null
+++ b/android/src/android/net/http/RequestHandle.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2006 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 android.net.http;
+
+import android.net.compatibility.WebAddress;
+import android.webkit.CookieManager;
+
+import org.apache.commons.codec.binary.Base64;
+
+import java.io.InputStream;
+import java.lang.Math;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * RequestHandle: handles a request session that may include multiple
+ * redirects, HTTP authentication requests, etc.
+ */
+public class RequestHandle {
+
+    private String        mUrl;
+    private WebAddress    mUri;
+    private String        mMethod;
+    private Map<String, String> mHeaders;
+    private RequestQueue  mRequestQueue;
+    private Request       mRequest;
+    private InputStream   mBodyProvider;
+    private int           mBodyLength;
+    private int           mRedirectCount = 0;
+    // Used only with synchronous requests.
+    private Connection    mConnection;
+
+    private final static String AUTHORIZATION_HEADER = "Authorization";
+    private final static String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization";
+
+    public final static int MAX_REDIRECT_COUNT = 16;
+
+    /**
+     * Creates a new request session.
+     */
+    public RequestHandle(RequestQueue requestQueue, String url, WebAddress uri,
+            String method, Map<String, String> headers,
+            InputStream bodyProvider, int bodyLength, Request request) {
+
+        if (headers == null) {
+            headers = new HashMap<String, String>();
+        }
+        mHeaders = headers;
+        mBodyProvider = bodyProvider;
+        mBodyLength = bodyLength;
+        mMethod = method == null? "GET" : method;
+
+        mUrl = url;
+        mUri = uri;
+
+        mRequestQueue = requestQueue;
+
+        mRequest = request;
+    }
+
+    /**
+     * Creates a new request session with a given Connection. This connection
+     * is used during a synchronous load to handle this request.
+     */
+    public RequestHandle(RequestQueue requestQueue, String url, WebAddress uri,
+            String method, Map<String, String> headers,
+            InputStream bodyProvider, int bodyLength, Request request,
+            Connection conn) {
+        this(requestQueue, url, uri, method, headers, bodyProvider, bodyLength,
+                request);
+        mConnection = conn;
+    }
+
+    /**
+     * Cancels this request
+     */
+    public void cancel() {
+        if (mRequest != null) {
+            mRequest.cancel();
+        }
+    }
+
+    /**
+     * Pauses the loading of this request. For example, called from the WebCore thread
+     * when the plugin can take no more data.
+     */
+    public void pauseRequest(boolean pause) {
+        if (mRequest != null) {
+            mRequest.setLoadingPaused(pause);
+        }
+    }
+
+    /**
+     * Handles SSL error(s) on the way down from the user (the user
+     * has already provided their feedback).
+     */
+    public void handleSslErrorResponse(boolean proceed) {
+        if (mRequest != null) {
+            mRequest.handleSslErrorResponse(proceed);
+        }
+    }
+
+    /**
+     * @return true if we've hit the max redirect count
+     */
+    public boolean isRedirectMax() {
+        return mRedirectCount >= MAX_REDIRECT_COUNT;
+    }
+
+    public int getRedirectCount() {
+        return mRedirectCount;
+    }
+
+    public void setRedirectCount(int count) {
+        mRedirectCount = count;
+    }
+
+    /**
+     * Create and queue a redirect request.
+     *
+     * @param redirectTo URL to redirect to
+     * @param statusCode HTTP status code returned from original request
+     * @param cacheHeaders Cache header for redirect URL
+     * @return true if setup succeeds, false otherwise (redirect loop
+     * count exceeded, body provider unable to rewind on 307 redirect)
+     */
+    public boolean setupRedirect(String redirectTo, int statusCode,
+            Map<String, String> cacheHeaders) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("RequestHandle.setupRedirect(): redirectCount " +
+                  mRedirectCount);
+        }
+
+        // be careful and remove authentication headers, if any
+        mHeaders.remove(AUTHORIZATION_HEADER);
+        mHeaders.remove(PROXY_AUTHORIZATION_HEADER);
+
+        if (++mRedirectCount == MAX_REDIRECT_COUNT) {
+            // Way too many redirects -- fail out
+            if (HttpLog.LOGV) HttpLog.v(
+                    "RequestHandle.setupRedirect(): too many redirects " +
+                    mRequest);
+            mRequest.error(EventHandler.ERROR_REDIRECT_LOOP,
+                    "The page contains too many server redirects.");
+            return false;
+        }
+
+        if (mUrl.startsWith("https:") && redirectTo.startsWith("http:")) {
+            // implement http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3
+            if (HttpLog.LOGV) {
+                HttpLog.v("blowing away the referer on an https -> http redirect");
+            }
+            mHeaders.remove("Referer");
+        }
+
+        mUrl = redirectTo;
+        try {
+            mUri = new WebAddress(mUrl);
+        } catch (IllegalArgumentException e) {
+            e.printStackTrace();
+        }
+
+        // update the "Cookie" header based on the redirected url
+        mHeaders.remove("Cookie");
+        String cookie = null;
+        if (mUri != null) {
+            cookie = CookieManager.getInstance().getCookie(mUri.toString());
+        }
+        if (cookie != null && cookie.length() > 0) {
+            mHeaders.put("Cookie", cookie);
+        }
+
+        if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) {
+            if (HttpLog.LOGV) {
+                HttpLog.v("replacing POST with GET on redirect to " + redirectTo);
+            }
+            mMethod = "GET";
+        }
+        /* Only repost content on a 307.  If 307, reset the body
+           provider so we can replay the body */
+        if (statusCode == 307) {
+            try {
+                if (mBodyProvider != null) mBodyProvider.reset();
+            } catch (java.io.IOException ex) {
+                if (HttpLog.LOGV) {
+                    HttpLog.v("setupRedirect() failed to reset body provider");
+                }
+                return false;
+            }
+
+        } else {
+            mHeaders.remove("Content-Type");
+            mBodyProvider = null;
+        }
+
+        // Update the cache headers for this URL
+        mHeaders.putAll(cacheHeaders);
+
+        createAndQueueNewRequest();
+        return true;
+    }
+
+    /**
+     * Create and queue an HTTP authentication-response (basic) request.
+     */
+    public void setupBasicAuthResponse(boolean isProxy, String username, String password) {
+        String response = computeBasicAuthResponse(username, password);
+        if (HttpLog.LOGV) {
+            HttpLog.v("setupBasicAuthResponse(): response: " + response);
+        }
+        mHeaders.put(authorizationHeader(isProxy), "Basic " + response);
+        setupAuthResponse();
+    }
+
+    /**
+     * Create and queue an HTTP authentication-response (digest) request.
+     */
+    public void setupDigestAuthResponse(boolean isProxy,
+                                        String username,
+                                        String password,
+                                        String realm,
+                                        String nonce,
+                                        String QOP,
+                                        String algorithm,
+                                        String opaque) {
+
+        String response = computeDigestAuthResponse(
+                username, password, realm, nonce, QOP, algorithm, opaque);
+        if (HttpLog.LOGV) {
+            HttpLog.v("setupDigestAuthResponse(): response: " + response);
+        }
+        mHeaders.put(authorizationHeader(isProxy), "Digest " + response);
+        setupAuthResponse();
+    }
+
+    private void setupAuthResponse() {
+        try {
+            if (mBodyProvider != null) mBodyProvider.reset();
+        } catch (java.io.IOException ex) {
+            if (HttpLog.LOGV) {
+                HttpLog.v("setupAuthResponse() failed to reset body provider");
+            }
+        }
+        createAndQueueNewRequest();
+    }
+
+    /**
+     * @return HTTP request method (GET, PUT, etc).
+     */
+    public String getMethod() {
+        return mMethod;
+    }
+
+    /**
+     * @return Basic-scheme authentication response: BASE64(username:password).
+     */
+    public static String computeBasicAuthResponse(String username, String password) {
+        if (username == null) {
+            throw new NullPointerException("username == null");
+        }
+
+        if (password == null) {
+            throw new NullPointerException("password == null");
+        }
+
+        // encode username:password to base64
+        return new String(Base64.encodeBase64((username + ':' + password).getBytes()));
+    }
+
+    public void waitUntilComplete() {
+        mRequest.waitUntilComplete();
+    }
+
+    public void processRequest() {
+        if (mConnection != null) {
+            mConnection.processRequests(mRequest);
+        }
+    }
+
+    /**
+     * @return Digest-scheme authentication response.
+     */
+    private String computeDigestAuthResponse(String username,
+                                             String password,
+                                             String realm,
+                                             String nonce,
+                                             String QOP,
+                                             String algorithm,
+                                             String opaque) {
+
+        if (username == null) {
+            throw new NullPointerException("username == null");
+        }
+
+        if (password == null) {
+            throw new NullPointerException("password == null");
+        }
+
+        if (realm == null) {
+            throw new NullPointerException("realm == null");
+        }
+
+        String A1 = username + ":" + realm + ":" + password;
+        String A2 = mMethod  + ":" + mUrl;
+
+        // because we do not preemptively send authorization headers, nc is always 1
+        String nc = "00000001";
+        String cnonce = computeCnonce();
+        String digest = computeDigest(A1, A2, nonce, QOP, nc, cnonce);
+
+        String response = "";
+        response += "username=" + doubleQuote(username) + ", ";
+        response += "realm="    + doubleQuote(realm)    + ", ";
+        response += "nonce="    + doubleQuote(nonce)    + ", ";
+        response += "uri="      + doubleQuote(mUrl)     + ", ";
+        response += "response=" + doubleQuote(digest) ;
+
+        if (opaque     != null) {
+            response += ", opaque=" + doubleQuote(opaque);
+        }
+
+         if (algorithm != null) {
+            response += ", algorithm=" +  algorithm;
+        }
+
+        if (QOP        != null) {
+            response += ", qop=" + QOP + ", nc=" + nc + ", cnonce=" + doubleQuote(cnonce);
+        }
+
+        return response;
+    }
+
+    /**
+     * @return The right authorization header (dependeing on whether it is a proxy or not).
+     */
+    public static String authorizationHeader(boolean isProxy) {
+        if (!isProxy) {
+            return AUTHORIZATION_HEADER;
+        } else {
+            return PROXY_AUTHORIZATION_HEADER;
+        }
+    }
+
+    /**
+     * @return Double-quoted MD5 digest.
+     */
+    private String computeDigest(
+        String A1, String A2, String nonce, String QOP, String nc, String cnonce) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("computeDigest(): QOP: " + QOP);
+        }
+
+        if (QOP == null) {
+            return KD(H(A1), nonce + ":" + H(A2));
+        } else {
+            if (QOP.equalsIgnoreCase("auth")) {
+                return KD(H(A1), nonce + ":" + nc + ":" + cnonce + ":" + QOP + ":" + H(A2));
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @return MD5 hash of concat(secret, ":", data).
+     */
+    private String KD(String secret, String data) {
+        return H(secret + ":" + data);
+    }
+
+    /**
+     * @return MD5 hash of param.
+     */
+    private String H(String param) {
+        if (param != null) {
+            try {
+                MessageDigest md5 = MessageDigest.getInstance("MD5");
+
+                byte[] d = md5.digest(param.getBytes());
+                if (d != null) {
+                    return bufferToHex(d);
+                }
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @return HEX buffer representation.
+     */
+    private String bufferToHex(byte[] buffer) {
+        final char hexChars[] =
+            { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
+
+        if (buffer != null) {
+            int length = buffer.length;
+            if (length > 0) {
+                StringBuilder hex = new StringBuilder(2 * length);
+
+                for (int i = 0; i < length; ++i) {
+                    byte l = (byte) (buffer[i] & 0x0F);
+                    byte h = (byte)((buffer[i] & 0xF0) >> 4);
+
+                    hex.append(hexChars[h]);
+                    hex.append(hexChars[l]);
+                }
+
+                return hex.toString();
+            } else {
+                return "";
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Computes a random cnonce value based on the current time.
+     */
+    private String computeCnonce() {
+        Random rand = new Random();
+        int nextInt = rand.nextInt();
+        nextInt = (nextInt == Integer.MIN_VALUE) ?
+                Integer.MAX_VALUE : Math.abs(nextInt);
+        return Integer.toString(nextInt, 16);
+    }
+
+    /**
+     * "Double-quotes" the argument.
+     */
+    private String doubleQuote(String param) {
+        if (param != null) {
+            return "\"" + param + "\"";
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates and queues new request.
+     */
+    private void createAndQueueNewRequest() {
+        // mConnection is non-null if and only if the requests are synchronous.
+        if (mConnection != null) {
+            RequestHandle newHandle = mRequestQueue.queueSynchronousRequest(
+                    mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler,
+                    mBodyProvider, mBodyLength);
+            mRequest = newHandle.mRequest;
+            mConnection = newHandle.mConnection;
+            newHandle.processRequest();
+            return;
+        }
+        mRequest = mRequestQueue.queueRequest(
+                mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler,
+                mBodyProvider,
+                mBodyLength).mRequest;
+    }
+}
diff --git a/android/src/android/net/http/RequestQueue.java b/android/src/android/net/http/RequestQueue.java
new file mode 100644
index 0000000..17f34a5
--- /dev/null
+++ b/android/src/android/net/http/RequestQueue.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/**
+ * High level HTTP Interface
+ * Queues requests as necessary
+ */
+
+package android.net.http;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Proxy;
+import android.net.compatibility.WebAddress;
+import android.util.Log;
+
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.apache.http.HttpHost;
+
+public class RequestQueue implements RequestFeeder {
+
+
+    /**
+     * Requests, indexed by HttpHost (scheme, host, port)
+     */
+    private final LinkedHashMap<HttpHost, LinkedList<Request>> mPending;
+    private final Context mContext;
+    private final ActivePool mActivePool;
+    private final ConnectivityManager mConnectivityManager;
+
+    private HttpHost mProxyHost = null;
+    private BroadcastReceiver mProxyChangeReceiver;
+
+    /* default simultaneous connection count */
+    private static final int CONNECTION_COUNT = 4;
+
+    /**
+     * This class maintains active connection threads
+     */
+    class ActivePool implements ConnectionManager {
+        /** Threads used to process requests */
+        ConnectionThread[] mThreads;
+
+        IdleCache mIdleCache;
+
+        private int mTotalRequest;
+        private int mTotalConnection;
+        private int mConnectionCount;
+
+        ActivePool(int connectionCount) {
+            mIdleCache = new IdleCache();
+            mConnectionCount = connectionCount;
+            mThreads = new ConnectionThread[mConnectionCount];
+
+            for (int i = 0; i < mConnectionCount; i++) {
+                mThreads[i] = new ConnectionThread(
+                        mContext, i, this, RequestQueue.this);
+            }
+        }
+
+        void startup() {
+            for (int i = 0; i < mConnectionCount; i++) {
+                mThreads[i].start();
+            }
+        }
+
+        void shutdown() {
+            for (int i = 0; i < mConnectionCount; i++) {
+                mThreads[i].requestStop();
+            }
+        }
+
+        void startConnectionThread() {
+            synchronized (RequestQueue.this) {
+                RequestQueue.this.notify();
+            }
+        }
+
+        public void startTiming() {
+            for (int i = 0; i < mConnectionCount; i++) {
+                ConnectionThread rt = mThreads[i];
+                rt.mCurrentThreadTime = -1;
+                rt.mTotalThreadTime = 0;
+            }
+            mTotalRequest = 0;
+            mTotalConnection = 0;
+        }
+
+        public void stopTiming() {
+            int totalTime = 0;
+            for (int i = 0; i < mConnectionCount; i++) {
+                ConnectionThread rt = mThreads[i];
+                if (rt.mCurrentThreadTime != -1) {
+                    totalTime += rt.mTotalThreadTime;
+                }
+                rt.mCurrentThreadTime = 0;
+            }
+            Log.d("Http", "Http thread used " + totalTime + " ms " + " for "
+                    + mTotalRequest + " requests and " + mTotalConnection
+                    + " new connections");
+        }
+
+        void logState() {
+            StringBuilder dump = new StringBuilder();
+            for (int i = 0; i < mConnectionCount; i++) {
+                dump.append(mThreads[i] + "\n");
+            }
+            HttpLog.v(dump.toString());
+        }
+
+
+        public HttpHost getProxyHost() {
+            return mProxyHost;
+        }
+
+        /**
+         * Turns off persistence on all live connections
+         */
+        void disablePersistence() {
+            for (int i = 0; i < mConnectionCount; i++) {
+                Connection connection = mThreads[i].mConnection;
+                if (connection != null) connection.setCanPersist(false);
+            }
+            mIdleCache.clear();
+        }
+
+        /* Linear lookup -- okay for small thread counts.  Might use
+           private HashMap<HttpHost, LinkedList<ConnectionThread>> mActiveMap;
+           if this turns out to be a hotspot */
+        ConnectionThread getThread(HttpHost host) {
+            synchronized(RequestQueue.this) {
+                for (int i = 0; i < mThreads.length; i++) {
+                    ConnectionThread ct = mThreads[i];
+                    Connection connection = ct.mConnection;
+                    if (connection != null && connection.mHost.equals(host)) {
+                        return ct;
+                    }
+                }
+            }
+            return null;
+        }
+
+        public Connection getConnection(Context context, HttpHost host) {
+            host = RequestQueue.this.determineHost(host);
+            Connection con = mIdleCache.getConnection(host);
+            if (con == null) {
+                mTotalConnection++;
+                con = Connection.getConnection(mContext, host, mProxyHost,
+                        RequestQueue.this);
+            }
+            return con;
+        }
+        public boolean recycleConnection(Connection connection) {
+            return mIdleCache.cacheConnection(connection.getHost(), connection);
+        }
+
+    }
+
+    /**
+     * A RequestQueue class instance maintains a set of queued
+     * requests.  It orders them, makes the requests against HTTP
+     * servers, and makes callbacks to supplied eventHandlers as data
+     * is read.  It supports request prioritization, connection reuse
+     * and pipelining.
+     *
+     * @param context application context
+     */
+    public RequestQueue(Context context) {
+        this(context, CONNECTION_COUNT);
+    }
+
+    /**
+     * A RequestQueue class instance maintains a set of queued
+     * requests.  It orders them, makes the requests against HTTP
+     * servers, and makes callbacks to supplied eventHandlers as data
+     * is read.  It supports request prioritization, connection reuse
+     * and pipelining.
+     *
+     * @param context application context
+     * @param connectionCount The number of simultaneous connections 
+     */
+    public RequestQueue(Context context, int connectionCount) {
+        mContext = context;
+
+        mPending = new LinkedHashMap<HttpHost, LinkedList<Request>>(32);
+
+        mActivePool = new ActivePool(connectionCount);
+        mActivePool.startup();
+
+        mConnectivityManager = (ConnectivityManager)
+                context.getSystemService(Context.CONNECTIVITY_SERVICE);
+    }
+
+    /**
+     * Enables data state and proxy tracking
+     */
+    public synchronized void enablePlatformNotifications() {
+        if (HttpLog.LOGV) HttpLog.v("RequestQueue.enablePlatformNotifications() network");
+
+        if (mProxyChangeReceiver == null) {
+            mProxyChangeReceiver =
+                    new BroadcastReceiver() {
+                        @Override
+                        public void onReceive(Context ctx, Intent intent) {
+                            setProxyConfig();
+                        }
+                    };
+            mContext.registerReceiver(mProxyChangeReceiver,
+                                      new IntentFilter(Proxy.PROXY_CHANGE_ACTION));
+        }
+        // we need to resample the current proxy setup
+        setProxyConfig();
+    }
+
+    /**
+     * If platform notifications have been enabled, call this method
+     * to disable before destroying RequestQueue
+     */
+    public synchronized void disablePlatformNotifications() {
+        if (HttpLog.LOGV) HttpLog.v("RequestQueue.disablePlatformNotifications() network");
+
+        if (mProxyChangeReceiver != null) {
+            mContext.unregisterReceiver(mProxyChangeReceiver);
+            mProxyChangeReceiver = null;
+        }
+    }
+
+    /**
+     * Because our IntentReceiver can run within a different thread,
+     * synchronize setting the proxy
+     */
+    private synchronized void setProxyConfig() {
+        NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
+        if (info != null && info.getType() == ConnectivityManager.TYPE_WIFI) {
+            mProxyHost = null;
+        } else {
+            String host = Proxy.getHost(mContext);
+            if (HttpLog.LOGV) HttpLog.v("RequestQueue.setProxyConfig " + host);
+            if (host == null) {
+                mProxyHost = null;
+            } else {
+                mActivePool.disablePersistence();
+                mProxyHost = new HttpHost(host, Proxy.getPort(mContext), "http");
+            }
+        }
+    }
+
+    /**
+     * used by webkit
+     * @return proxy host if set, null otherwise
+     */
+    public HttpHost getProxyHost() {
+        return mProxyHost;
+    }
+
+    /**
+     * Queues an HTTP request
+     * @param url The url to load.
+     * @param method "GET" or "POST."
+     * @param headers A hashmap of http headers.
+     * @param eventHandler The event handler for handling returned
+     * data.  Callbacks will be made on the supplied instance.
+     * @param bodyProvider InputStream providing HTTP body, null if none
+     * @param bodyLength length of body, must be 0 if bodyProvider is null
+     */
+    public RequestHandle queueRequest(
+            String url, String method,
+            Map<String, String> headers, EventHandler eventHandler,
+            InputStream bodyProvider, int bodyLength) {
+        WebAddress uri = new WebAddress(url);
+        return queueRequest(url, uri, method, headers, eventHandler,
+                            bodyProvider, bodyLength);
+    }
+
+    /**
+     * Queues an HTTP request
+     * @param url The url to load.
+     * @param uri The uri of the url to load.
+     * @param method "GET" or "POST."
+     * @param headers A hashmap of http headers.
+     * @param eventHandler The event handler for handling returned
+     * data.  Callbacks will be made on the supplied instance.
+     * @param bodyProvider InputStream providing HTTP body, null if none
+     * @param bodyLength length of body, must be 0 if bodyProvider is null
+     */
+    public RequestHandle queueRequest(
+            String url, WebAddress uri, String method, Map<String, String> headers,
+            EventHandler eventHandler,
+            InputStream bodyProvider, int bodyLength) {
+
+        if (HttpLog.LOGV) HttpLog.v("RequestQueue.queueRequest " + uri);
+
+        // Ensure there is an eventHandler set
+        if (eventHandler == null) {
+            eventHandler = new LoggingEventHandler();
+        }
+
+        /* Create and queue request */
+        Request req;
+        HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
+
+        // set up request
+        req = new Request(method, httpHost, mProxyHost, uri.getPath(), bodyProvider,
+                          bodyLength, eventHandler, headers);
+
+        queueRequest(req, false);
+
+        mActivePool.mTotalRequest++;
+
+        // dump();
+        mActivePool.startConnectionThread();
+
+        return new RequestHandle(
+                this, url, uri, method, headers, bodyProvider, bodyLength,
+                req);
+    }
+
+    private static class SyncFeeder implements RequestFeeder {
+        // This is used in the case where the request fails and needs to be
+        // requeued into the RequestFeeder.
+        private Request mRequest;
+        SyncFeeder() {
+        }
+        public Request getRequest() {
+            Request r = mRequest;
+            mRequest = null;
+            return r;
+        }
+        public Request getRequest(HttpHost host) {
+            return getRequest();
+        }
+        public boolean haveRequest(HttpHost host) {
+            return mRequest != null;
+        }
+        public void requeueRequest(Request r) {
+            mRequest = r;
+        }
+    }
+
+    public RequestHandle queueSynchronousRequest(String url, WebAddress uri,
+            String method, Map<String, String> headers,
+            EventHandler eventHandler, InputStream bodyProvider,
+            int bodyLength) {
+        if (HttpLog.LOGV) {
+            HttpLog.v("RequestQueue.dispatchSynchronousRequest " + uri);
+        }
+
+        HttpHost host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
+
+        Request req = new Request(method, host, mProxyHost, uri.getPath(),
+                bodyProvider, bodyLength, eventHandler, headers);
+
+        // Open a new connection that uses our special RequestFeeder
+        // implementation.
+        host = determineHost(host);
+        Connection conn = Connection.getConnection(mContext, host, mProxyHost,
+                new SyncFeeder());
+
+        // TODO: I would like to process the request here but LoadListener
+        // needs a RequestHandle to process some messages.
+        return new RequestHandle(this, url, uri, method, headers, bodyProvider,
+                bodyLength, req, conn);
+
+    }
+
+    // Chooses between the proxy and the request's host.
+    private HttpHost determineHost(HttpHost host) {
+        // There used to be a comment in ConnectionThread about t-mob's proxy
+        // being really bad about https. But, HttpsConnection actually looks
+        // for a proxy and connects through it anyway. I think that this check
+        // is still valid because if a site is https, we will use
+        // HttpsConnection rather than HttpConnection if the proxy address is
+        // not secure.
+        return (mProxyHost == null || "https".equals(host.getSchemeName()))
+                ? host : mProxyHost;
+    }
+
+    /**
+     * @return true iff there are any non-active requests pending
+     */
+    synchronized boolean requestsPending() {
+        return !mPending.isEmpty();
+    }
+
+
+    /**
+     * debug tool: prints request queue to log
+     */
+    synchronized void dump() {
+        HttpLog.v("dump()");
+        StringBuilder dump = new StringBuilder();
+        int count = 0;
+        Iterator<Map.Entry<HttpHost, LinkedList<Request>>> iter;
+
+        // mActivePool.log(dump);
+
+        if (!mPending.isEmpty()) {
+            iter = mPending.entrySet().iterator();
+            while (iter.hasNext()) {
+                Map.Entry<HttpHost, LinkedList<Request>> entry = iter.next();
+                String hostName = entry.getKey().getHostName();
+                StringBuilder line = new StringBuilder("p" + count++ + " " + hostName + " ");
+
+                LinkedList<Request> reqList = entry.getValue();
+                ListIterator reqIter = reqList.listIterator(0);
+                while (iter.hasNext()) {
+                    Request request = (Request)iter.next();
+                    line.append(request + " ");
+                }
+                dump.append(line);
+                dump.append("\n");
+            }
+        }
+        HttpLog.v(dump.toString());
+    }
+
+    /*
+     * RequestFeeder implementation
+     */
+    public synchronized Request getRequest() {
+        Request ret = null;
+
+        if (!mPending.isEmpty()) {
+            ret = removeFirst(mPending);
+        }
+        if (HttpLog.LOGV) HttpLog.v("RequestQueue.getRequest() => " + ret);
+        return ret;
+    }
+
+    /**
+     * @return a request for given host if possible
+     */
+    public synchronized Request getRequest(HttpHost host) {
+        Request ret = null;
+
+        if (mPending.containsKey(host)) {
+            LinkedList<Request> reqList = mPending.get(host);
+            ret = reqList.removeFirst();
+            if (reqList.isEmpty()) {
+                mPending.remove(host);
+            }
+        }
+        if (HttpLog.LOGV) HttpLog.v("RequestQueue.getRequest(" + host + ") => " + ret);
+        return ret;
+    }
+
+    /**
+     * @return true if a request for this host is available
+     */
+    public synchronized boolean haveRequest(HttpHost host) {
+        return mPending.containsKey(host);
+    }
+
+    /**
+     * Put request back on head of queue
+     */
+    public void requeueRequest(Request request) {
+        queueRequest(request, true);
+    }
+
+    /**
+     * This must be called to cleanly shutdown RequestQueue
+     */
+    public void shutdown() {
+        mActivePool.shutdown();
+    }
+
+    protected synchronized void queueRequest(Request request, boolean head) {
+        HttpHost host = request.mProxyHost == null ? request.mHost : request.mProxyHost;
+        LinkedList<Request> reqList;
+        if (mPending.containsKey(host)) {
+            reqList = mPending.get(host);
+        } else {
+            reqList = new LinkedList<Request>();
+            mPending.put(host, reqList);
+        }
+        if (head) {
+            reqList.addFirst(request);
+        } else {
+            reqList.add(request);
+        }
+    }
+
+
+    public void startTiming() {
+        mActivePool.startTiming();
+    }
+
+    public void stopTiming() {
+        mActivePool.stopTiming();
+    }
+
+    /* helper */
+    private Request removeFirst(LinkedHashMap<HttpHost, LinkedList<Request>> requestQueue) {
+        Request ret = null;
+        Iterator<Map.Entry<HttpHost, LinkedList<Request>>> iter = requestQueue.entrySet().iterator();
+        if (iter.hasNext()) {
+            Map.Entry<HttpHost, LinkedList<Request>> entry = iter.next();
+            LinkedList<Request> reqList = entry.getValue();
+            ret = reqList.removeFirst();
+            if (reqList.isEmpty()) {
+                requestQueue.remove(entry.getKey());
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * This interface is exposed to each connection
+     */
+    interface ConnectionManager {
+        HttpHost getProxyHost();
+        Connection getConnection(Context context, HttpHost host);
+        boolean recycleConnection(Connection connection);
+    }
+}
diff --git a/android/src/com/android/internal/http/multipart/ByteArrayPartSource.java b/android/src/com/android/internal/http/multipart/ByteArrayPartSource.java
new file mode 100644
index 0000000..faaac7f
--- /dev/null
+++ b/android/src/com/android/internal/http/multipart/ByteArrayPartSource.java
@@ -0,0 +1,86 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/ByteArrayPartSource.java,v 1.7 2004/04/18 23:51:37 jsdever Exp $
+ * $Revision: 480424 $
+ * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package com.android.internal.http.multipart;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * A PartSource that reads from a byte array.  This class should be used when
+ * the data to post is already loaded into memory.
+ * 
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ *   
+ * @since 2.0 
+ */
+public class ByteArrayPartSource implements PartSource {
+
+    /** Name of the source file. */
+    private String fileName;
+
+    /** Byte array of the source file. */
+    private byte[] bytes;
+
+    /**
+     * Constructor for ByteArrayPartSource.
+     * 
+     * @param fileName the name of the file these bytes represent
+     * @param bytes the content of this part
+     */
+    public ByteArrayPartSource(String fileName, byte[] bytes) {
+
+        this.fileName = fileName;
+        this.bytes = bytes;
+
+    }
+
+    /**
+     * @see PartSource#getLength()
+     */
+    public long getLength() {
+        return bytes.length;
+    }
+
+    /**
+     * @see PartSource#getFileName()
+     */
+    public String getFileName() {
+        return fileName;
+    }
+
+    /**
+     * @see PartSource#createInputStream()
+     */
+    public InputStream createInputStream() {
+        return new ByteArrayInputStream(bytes);
+    }
+
+}
diff --git a/android/src/com/android/internal/http/multipart/FilePart.java b/android/src/com/android/internal/http/multipart/FilePart.java
new file mode 100644
index 0000000..bfcda00
--- /dev/null
+++ b/android/src/com/android/internal/http/multipart/FilePart.java
@@ -0,0 +1,254 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java,v 1.19 2004/04/18 23:51:37 jsdever Exp $
+ * $Revision: 480424 $
+ * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package com.android.internal.http.multipart;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import org.apache.http.util.EncodingUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This class implements a part of a Multipart post object that
+ * consists of a file.  
+ *
+ * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ * @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ *   
+ * @since 2.0 
+ *
+ */
+public class FilePart extends PartBase {
+
+    /** Default content encoding of file attachments. */
+    public static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
+
+    /** Default charset of file attachments. */
+    public static final String DEFAULT_CHARSET = "ISO-8859-1";
+
+    /** Default transfer encoding of file attachments. */
+    public static final String DEFAULT_TRANSFER_ENCODING = "binary";
+
+    /** Log object for this class. */
+    private static final Log LOG = LogFactory.getLog(FilePart.class);
+
+    /** Attachment's file name */
+    protected static final String FILE_NAME = "; filename=";
+
+    /** Attachment's file name as a byte array */
+    private static final byte[] FILE_NAME_BYTES = 
+        EncodingUtils.getAsciiBytes(FILE_NAME);
+
+    /** Source of the file part. */
+    private PartSource source;
+
+    /**
+     * FilePart Constructor.
+     *
+     * @param name the name for this part
+     * @param partSource the source for this part
+     * @param contentType the content type for this part, if <code>null</code> the 
+     * {@link #DEFAULT_CONTENT_TYPE default} is used
+     * @param charset the charset encoding for this part, if <code>null</code> the 
+     * {@link #DEFAULT_CHARSET default} is used
+     */
+    public FilePart(String name, PartSource partSource, String contentType, String charset) {
+        
+        super(
+            name, 
+            contentType == null ? DEFAULT_CONTENT_TYPE : contentType, 
+            charset == null ? "ISO-8859-1" : charset, 
+            DEFAULT_TRANSFER_ENCODING
+        );
+
+        if (partSource == null) {
+            throw new IllegalArgumentException("Source may not be null");
+        }
+        this.source = partSource;
+    }
+        
+    /**
+     * FilePart Constructor.
+     *
+     * @param name the name for this part
+     * @param partSource the source for this part
+     */
+    public FilePart(String name, PartSource partSource) {
+        this(name, partSource, null, null);
+    }
+
+    /**
+     * FilePart Constructor.
+     *
+     * @param name the name of the file part
+     * @param file the file to post
+     *
+     * @throws FileNotFoundException if the <i>file</i> is not a normal
+     * file or if it is not readable.
+     */
+    public FilePart(String name, File file) 
+    throws FileNotFoundException {
+        this(name, new FilePartSource(file), null, null);
+    }
+
+    /**
+     * FilePart Constructor.
+     *
+     * @param name the name of the file part
+     * @param file the file to post
+     * @param contentType the content type for this part, if <code>null</code> the 
+     * {@link #DEFAULT_CONTENT_TYPE default} is used
+     * @param charset the charset encoding for this part, if <code>null</code> the 
+     * {@link #DEFAULT_CHARSET default} is used
+     *
+     * @throws FileNotFoundException if the <i>file</i> is not a normal
+     * file or if it is not readable.
+     */
+    public FilePart(String name, File file, String contentType, String charset) 
+    throws FileNotFoundException {
+        this(name, new FilePartSource(file), contentType, charset);
+    }
+
+     /**
+     * FilePart Constructor.
+     *
+     * @param name the name of the file part
+     * @param fileName the file name 
+     * @param file the file to post
+     *
+     * @throws FileNotFoundException if the <i>file</i> is not a normal
+     * file or if it is not readable.
+     */
+    public FilePart(String name, String fileName, File file) 
+    throws FileNotFoundException {
+        this(name, new FilePartSource(fileName, file), null, null);
+    }
+    
+     /**
+     * FilePart Constructor.
+     *
+     * @param name the name of the file part
+     * @param fileName the file name 
+     * @param file the file to post
+     * @param contentType the content type for this part, if <code>null</code> the 
+     * {@link #DEFAULT_CONTENT_TYPE default} is used
+     * @param charset the charset encoding for this part, if <code>null</code> the 
+     * {@link #DEFAULT_CHARSET default} is used
+     *
+     * @throws FileNotFoundException if the <i>file</i> is not a normal
+     * file or if it is not readable.
+     */
+    public FilePart(String name, String fileName, File file, String contentType, String charset) 
+    throws FileNotFoundException {
+        this(name, new FilePartSource(fileName, file), contentType, charset);
+    }
+    
+    /**
+     * Write the disposition header to the output stream
+     * @param out The output stream
+     * @throws IOException If an IO problem occurs
+     * @see Part#sendDispositionHeader(OutputStream)
+     */
+    @Override
+    protected void sendDispositionHeader(OutputStream out) 
+    throws IOException {
+        LOG.trace("enter sendDispositionHeader(OutputStream out)");
+        super.sendDispositionHeader(out);
+        String filename = this.source.getFileName();
+        if (filename != null) {
+            out.write(FILE_NAME_BYTES);
+            out.write(QUOTE_BYTES);
+            out.write(EncodingUtils.getAsciiBytes(filename));
+            out.write(QUOTE_BYTES);
+        }
+    }
+    
+    /**
+     * Write the data in "source" to the specified stream.
+     * @param out The output stream.
+     * @throws IOException if an IO problem occurs.
+     * @see Part#sendData(OutputStream)
+     */
+    @Override
+    protected void sendData(OutputStream out) throws IOException {
+        LOG.trace("enter sendData(OutputStream out)");
+        if (lengthOfData() == 0) {
+            
+            // this file contains no data, so there is nothing to send.
+            // we don't want to create a zero length buffer as this will
+            // cause an infinite loop when reading.
+            LOG.debug("No data to send.");
+            return;
+        }
+        
+        byte[] tmp = new byte[4096];
+        InputStream instream = source.createInputStream();
+        try {
+            int len;
+            while ((len = instream.read(tmp)) >= 0) {
+                out.write(tmp, 0, len);
+            }
+        } finally {
+            // we're done with the stream, close it
+            instream.close();
+        }
+    }
+
+    /** 
+     * Returns the source of the file part.
+     *  
+     * @return The source.
+     */
+    protected PartSource getSource() {
+        LOG.trace("enter getSource()");
+        return this.source;
+    }
+
+    /**
+     * Return the length of the data.
+     * @return The length.
+     * @see Part#lengthOfData()
+     */    
+    @Override
+    protected long lengthOfData() {
+        LOG.trace("enter lengthOfData()");
+        return source.getLength();
+    }    
+
+}
diff --git a/android/src/com/android/internal/http/multipart/FilePartSource.java b/android/src/com/android/internal/http/multipart/FilePartSource.java
new file mode 100644
index 0000000..eb5cc0f
--- /dev/null
+++ b/android/src/com/android/internal/http/multipart/FilePartSource.java
@@ -0,0 +1,131 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePartSource.java,v 1.10 2004/04/18 23:51:37 jsdever Exp $
+ * $Revision: 480424 $
+ * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package com.android.internal.http.multipart;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A PartSource that reads from a File.
+ * 
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ * @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ *   
+ * @since 2.0 
+ */
+public class FilePartSource implements PartSource {
+
+    /** File part file. */
+    private File file = null;
+
+    /** File part file name. */
+    private String fileName = null;
+    
+    /**
+     * Constructor for FilePartSource.
+     * 
+     * @param file the FilePart source File. 
+     *
+     * @throws FileNotFoundException if the file does not exist or 
+     * cannot be read
+     */
+    public FilePartSource(File file) throws FileNotFoundException {
+        this.file = file;
+        if (file != null) {
+            if (!file.isFile()) {
+                throw new FileNotFoundException("File is not a normal file.");
+            }
+            if (!file.canRead()) {
+                throw new FileNotFoundException("File is not readable.");
+            }
+            this.fileName = file.getName();       
+        }
+    }
+
+    /**
+     * Constructor for FilePartSource.
+     * 
+     * @param fileName the file name of the FilePart
+     * @param file the source File for the FilePart
+     *
+     * @throws FileNotFoundException if the file does not exist or 
+     * cannot be read
+     */
+    public FilePartSource(String fileName, File file) 
+      throws FileNotFoundException {
+        this(file);
+        if (fileName != null) {
+            this.fileName = fileName;
+        }
+    }
+    
+    /**
+     * Return the length of the file
+     * @return the length of the file.
+     * @see PartSource#getLength()
+     */
+    public long getLength() {
+        if (this.file != null) {
+            return this.file.length();
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Return the current filename
+     * @return the filename.
+     * @see PartSource#getFileName()
+     */
+    public String getFileName() {
+        return (fileName == null) ? "noname" : fileName;
+    }
+
+    /**
+     * Return a new {@link FileInputStream} for the current filename.
+     * @return the new input stream.
+     * @throws IOException If an IO problem occurs.
+     * @see PartSource#createInputStream()
+     */
+    public InputStream createInputStream() throws IOException {
+        if (this.file != null) {
+            return new FileInputStream(this.file);
+        } else {
+            return new ByteArrayInputStream(new byte[] {});
+        }
+    }
+
+}
diff --git a/android/src/com/android/internal/http/multipart/MultipartEntity.java b/android/src/com/android/internal/http/multipart/MultipartEntity.java
new file mode 100644
index 0000000..2c5e7f6
--- /dev/null
+++ b/android/src/com/android/internal/http/multipart/MultipartEntity.java
@@ -0,0 +1,230 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/MultipartRequestEntity.java,v 1.1 2004/10/06 03:39:59 mbecke Exp $
+ * $Revision: 502647 $
+ * $Date: 2007-02-02 17:22:54 +0100 (Fri, 02 Feb 2007) $
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package com.android.internal.http.multipart;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Random;
+
+import org.apache.http.Header;
+import org.apache.http.entity.AbstractHttpEntity;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.EncodingUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Implements a request entity suitable for an HTTP multipart POST method.
+ * <p>
+ * The HTTP multipart POST method is defined in section 3.3 of
+ * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC1867</a>:
+ * <blockquote>
+ * The media-type multipart/form-data follows the rules of all multipart
+ * MIME data streams as outlined in RFC 1521. The multipart/form-data contains 
+ * a series of parts. Each part is expected to contain a content-disposition 
+ * header where the value is "form-data" and a name attribute specifies 
+ * the field name within the form, e.g., 'content-disposition: form-data; 
+ * name="xxxxx"', where xxxxx is the field name corresponding to that field.
+ * Field names originally in non-ASCII character sets may be encoded using 
+ * the method outlined in RFC 1522.
+ * </blockquote>
+ * </p>
+ * <p>This entity is designed to be used in conjunction with the 
+ * {@link org.apache.http.HttpRequest} to provide
+ * multipart posts.  Example usage:</p>
+ * <pre>
+ *  File f = new File("/path/fileToUpload.txt");
+ *  HttpRequest request = new HttpRequest("http://host/some_path");
+ *  Part[] parts = {
+ *      new StringPart("param_name", "value"),
+ *      new FilePart(f.getName(), f)
+ *  };
+ *  filePost.setEntity(
+ *      new MultipartRequestEntity(parts, filePost.getParams())
+ *      );
+ *  HttpClient client = new HttpClient();
+ *  int status = client.executeMethod(filePost);
+ * </pre>
+ * 
+ * @since 3.0
+ */
+public class MultipartEntity extends AbstractHttpEntity {
+
+    private static final Log log = LogFactory.getLog(MultipartEntity.class);
+    
+    /** The Content-Type for multipart/form-data. */
+    private static final String MULTIPART_FORM_CONTENT_TYPE = "multipart/form-data";
+    
+    /**
+     * Sets the value to use as the multipart boundary.
+     * <p>
+     * This parameter expects a value if type {@link String}.
+     * </p>
+     */
+    public static final String MULTIPART_BOUNDARY = "http.method.multipart.boundary";
+    
+    /**
+     * The pool of ASCII chars to be used for generating a multipart boundary.
+     */
+    private static byte[] MULTIPART_CHARS = EncodingUtils.getAsciiBytes(
+        "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+    
+    /**
+     * Generates a random multipart boundary string.
+    */
+    private static byte[] generateMultipartBoundary() {
+        Random rand = new Random();
+        byte[] bytes = new byte[rand.nextInt(11) + 30]; // a random size from 30 to 40
+        for (int i = 0; i < bytes.length; i++) {
+            bytes[i] = MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)];
+        }
+        return bytes;
+    }
+    
+    /** The MIME parts as set by the constructor */
+    protected Part[] parts;
+    
+    private byte[] multipartBoundary;
+    
+    private HttpParams params;
+    
+    private boolean contentConsumed = false;
+    
+    /**
+     * Creates a new multipart entity containing the given parts.
+     * @param parts The parts to include.
+     * @param params The params of the HttpMethod using this entity.
+     */
+    public MultipartEntity(Part[] parts, HttpParams params) {      
+      if (parts == null) {
+          throw new IllegalArgumentException("parts cannot be null");
+      }
+      if (params == null) {
+          throw new IllegalArgumentException("params cannot be null");
+      }
+      this.parts = parts;
+      this.params = params;
+    }
+    
+    public MultipartEntity(Part[] parts) {
+      setContentType(MULTIPART_FORM_CONTENT_TYPE);
+      if (parts == null) {
+          throw new IllegalArgumentException("parts cannot be null");
+      }
+      this.parts = parts;
+      this.params = null;
+    }
+    
+    /**
+     * Returns the MIME boundary string that is used to demarcate boundaries of
+     * this part. The first call to this method will implicitly create a new
+     * boundary string. To create a boundary string first the 
+     * HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise 
+     * a random one is generated.
+     * 
+     * @return The boundary string of this entity in ASCII encoding.
+     */
+    protected byte[] getMultipartBoundary() {
+        if (multipartBoundary == null) {
+            String temp = null;
+            if (params != null) {
+              temp = (String) params.getParameter(MULTIPART_BOUNDARY);
+            }
+            if (temp != null) {
+                multipartBoundary = EncodingUtils.getAsciiBytes(temp);
+            } else {
+                multipartBoundary = generateMultipartBoundary();
+            }
+        }
+        return multipartBoundary;
+    }
+
+    /**
+     * Returns <code>true</code> if all parts are repeatable, <code>false</code> otherwise.
+     */
+    public boolean isRepeatable() {
+        for (int i = 0; i < parts.length; i++) {
+            if (!parts[i].isRepeatable()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /* (non-Javadoc)
+     */
+    public void writeTo(OutputStream out) throws IOException {
+        Part.sendParts(out, parts, getMultipartBoundary());
+    }
+    /* (non-Javadoc)
+     * @see org.apache.commons.http.AbstractHttpEntity.#getContentType()
+     */
+    @Override
+    public Header getContentType() {
+      StringBuffer buffer = new StringBuffer(MULTIPART_FORM_CONTENT_TYPE);
+      buffer.append("; boundary=");
+      buffer.append(EncodingUtils.getAsciiString(getMultipartBoundary()));
+      return new BasicHeader(HTTP.CONTENT_TYPE, buffer.toString());
+
+    }
+
+    /* (non-Javadoc)
+     */
+    public long getContentLength() {
+        try {
+            return Part.getLengthOfParts(parts, getMultipartBoundary());            
+        } catch (Exception e) {
+            log.error("An exception occurred while getting the length of the parts", e);
+            return 0;
+        }
+    }    
+ 
+    public InputStream getContent() throws IOException, IllegalStateException {
+          if(!isRepeatable() && this.contentConsumed ) {
+              throw new IllegalStateException("Content has been consumed");
+          }
+          this.contentConsumed = true;
+          
+          ByteArrayOutputStream baos = new ByteArrayOutputStream();
+          Part.sendParts(baos, this.parts, this.multipartBoundary);
+          ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+          return bais;
+    }
+  
+    public boolean isStreaming() {
+        return false;
+    }
+}
diff --git a/android/src/com/android/internal/http/multipart/Part.java b/android/src/com/android/internal/http/multipart/Part.java
new file mode 100644
index 0000000..cb1b546
--- /dev/null
+++ b/android/src/com/android/internal/http/multipart/Part.java
@@ -0,0 +1,439 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/Part.java,v 1.16 2005/01/14 21:16:40 olegk Exp $
+ * $Revision: 480424 $
+ * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package com.android.internal.http.multipart;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.util.EncodingUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Abstract class for one Part of a multipart post object.
+ *
+ * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 2.0
+ */
+public abstract class Part {
+
+    /** Log object for this class. */
+    private static final Log LOG = LogFactory.getLog(Part.class);
+
+    /** 
+     * The boundary 
+     * @deprecated use {@link org.apache.http.client.methods.multipart#MULTIPART_BOUNDARY}
+     */
+    protected static final String BOUNDARY = "----------------314159265358979323846";
+    
+    /** 
+     * The boundary as a byte array.
+     * @deprecated
+     */
+    protected static final byte[] BOUNDARY_BYTES = EncodingUtils.getAsciiBytes(BOUNDARY);
+
+    /**
+     * The default boundary to be used if {@link #setPartBoundary(byte[])} has not
+     * been called.
+     */
+    private static final byte[] DEFAULT_BOUNDARY_BYTES = BOUNDARY_BYTES;    
+    
+    /** Carriage return/linefeed */
+    protected static final String CRLF = "\r\n";
+    
+    /** Carriage return/linefeed as a byte array */
+    protected static final byte[] CRLF_BYTES = EncodingUtils.getAsciiBytes(CRLF);
+    
+    /** Content dispostion characters */
+    protected static final String QUOTE = "\"";
+    
+    /** Content dispostion as a byte array */
+    protected static final byte[] QUOTE_BYTES = 
+      EncodingUtils.getAsciiBytes(QUOTE);
+
+    /** Extra characters */
+    protected static final String EXTRA = "--";
+    
+    /** Extra characters as a byte array */
+    protected static final byte[] EXTRA_BYTES = 
+      EncodingUtils.getAsciiBytes(EXTRA);
+    
+    /** Content dispostion characters */
+    protected static final String CONTENT_DISPOSITION = "Content-Disposition: form-data; name=";
+    
+    /** Content dispostion as a byte array */
+    protected static final byte[] CONTENT_DISPOSITION_BYTES = 
+      EncodingUtils.getAsciiBytes(CONTENT_DISPOSITION);
+
+    /** Content type header */
+    protected static final String CONTENT_TYPE = "Content-Type: ";
+
+    /** Content type header as a byte array */
+    protected static final byte[] CONTENT_TYPE_BYTES = 
+      EncodingUtils.getAsciiBytes(CONTENT_TYPE);
+
+    /** Content charset */
+    protected static final String CHARSET = "; charset=";
+
+    /** Content charset as a byte array */
+    protected static final byte[] CHARSET_BYTES = 
+      EncodingUtils.getAsciiBytes(CHARSET);
+
+    /** Content type header */
+    protected static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding: ";
+
+    /** Content type header as a byte array */
+    protected static final byte[] CONTENT_TRANSFER_ENCODING_BYTES = 
+      EncodingUtils.getAsciiBytes(CONTENT_TRANSFER_ENCODING);
+
+    /**
+     * Return the boundary string.
+     * @return the boundary string
+     * @deprecated uses a constant string. Rather use {@link #getPartBoundary}
+     */
+    public static String getBoundary() {
+        return BOUNDARY;
+    }
+
+    /**
+     * The ASCII bytes to use as the multipart boundary.
+     */
+    private byte[] boundaryBytes;
+    
+    /**
+     * Return the name of this part.
+     * @return The name.
+     */
+    public abstract String getName();
+    
+    /**
+     * Returns the content type of this part.
+     * @return the content type, or <code>null</code> to exclude the content type header
+     */
+    public abstract String getContentType();
+
+    /**
+     * Return the character encoding of this part.
+     * @return the character encoding, or <code>null</code> to exclude the character 
+     * encoding header
+     */
+    public abstract String getCharSet();
+
+    /**
+     * Return the transfer encoding of this part.
+     * @return the transfer encoding, or <code>null</code> to exclude the transfer encoding header
+     */
+    public abstract String getTransferEncoding();
+
+    /**
+     * Gets the part boundary to be used.
+     * @return the part boundary as an array of bytes.
+     * 
+     * @since 3.0
+     */
+    protected byte[] getPartBoundary() {
+        if (boundaryBytes == null) {
+            // custom boundary bytes have not been set, use the default.
+            return DEFAULT_BOUNDARY_BYTES;
+        } else {
+            return boundaryBytes;            
+        }
+    }
+    
+    /**
+     * Sets the part boundary.  Only meant to be used by 
+     * {@link Part#sendParts(OutputStream, Part[], byte[])}
+     * and {@link Part#getLengthOfParts(Part[], byte[])}
+     * @param boundaryBytes An array of ASCII bytes.
+     * @since 3.0
+     */
+    void setPartBoundary(byte[] boundaryBytes) {
+        this.boundaryBytes = boundaryBytes;
+    }
+    
+    /**
+     * Tests if this part can be sent more than once.
+     * @return <code>true</code> if {@link #sendData(OutputStream)} can be successfully called 
+     * more than once.
+     * @since 3.0
+     */
+    public boolean isRepeatable() {
+        return true;
+    }
+    
+    /**
+     * Write the start to the specified output stream
+     * @param out The output stream
+     * @throws IOException If an IO problem occurs.
+     */
+    protected void sendStart(OutputStream out) throws IOException {
+        LOG.trace("enter sendStart(OutputStream out)");
+        out.write(EXTRA_BYTES);
+        out.write(getPartBoundary());
+        out.write(CRLF_BYTES);
+    }
+    
+    /**
+     * Write the content disposition header to the specified output stream
+     * 
+     * @param out The output stream
+     * @throws IOException If an IO problem occurs.
+     */
+    protected void sendDispositionHeader(OutputStream out) throws IOException {
+        LOG.trace("enter sendDispositionHeader(OutputStream out)");
+        out.write(CONTENT_DISPOSITION_BYTES);
+        out.write(QUOTE_BYTES);
+        out.write(EncodingUtils.getAsciiBytes(getName()));
+        out.write(QUOTE_BYTES);
+    }
+    
+    /**
+     * Write the content type header to the specified output stream
+     * @param out The output stream
+     * @throws IOException If an IO problem occurs.
+     */
+     protected void sendContentTypeHeader(OutputStream out) throws IOException {
+        LOG.trace("enter sendContentTypeHeader(OutputStream out)");
+        String contentType = getContentType();
+        if (contentType != null) {
+            out.write(CRLF_BYTES);
+            out.write(CONTENT_TYPE_BYTES);
+            out.write(EncodingUtils.getAsciiBytes(contentType));
+            String charSet = getCharSet();
+            if (charSet != null) {
+                out.write(CHARSET_BYTES);
+                out.write(EncodingUtils.getAsciiBytes(charSet));
+            }
+        }
+    }
+
+    /**
+     * Write the content transfer encoding header to the specified 
+     * output stream
+     * 
+     * @param out The output stream
+     * @throws IOException If an IO problem occurs.
+     */
+     protected void sendTransferEncodingHeader(OutputStream out) throws IOException {
+        LOG.trace("enter sendTransferEncodingHeader(OutputStream out)");
+        String transferEncoding = getTransferEncoding();
+        if (transferEncoding != null) {
+            out.write(CRLF_BYTES);
+            out.write(CONTENT_TRANSFER_ENCODING_BYTES);
+            out.write(EncodingUtils.getAsciiBytes(transferEncoding));
+        }
+    }
+
+    /**
+     * Write the end of the header to the output stream
+     * @param out The output stream
+     * @throws IOException If an IO problem occurs.
+     */
+    protected void sendEndOfHeader(OutputStream out) throws IOException {
+        LOG.trace("enter sendEndOfHeader(OutputStream out)");
+        out.write(CRLF_BYTES);
+        out.write(CRLF_BYTES);
+    }
+    
+    /**
+     * Write the data to the specified output stream
+     * @param out The output stream
+     * @throws IOException If an IO problem occurs.
+     */
+    protected abstract void sendData(OutputStream out) throws IOException;
+    
+    /**
+     * Return the length of the main content
+     * 
+     * @return long The length.
+     * @throws IOException If an IO problem occurs
+     */
+    protected abstract long lengthOfData() throws IOException;
+    
+    /**
+     * Write the end data to the output stream.
+     * @param out The output stream
+     * @throws IOException If an IO problem occurs.
+     */
+    protected void sendEnd(OutputStream out) throws IOException {
+        LOG.trace("enter sendEnd(OutputStream out)");
+        out.write(CRLF_BYTES);
+    }
+    
+    /**
+     * Write all the data to the output stream.
+     * If you override this method make sure to override 
+     * #length() as well
+     * 
+     * @param out The output stream
+     * @throws IOException If an IO problem occurs.
+     */
+    public void send(OutputStream out) throws IOException {
+        LOG.trace("enter send(OutputStream out)");
+        sendStart(out);
+        sendDispositionHeader(out);
+        sendContentTypeHeader(out);
+        sendTransferEncodingHeader(out);
+        sendEndOfHeader(out);
+        sendData(out);
+        sendEnd(out);
+    }
+
+
+    /**
+     * Return the full length of all the data.
+     * If you override this method make sure to override 
+     * #send(OutputStream) as well
+     * 
+     * @return long The length.
+     * @throws IOException If an IO problem occurs
+     */
+    public long length() throws IOException {
+        LOG.trace("enter length()");
+        if (lengthOfData() < 0) {
+            return -1;
+        }
+        ByteArrayOutputStream overhead = new ByteArrayOutputStream();
+        sendStart(overhead);
+        sendDispositionHeader(overhead);
+        sendContentTypeHeader(overhead);
+        sendTransferEncodingHeader(overhead);
+        sendEndOfHeader(overhead);
+        sendEnd(overhead);
+        return overhead.size() + lengthOfData();
+    }
+
+    /**
+     * Return a string representation of this object.
+     * @return A string representation of this object.
+     * @see java.lang.Object#toString()
+     */    
+    @Override
+    public String toString() {
+        return this.getName();
+    }
+
+    /**
+     * Write all parts and the last boundary to the specified output stream.
+     * 
+     * @param out The stream to write to.
+     * @param parts The parts to write.
+     * 
+     * @throws IOException If an I/O error occurs while writing the parts.
+     */
+    public static void sendParts(OutputStream out, final Part[] parts)
+        throws IOException {
+        sendParts(out, parts, DEFAULT_BOUNDARY_BYTES);
+    }
+
+    /**
+     * Write all parts and the last boundary to the specified output stream.
+     * 
+     * @param out The stream to write to.
+     * @param parts The parts to write.
+     * @param partBoundary The ASCII bytes to use as the part boundary.
+     * 
+     * @throws IOException If an I/O error occurs while writing the parts.
+     * 
+     * @since 3.0
+     */
+    public static void sendParts(OutputStream out, Part[] parts, byte[] partBoundary)
+        throws IOException {
+        
+        if (parts == null) {
+            throw new IllegalArgumentException("Parts may not be null"); 
+        }
+        if (partBoundary == null || partBoundary.length == 0) {
+            throw new IllegalArgumentException("partBoundary may not be empty");
+        }
+        for (int i = 0; i < parts.length; i++) {
+            // set the part boundary before the part is sent
+            parts[i].setPartBoundary(partBoundary);
+            parts[i].send(out);
+        }
+        out.write(EXTRA_BYTES);
+        out.write(partBoundary);
+        out.write(EXTRA_BYTES);
+        out.write(CRLF_BYTES);
+    }
+    
+    /**
+     * Return the total sum of all parts and that of the last boundary
+     * 
+     * @param parts The parts.
+     * @return The total length
+     * 
+     * @throws IOException If an I/O error occurs while writing the parts.
+     */
+    public static long getLengthOfParts(Part[] parts)
+    throws IOException {
+        return getLengthOfParts(parts, DEFAULT_BOUNDARY_BYTES);
+    }
+    
+    /**
+     * Gets the length of the multipart message including the given parts.
+     * 
+     * @param parts The parts.
+     * @param partBoundary The ASCII bytes to use as the part boundary.
+     * @return The total length
+     * 
+     * @throws IOException If an I/O error occurs while writing the parts.
+     * 
+     * @since 3.0
+     */
+    public static long getLengthOfParts(Part[] parts, byte[] partBoundary) throws IOException {
+        LOG.trace("getLengthOfParts(Parts[])");
+        if (parts == null) {
+            throw new IllegalArgumentException("Parts may not be null"); 
+        }
+        long total = 0;
+        for (int i = 0; i < parts.length; i++) {
+            // set the part boundary before we calculate the part's length
+            parts[i].setPartBoundary(partBoundary);
+            long l = parts[i].length();
+            if (l < 0) {
+                return -1;
+            }
+            total += l;
+        }
+        total += EXTRA_BYTES.length;
+        total += partBoundary.length;
+        total += EXTRA_BYTES.length;
+        total += CRLF_BYTES.length;
+        return total;
+    }        
+}
diff --git a/android/src/com/android/internal/http/multipart/PartBase.java b/android/src/com/android/internal/http/multipart/PartBase.java
new file mode 100644
index 0000000..876d15d
--- /dev/null
+++ b/android/src/com/android/internal/http/multipart/PartBase.java
@@ -0,0 +1,150 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/PartBase.java,v 1.5 2004/04/18 23:51:37 jsdever Exp $
+ * $Revision: 480424 $
+ * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package com.android.internal.http.multipart;
+
+
+/**
+ * Provides setters and getters for the basic Part properties.
+ * 
+ * @author Michael Becke
+ */
+public abstract class PartBase extends Part {
+
+    /** Name of the file part. */
+    private String name;
+        
+    /** Content type of the file part. */
+    private String contentType;
+
+    /** Content encoding of the file part. */
+    private String charSet;
+    
+    /** The transfer encoding. */
+    private String transferEncoding;
+
+    /**
+     * Constructor.
+     * 
+     * @param name The name of the part
+     * @param contentType The content type, or <code>null</code>
+     * @param charSet The character encoding, or <code>null</code> 
+     * @param transferEncoding The transfer encoding, or <code>null</code>
+     */
+    public PartBase(String name, String contentType, String charSet, String transferEncoding) {
+
+        if (name == null) {
+            throw new IllegalArgumentException("Name must not be null");
+        }
+        this.name = name;
+        this.contentType = contentType;
+        this.charSet = charSet;
+        this.transferEncoding = transferEncoding;
+    }
+
+    /**
+     * Returns the name.
+     * @return The name.
+     * @see Part#getName()
+     */
+    @Override
+    public String getName() { 
+        return this.name; 
+    }
+
+    /**
+     * Returns the content type of this part.
+     * @return String The name.
+     */
+    @Override
+    public String getContentType() {
+        return this.contentType;
+    }
+
+    /**
+     * Return the character encoding of this part.
+     * @return String The name.
+     */
+    @Override
+    public String getCharSet() {
+        return this.charSet;
+    }
+
+    /**
+     * Returns the transfer encoding of this part.
+     * @return String The name.
+     */
+    @Override
+    public String getTransferEncoding() {
+        return transferEncoding;
+    }
+
+    /**
+     * Sets the character encoding.
+     * 
+     * @param charSet the character encoding, or <code>null</code> to exclude the character 
+     * encoding header
+     */
+    public void setCharSet(String charSet) {
+        this.charSet = charSet;
+    }
+
+    /**
+     * Sets the content type.
+     * 
+     * @param contentType the content type, or <code>null</code> to exclude the content type header
+     */
+    public void setContentType(String contentType) {
+        this.contentType = contentType;
+    }
+
+    /**
+     * Sets the part name.
+     * 
+     * @param name
+     */
+    public void setName(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Name must not be null");
+        }
+        this.name = name;
+    }
+
+    /**
+     * Sets the transfer encoding.
+     * 
+     * @param transferEncoding the transfer encoding, or <code>null</code> to exclude the 
+     * transfer encoding header
+     */
+    public void setTransferEncoding(String transferEncoding) {
+        this.transferEncoding = transferEncoding;
+    }
+
+}
diff --git a/android/src/com/android/internal/http/multipart/PartSource.java b/android/src/com/android/internal/http/multipart/PartSource.java
new file mode 100644
index 0000000..3740696
--- /dev/null
+++ b/android/src/com/android/internal/http/multipart/PartSource.java
@@ -0,0 +1,72 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/PartSource.java,v 1.6 2004/04/18 23:51:37 jsdever Exp $
+ * $Revision: 480424 $
+ * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package com.android.internal.http.multipart;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * An interface for providing access to data when posting MultiPart messages.
+ * 
+ * @see FilePart
+ * 
+ * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
+ *   
+ * @since 2.0 
+ */
+public interface PartSource {
+
+    /**
+     * Gets the number of bytes contained in this source.
+     * 
+     * @return a value >= 0
+     */
+    long getLength();
+    
+    /**
+     * Gets the name of the file this source represents.
+     * 
+     * @return the fileName used for posting a MultiPart file part
+     */
+    String getFileName();
+    
+    /**
+     * Gets a new InputStream for reading this source.  This method can be 
+     * called more than once and should therefore return a new stream every
+     * time.
+     * 
+     * @return a new InputStream
+     * 
+     * @throws IOException if an error occurs when creating the InputStream
+     */
+    InputStream createInputStream() throws IOException;
+
+}
diff --git a/android/src/com/android/internal/http/multipart/StringPart.java b/android/src/com/android/internal/http/multipart/StringPart.java
new file mode 100644
index 0000000..c98257e
--- /dev/null
+++ b/android/src/com/android/internal/http/multipart/StringPart.java
@@ -0,0 +1,150 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/StringPart.java,v 1.11 2004/04/18 23:51:37 jsdever Exp $
+ * $Revision: 480424 $
+ * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package com.android.internal.http.multipart;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+import org.apache.http.util.EncodingUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Simple string parameter for a multipart post
+ *
+ * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
+ * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
+ * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ *
+ * @since 2.0
+ */
+public class StringPart extends PartBase {
+
+    /** Log object for this class. */
+    private static final Log LOG = LogFactory.getLog(StringPart.class);
+
+    /** Default content encoding of string parameters. */
+    public static final String DEFAULT_CONTENT_TYPE = "text/plain";
+
+    /** Default charset of string parameters*/
+    public static final String DEFAULT_CHARSET = "US-ASCII";
+
+    /** Default transfer encoding of string parameters*/
+    public static final String DEFAULT_TRANSFER_ENCODING = "8bit";
+
+    /** Contents of this StringPart. */
+    private byte[] content;
+    
+    /** The String value of this part. */
+    private String value;
+
+    /**
+     * Constructor.
+     *
+     * @param name The name of the part
+     * @param value the string to post
+     * @param charset the charset to be used to encode the string, if <code>null</code> 
+     * the {@link #DEFAULT_CHARSET default} is used
+     */
+    public StringPart(String name, String value, String charset) {
+        
+        super(
+            name,
+            DEFAULT_CONTENT_TYPE,
+            charset == null ? DEFAULT_CHARSET : charset,
+            DEFAULT_TRANSFER_ENCODING
+        );
+        if (value == null) {
+            throw new IllegalArgumentException("Value may not be null");
+        }
+        if (value.indexOf(0) != -1) {
+            // See RFC 2048, 2.8. "8bit Data"
+            throw new IllegalArgumentException("NULs may not be present in string parts");
+        }
+        this.value = value;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param name The name of the part
+     * @param value the string to post
+     */
+    public StringPart(String name, String value) {
+        this(name, value, null);
+    }
+    
+    /**
+     * Gets the content in bytes.  Bytes are lazily created to allow the charset to be changed
+     * after the part is created.
+     * 
+     * @return the content in bytes
+     */
+    private byte[] getContent() {
+        if (content == null) {
+            content = EncodingUtils.getBytes(value, getCharSet());
+        }
+        return content;
+    }
+    
+    /**
+     * Writes the data to the given OutputStream.
+     * @param out the OutputStream to write to
+     * @throws IOException if there is a write error
+     */
+    @Override
+    protected void sendData(OutputStream out) throws IOException {
+        LOG.trace("enter sendData(OutputStream)");
+        out.write(getContent());
+    }
+    
+    /**
+     * Return the length of the data.
+     * @return The length of the data.
+     * @see Part#lengthOfData()
+     */
+    @Override
+    protected long lengthOfData() {
+        LOG.trace("enter lengthOfData()");
+        return getContent().length;
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.commons.httpclient.methods.multipart.BasePart#setCharSet(java.lang.String)
+     */
+    @Override
+    public void setCharSet(String charSet) {
+        super.setCharSet(charSet);
+        this.content = null;
+    }
+
+}
diff --git a/src/org/apache/http/conn/ConnectTimeoutException.java b/src/org/apache/http/conn/ConnectTimeoutException.java
deleted file mode 100644
index 6cc6922..0000000
--- a/src/org/apache/http/conn/ConnectTimeoutException.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ConnectTimeoutException.java $
- * $Revision: 617645 $
- * $Date: 2008-02-01 13:05:31 -0800 (Fri, 01 Feb 2008) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.conn;
-
-import java.io.InterruptedIOException;
-
-/**
- * A timeout while connecting to an HTTP server or waiting for an
- * available connection from an HttpConnectionManager.
- * 
- * @author <a href="mailto:laura@lwerner.org">Laura Werner</a>
- * 
- * @since 4.0
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-@Deprecated
-public class ConnectTimeoutException extends InterruptedIOException {
-
-    private static final long serialVersionUID = -4816682903149535989L;
-
-    /**
-     * Creates a ConnectTimeoutException with a <tt>null</tt> detail message.
-     */
-    public ConnectTimeoutException() {
-        super();
-    }
-
-    /**
-     * Creates a ConnectTimeoutException with the specified detail message.
-     * 
-     * @param message The exception detail message 
-     */
-    public ConnectTimeoutException(final String message) {
-        super(message);
-    }
-
-}
diff --git a/src/org/apache/http/conn/scheme/HostNameResolver.java b/src/org/apache/http/conn/scheme/HostNameResolver.java
deleted file mode 100644
index d488a4b..0000000
--- a/src/org/apache/http/conn/scheme/HostNameResolver.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * $HeadURL:$
- * $Revision:$
- * $Date:$
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.conn.scheme;
-
-import java.io.IOException;
-import java.net.InetAddress;
-/**
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
-*/
-
-@Deprecated
-public interface HostNameResolver {
-
-    InetAddress resolve (String hostname) throws IOException;
-
-}
diff --git a/src/org/apache/http/conn/scheme/LayeredSocketFactory.java b/src/org/apache/http/conn/scheme/LayeredSocketFactory.java
deleted file mode 100644
index b9f5348..0000000
--- a/src/org/apache/http/conn/scheme/LayeredSocketFactory.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactory.java $
- * $Revision: 645850 $
- * $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.conn.scheme;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-/**
- * A {@link SocketFactory SocketFactory} for layered sockets (SSL/TLS).
- * See there for things to consider when implementing a socket factory.
- * 
- * @author Michael Becke
- * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
- * @since 4.0
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-@Deprecated
-public interface LayeredSocketFactory extends SocketFactory {
-
-    /**
-     * Returns a socket connected to the given host that is layered over an
-     * existing socket.  Used primarily for creating secure sockets through
-     * proxies.
-     * 
-     * @param socket the existing socket 
-     * @param host the host name/IP
-     * @param port the port on the host
-     * @param autoClose a flag for closing the underling socket when the created
-     * socket is closed
-     * 
-     * @return Socket a new socket
-     * 
-     * @throws IOException if an I/O error occurs while creating the socket
-     * @throws UnknownHostException if the IP address of the host cannot be
-     * determined
-     */
-    Socket createSocket(
-        Socket socket, 
-        String host, 
-        int port, 
-        boolean autoClose
-    ) throws IOException, UnknownHostException;              
-
-}
diff --git a/src/org/apache/http/conn/scheme/SocketFactory.java b/src/org/apache/http/conn/scheme/SocketFactory.java
deleted file mode 100644
index c6bc03c..0000000
--- a/src/org/apache/http/conn/scheme/SocketFactory.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/SocketFactory.java $
- * $Revision: 645850 $
- * $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.conn.scheme;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
-import org.apache.http.conn.ConnectTimeoutException;
-import org.apache.http.params.HttpParams;
-
-/**
- * A factory for creating and connecting sockets.
- * The factory encapsulates the logic for establishing a socket connection.
- * <br/>
- * Both {@link java.lang.Object#equals(java.lang.Object) Object.equals()}
- * and {@link java.lang.Object#hashCode() Object.hashCode()}
- * must be overridden for the correct operation of some connection managers.
- * 
- * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
- * @author Michael Becke
- * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-@Deprecated
-public interface SocketFactory {
-
-    /**
-     * Creates a new, unconnected socket.
-     * The socket should subsequently be passed to
-     * {@link #connectSocket connectSocket}.
-     *
-     * @return  a new socket
-     * 
-     * @throws IOException if an I/O error occurs while creating the socket
-     */
-    Socket createSocket()
-        throws IOException
-        ;
-
-
-    /**
-     * Connects a socket to the given host.
-     * 
-     * @param sock      the socket to connect, as obtained from
-     *                  {@link #createSocket createSocket}.
-     *                  <code>null</code> indicates that a new socket
-     *                  should be created and connected.
-     * @param host      the host to connect to
-     * @param port      the port to connect to on the host
-     * @param localAddress the local address to bind the socket to, or
-     *                  <code>null</code> for any
-     * @param localPort the port on the local machine,
-     *                  0 or a negative number for any
-     * @param params    additional {@link HttpParams parameters} for connecting
-     * 
-     * @return  the connected socket. The returned object may be different
-     *          from the <code>sock</code> argument if this factory supports
-     *          a layered protocol.
-     * 
-     * @throws IOException if an I/O error occurs
-     * @throws UnknownHostException if the IP address of the target host
-     *          can not be determined
-     * @throws ConnectTimeoutException if the socket cannot be connected
-     *          within the time limit defined in the <code>params</code>
-     */
-    Socket connectSocket(
-        Socket sock,
-        String host, 
-        int port, 
-        InetAddress localAddress, 
-        int localPort,
-        HttpParams params
-    ) throws IOException, UnknownHostException, ConnectTimeoutException;
-
-
-    /**
-     * Checks whether a socket provides a secure connection.
-     * The socket must be {@link #connectSocket connected}
-     * by this factory.
-     * The factory will <i>not</i> perform I/O operations
-     * in this method.
-     * <br/>
-     * As a rule of thumb, plain sockets are not secure and
-     * TLS/SSL sockets are secure. However, there may be
-     * application specific deviations. For example, a plain
-     * socket to a host in the same intranet ("trusted zone")
-     * could be considered secure. On the other hand, a
-     * TLS/SSL socket could be considered insecure based on
-     * the cypher suite chosen for the connection.
-     *
-     * @param sock      the connected socket to check
-     *
-     * @return  <code>true</code> if the connection of the socket
-     *          should be considered secure, or
-     *          <code>false</code> if it should not
-     *
-     * @throws IllegalArgumentException
-     *  if the argument is invalid, for example because it is
-     *  not a connected socket or was created by a different
-     *  socket factory.
-     *  Note that socket factories are <i>not</i> required to
-     *  check these conditions, they may simply return a default
-     *  value when called with an invalid socket argument.
-     */
-    boolean isSecure(Socket sock)
-        throws IllegalArgumentException
-        ;
-
-}
diff --git a/src/org/apache/http/conn/ssl/AbstractVerifier.java b/src/org/apache/http/conn/ssl/AbstractVerifier.java
deleted file mode 100644
index a56a6d4..0000000
--- a/src/org/apache/http/conn/ssl/AbstractVerifier.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java $
- * $Revision: 653041 $
- * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.conn.ssl;
-
-import org.apache.http.conn.util.InetAddressUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.logging.Logger;
-import java.util.logging.Level;
-
-import javax.net.ssl.DistinguishedNameParser;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-
-/**
- * Abstract base class for all standard {@link X509HostnameVerifier} 
- * implementations.
- * 
- * @author Julius Davies
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-@Deprecated
-public abstract class AbstractVerifier implements X509HostnameVerifier {
-
-    /**
-     * This contains a list of 2nd-level domains that aren't allowed to
-     * have wildcards when combined with country-codes.
-     * For example: [*.co.uk].
-     * <p/>
-     * The [*.co.uk] problem is an interesting one.  Should we just hope
-     * that CA's would never foolishly allow such a certificate to happen?
-     * Looks like we're the only implementation guarding against this.
-     * Firefox, Curl, Sun Java 1.4, 5, 6 don't bother with this check.
-     */
-    private final static String[] BAD_COUNTRY_2LDS =
-          { "ac", "co", "com", "ed", "edu", "go", "gouv", "gov", "info",
-            "lg", "ne", "net", "or", "org" };
-
-    static {
-        // Just in case developer forgot to manually sort the array.  :-)
-        Arrays.sort(BAD_COUNTRY_2LDS);
-    }
-
-    public AbstractVerifier() {
-        super();
-    }
-
-    public final void verify(String host, SSLSocket ssl)
-          throws IOException {
-        if(host == null) {
-            throw new NullPointerException("host to verify is null");
-        }
-
-        SSLSession session = ssl.getSession();
-        Certificate[] certs = session.getPeerCertificates();
-        X509Certificate x509 = (X509Certificate) certs[0];
-        verify(host, x509);
-    }
-
-    public final boolean verify(String host, SSLSession session) {
-        try {
-            Certificate[] certs = session.getPeerCertificates();
-            X509Certificate x509 = (X509Certificate) certs[0];
-            verify(host, x509);
-            return true;
-        }
-        catch(SSLException e) {
-            return false;
-        }
-    }
-
-    public final void verify(String host, X509Certificate cert)
-          throws SSLException {
-        String[] cns = getCNs(cert);
-        String[] subjectAlts = getDNSSubjectAlts(cert);
-        verify(host, cns, subjectAlts);
-    }
-
-    public final void verify(final String host, final String[] cns,
-                             final String[] subjectAlts,
-                             final boolean strictWithSubDomains)
-          throws SSLException {
-
-        // Build the list of names we're going to check.  Our DEFAULT and
-        // STRICT implementations of the HostnameVerifier only use the
-        // first CN provided.  All other CNs are ignored.
-        // (Firefox, wget, curl, Sun Java 1.4, 5, 6 all work this way).
-        LinkedList<String> names = new LinkedList<String>();
-        if(cns != null && cns.length > 0 && cns[0] != null) {
-            names.add(cns[0]);
-        }
-        if(subjectAlts != null) {
-            for (String subjectAlt : subjectAlts) {
-                if (subjectAlt != null) {
-                    names.add(subjectAlt);
-                }
-            }
-        }
-
-        if(names.isEmpty()) {
-            String msg = "Certificate for <" + host + "> doesn't contain CN or DNS subjectAlt";
-            throw new SSLException(msg);
-        }
-
-        // StringBuffer for building the error message.
-        StringBuffer buf = new StringBuffer();
-
-        // We're can be case-insensitive when comparing the host we used to
-        // establish the socket to the hostname in the certificate.
-        String hostName = host.trim().toLowerCase(Locale.ENGLISH);
-        boolean match = false;
-        for(Iterator<String> it = names.iterator(); it.hasNext();) {
-            // Don't trim the CN, though!
-            String cn = it.next();
-            cn = cn.toLowerCase(Locale.ENGLISH);
-            // Store CN in StringBuffer in case we need to report an error.
-            buf.append(" <");
-            buf.append(cn);
-            buf.append('>');
-            if(it.hasNext()) {
-                buf.append(" OR");
-            }
-
-            // The CN better have at least two dots if it wants wildcard
-            // action.  It also can't be [*.co.uk] or [*.co.jp] or
-            // [*.org.uk], etc...
-            boolean doWildcard = cn.startsWith("*.") &&
-                                 cn.indexOf('.', 2) != -1 &&
-                                 acceptableCountryWildcard(cn) &&
-                                 !InetAddressUtils.isIPv4Address(host);
-
-            if(doWildcard) {
-                match = hostName.endsWith(cn.substring(1));
-                if(match && strictWithSubDomains) {
-                    // If we're in strict mode, then [*.foo.com] is not
-                    // allowed to match [a.b.foo.com]
-                    match = countDots(hostName) == countDots(cn);
-                }
-            } else {
-                match = hostName.equals(cn);
-            }
-            if(match) {
-                break;
-            }
-        }
-        if(!match) {
-            throw new SSLException("hostname in certificate didn't match: <" + host + "> !=" + buf);
-        }
-    }
-
-    public static boolean acceptableCountryWildcard(String cn) {
-        int cnLen = cn.length();
-        if(cnLen >= 7 && cnLen <= 9) {
-            // Look for the '.' in the 3rd-last position:
-            if(cn.charAt(cnLen - 3) == '.') {
-                // Trim off the [*.] and the [.XX].
-                String s = cn.substring(2, cnLen - 3);
-                // And test against the sorted array of bad 2lds:
-                int x = Arrays.binarySearch(BAD_COUNTRY_2LDS, s);
-                return x < 0;
-            }
-        }
-        return true;
-    }
-
-    public static String[] getCNs(X509Certificate cert) {
-        DistinguishedNameParser dnParser =
-                new DistinguishedNameParser(cert.getSubjectX500Principal());
-        List<String> cnList = dnParser.getAllMostSpecificFirst("cn");
-
-        if(!cnList.isEmpty()) {
-            String[] cns = new String[cnList.size()];
-            cnList.toArray(cns);
-            return cns;
-        } else {
-            return null;
-        }
-    }
-
-
-    /**
-     * Extracts the array of SubjectAlt DNS names from an X509Certificate.
-     * Returns null if there aren't any.
-     * <p/>
-     * Note:  Java doesn't appear able to extract international characters
-     * from the SubjectAlts.  It can only extract international characters
-     * from the CN field.
-     * <p/>
-     * (Or maybe the version of OpenSSL I'm using to test isn't storing the
-     * international characters correctly in the SubjectAlts?).
-     *
-     * @param cert X509Certificate
-     * @return Array of SubjectALT DNS names stored in the certificate.
-     */
-    public static String[] getDNSSubjectAlts(X509Certificate cert) {
-        LinkedList<String> subjectAltList = new LinkedList<String>();
-        Collection<List<?>> c = null;
-        try {
-            c = cert.getSubjectAlternativeNames();
-        }
-        catch(CertificateParsingException cpe) {
-            Logger.getLogger(AbstractVerifier.class.getName())
-                    .log(Level.FINE, "Error parsing certificate.", cpe);
-        }
-        if(c != null) {
-            for (List<?> aC : c) {
-                List<?> list = aC;
-                int type = ((Integer) list.get(0)).intValue();
-                // If type is 2, then we've got a dNSName
-                if (type == 2) {
-                    String s = (String) list.get(1);
-                    subjectAltList.add(s);
-                }
-            }
-        }
-        if(!subjectAltList.isEmpty()) {
-            String[] subjectAlts = new String[subjectAltList.size()];
-            subjectAltList.toArray(subjectAlts);
-            return subjectAlts;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Counts the number of dots "." in a string.
-     * @param s  string to count dots from
-     * @return  number of dots
-     */
-    public static int countDots(final String s) {
-        int count = 0;
-        for(int i = 0; i < s.length(); i++) {
-            if(s.charAt(i) == '.') {
-                count++;
-            }
-        }
-        return count;
-    }
-    
-}
diff --git a/src/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java b/src/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java
deleted file mode 100644
index c2bf4c4..0000000
--- a/src/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java $
- * $Revision: 617642 $
- * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.conn.ssl;
-
-/**
- * The ALLOW_ALL HostnameVerifier essentially turns hostname verification
- * off. This implementation is a no-op, and never throws the SSLException.
- * 
- * @author Julius Davies
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-@Deprecated
-public class AllowAllHostnameVerifier extends AbstractVerifier {
-
-    public final void verify(
-            final String host, 
-            final String[] cns,
-            final String[] subjectAlts) {
-        // Allow everything - so never blowup.
-    }
-
-    @Override
-    public final String toString() { 
-        return "ALLOW_ALL"; 
-    }
-    
-}
diff --git a/src/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java b/src/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java
deleted file mode 100644
index 48a7bf9..0000000
--- a/src/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java $
- * $Revision: 617642 $
- * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.conn.ssl;
-
-import javax.net.ssl.SSLException;
-
-/**
- * The HostnameVerifier that works the same way as Curl and Firefox.
- * <p/>
- * The hostname must match either the first CN, or any of the subject-alts.
- * A wildcard can occur in the CN, and in any of the subject-alts.
- * <p/>
- * The only difference between BROWSER_COMPATIBLE and STRICT is that a wildcard 
- * (such as "*.foo.com") with BROWSER_COMPATIBLE matches all subdomains, 
- * including "a.b.foo.com".
- * 
- * @author Julius Davies
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-@Deprecated
-public class BrowserCompatHostnameVerifier extends AbstractVerifier {
-
-    public final void verify(
-            final String host, 
-            final String[] cns,
-            final String[] subjectAlts) throws SSLException {
-        verify(host, cns, subjectAlts, false);
-    }
-
-    @Override
-    public final String toString() { 
-        return "BROWSER_COMPATIBLE"; 
-    }
-    
-}
diff --git a/src/org/apache/http/conn/ssl/SSLSocketFactory.java b/src/org/apache/http/conn/ssl/SSLSocketFactory.java
deleted file mode 100644
index 0b5aa37..0000000
--- a/src/org/apache/http/conn/ssl/SSLSocketFactory.java
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java $
- * $Revision: 659194 $
- * $Date: 2008-05-22 11:33:47 -0700 (Thu, 22 May 2008) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.conn.ssl;
-
-import org.apache.http.conn.scheme.HostNameResolver;
-import org.apache.http.conn.scheme.LayeredSocketFactory;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
-
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.UnknownHostException;
-import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.UnrecoverableKeyException;
-
-/**
- * Layered socket factory for TLS/SSL connections, based on JSSE.
- *.
- * <p>
- * SSLSocketFactory can be used to validate the identity of the HTTPS 
- * server against a list of trusted certificates and to authenticate to
- * the HTTPS server using a private key. 
- * </p>
- * 
- * <p>
- * SSLSocketFactory will enable server authentication when supplied with
- * a {@link KeyStore truststore} file containg one or several trusted
- * certificates. The client secure socket will reject the connection during
- * the SSL session handshake if the target HTTPS server attempts to
- * authenticate itself with a non-trusted certificate.
- * </p>
- * 
- * <p>
- * Use JDK keytool utility to import a trusted certificate and generate a truststore file:    
- *    <pre>
- *     keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
- *    </pre>
- * </p>
- * 
- * <p>
- * SSLSocketFactory will enable client authentication when supplied with
- * a {@link KeyStore keystore} file containg a private key/public certificate
- * pair. The client secure socket will use the private key to authenticate
- * itself to the target HTTPS server during the SSL session handshake if
- * requested to do so by the server.
- * The target HTTPS server will in its turn verify the certificate presented
- * by the client in order to establish client's authenticity
- * </p>
- * 
- * <p>
- * Use the following sequence of actions to generate a keystore file
- * </p>
- *   <ul>
- *     <li>
- *      <p>
- *      Use JDK keytool utility to generate a new key
- *      <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre>
- *      For simplicity use the same password for the key as that of the keystore
- *      </p>
- *     </li>
- *     <li>
- *      <p>
- *      Issue a certificate signing request (CSR)
- *      <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre>
- *     </p>
- *     </li>
- *     <li>
- *      <p>
- *      Send the certificate request to the trusted Certificate Authority for signature. 
- *      One may choose to act as her own CA and sign the certificate request using a PKI 
- *      tool, such as OpenSSL.
- *      </p>
- *     </li>
- *     <li>
- *      <p>
- *       Import the trusted CA root certificate
- *       <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre> 
- *      </p>
- *     </li>
- *     <li>
- *      <p>
- *       Import the PKCS#7 file containg the complete certificate chain
- *       <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre> 
- *      </p>
- *     </li>
- *     <li>
- *      <p>
- *       Verify the content the resultant keystore file
- *       <pre>keytool -list -v -keystore my.keystore</pre> 
- *      </p>
- *     </li>
- *   </ul>
- * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
- * @author Julius Davies
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-
-@Deprecated
-public class SSLSocketFactory implements LayeredSocketFactory {
-
-    public static final String TLS   = "TLS";
-    public static final String SSL   = "SSL";
-    public static final String SSLV2 = "SSLv2";
-    
-    public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER 
-        = new AllowAllHostnameVerifier();
-    
-    public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER 
-        = new BrowserCompatHostnameVerifier();
-    
-    public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER 
-        = new StrictHostnameVerifier();
-
-    /*
-     * Put defaults into holder class to avoid class preloading creating an
-     * instance of the classes referenced.
-     */
-    private static class NoPreloadHolder {
-        /**
-         * The factory using the default JVM settings for secure connections.
-         */
-        private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory();
-    }
-
-    /**
-     * Gets an singleton instance of the SSLProtocolSocketFactory.
-     * @return a SSLProtocolSocketFactory
-     */
-    public static SSLSocketFactory getSocketFactory() {
-        return NoPreloadHolder.DEFAULT_FACTORY;
-    }
-
-    private final SSLContext sslcontext;
-    private final javax.net.ssl.SSLSocketFactory socketfactory;
-    private final HostNameResolver nameResolver;
-    private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
-
-    public SSLSocketFactory(
-        String algorithm, 
-        final KeyStore keystore, 
-        final String keystorePassword, 
-        final KeyStore truststore,
-        final SecureRandom random,
-        final HostNameResolver nameResolver) 
-        throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
-    {
-        super();
-        if (algorithm == null) {
-            algorithm = TLS;
-        }
-        KeyManager[] keymanagers = null;
-        if (keystore != null) {
-            keymanagers = createKeyManagers(keystore, keystorePassword);
-        }
-        TrustManager[] trustmanagers = null;
-        if (truststore != null) {
-            trustmanagers = createTrustManagers(truststore);
-        }
-        this.sslcontext = SSLContext.getInstance(algorithm);
-        this.sslcontext.init(keymanagers, trustmanagers, random);
-        this.socketfactory = this.sslcontext.getSocketFactory();
-        this.nameResolver = nameResolver;
-    }
-
-    public SSLSocketFactory(
-            final KeyStore keystore, 
-            final String keystorePassword, 
-            final KeyStore truststore) 
-            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
-    {
-        this(TLS, keystore, keystorePassword, truststore, null, null);
-    }
-
-    public SSLSocketFactory(final KeyStore keystore, final String keystorePassword) 
-            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
-    {
-        this(TLS, keystore, keystorePassword, null, null, null);
-    }
-
-    public SSLSocketFactory(final KeyStore truststore) 
-            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
-    {
-        this(TLS, null, null, truststore, null, null);
-    }
-
-    /**
-     * Constructs an HttpClient SSLSocketFactory backed by the given JSSE
-     * SSLSocketFactory.
-     *
-     * @hide
-     */
-    public SSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory) {
-        super();
-        this.sslcontext = null;
-        this.socketfactory = socketfactory;
-        this.nameResolver = null;
-    }
-
-    /**
-     * Creates the default SSL socket factory.
-     * This constructor is used exclusively to instantiate the factory for
-     * {@link #getSocketFactory getSocketFactory}.
-     */
-    private SSLSocketFactory() {
-        super();
-        this.sslcontext = null;
-        this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory();
-        this.nameResolver = null;
-    }
-
-    private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password)
-        throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
-        if (keystore == null) {
-            throw new IllegalArgumentException("Keystore may not be null");
-        }
-        KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
-            KeyManagerFactory.getDefaultAlgorithm());
-        kmfactory.init(keystore, password != null ? password.toCharArray(): null);
-        return kmfactory.getKeyManagers(); 
-    }
-
-    private static TrustManager[] createTrustManagers(final KeyStore keystore)
-        throws KeyStoreException, NoSuchAlgorithmException { 
-        if (keystore == null) {
-            throw new IllegalArgumentException("Keystore may not be null");
-        }
-        TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
-            TrustManagerFactory.getDefaultAlgorithm());
-        tmfactory.init(keystore);
-        return tmfactory.getTrustManagers();
-    }
-
-
-    // non-javadoc, see interface org.apache.http.conn.SocketFactory
-    public Socket createSocket()
-        throws IOException {
-
-        // the cast makes sure that the factory is working as expected
-        return (SSLSocket) this.socketfactory.createSocket();
-    }
-
-
-    // non-javadoc, see interface org.apache.http.conn.SocketFactory
-    public Socket connectSocket(
-        final Socket sock,
-        final String host,
-        final int port,
-        final InetAddress localAddress,
-        int localPort,
-        final HttpParams params
-    ) throws IOException {
-
-        if (host == null) {
-            throw new IllegalArgumentException("Target host may not be null.");
-        }
-        if (params == null) {
-            throw new IllegalArgumentException("Parameters may not be null.");
-        }
-
-        SSLSocket sslsock = (SSLSocket)
-            ((sock != null) ? sock : createSocket());
-
-        if ((localAddress != null) || (localPort > 0)) {
-
-            // we need to bind explicitly
-            if (localPort < 0)
-                localPort = 0; // indicates "any"
-
-            InetSocketAddress isa =
-                new InetSocketAddress(localAddress, localPort);
-            sslsock.bind(isa);
-        }
-
-        int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
-        int soTimeout = HttpConnectionParams.getSoTimeout(params);
-
-        InetSocketAddress remoteAddress;
-        if (this.nameResolver != null) {
-            remoteAddress = new InetSocketAddress(this.nameResolver.resolve(host), port); 
-        } else {
-            remoteAddress = new InetSocketAddress(host, port);            
-        }
-        
-        sslsock.connect(remoteAddress, connTimeout);
-
-        sslsock.setSoTimeout(soTimeout);
-        try {
-            // BEGIN android-added
-            /*
-             * Make sure we have started the handshake before verifying.
-             * Otherwise when we go to the hostname verifier, it directly calls
-             * SSLSocket#getSession() which swallows SSL handshake errors.
-             */
-            sslsock.startHandshake();
-            // END android-added
-            hostnameVerifier.verify(host, sslsock);
-            // verifyHostName() didn't blowup - good!
-        } catch (IOException iox) {
-            // close the socket before re-throwing the exception
-            try { sslsock.close(); } catch (Exception x) { /*ignore*/ }
-            throw iox;
-        }
-
-        return sslsock;
-    }
-
-
-    /**
-     * Checks whether a socket connection is secure.
-     * This factory creates TLS/SSL socket connections
-     * which, by default, are considered secure.
-     * <br/>
-     * Derived classes may override this method to perform
-     * runtime checks, for example based on the cypher suite.
-     *
-     * @param sock      the connected socket
-     *
-     * @return  <code>true</code>
-     *
-     * @throws IllegalArgumentException if the argument is invalid
-     */
-    public boolean isSecure(Socket sock)
-        throws IllegalArgumentException {
-
-        if (sock == null) {
-            throw new IllegalArgumentException("Socket may not be null.");
-        }
-        // This instanceof check is in line with createSocket() above.
-        if (!(sock instanceof SSLSocket)) {
-            throw new IllegalArgumentException
-                ("Socket not created by this factory.");
-        }
-        // This check is performed last since it calls the argument object.
-        if (sock.isClosed()) {
-            throw new IllegalArgumentException("Socket is closed.");
-        }
-
-        return true;
-
-    } // isSecure
-
-
-    // non-javadoc, see interface LayeredSocketFactory
-    public Socket createSocket(
-        final Socket socket,
-        final String host,
-        final int port,
-        final boolean autoClose
-    ) throws IOException, UnknownHostException {
-        SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
-              socket,
-              host,
-              port,
-              autoClose
-        );
-        hostnameVerifier.verify(host, sslSocket);
-        // verifyHostName() didn't blowup - good!
-        return sslSocket;
-    }
-
-    public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) {
-        if ( hostnameVerifier == null ) {
-            throw new IllegalArgumentException("Hostname verifier may not be null");
-        }
-        this.hostnameVerifier = hostnameVerifier;
-    }
-
-    public X509HostnameVerifier getHostnameVerifier() {
-        return hostnameVerifier;
-    }
-
-}
diff --git a/src/org/apache/http/conn/ssl/StrictHostnameVerifier.java b/src/org/apache/http/conn/ssl/StrictHostnameVerifier.java
deleted file mode 100644
index bd9e70d..0000000
--- a/src/org/apache/http/conn/ssl/StrictHostnameVerifier.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java $
- * $Revision: 617642 $
- * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.conn.ssl;
-
-import javax.net.ssl.SSLException;
-
-/**
- * The Strict HostnameVerifier works the same way as Sun Java 1.4, Sun
- * Java 5, Sun Java 6-rc.  It's also pretty close to IE6.  This
- * implementation appears to be compliant with RFC 2818 for dealing with
- * wildcards.
- * <p/>
- * The hostname must match either the first CN, or any of the subject-alts.
- * A wildcard can occur in the CN, and in any of the subject-alts.  The
- * one divergence from IE6 is how we only check the first CN.  IE6 allows
- * a match against any of the CNs present.  We decided to follow in
- * Sun Java 1.4's footsteps and only check the first CN.  (If you need
- * to check all the CN's, feel free to write your own implementation!).
- * <p/>
- * A wildcard such as "*.foo.com" matches only subdomains in the same
- * level, for example "a.foo.com".  It does not match deeper subdomains
- * such as "a.b.foo.com".
- * 
- * @author Julius Davies
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-@Deprecated
-public class StrictHostnameVerifier extends AbstractVerifier {
-
-    public final void verify(
-            final String host, 
-            final String[] cns,
-            final String[] subjectAlts) throws SSLException {
-        verify(host, cns, subjectAlts, true);
-    }
-
-    @Override
-    public final String toString() { 
-        return "STRICT"; 
-    }
-    
-}
diff --git a/src/org/apache/http/conn/ssl/X509HostnameVerifier.java b/src/org/apache/http/conn/ssl/X509HostnameVerifier.java
deleted file mode 100644
index e38db5f..0000000
--- a/src/org/apache/http/conn/ssl/X509HostnameVerifier.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/X509HostnameVerifier.java $
- * $Revision: 618365 $
- * $Date: 2008-02-04 10:20:08 -0800 (Mon, 04 Feb 2008) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.conn.ssl;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-import java.io.IOException;
-import java.security.cert.X509Certificate;
-
-/**
- * Interface for checking if a hostname matches the names stored inside the
- * server's X.509 certificate.  Implements javax.net.ssl.HostnameVerifier, but
- * we don't actually use that interface.  Instead we added some methods that
- * take String parameters (instead of javax.net.ssl.HostnameVerifier's
- * SSLSession).  JUnit is a lot easier this way!  :-)
- * <p/>
- * We provide the HostnameVerifier.DEFAULT, HostnameVerifier.STRICT, and
- * HostnameVerifier.ALLOW_ALL implementations.  But feel free to define
- * your own implementation!
- * <p/>
- * Inspired by Sebastian Hauer's original StrictSSLProtocolSocketFactory in the
- * HttpClient "contrib" repository.
- *
- * @author Julius Davies
- * @author <a href="mailto:hauer@psicode.com">Sebastian Hauer</a>
- *
- * @since 4.0 (8-Dec-2006)
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-@Deprecated
-public interface X509HostnameVerifier extends HostnameVerifier {
-
-    boolean verify(String host, SSLSession session);
-
-    void verify(String host, SSLSocket ssl) throws IOException;
-
-    void verify(String host, X509Certificate cert) throws SSLException;
-
-    /**
-     * Checks to see if the supplied hostname matches any of the supplied CNs
-     * or "DNS" Subject-Alts.  Most implementations only look at the first CN,
-     * and ignore any additional CNs.  Most implementations do look at all of
-     * the "DNS" Subject-Alts. The CNs or Subject-Alts may contain wildcards
-     * according to RFC 2818.
-     *
-     * @param cns         CN fields, in order, as extracted from the X.509
-     *                    certificate.
-     * @param subjectAlts Subject-Alt fields of type 2 ("DNS"), as extracted
-     *                    from the X.509 certificate.
-     * @param host        The hostname to verify.
-     * @throws SSLException If verification failed.
-     */
-    void verify(String host, String[] cns, String[] subjectAlts)
-          throws SSLException;
-
-
-}
diff --git a/src/org/apache/http/conn/ssl/package.html b/src/org/apache/http/conn/ssl/package.html
deleted file mode 100644
index a5c737f..0000000
--- a/src/org/apache/http/conn/ssl/package.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<html>
-<head>
-<!--
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/package.html $
- * $Revision: 555193 $
- * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
--->
-</head>
-<body>
-TLS/SSL specific parts of the <i>HttpConn</i> API.
-
-</body>
-</html>
diff --git a/src/org/apache/http/impl/conn/DefaultClientConnectionOperator.java b/src/org/apache/http/impl/conn/DefaultClientConnectionOperator.java
index 9567eb1..62169bc 100644
--- a/src/org/apache/http/impl/conn/DefaultClientConnectionOperator.java
+++ b/src/org/apache/http/impl/conn/DefaultClientConnectionOperator.java
@@ -183,8 +183,13 @@
             //       catch SocketException to cover any kind of connect failure
             } catch (SocketException ex) {
                 if (i == addresses.length - 1) {
-                    ConnectException cause = ex instanceof ConnectException
-                            ? (ConnectException) ex : new ConnectException(ex.getMessage(), ex);
+                    final ConnectException cause;
+                    if (ex instanceof ConnectException) {
+                        cause = (ConnectException) ex;
+                    } else {
+                        cause = new ConnectException(ex.getMessage());
+                        cause.initCause(ex);
+                    }
                     throw new HttpHostConnectException(target, cause);
                 }
             // END android-changed
diff --git a/src/org/apache/http/impl/conn/SingleClientConnManager.java b/src/org/apache/http/impl/conn/SingleClientConnManager.java
index 55e9757..d8fb956 100644
--- a/src/org/apache/http/impl/conn/SingleClientConnManager.java
+++ b/src/org/apache/http/impl/conn/SingleClientConnManager.java
@@ -31,11 +31,11 @@
 
 package org.apache.http.impl.conn;
 
-import dalvik.system.SocketTagger;
 import java.io.IOException;
 import java.net.Socket;
 import java.util.concurrent.TimeUnit;
 
+import android.net.TrafficStats;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.http.conn.ClientConnectionManager;
@@ -261,7 +261,7 @@
         try {
             final Socket socket = uniquePoolEntry.connection.getSocket();
             if (socket != null) {
-                SocketTagger.get().tag(socket);
+                TrafficStats.tagSocket(socket);
             }
         } catch (IOException iox) {
             log.debug("Problem tagging socket.", iox);
@@ -303,7 +303,7 @@
             // statistics from future users.
             final Socket socket = uniquePoolEntry.connection.getSocket();
             if (socket != null) {
-                SocketTagger.get().untag(socket);
+                TrafficStats.untagSocket(socket);
             }
             // END android-changed
 
diff --git a/src/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java b/src/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
index e9c11b0..7d6a560 100644
--- a/src/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
+++ b/src/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
@@ -30,11 +30,11 @@
 
 package org.apache.http.impl.conn.tsccm;
 
-import dalvik.system.SocketTagger;
 import java.io.IOException;
 import java.net.Socket;
 import java.util.concurrent.TimeUnit;
 
+import android.net.TrafficStats;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.http.conn.routing.HttpRoute;
@@ -185,7 +185,7 @@
                 try {
                     final Socket socket = entry.getConnection().getSocket();
                     if (socket != null) {
-                        SocketTagger.get().tag(socket);
+                        TrafficStats.tagSocket(socket);
                     }
                 } catch (IOException iox) {
                     log.debug("Problem tagging socket.", iox);
@@ -220,7 +220,7 @@
             final BasicPoolEntry entry = (BasicPoolEntry) hca.getPoolEntry();
             final Socket socket = entry.getConnection().getSocket();
             if (socket != null) {
-                SocketTagger.get().untag(socket);
+                TrafficStats.untagSocket(socket);
             }
             // END android-changed
 
diff --git a/src/org/apache/http/impl/io/SocketInputBuffer.java b/src/org/apache/http/impl/io/SocketInputBuffer.java
index f525833..5e1869c 100644
--- a/src/org/apache/http/impl/io/SocketInputBuffer.java
+++ b/src/org/apache/http/impl/io/SocketInputBuffer.java
@@ -101,8 +101,6 @@
      * to rely on isDataAvailable() returning normally; that approach cannot
      * distinguish between an exhausted stream and a stream with zero bytes
      * buffered.
-     *
-     * @hide
      */
     public boolean isStale() throws IOException {
         if (hasBufferedData()) {
diff --git a/src/org/apache/http/params/CoreConnectionPNames.java b/src/org/apache/http/params/CoreConnectionPNames.java
deleted file mode 100644
index 9479db1..0000000
--- a/src/org/apache/http/params/CoreConnectionPNames.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/CoreConnectionPNames.java $
- * $Revision: 576077 $
- * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.params;
-
-
-/**
- * Defines parameter names for connections in HttpCore.
- * 
- * @version $Revision: 576077 $
- * 
- * @since 4.0
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-@Deprecated
-public interface CoreConnectionPNames {
-
-    /**
-     * Defines the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the 
-     * timeout for waiting for data. A timeout value of zero is interpreted as an infinite 
-     * timeout. This value is used when no socket timeout is set in the 
-     * method parameters. 
-     * <p>
-     * This parameter expects a value of type {@link Integer}.
-     * </p>
-     * @see java.net.SocketOptions#SO_TIMEOUT
-     */
-    public static final String SO_TIMEOUT = "http.socket.timeout"; 
-
-    /**
-     * Determines whether Nagle's algorithm is to be used. The Nagle's algorithm 
-     * tries to conserve bandwidth by minimizing the number of segments that are 
-     * sent. When applications wish to decrease network latency and increase 
-     * performance, they can disable Nagle's algorithm (that is enable TCP_NODELAY). 
-     * Data will be sent earlier, at the cost of an increase in bandwidth consumption. 
-     * <p>
-     * This parameter expects a value of type {@link Boolean}.
-     * </p>
-     * @see java.net.SocketOptions#TCP_NODELAY
-     */
-    public static final String TCP_NODELAY = "http.tcp.nodelay"; 
-
-    /**
-     * Determines the size of the internal socket buffer used to buffer data
-     * while receiving / transmitting HTTP messages.
-     * <p>
-     * This parameter expects a value of type {@link Integer}.
-     * </p>
-     */
-    public static final String SOCKET_BUFFER_SIZE = "http.socket.buffer-size"; 
-
-    /**
-     * Sets SO_LINGER with the specified linger time in seconds. The maximum timeout 
-     * value is platform specific. Value <tt>0</tt> implies that the option is disabled.
-     * Value <tt>-1</tt> implies that the JRE default is used. The setting only affects 
-     * socket close.  
-     * <p>
-     * This parameter expects a value of type {@link Integer}.
-     * </p>
-     * @see java.net.SocketOptions#SO_LINGER
-     */
-    public static final String SO_LINGER = "http.socket.linger"; 
-
-    /**
-     * Determines the timeout until a connection is etablished. A value of zero 
-     * means the timeout is not used. The default value is zero.
-     * <p>
-     * This parameter expects a value of type {@link Integer}.
-     * </p>
-     */
-    public static final String CONNECTION_TIMEOUT = "http.connection.timeout"; 
-
-    /**
-     * Determines whether stale connection check is to be used. Disabling 
-     * stale connection check may result in slight performance improvement 
-     * at the risk of getting an I/O error when executing a request over a
-     * connection that has been closed at the server side. 
-     * <p>
-     * This parameter expects a value of type {@link Boolean}.
-     * </p>
-     */
-    public static final String STALE_CONNECTION_CHECK = "http.connection.stalecheck"; 
-
-    /**
-     * Determines the maximum line length limit. If set to a positive value, any HTTP 
-     * line exceeding this limit will cause an IOException. A negative or zero value
-     * will effectively disable the check.
-     * <p>
-     * This parameter expects a value of type {@link Integer}.
-     * </p>
-     */
-    public static final String MAX_LINE_LENGTH = "http.connection.max-line-length";
-    
-    /**
-     * Determines the maximum HTTP header count allowed. If set to a positive value, 
-     * the number of HTTP headers received from the data stream exceeding this limit 
-     * will cause an IOException. A negative or zero value will effectively disable 
-     * the check. 
-     * <p>
-     * This parameter expects a value of type {@link Integer}.
-     * </p>
-     */
-    public static final String MAX_HEADER_COUNT = "http.connection.max-header-count";
-
-}
diff --git a/src/org/apache/http/params/HttpConnectionParams.java b/src/org/apache/http/params/HttpConnectionParams.java
deleted file mode 100644
index a7b31fc..0000000
--- a/src/org/apache/http/params/HttpConnectionParams.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpConnectionParams.java $
- * $Revision: 576089 $
- * $Date: 2007-09-16 05:39:56 -0700 (Sun, 16 Sep 2007) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.params;
-
-/**
- * An adaptor for accessing connection parameters in {@link HttpParams}.
- * <br/>
- * Note that the <i>implements</i> relation to {@link CoreConnectionPNames}
- * is for compatibility with existing application code only. References to
- * the parameter names should use the interface, not this class.
- * 
- * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
- * 
- * @version $Revision: 576089 $
- * 
- * @since 4.0
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-@Deprecated
-public final class HttpConnectionParams implements CoreConnectionPNames {
-
-    /**
-     */
-    private HttpConnectionParams() {
-        super();
-    }
-
-    /**
-     * Returns the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the 
-     * timeout for waiting for data. A timeout value of zero is interpreted as an infinite 
-     * timeout. This value is used when no socket timeout is set in the 
-     * method parameters. 
-     *
-     * @return timeout in milliseconds
-     */
-    public static int getSoTimeout(final HttpParams params) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        return params.getIntParameter(CoreConnectionPNames.SO_TIMEOUT, 0);
-    }
-
-    /**
-     * Sets the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the 
-     * timeout for waiting for data. A timeout value of zero is interpreted as an infinite 
-     * timeout. This value is used when no socket timeout is set in the 
-     * method parameters. 
-     *
-     * @param timeout Timeout in milliseconds
-     */
-    public static void setSoTimeout(final HttpParams params, int timeout) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout);
-        
-    }
-
-    /**
-     * Tests if Nagle's algorithm is to be used.  
-     *
-     * @return <tt>true</tt> if the Nagle's algorithm is to NOT be used
-     *   (that is enable TCP_NODELAY), <tt>false</tt> otherwise.
-     */
-    public static boolean getTcpNoDelay(final HttpParams params) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        return params.getBooleanParameter
-            (CoreConnectionPNames.TCP_NODELAY, true);
-    }
-
-    /**
-     * Determines whether Nagle's algorithm is to be used. The Nagle's algorithm 
-     * tries to conserve bandwidth by minimizing the number of segments that are 
-     * sent. When applications wish to decrease network latency and increase 
-     * performance, they can disable Nagle's algorithm (that is enable TCP_NODELAY). 
-     * Data will be sent earlier, at the cost of an increase in bandwidth consumption. 
-     *
-     * @param value <tt>true</tt> if the Nagle's algorithm is to NOT be used
-     *   (that is enable TCP_NODELAY), <tt>false</tt> otherwise.
-     */
-    public static void setTcpNoDelay(final HttpParams params, boolean value) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, value);
-    }
-
-    public static int getSocketBufferSize(final HttpParams params) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        return params.getIntParameter
-            (CoreConnectionPNames.SOCKET_BUFFER_SIZE, -1);
-    }
-    
-    public static void setSocketBufferSize(final HttpParams params, int size) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        params.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, size);
-    }
-
-    /**
-     * Returns linger-on-close timeout. Value <tt>0</tt> implies that the option is 
-     * disabled. Value <tt>-1</tt> implies that the JRE default is used.
-     * 
-     * @return the linger-on-close timeout
-     */
-    public static int getLinger(final HttpParams params) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        return params.getIntParameter(CoreConnectionPNames.SO_LINGER, -1);
-    }
-
-    /**
-     * Returns linger-on-close timeout. This option disables/enables immediate return 
-     * from a close() of a TCP Socket. Enabling this option with a non-zero Integer 
-     * timeout means that a close() will block pending the transmission and 
-     * acknowledgement of all data written to the peer, at which point the socket is 
-     * closed gracefully. Value <tt>0</tt> implies that the option is 
-     * disabled. Value <tt>-1</tt> implies that the JRE default is used.
-     *
-     * @param value the linger-on-close timeout
-     */
-    public static void setLinger(final HttpParams params, int value) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        params.setIntParameter(CoreConnectionPNames.SO_LINGER, value);
-    }
-
-    /**
-     * Returns the timeout until a connection is etablished. A value of zero 
-     * means the timeout is not used. The default value is zero.
-     * 
-     * @return timeout in milliseconds.
-     */
-    public static int getConnectionTimeout(final HttpParams params) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        return params.getIntParameter
-            (CoreConnectionPNames.CONNECTION_TIMEOUT, 0);
-    }
-
-    /**
-     * Sets the timeout until a connection is etablished. A value of zero 
-     * means the timeout is not used. The default value is zero.
-     * 
-     * @param timeout Timeout in milliseconds.
-     */
-    public static void setConnectionTimeout(final HttpParams params, int timeout) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        params.setIntParameter
-            (CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
-    }
-    
-    /**
-     * Tests whether stale connection check is to be used. Disabling 
-     * stale connection check may result in slight performance improvement 
-     * at the risk of getting an I/O error when executing a request over a
-     * connection that has been closed at the server side. 
-     * 
-     * @return <tt>true</tt> if stale connection check is to be used, 
-     *   <tt>false</tt> otherwise.
-     */
-    public static boolean isStaleCheckingEnabled(final HttpParams params) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        return params.getBooleanParameter
-            (CoreConnectionPNames.STALE_CONNECTION_CHECK, true);
-    }
-
-    /**
-     * Defines whether stale connection check is to be used. Disabling 
-     * stale connection check may result in slight performance improvement 
-     * at the risk of getting an I/O error when executing a request over a
-     * connection that has been closed at the server side. 
-     * 
-     * @param value <tt>true</tt> if stale connection check is to be used, 
-     *   <tt>false</tt> otherwise.
-     */
-    public static void setStaleCheckingEnabled(final HttpParams params, boolean value) {
-        if (params == null) {
-            throw new IllegalArgumentException("HTTP parameters may not be null");
-        }
-        params.setBooleanParameter
-            (CoreConnectionPNames.STALE_CONNECTION_CHECK, value);
-    }
-    
-}
diff --git a/src/org/apache/http/params/HttpParams.java b/src/org/apache/http/params/HttpParams.java
deleted file mode 100644
index 9562e54..0000000
--- a/src/org/apache/http/params/HttpParams.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpParams.java $
- * $Revision: 610763 $
- * $Date: 2008-01-10 04:01:13 -0800 (Thu, 10 Jan 2008) $
- *
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.params;
-
-/**
- * Represents a collection of HTTP protocol and framework parameters.
- *   
- * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
- * 
- * @version $Revision: 610763 $
- *
- * @since 4.0
- *
- * @deprecated Please use {@link java.net.URL#openConnection} instead.
- *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
- *     for further details.
- */
-@Deprecated
-public interface HttpParams {
-
-    /** 
-     * Obtains the value of the given parameter.
-     * 
-     * @param name the parent name.
-     * 
-     * @return  an object that represents the value of the parameter,
-     *          <code>null</code> if the parameter is not set or if it
-     *          is explicitly set to <code>null</code>
-     * 
-     * @see #setParameter(String, Object)
-     */
-    Object getParameter(String name);
-
-    /**
-     * Assigns the value to the parameter with the given name.
-     *
-     * @param name parameter name
-     * @param value parameter value
-     */
-    HttpParams setParameter(String name, Object value);
-
-    /**
-     * Creates a copy of these parameters.
-     *
-     * @return  a new set of parameters holding the same values as this one
-     */
-    HttpParams copy();
-    
-    /**
-     * Removes the parameter with the specified name.
-     * 
-     * @param name parameter name
-     * 
-     * @return true if the parameter existed and has been removed, false else.
-     */
-    boolean removeParameter(String name);
-
-    /** 
-     * Returns a {@link Long} parameter value with the given name. 
-     * If the parameter is not explicitly set, the default value is returned.  
-     * 
-     * @param name the parent name.
-     * @param defaultValue the default value.
-     * 
-     * @return a {@link Long} that represents the value of the parameter.
-     * 
-     * @see #setLongParameter(String, long)
-     */
-    long getLongParameter(String name, long defaultValue);
-
-    /**
-     * Assigns a {@link Long} to the parameter with the given name
-     * 
-     * @param name parameter name
-     * @param value parameter value
-     */ 
-    HttpParams setLongParameter(String name, long value);
-
-    /** 
-     * Returns an {@link Integer} parameter value with the given name. 
-     * If the parameter is not explicitly set, the default value is returned.  
-     * 
-     * @param name the parent name.
-     * @param defaultValue the default value.
-     * 
-     * @return a {@link Integer} that represents the value of the parameter.
-     * 
-     * @see #setIntParameter(String, int)
-     */
-    int getIntParameter(String name, int defaultValue);
-
-    /**
-     * Assigns an {@link Integer} to the parameter with the given name
-     * 
-     * @param name parameter name
-     * @param value parameter value
-     */ 
-    HttpParams setIntParameter(String name, int value);
-
-    /** 
-     * Returns a {@link Double} parameter value with the given name. 
-     * If the parameter is not explicitly set, the default value is returned.  
-     *
-     * @param name the parent name.
-     * @param defaultValue the default value.
-     * 
-     * @return a {@link Double} that represents the value of the parameter.
-     * 
-     * @see #setDoubleParameter(String, double)
-     */
-    double getDoubleParameter(String name, double defaultValue);
-
-    /**
-     * Assigns a {@link Double} to the parameter with the given name
-     * 
-     * @param name parameter name
-     * @param value parameter value
-     */ 
-    HttpParams setDoubleParameter(String name, double value);
-
-    /** 
-     * Returns a {@link Boolean} parameter value with the given name. 
-     * If the parameter is not explicitly set, the default value is returned.  
-     * 
-     * @param name the parent name.
-     * @param defaultValue the default value.
-     * 
-     * @return a {@link Boolean} that represents the value of the parameter.
-     * 
-     * @see #setBooleanParameter(String, boolean)
-     */
-    boolean getBooleanParameter(String name, boolean defaultValue);
-
-    /**
-     * Assigns a {@link Boolean} to the parameter with the given name
-     * 
-     * @param name parameter name
-     * @param value parameter value
-     */ 
-    HttpParams setBooleanParameter(String name, boolean value);
-
-    /**
-     * Checks if a boolean parameter is set to <code>true</code>.
-     * 
-     * @param name parameter name
-     * 
-     * @return <tt>true</tt> if the parameter is set to value <tt>true</tt>,
-     *         <tt>false</tt> if it is not set or set to <code>false</code>
-     */
-    boolean isParameterTrue(String name);
-
-    /**
-     * Checks if a boolean parameter is not set or <code>false</code>.
-     * 
-     * @param name parameter name
-     * 
-     * @return <tt>true</tt> if the parameter is either not set or
-     *         set to value <tt>false</tt>,
-     *         <tt>false</tt> if it is set to <code>true</code>
-     */
-    boolean isParameterFalse(String name);
-
-}
diff --git a/unbundled-apache b/unbundled-apache
new file mode 100644
index 0000000..c301258
--- /dev/null
+++ b/unbundled-apache
@@ -0,0 +1,4 @@
+The existence of this file in external/apache-http implies
+that the version of the library here is unbundled. The build system
+can test for its existence to decide whether or not to add a
+dependency to this library.