Merge "Fix a FormatFlagsConversionMismatchException bug I introduced in Froyo."
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index 508df3a..7698035 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -38,30 +38,6 @@
     
     public static native void EVP_PKEY_free(int pkey);
   
-  // --- RSA public/private key handling functions ---------------------------
-  
-//  public static native int rsaCreatePublicKey(byte[] n, byte[] e);
-//  
-//  public static native int rsaCreatePrivateKey(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
-//
-//  public static native void rsaDestroyKey(int rsa);
-    
-    // --- DSA public/private key handling functions ---------------------------
-    
-//    public static native int dsaCreatePublicKey(byte[] p, byte[] q, byte[] g, byte[] pub_key);
-//    
-//    public static native int dsaCreatePrivateKey(byte[] p, byte[] q, byte[] g, byte[] priv_key, byte[] pub_key);
-//
-//    public static native void dsaDestroyKey(int dsa);
-    
-    // --- RSA public/private key handling functions ---------------------------
-    
-//    public static native int rsaCreatePublicKey(byte[] n, byte[] e);
-//    
-//    public static native int rsaCreatePrivateKey(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
-//
-//    public static native void rsaDestroyKey(int rsa);
-    
     // --- General context handling functions (despite the names) --------------
     
     public static native int EVP_new();
diff --git a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
index 69ab7fe..407ca0d 100644
--- a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
+++ b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2007-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.
@@ -20,12 +20,29 @@
 
 #define LOG_TAG "NativeCrypto"
 
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
 #include <jni.h>
+
 #include <JNIHelp.h>
+#include <LocalArray.h>
+
+#include <openssl/dsa.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
-#include <openssl/dsa.h>
+#include <openssl/rand.h>
 #include <openssl/rsa.h>
+#include <openssl/ssl.h>
+
+/**
+ * Structure to hold JNI state for openssl callback
+ */
+struct jsse_ssl_app_data_t {
+    JNIEnv* env;
+    jobject object;
+};
 
 /**
  * Frees the SSL error state.
@@ -76,65 +93,6 @@
     return bignum;
 }
 
-static void rsaDestroyKey(JNIEnv* env, jclass clazz, RSA* rsa);
-
-/**
- * private static native int rsaCreatePublicKey(byte[] n, byte[] e);
- */
-static RSA* rsaCreatePublicKey(JNIEnv* env, jclass clazz, jbyteArray n, jbyteArray e) {
-    // LOGD("Entering rsaCreatePublicKey()");
-    
-    RSA* rsa = RSA_new();
-    
-    rsa->n = arrayToBignum(env, n);
-    rsa->e = arrayToBignum(env, e);
-    
-    if (rsa->n == NULL || rsa->e == NULL) {
-        rsaDestroyKey(env, clazz, rsa);
-        jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
-        return NULL;
-    }
-    
-    return rsa;
-}
-
-/**
- * private static native int rsaCreatePrivateKey(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q);
- */
-static RSA* rsaCreatePrivateKey(JNIEnv* env, jclass clazz, jbyteArray n, jbyteArray e, jbyteArray d, jbyteArray p, jbyteArray q) {
-    // LOGD("Entering rsaCreatePrivateKey()");
-  
-    RSA* rsa = RSA_new();
-  
-    rsa->n = arrayToBignum(env, n);
-    rsa->e = arrayToBignum(env, e);
-    rsa->d = arrayToBignum(env, d);
-    rsa->p = arrayToBignum(env, p);
-    rsa->q = arrayToBignum(env, q);
-
-    int check = RSA_check_key(rsa);
-    LOGI("RSA_check_key returns %d", check);
-    
-    if (rsa->n == NULL || rsa->e == NULL || rsa->d == NULL || rsa->p == NULL || rsa->q == NULL) {
-        rsaDestroyKey(env, clazz, rsa);
-        jniThrowRuntimeException(env, "Unable to convert BigInteger to BIGNUM");
-        return NULL;
-    }
-    
-    return rsa;
-}
-
-/**
- * private static native void rsaDestroyKey(int rsa);
- */
-static void rsaDestroyKey(JNIEnv* env, jclass clazz, RSA* rsa) {
-    // LOGD("Entering rsaDestroyKey()");
-    
-    if (rsa != NULL) {
-        RSA_free(rsa);
-    }
-}
-
 /**
  * private static native int EVP_PKEY_new_DSA(byte[] p, byte[] q, byte[] g, byte[] pub_key, byte[] priv_key);
  */
@@ -404,15 +362,7 @@
  * to native functions. Order is (1) Java name, (2) signature,
  * (3) pointer to C function.
  */
-static JNINativeMethod methods[] = {
-/*
-    { "dsaCreatePublicKey",  "([B[B[B[B)I",   (void*)dsaCreatePublicKey  },
-    { "dsaCreatePrivateKey", "([B[B[B[B[B)I", (void*)dsaCreatePrivateKey },
-    { "dsaDestroyKey",       "(I)V",          (void*)dsaDestroyKey },
-    { "rsaCreatePublicKey",  "([B[B)I",       (void*)rsaCreatePublicKey  },
-    { "rsaCreatePrivateKey", "([B[B[B[B[B)I", (void*)rsaCreatePrivateKey },
-    { "rsaDestroyKey",       "(I)V",          (void*)rsaDestroyKey },
-*/    
+static JNINativeMethod sNativeCryptoMethods[] = {
     { "EVP_PKEY_new_DSA",    "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_DSA },
     { "EVP_PKEY_new_RSA",    "([B[B[B[B[B)I", (void*)NativeCrypto_EVP_PKEY_new_RSA },
     { "EVP_PKEY_free",       "(I)V",          (void*)NativeCrypto_EVP_PKEY_free },
@@ -428,6 +378,2181 @@
     { "EVP_VerifyFinal",     "(I[BIII)I",     (void*)NativeCrypto_EVP_VerifyFinal }
 };
 
+/**
+ * Module scope variables initialized during JNI registration.
+ */
+static jfieldID field_Socket_ssl_ctx;
+static jfieldID field_Socket_ssl;
+static jfieldID field_FileDescriptor_descriptor;
+static jfieldID field_Socket_mImpl;
+static jfieldID field_Socket_mFD;
+static jfieldID field_Socket_timeout;
+
+/**
+ * Gets the chars of a String object as a '\0'-terminated UTF-8 string,
+ * stored in a freshly-allocated BIO memory buffer.
+ */
+static BIO *stringToMemBuf(JNIEnv* env, jstring string) {
+    jsize byteCount = env->GetStringUTFLength(string);
+    LocalArray<1024> buf(byteCount + 1);
+    env->GetStringUTFRegion(string, 0, env->GetStringLength(string), &buf[0]);
+
+    BIO* result = BIO_new(BIO_s_mem());
+    BIO_puts(result, &buf[0]);
+    return result;
+}
+
+/**
+ * Throws an SocketTimeoutException with the given string as a message.
+ */
+static void throwSocketTimeoutException(JNIEnv* env, const char* message) {
+    if (jniThrowException(env, "java/net/SocketTimeoutException", message)) {
+        LOGE("Unable to throw");
+    }
+}
+
+/**
+ * Throws a java.io.IOException with the given string as a message.
+ */
+static void throwIOExceptionStr(JNIEnv* env, const char* message) {
+    if (jniThrowException(env, "java/io/IOException", message)) {
+        LOGE("Unable to throw");
+    }
+}
+
+/**
+ * Throws an IOException with a message constructed from the current
+ * SSL errors. This will also log the errors.
+ * 
+ * @param env the JNI environment
+ * @param sslReturnCode return code from failing SSL function
+ * @param sslErrorCode error code returned from SSL_get_error()
+ * @param message null-ok; general error message
+ */
+static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode,
+        int sslErrorCode, const char* message) {
+    const char* messageStr = NULL;
+    char* str;
+    int ret;
+
+    // First consult the SSL error code for the general message. 
+    switch (sslErrorCode) {
+        case SSL_ERROR_NONE:
+            messageStr = "Ok";
+            break;
+        case SSL_ERROR_SSL:
+            messageStr = "Failure in SSL library, usually a protocol error";
+            break;
+        case SSL_ERROR_WANT_READ:
+            messageStr = "SSL_ERROR_WANT_READ occured. You should never see this.";
+            break;
+        case SSL_ERROR_WANT_WRITE:
+            messageStr = "SSL_ERROR_WANT_WRITE occured. You should never see this.";
+            break;
+        case SSL_ERROR_WANT_X509_LOOKUP:
+            messageStr = "SSL_ERROR_WANT_X509_LOOKUP occured. You should never see this.";
+            break;
+        case SSL_ERROR_SYSCALL:
+            messageStr = "I/O error during system call";
+            break;
+        case SSL_ERROR_ZERO_RETURN:
+            messageStr = "SSL_ERROR_ZERO_RETURN occured. You should never see this.";
+            break;
+        case SSL_ERROR_WANT_CONNECT:
+            messageStr = "SSL_ERROR_WANT_CONNECT occured. You should never see this.";
+            break;
+        case SSL_ERROR_WANT_ACCEPT:
+            messageStr = "SSL_ERROR_WANT_ACCEPT occured. You should never see this.";
+            break;
+        default:
+            messageStr = "Unknown SSL error";
+    }
+
+    // Prepend either our explicit message or a default one.
+    if (asprintf(&str, "%s: %s",
+            (message != NULL) ? message : "SSL error", messageStr) == 0) {
+        throwIOExceptionStr(env, messageStr);
+        LOGV("%s", messageStr);
+        freeSslErrorState();
+        return;
+    }
+
+    char* allocStr = str;
+
+    // For SSL protocol errors, SSL might have more information.
+    if (sslErrorCode == SSL_ERROR_SSL) {
+        // Append each error as an additional line to the message.
+        for (;;) {
+            char errStr[256];
+            const char* file;
+            int line;
+            const char* data;
+            int flags;
+            unsigned long err =
+                ERR_get_error_line_data(&file, &line, &data, &flags);
+            if (err == 0) {
+                break;
+            }
+
+            ERR_error_string_n(err, errStr, sizeof(errStr));
+
+            ret = asprintf(&str, "%s\n%s (%s:%d %p:0x%08x)",
+                    (allocStr == NULL) ? "" : allocStr,
+                    errStr,
+                    file,
+                    line,
+                    data,
+                    flags);
+            LOGD("XXX bdc str %s", str);
+
+            if (ret < 0) {
+                break;
+            }
+
+            free(allocStr);
+            allocStr = str;
+        }
+    // For errors during system calls, errno might be our friend.        
+    } else if (sslErrorCode == SSL_ERROR_SYSCALL) {
+        if (asprintf(&str, "%s, %s", allocStr, strerror(errno)) >= 0) {
+            free(allocStr);
+            allocStr = str;
+        }
+    // If the error code is invalid, print it.
+    } else if (sslErrorCode > SSL_ERROR_WANT_ACCEPT) {
+        if (asprintf(&str, ", error code is %d", sslErrorCode) >= 0) {
+            free(allocStr);
+            allocStr = str;
+        }
+    }
+
+    throwIOExceptionStr(env, allocStr);
+
+    LOGV("%s", allocStr);
+    free(allocStr);
+    freeSslErrorState();
+}
+
+/**
+ * Helper function that grabs the ssl pointer out of the given object.
+ * If this function returns NULL and <code>throwIfNull</code> is
+ * passed as <code>true</code>, then this function will call
+ * <code>throwIOExceptionStr</code> before returning, so in this case of
+ * NULL, a caller of this function should simply return and allow JNI
+ * to do its thing.
+ * 
+ * @param env non-null; the JNI environment
+ * @param obj non-null; socket object
+ * @param throwIfNull whether to throw if the SSL pointer is NULL
+ * @returns the pointer, which may be NULL
+ */
+static SSL *getSslPointer(JNIEnv* env, jobject obj, bool throwIfNull) {
+    SSL *ssl = (SSL *)env->GetIntField(obj, field_Socket_ssl);
+
+    if ((ssl == NULL) && throwIfNull) {
+        throwIOExceptionStr(env, "null SSL pointer");
+    }
+
+    return ssl;
+}
+
+// ============================================================================
+// === OpenSSL-related helper stuff begins here. ==============================
+// ============================================================================
+
+/**
+ * OpenSSL locking support. Taken from the O'Reilly book by Viega et al., but I
+ * suppose there are not many other ways to do this on a Linux system (modulo
+ * isomorphism).
+ */
+#define MUTEX_TYPE pthread_mutex_t
+#define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
+#define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
+#define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
+#define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
+#define THREAD_ID pthread_self()
+#define THROW_EXCEPTION (-2)
+#define THROW_SOCKETTIMEOUTEXCEPTION (-3)
+
+static MUTEX_TYPE *mutex_buf = NULL;
+
+static void locking_function(int mode, int n, const char * file, int line) {
+    if (mode & CRYPTO_LOCK) {
+        MUTEX_LOCK(mutex_buf[n]);
+    } else {
+        MUTEX_UNLOCK(mutex_buf[n]);
+    }
+}
+
+static unsigned long id_function(void) {
+    return ((unsigned long)THREAD_ID);
+}
+
+int THREAD_setup(void) {
+    int i;
+
+    mutex_buf = (MUTEX_TYPE *)malloc(CRYPTO_num_locks( ) * sizeof(MUTEX_TYPE));
+
+    if(!mutex_buf) {
+        return 0;
+    }
+
+    for (i = 0; i < CRYPTO_num_locks( ); i++) {
+        MUTEX_SETUP(mutex_buf[i]);
+    }
+
+    CRYPTO_set_id_callback(id_function);
+    CRYPTO_set_locking_callback(locking_function);
+
+    return 1;
+}
+
+int THREAD_cleanup(void) {
+    int i;
+
+    if (!mutex_buf) {
+      return 0;
+    }
+
+    CRYPTO_set_id_callback(NULL);
+    CRYPTO_set_locking_callback(NULL);
+
+    for (i = 0; i < CRYPTO_num_locks( ); i++) {
+        MUTEX_CLEANUP(mutex_buf[i]);
+    }
+
+    free(mutex_buf);
+    mutex_buf = NULL;
+
+    return 1;
+}
+
+int get_socket_timeout(int type, int sd) {
+    struct timeval tv;
+    socklen_t len = sizeof(tv);
+    if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
+         LOGE("getsockopt(%d, SOL_SOCKET): %s (%d)",
+              sd,
+              strerror(errno),
+              errno);
+        return 0;         
+    }
+    // LOGI("Current socket timeout (%d(s), %d(us))!",
+    //      (int)tv.tv_sec, (int)tv.tv_usec);
+    int timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+    return timeout;
+}
+
+#ifdef TIMEOUT_DEBUG_SSL
+
+void print_socket_timeout(const char* name, int type, int sd) {
+    struct timeval tv;
+    int len = sizeof(tv);
+    if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
+         LOGE("getsockopt(%d, SOL_SOCKET, %s): %s (%d)",
+              sd,
+              name,
+              strerror(errno),
+              errno);
+    }
+    LOGI("Current socket %s is (%d(s), %d(us))!",
+          name, (int)tv.tv_sec, (int)tv.tv_usec);
+}
+
+void print_timeout(const char* method, SSL* ssl) {    
+    LOGI("SSL_get_default_timeout %d in %s", SSL_get_default_timeout(ssl), method);
+    int fd = SSL_get_fd(ssl);
+    print_socket_timeout("SO_RCVTIMEO", SO_RCVTIMEO, fd);
+    print_socket_timeout("SO_SNDTIMEO", SO_SNDTIMEO, fd);
+}
+
+#endif
+
+/**
+ * Our additional application data needed for getting synchronization right.
+ * This maybe warrants a bit of lengthy prose:
+ * 
+ * (1) We use a flag to reflect whether we consider the SSL connection alive.
+ * Any read or write attempt loops will be cancelled once this flag becomes 0.
+ * 
+ * (2) We use an int to count the number of threads that are blocked by the
+ * underlying socket. This may be at most two (one reader and one writer), since
+ * the Java layer ensures that no more threads will enter the native code at the
+ * same time.
+ * 
+ * (3) The pipe is used primarily as a means of cancelling a blocking select()
+ * when we want to close the connection (aka "emergency button"). It is also
+ * necessary for dealing with a possible race condition situation: There might
+ * be cases where both threads see an SSL_ERROR_WANT_READ or
+ * SSL_ERROR_WANT_WRITE. Both will enter a select() with the proper argument.
+ * If one leaves the select() successfully before the other enters it, the
+ * "success" event is already consumed and the second thread will be blocked,
+ * possibly forever (depending on network conditions).
+ *  
+ * The idea for solving the problem looks like this: Whenever a thread is
+ * successful in moving around data on the network, and it knows there is
+ * another thread stuck in a select(), it will write a byte to the pipe, waking
+ * up the other thread. A thread that returned from select(), on the other hand,
+ * knows whether it's been woken up by the pipe. If so, it will consume the
+ * byte, and the original state of affairs has been restored.
+ * 
+ * The pipe may seem like a bit of overhead, but it fits in nicely with the
+ * other file descriptors of the select(), so there's only one condition to wait
+ * for.
+ * 
+ * (4) Finally, a mutex is needed to make sure that at most one thread is in
+ * either SSL_read() or SSL_write() at any given time. This is an OpenSSL
+ * requirement. We use the same mutex to guard the field for counting the
+ * waiting threads.
+ * 
+ * Note: The current implementation assumes that we don't have to deal with
+ * problems induced by multiple cores or processors and their respective
+ * memory caches. One possible problem is that of inconsistent views on the
+ * "aliveAndKicking" field. This could be worked around by also enclosing all
+ * accesses to that field inside a lock/unlock sequence of our mutex, but
+ * currently this seems a bit like overkill.
+ */
+typedef struct app_data {
+    int aliveAndKicking;
+    int waitingThreads;
+    int fdsEmergency[2];
+    MUTEX_TYPE mutex;
+} APP_DATA;
+
+/**
+ * Creates our application data and attaches it to a given SSL connection.
+ * 
+ * @param ssl The SSL connection to attach the data to.
+ * @return 0 on success, -1 on failure.
+ */
+static int sslCreateAppData(SSL* ssl) {
+    APP_DATA* data = (APP_DATA*) malloc(sizeof(APP_DATA));
+
+    memset(data, 0, sizeof(APP_DATA));
+
+    data->aliveAndKicking = 1;
+    data->waitingThreads = 0;
+    data->fdsEmergency[0] = -1;
+    data->fdsEmergency[1] = -1;
+
+    if (pipe(data->fdsEmergency) == -1) {
+        return -1;
+    }
+
+    if (MUTEX_SETUP(data->mutex) == -1) {
+        return -1;
+    }
+
+    SSL_set_app_data(ssl, (char*) data);
+
+    return 0;
+}
+
+/**
+ * Destroys our application data, cleaning up everything in the process.
+ * 
+ * @param ssl The SSL connection to take the data from.
+ */ 
+static void sslDestroyAppData(SSL* ssl) {
+    APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
+
+    if (data != NULL) {
+        SSL_set_app_data(ssl, NULL);
+
+        data -> aliveAndKicking = 0;
+
+        if (data->fdsEmergency[0] != -1) {
+            close(data->fdsEmergency[0]);
+        }
+
+        if (data->fdsEmergency[1] != -1) {
+            close(data->fdsEmergency[1]);
+        }
+
+        MUTEX_CLEANUP(data->mutex);
+
+        free(data);
+    }
+}
+
+
+/**
+ * Frees the SSL_CTX struct for the given instance.
+ */
+static void free_ssl_ctx(JNIEnv* env, jobject object) {
+    /*
+     * Preserve and restore the exception state around this call, so
+     * that GetIntField and SetIntField will operate without complaint.
+     */
+    jthrowable exception = env->ExceptionOccurred();
+
+    if (exception != NULL) {
+        env->ExceptionClear();
+    }
+
+    SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_Socket_ssl_ctx);
+
+    if (ctx != NULL) {
+        SSL_CTX_free(ctx);
+        env->SetIntField(object, field_Socket_ssl_ctx, (int) NULL);
+    }
+
+    if (exception != NULL) {
+        env->Throw(exception);
+    }
+}
+
+/**
+ * Frees the SSL struct for the given instance.
+ */
+static void free_ssl(JNIEnv* env, jobject object) {
+    /*
+     * Preserve and restore the exception state around this call, so
+     * that GetIntField and SetIntField will operate without complaint.
+     */
+    jthrowable exception = env->ExceptionOccurred();
+
+    if (exception != NULL) {
+        env->ExceptionClear();
+    }
+
+    SSL *ssl = (SSL *)env->GetIntField(object, field_Socket_ssl);
+
+    if (ssl != NULL) {
+        sslDestroyAppData(ssl);
+        SSL_free(ssl);
+        env->SetIntField(object, field_Socket_ssl, (int) NULL);
+    }
+
+    if (exception != NULL) {
+        env->Throw(exception);
+    }
+}
+
+/**
+ * Constructs the SSL struct for the given instance, replacing one
+ * that was already made, if any.
+ */
+static SSL* create_ssl(JNIEnv* env, jobject object, SSL_CTX*  ssl_ctx) {
+    free_ssl(env, object);
+
+    SSL *ssl = SSL_new(ssl_ctx);
+    env->SetIntField(object, field_Socket_ssl, (int) ssl);
+    return ssl;
+}
+
+/**
+ * Dark magic helper function that checks, for a given SSL session, whether it
+ * can SSL_read() or SSL_write() without blocking. Takes into account any
+ * concurrent attempts to close the SSL session from the Java side. This is
+ * needed to get rid of the hangs that occur when thread #1 closes the SSLSocket
+ * while thread #2 is sitting in a blocking read or write. The type argument
+ * specifies whether we are waiting for readability or writability. It expects
+ * to be passed either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, since we
+ * only need to wait in case one of these problems occurs.
+ * 
+ * @param type Either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
+ * @param fd The file descriptor to wait for (the underlying socket)
+ * @param data The application data structure with mutex info etc. 
+ * @param timeout The timeout value for select call, with the special value
+ *                0 meaning no timeout at all (wait indefinitely). Note: This is
+ *                the Java semantics of the timeout value, not the usual
+ *                select() semantics.
+ * @return The result of the inner select() call, -1 on additional errors 
+ */
+static int sslSelect(int type, int fd, APP_DATA *data, int timeout) {
+    fd_set rfds;
+    fd_set wfds;
+
+    FD_ZERO(&rfds);
+    FD_ZERO(&wfds);
+
+    if (type == SSL_ERROR_WANT_READ) {
+        FD_SET(fd, &rfds);
+    } else {
+        FD_SET(fd, &wfds);
+    }
+
+    FD_SET(data->fdsEmergency[0], &rfds);
+
+    int max = fd > data->fdsEmergency[0] ? fd : data->fdsEmergency[0];
+
+    // Build a struct for the timeout data if we actually want a timeout.
+    struct timeval tv;
+    struct timeval *ptv;
+    if (timeout > 0) {
+        tv.tv_sec = timeout / 1000;
+        tv.tv_usec = 0;
+        ptv = &tv;
+    } else {
+        ptv = NULL;
+    }
+    
+    // LOGD("Doing select() for SSL_ERROR_WANT_%s...", type == SSL_ERROR_WANT_READ ? "READ" : "WRITE");
+    int result = select(max + 1, &rfds, &wfds, NULL, ptv);
+    // LOGD("Returned from select(), result is %d", result);
+    
+    // Lock
+    if (MUTEX_LOCK(data->mutex) == -1) {
+        return -1;
+    }
+    
+    // If we have been woken up by the emergency pipe, there must be a token in
+    // it. Thus we can safely read it (even in a blocking way).
+    if (FD_ISSET(data->fdsEmergency[0], &rfds)) {
+        char token;
+        do {
+            read(data->fdsEmergency[0], &token, 1);
+        } while (errno == EINTR);
+    }
+
+    // Tell the world that there is now one thread less waiting for the
+    // underlying network.
+    data->waitingThreads--;
+    
+    // Unlock
+    MUTEX_UNLOCK(data->mutex);
+    // LOGD("leave sslSelect");
+    return result;
+}
+
+/**
+ * Helper function that wakes up a thread blocked in select(), in case there is
+ * one. Is being called by sslRead() and sslWrite() as well as by JNI glue
+ * before closing the connection.
+ * 
+ * @param data The application data structure with mutex info etc. 
+ */
+static void sslNotify(APP_DATA *data) {
+    // Write a byte to the emergency pipe, so a concurrent select() can return.
+    // Note we have to restore the errno of the original system call, since the
+    // caller relies on it for generating error messages.
+    int errnoBackup = errno;
+    char token = '*';
+    do {
+        errno = 0;
+        write(data->fdsEmergency[1], &token, 1);
+    } while (errno == EINTR);
+    errno = errnoBackup;
+}
+
+/**
+ * Helper function which does the actual reading. The Java layer guarantees that
+ * at most one thread will enter this function at any given time.
+ * 
+ * @param ssl non-null; the SSL context
+ * @param buf non-null; buffer to read into
+ * @param len length of the buffer, in bytes
+ * @param sslReturnCode original SSL return code
+ * @param sslErrorCode filled in with the SSL error code in case of error
+ * @return number of bytes read on success, -1 if the connection was
+ * cleanly shut down, or THROW_EXCEPTION if an exception should be thrown.
+ */
+static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode,
+        int* sslErrorCode, int timeout) {
+
+    // LOGD("Entering sslRead, caller requests to read %d bytes...", len);
+    
+    if (len == 0) {
+        // Don't bother doing anything in this case.
+        return 0;
+    }
+
+    int fd = SSL_get_fd(ssl);
+    BIO *bio = SSL_get_rbio(ssl);
+    
+    APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
+
+    while (data->aliveAndKicking) {
+        errno = 0;
+
+        // Lock
+        if (MUTEX_LOCK(data->mutex) == -1) {
+            return -1;
+        }
+
+        unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
+        
+        // LOGD("Doing SSL_Read()");
+        int result = SSL_read(ssl, buf, len);
+        int error = SSL_ERROR_NONE;
+        if (result <= 0) {
+            error = SSL_get_error(ssl, result);
+            freeSslErrorState();
+        }
+        // LOGD("Returned from SSL_Read() with result %d, error code %d", result, error);
+
+        // If we have been successful in moving data around, check whether it
+        // might make sense to wake up other blocked threads, so they can give
+        // it a try, too.
+        if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) {
+            sslNotify(data);
+        }
+        
+        // If we are blocked by the underlying socket, tell the world that
+        // there will be one more waiting thread now.
+        if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
+            data->waitingThreads++;
+        }
+        
+        // Unlock
+        MUTEX_UNLOCK(data->mutex);
+
+        switch (error) {
+             // Sucessfully read at least one byte.
+            case SSL_ERROR_NONE: {
+                return result;
+            }
+
+            // Read zero bytes. End of stream reached.
+            case SSL_ERROR_ZERO_RETURN: {
+                return -1;
+            }
+
+            // Need to wait for availability of underlying layer, then retry. 
+            case SSL_ERROR_WANT_READ:
+            case SSL_ERROR_WANT_WRITE: {
+                int selectResult = sslSelect(error, fd, data, timeout);
+                if (selectResult == -1) {
+                    *sslReturnCode = -1;
+                    *sslErrorCode = error;
+                    return THROW_EXCEPTION;
+                } else if (selectResult == 0) {
+                    return THROW_SOCKETTIMEOUTEXCEPTION;
+                }
+                
+                break;
+            }
+
+            // A problem occured during a system call, but this is not
+            // necessarily an error.
+            case SSL_ERROR_SYSCALL: {
+                // Connection closed without proper shutdown. Tell caller we
+                // have reached end-of-stream.
+                if (result == 0) {
+                    return -1;
+                }
+                
+                // System call has been interrupted. Simply retry.
+                if (errno == EINTR) {
+                    break;
+                }
+                
+                // Note that for all other system call errors we fall through
+                // to the default case, which results in an Exception. 
+            }
+            
+            // Everything else is basically an error.
+            default: {
+                *sslReturnCode = result;
+                *sslErrorCode = error;
+                return THROW_EXCEPTION;
+            }
+        }
+    }
+    
+    return -1;
+}
+
+/**
+ * Helper function which does the actual writing. The Java layer guarantees that
+ * at most one thread will enter this function at any given time.
+ * 
+ * @param ssl non-null; the SSL context
+ * @param buf non-null; buffer to write
+ * @param len length of the buffer, in bytes
+ * @param sslReturnCode original SSL return code
+ * @param sslErrorCode filled in with the SSL error code in case of error
+ * @return number of bytes read on success, -1 if the connection was
+ * cleanly shut down, or THROW_EXCEPTION if an exception should be thrown.
+ */
+static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode,
+        int* sslErrorCode) {
+  
+    // LOGD("Entering sslWrite(), caller requests to write %d bytes...", len);
+
+    if (len == 0) {
+        // Don't bother doing anything in this case.
+        return 0;
+    }
+    
+    int fd = SSL_get_fd(ssl);
+    BIO *bio = SSL_get_wbio(ssl);
+    
+    APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
+    
+    int count = len;
+    
+    while(data->aliveAndKicking && len > 0) {
+        errno = 0;
+        if (MUTEX_LOCK(data->mutex) == -1) {
+            return -1;
+        }
+        
+        unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
+        
+        // LOGD("Doing SSL_write() with %d bytes to go", len);
+        int result = SSL_write(ssl, buf, len);
+        int error = SSL_ERROR_NONE;
+        if (result <= 0) {
+            error = SSL_get_error(ssl, result);
+            freeSslErrorState();
+        }
+        // LOGD("Returned from SSL_write() with result %d, error code %d", result, error);
+
+        // If we have been successful in moving data around, check whether it
+        // might make sense to wake up other blocked threads, so they can give
+        // it a try, too.
+        if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) {
+            sslNotify(data);
+        }
+        
+        // If we are blocked by the underlying socket, tell the world that
+        // there will be one more waiting thread now.
+        if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
+            data->waitingThreads++;
+        }
+        
+        MUTEX_UNLOCK(data->mutex);
+        
+        switch (error) {
+             // Sucessfully write at least one byte.
+            case SSL_ERROR_NONE: {
+                buf += result;
+                len -= result;
+                break;
+            }
+
+            // Wrote zero bytes. End of stream reached.
+            case SSL_ERROR_ZERO_RETURN: {
+                return -1;
+            }
+                
+            // Need to wait for availability of underlying layer, then retry.
+            // The concept of a write timeout doesn't really make sense, and
+            // it's also not standard Java behavior, so we wait forever here.
+            case SSL_ERROR_WANT_READ:
+            case SSL_ERROR_WANT_WRITE: {
+                int selectResult = sslSelect(error, fd, data, 0);
+                if (selectResult == -1) {
+                    *sslReturnCode = -1;
+                    *sslErrorCode = error;
+                    return THROW_EXCEPTION;
+                } else if (selectResult == 0) {
+                    return THROW_SOCKETTIMEOUTEXCEPTION;
+                }
+                
+                break;
+            }
+
+            // An problem occured during a system call, but this is not
+            // necessarily an error.
+            case SSL_ERROR_SYSCALL: {
+                // Connection closed without proper shutdown. Tell caller we
+                // have reached end-of-stream.
+                if (result == 0) {
+                    return -1;
+                }
+                
+                // System call has been interrupted. Simply retry.
+                if (errno == EINTR) {
+                    break;
+                }
+                
+                // Note that for all other system call errors we fall through
+                // to the default case, which results in an Exception. 
+            }
+            
+            // Everything else is basically an error.
+            default: {
+                *sslReturnCode = result;
+                *sslErrorCode = error;
+                return THROW_EXCEPTION;
+            }
+        }
+    }
+    // LOGD("Successfully wrote %d bytes", count);
+    
+    return count;
+}
+
+/**
+ * Helper function that creates an RSA public key from two buffers containing
+ * the big-endian bit representation of the modulus and the public exponent.
+ * 
+ * @param mod The data of the modulus
+ * @param modLen The length of the modulus data
+ * @param exp The data of the exponent
+ * @param expLen The length of the exponent data
+ * 
+ * @return A pointer to the new RSA structure, or NULL on error 
+ */
+static RSA* rsaCreateKey(unsigned char* mod, int modLen, unsigned char* exp, int expLen) {
+    // LOGD("Entering rsaCreateKey()");
+
+    RSA* rsa = RSA_new();
+
+    rsa->n = BN_bin2bn((unsigned char*) mod, modLen, NULL);
+    rsa->e = BN_bin2bn((unsigned char*) exp, expLen, NULL);
+
+    if (rsa->n == NULL || rsa->e == NULL) {
+        RSA_free(rsa);
+        return NULL;
+    }
+
+    return rsa;
+}
+
+/**
+ * Helper function that frees an RSA key. Just calls the corresponding OpenSSL
+ * function.
+ * 
+ * @param rsa The pointer to the new RSA structure to free.
+ */
+static void rsaFreeKey(RSA* rsa) {
+    // LOGD("Entering rsaFreeKey()");
+
+    if (rsa != NULL) {
+        RSA_free(rsa);
+    }
+}
+
+/**
+ * Helper function that verifies a given RSA signature for a given message.
+ * 
+ * @param msg The message to verify
+ * @param msgLen The length of the message
+ * @param sig The signature to verify
+ * @param sigLen The length of the signature
+ * @param algorithm The name of the hash/sign algorithm to use, e.g. "RSA-SHA1"
+ * @param rsa The RSA public key to use
+ * 
+ * @return 1 on success, 0 on failure, -1 on error (check SSL errors then)
+ * 
+ */
+static int rsaVerify(unsigned char* msg, unsigned int msgLen, unsigned char* sig,
+                     unsigned int sigLen, char* algorithm, RSA* rsa) {
+
+    // LOGD("Entering rsaVerify(%x, %d, %x, %d, %s, %x)", msg, msgLen, sig, sigLen, algorithm, rsa);
+
+    int result = -1;
+
+    EVP_PKEY* key = EVP_PKEY_new();
+    EVP_PKEY_set1_RSA(key, rsa);
+
+    const EVP_MD *type = EVP_get_digestbyname(algorithm);
+    if (type == NULL) {
+        goto cleanup;
+    }
+
+    EVP_MD_CTX ctx;
+
+    EVP_MD_CTX_init(&ctx);    
+    if (EVP_VerifyInit_ex(&ctx, type, NULL) == 0) {
+        goto cleanup;
+    }
+
+    EVP_VerifyUpdate(&ctx, msg, msgLen);
+    result = EVP_VerifyFinal(&ctx, sig, sigLen, key);
+    EVP_MD_CTX_cleanup(&ctx);
+
+    cleanup:
+
+    if (key != NULL) {
+        EVP_PKEY_free(key);
+    }
+
+    return result;
+}
+
+// ============================================================================
+// === OpenSSL-related helper stuff ends here. JNI glue follows. ==============
+// ============================================================================
+
+/**
+ * Initialization phase for every OpenSSL job: Loads the Error strings, the 
+ * crypto algorithms and reset the OpenSSL library
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic(JNIEnv* env, jobject obj)
+{
+    SSL_load_error_strings();
+    ERR_load_crypto_strings();
+    SSL_library_init();
+    OpenSSL_add_all_algorithms();
+    THREAD_setup();
+}
+
+/**
+ * Initialization phase for a socket with OpenSSL.  The server's private key
+ * and X509 certificate are read and the Linux /dev/urandom file is loaded 
+ * as RNG for the session keys.
+ *  
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init(JNIEnv* env, jobject object,
+        jstring privatekey, jstring certificates, jbyteArray seed)
+{   
+    SSL_CTX* ssl_ctx;
+
+    // 'seed == null' when no SecureRandom Object is set
+    // in the SSLContext.
+    if (seed != NULL) {
+        jbyte* randseed = env->GetByteArrayElements(seed, NULL);
+        RAND_seed((unsigned char*) randseed, 1024);
+        env->ReleaseByteArrayElements(seed, randseed, 0);
+    } else {
+        RAND_load_file("/dev/urandom", 1024);
+    }
+
+    ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+
+    // Note: We explicitly do not allow SSLv2 to be used. It
+    SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
+
+    /* Java code in class OpenSSLSocketImpl does the verification. Meaning of 
+     * SSL_VERIFY_NONE flag in client mode: if not using an anonymous cipher
+     * (by default disabled), the server will send a certificate which will 
+     * be checked. The result of the certificate verification process can be  
+     * checked after the TLS/SSL handshake using the SSL_get_verify_result(3) 
+     * function. The handshake will be continued regardless of the 
+     * verification result.    
+     */
+    SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
+
+    int mode = SSL_CTX_get_mode(ssl_ctx);
+    /*
+     * Turn on "partial write" mode. This means that SSL_write() will
+     * behave like Posix write() and possibly return after only
+     * writing a partial buffer. Note: The alternative, perhaps
+     * surprisingly, is not that SSL_write() always does full writes
+     * but that it will force you to retry write calls having
+     * preserved the full state of the original call. (This is icky
+     * and undesirable.)
+     */
+    mode |= SSL_MODE_ENABLE_PARTIAL_WRITE;
+#if defined(SSL_MODE_SMALL_BUFFERS) /* not all SSL versions have this */
+    mode |= SSL_MODE_SMALL_BUFFERS;  /* lazily allocate record buffers; usually saves
+                                      * 44k over the default */
+#endif
+#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) /* not all SSL versions have this */
+    mode |= SSL_MODE_HANDSHAKE_CUTTHROUGH;  /* enable sending of client data as soon as
+                                             * ClientCCS and ClientFinished are sent */
+#endif
+
+    SSL_CTX_set_mode(ssl_ctx, mode);
+
+    if (privatekey != NULL) {
+        BIO* privatekeybio = stringToMemBuf(env, (jstring) privatekey);
+        EVP_PKEY* privatekeyevp =
+          PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
+        BIO_free(privatekeybio);
+
+        if (privatekeyevp == NULL) {
+            throwIOExceptionWithSslErrors(env, 0, 0,
+                    "Error parsing the private key");
+            SSL_CTX_free(ssl_ctx);
+            return;
+        }
+
+        BIO* certificatesbio = stringToMemBuf(env, (jstring) certificates);
+        X509* certificatesx509 =
+          PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
+        BIO_free(certificatesbio);
+
+        if (certificatesx509 == NULL) {
+            throwIOExceptionWithSslErrors(env, 0, 0,
+                    "Error parsing the certificates");
+            EVP_PKEY_free(privatekeyevp);
+            SSL_CTX_free(ssl_ctx);
+            return;
+        }
+
+        int ret = SSL_CTX_use_certificate(ssl_ctx, certificatesx509);
+        if (ret != 1) {
+            throwIOExceptionWithSslErrors(env, ret, 0,
+                    "Error setting the certificates");
+            X509_free(certificatesx509);
+            EVP_PKEY_free(privatekeyevp);
+            SSL_CTX_free(ssl_ctx);
+            return;
+        }
+
+        ret = SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp);
+        if (ret != 1) {
+            throwIOExceptionWithSslErrors(env, ret, 0,
+                    "Error setting the private key");
+            X509_free(certificatesx509);
+            EVP_PKEY_free(privatekeyevp);
+            SSL_CTX_free(ssl_ctx);
+            return;
+        }
+
+        ret = SSL_CTX_check_private_key(ssl_ctx);
+        if (ret != 1) {
+            throwIOExceptionWithSslErrors(env, ret, 0,
+                    "Error checking the private key");
+            X509_free(certificatesx509);
+            EVP_PKEY_free(privatekeyevp);
+            SSL_CTX_free(ssl_ctx);
+            return;
+        }
+    }
+
+    env->SetIntField(object, field_Socket_ssl_ctx, (int)ssl_ctx);
+}
+
+/**
+ * A connection within an OpenSSL context is established. (1) A new socket is
+ * constructed, (2) the TLS/SSL handshake with a server is initiated. 
+ */
+static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect(JNIEnv* env, jobject object,
+        jint ctx, jobject socketObject, jboolean client_mode, jint session)
+{
+    // LOGD("ENTER connect");
+    int ret, fd;
+    SSL_CTX* ssl_ctx;
+    SSL* ssl;
+    SSL_SESSION* ssl_session;
+
+    ssl_ctx = (SSL_CTX*)env->GetIntField(object, field_Socket_ssl_ctx);
+
+    ssl = create_ssl(env, object, ssl_ctx);
+    if (ssl == NULL) {
+        throwIOExceptionWithSslErrors(env, 0, 0,
+                "Unable to create SSL structure");
+        free_ssl_ctx(env, object);
+        return (jboolean) false;
+    }
+
+    jobject socketImplObject = env->GetObjectField(socketObject, field_Socket_mImpl);
+    if (socketImplObject == NULL) {
+        free_ssl(env, object);
+        free_ssl_ctx(env, object);
+        throwIOExceptionStr(env,
+            "couldn't get the socket impl from the socket");
+        return (jboolean) false;
+    }
+
+    jobject fdObject = env->GetObjectField(socketImplObject, field_Socket_mFD);
+    if (fdObject == NULL) {
+        free_ssl(env, object);
+        free_ssl_ctx(env, object);
+        throwIOExceptionStr(env,
+            "couldn't get the file descriptor from the socket impl");
+        return (jboolean) false;
+    }
+
+    fd = jniGetFDFromFileDescriptor(env, fdObject);
+
+    ssl_session = (SSL_SESSION *) session;
+
+    ret = SSL_set_fd(ssl, fd);
+
+    if (ret != 1) {
+        throwIOExceptionWithSslErrors(env, ret, 0,
+                "Error setting the file descriptor");
+        free_ssl(env, object);
+        free_ssl_ctx(env, object);
+        return (jboolean) false;
+    }
+
+    if (ssl_session != NULL) {
+        ret = SSL_set_session(ssl, ssl_session);
+
+        if (ret != 1) {
+            /*
+             * Translate the error, and throw if it turns out to be a real
+             * problem.
+             */
+            int sslErrorCode = SSL_get_error(ssl, ret);
+            if (sslErrorCode != SSL_ERROR_ZERO_RETURN) {
+                throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
+                        "SSL session set");
+                free_ssl(env, object);
+                free_ssl_ctx(env, object);
+                return (jboolean) false;
+            }
+        }
+    }
+
+    /*
+     * Make socket non-blocking, so SSL_connect SSL_read() and SSL_write() don't hang
+     * forever and we can use select() to find out if the socket is ready.
+     */
+    int mode = fcntl(fd, F_GETFL);
+    if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) {
+        throwIOExceptionStr(env, "Unable to make socket non blocking");
+        free_ssl(env, object);
+        free_ssl_ctx(env, object);
+        return (jboolean) false;
+    }
+
+    /*
+     * Create our special application data.
+     */
+    if (sslCreateAppData(ssl) == -1) {
+        throwIOExceptionStr(env, "Unable to create application data");
+        free_ssl(env, object);
+        free_ssl_ctx(env, object);
+        // TODO
+        return (jboolean) false;
+    }
+    
+    APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
+    env->SetIntField(object, field_Socket_ssl, (int)ssl);
+    
+    int timeout = (int)env->GetIntField(object, field_Socket_timeout);
+    
+    while (data->aliveAndKicking) {
+        errno = 0;        
+        ret = SSL_connect(ssl);
+        if (ret == 1) {
+            break;
+        } else if (errno == EINTR) {
+            continue;
+        } else {
+            // LOGD("SSL_connect: result %d, errno %d, timeout %d", ret, errno, timeout);
+            int error = SSL_get_error(ssl, ret);
+
+            /*
+             * If SSL_connect doesn't succeed due to the socket being
+             * either unreadable or unwritable, we use sslSelect to
+             * wait for it to become ready. If that doesn't happen
+             * before the specified timeout or an error occurs, we
+             * cancel the handshake. Otherwise we try the SSL_connect
+             * again.
+             */
+            if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
+                data->waitingThreads++;
+                int selectResult = sslSelect(error, fd, data, timeout);
+                
+                if (selectResult == -1) {
+                    throwIOExceptionWithSslErrors(env, -1, error,
+                        "Connect error");
+                    free_ssl(env, object);
+                    free_ssl_ctx(env, object);
+                    return (jboolean) false;
+                } else if (selectResult == 0) {
+                    throwSocketTimeoutException(env, "SSL handshake timed out");
+                    freeSslErrorState();
+                    free_ssl(env, object);
+                    free_ssl_ctx(env, object);
+                    return (jboolean) false;
+                }
+            } else {
+                LOGE("Unknown error %d during connect", error);
+                break;
+            }
+        }        
+    } 
+
+    if (ret != 1) {
+        /*
+         * Translate the error, and throw if it turns out to be a real
+         * problem.
+         */
+        int sslErrorCode = SSL_get_error(ssl, ret);
+        if (sslErrorCode != SSL_ERROR_ZERO_RETURN) {
+            throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
+                    "SSL handshake failure");
+            free_ssl(env, object);
+            free_ssl_ctx(env, object);
+            return (jboolean) false;
+        }
+    }
+
+    if (ssl_session != NULL) {
+        ret = SSL_session_reused(ssl);
+        // if (ret == 1) LOGD("A session was reused");
+        // else LOGD("A new session was negotiated");
+        return (jboolean) ret;
+    } else {
+        // LOGD("A new session was negotiated");
+        return (jboolean) 0;
+    }
+    // LOGD("LEAVE connect");
+}
+
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession(JNIEnv* env, jobject object,
+        jint jssl)
+{
+    return (jint) SSL_get1_session((SSL *) jssl);
+}
+
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEnv* env, jobject object,
+        jobject socketObject, jint jssl_ctx, jboolean client_mode)
+{
+    int sd, ret;
+    BIO *bio;
+    SSL *ssl;
+    SSL_CTX *ssl_ctx;
+    jsse_ssl_app_data_t appdata;
+
+    ssl_ctx = (SSL_CTX *)jssl_ctx;
+
+    ssl = create_ssl(env, object, ssl_ctx);
+    if (ssl == NULL) {
+        throwIOExceptionWithSslErrors(env, 0, 0,
+                "Unable to create SSL structure");
+        return;
+    }
+
+    jobject socketImplObject = env->GetObjectField(socketObject, field_Socket_mImpl);
+    if (socketImplObject == NULL) {
+        free_ssl(env, object);
+        throwIOExceptionStr(env, "couldn't get the socket impl from the socket");
+        return;
+    }
+
+    jobject fdObject = env->GetObjectField(socketImplObject, field_Socket_mFD);
+    if (fdObject == NULL) {
+        free_ssl(env, object);
+        throwIOExceptionStr(env, "couldn't get the file descriptor from the socket impl");
+        return;
+    }
+
+
+    sd = jniGetFDFromFileDescriptor(env, fdObject);
+
+    bio = BIO_new_socket(sd, BIO_NOCLOSE);
+
+    /* The parameter client_mode must be 1 */
+    if (client_mode != 0)
+        client_mode = 1;
+    BIO_set_ssl_mode(bio, client_mode);
+
+    SSL_set_bio(ssl, bio, bio);
+
+    /*
+     * Fill in the appdata structure needed for the certificate callback and
+     * store this in the SSL application data slot.
+     */
+    appdata.env = env;
+    appdata.object = object;
+    SSL_set_app_data(ssl, &appdata);
+
+    /*
+     * Do the actual SSL_accept(). It is possible this code is insufficient.
+     * Maybe we need to deal with all the special SSL error cases (WANT_*),
+     * just like we do for SSL_connect(). But currently it is looking ok.
+     */
+    ret = SSL_accept(ssl);
+
+    /*
+     * Clear the SSL application data slot again, so we can safely use it for
+     * our ordinary synchronization structure afterwards. Also, we don't want
+     * sslDestroyAppData() to think that there is something that needs to be
+     * freed right now (in case of an error).
+     */
+    SSL_set_app_data(ssl, NULL);
+
+    if (ret == 0) {
+        /*
+         * The other side closed the socket before the handshake could be
+         * completed, but everything is within the bounds of the TLS protocol.
+         * We still might want to find out the real reason of the failure.
+         */
+        int sslErrorCode = SSL_get_error(ssl, ret);
+        if (sslErrorCode == SSL_ERROR_NONE ||
+            (sslErrorCode == SSL_ERROR_SYSCALL && errno == 0)) {
+          throwIOExceptionStr(env, "Connection closed by peer");
+        } else {
+          throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
+              "Trouble accepting connection");
+    	}
+        free_ssl(env, object);
+        return;
+    } else if (ret < 0) {
+        /*
+         * Translate the error and throw exception. We are sure it is an error
+         * at this point.
+         */
+        int sslErrorCode = SSL_get_error(ssl, ret);
+        throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
+                "Trouble accepting connection");
+        free_ssl(env, object);
+        return;
+    }
+
+    /*
+     * Make socket non-blocking, so SSL_read() and SSL_write() don't hang
+     * forever and we can use select() to find out if the socket is ready.
+     */
+    int fd = SSL_get_fd(ssl);
+    int mode = fcntl(fd, F_GETFL);
+    if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) {
+        throwIOExceptionStr(env, "Unable to make socket non blocking");
+        free_ssl(env, object);
+        return;
+    }
+
+    /*
+     * Create our special application data.
+     */
+    if (sslCreateAppData(ssl) == -1) {
+        throwIOExceptionStr(env, "Unable to create application data");
+        free_ssl(env, object);
+        return;
+    }
+}
+
+/**
+ * Loads the desired protocol for the OpenSSL client and enables it.  
+ * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols(JNIEnv* env, jobject object,
+        jlong protocol)
+{
+    if (protocol != 0x00000000L) {
+        if (protocol & SSL_OP_NO_SSLv3)
+            LOGD("SSL_OP_NO_SSLv3 is set");
+        if (protocol & SSL_OP_NO_TLSv1)
+            LOGD("SSL_OP_NO_TLSv1 is set");
+
+        SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_Socket_ssl_ctx);
+        int options = SSL_CTX_get_options(ctx);
+        options |= protocol; // Note: SSLv2 disabled earlier.
+        SSL_CTX_set_options(ctx, options);
+    }
+}
+
+static jobjectArray makeCipherList(JNIEnv* env, SSL* ssl) {
+    // Count the ciphers.
+    int cipherCount = 0;
+    while (SSL_get_cipher_list(ssl, cipherCount) != NULL) {
+        ++cipherCount;
+    }
+
+    // Create a String[].
+    jclass stringClass = env->FindClass("java/lang/String");
+    if (stringClass == NULL) {
+        return NULL;
+    }
+    jobjectArray array = env->NewObjectArray(cipherCount, stringClass, NULL);
+    if (array == NULL) {
+        return NULL;
+    }
+
+    // Fill in the cipher names.
+    for (int i = 0; i < cipherCount; ++i) {
+        const char* c = SSL_get_cipher_list(ssl, i);
+        env->SetObjectArrayElement(array, i, env->NewStringUTF(c));
+    }
+    return array;
+}
+
+jobjectArray makeCipherList(JNIEnv* env, SSL_CTX* ssl_ctx) {
+    SSL* ssl = SSL_new(ssl_ctx);
+    if (ssl == NULL) {
+        return NULL;
+    }
+    jobjectArray result = makeCipherList(env, ssl);
+    SSL_free(ssl);
+    return result;
+}
+
+/**
+ * Loads the ciphers suites that are supported by the OpenSSL client
+ * and returns them in a string array.
+ */
+static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites(JNIEnv* env,
+        jobject object)
+{
+    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+    if (ssl_ctx == NULL) {
+        return NULL;
+    }
+    jobjectArray result = makeCipherList(env, ssl_ctx);
+    SSL_CTX_free(ssl_ctx);
+    return result;
+}
+
+/**
+ * Loads the ciphers suites that are enabled in the OpenSSL client
+ * and returns them in a string array.
+ */
+static jobjectArray OpenSSLSocketImpl_nativeGetEnabledCipherSuites(JNIEnv* env,
+        jclass, jint ssl_ctx_address)
+{
+    SSL_CTX* ssl_ctx =
+            reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+    return makeCipherList(env, ssl_ctx);
+}
+
+void setEnabledCipherSuites(JNIEnv* env, jstring controlString, SSL_CTX* ssl_ctx) {
+    const char* str = env->GetStringUTFChars(controlString, NULL);
+    int rc = SSL_CTX_set_cipher_list(ssl_ctx, str);
+    env->ReleaseStringUTFChars(controlString, str);
+    if (rc == 0) {
+        freeSslErrorState();
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "Illegal cipher suite strings.");
+    }
+}
+
+/**
+ * Sets the ciphers suites that are enabled in the OpenSSL client.
+ */
+static void OpenSSLSocketImpl_nativeSetEnabledCipherSuites(JNIEnv* env, jclass,
+        jint ssl_ctx_address, jstring controlString)
+{
+    SSL_CTX* ssl_ctx =
+            reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
+    setEnabledCipherSuites(env, controlString, ssl_ctx);
+}
+
+#define SSL_AUTH_MASK           0x00007F00L
+#define SSL_aRSA                0x00000100L /* Authenticate with RSA */
+#define SSL_aDSS                0x00000200L /* Authenticate with DSS */
+#define SSL_DSS                 SSL_aDSS
+#define SSL_aFZA                0x00000400L
+#define SSL_aNULL               0x00000800L /* no Authenticate, ADH */
+#define SSL_aDH                 0x00001000L /* no Authenticate, ADH */
+#define SSL_aKRB5               0x00002000L /* Authenticate with KRB5 */
+#define SSL_aECDSA              0x00004000L /* Authenticate with ECDSA */
+
+/**
+ * Sets  the client's crypto algorithms and authentication methods.
+ */
+static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod(JNIEnv* env,
+        jobject object)
+{
+    SSL* ssl;
+    SSL_CIPHER *cipher;
+    jstring ret;
+    char buf[512];
+    unsigned long alg;
+    const char *au;
+
+    ssl = getSslPointer(env, object, true);
+    if (ssl == NULL) {
+        return NULL;
+    }
+
+    cipher = SSL_get_current_cipher(ssl);
+
+    alg = cipher->algorithms;
+
+    switch (alg&SSL_AUTH_MASK) {
+        case SSL_aRSA:
+            au="RSA";
+            break;
+        case SSL_aDSS:
+            au="DSS";
+            break;
+        case SSL_aDH:
+            au="DH";
+            break;
+        case SSL_aFZA:
+            au = "FZA";
+            break;
+        case SSL_aNULL:
+            au="None";
+            break;
+        case SSL_aECDSA:
+            au="ECDSA";
+            break;
+        default:
+            au="unknown";
+            break;
+    }
+
+    ret = env->NewStringUTF(au);
+
+    return ret;
+}
+
+/**
+ * OpenSSL read function (1): only one chunk is read (returned as jint).
+ */
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv* env, jobject object, jint timeout)
+{
+    SSL *ssl = getSslPointer(env, object, true);
+    if (ssl == NULL) {
+        return 0;
+    }
+
+    unsigned char byteRead;
+    int returnCode = 0;
+    int errorCode = 0;
+
+    int ret = sslRead(ssl, (char *) &byteRead, 1, &returnCode, &errorCode, timeout);
+
+    switch (ret) {
+        case THROW_EXCEPTION:
+            // See sslRead() regarding improper failure to handle normal cases.
+            throwIOExceptionWithSslErrors(env, returnCode, errorCode,
+                    "Read error");
+            return -1;
+        case THROW_SOCKETTIMEOUTEXCEPTION:
+            throwSocketTimeoutException(env, "Read timed out");
+            return -1;
+        case -1:
+            // Propagate EOF upwards.
+            return -1;
+        default:
+            // Return the actual char read, make sure it stays 8 bits wide.
+            return ((jint) byteRead) & 0xFF;
+    }
+}
+
+/**
+ * OpenSSL read function (2): read into buffer at offset n chunks. 
+ * Returns 1 (success) or value <= 0 (failure).
+ */
+static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEnv* env, jobject obj, jbyteArray dest, jint offset, jint len, jint timeout)
+{
+    SSL *ssl = getSslPointer(env, obj, true);
+    if (ssl == NULL) {
+        return 0;
+    }
+
+    jbyte* bytes = env->GetByteArrayElements(dest, NULL);
+    int returnCode = 0;
+    int errorCode = 0;
+
+    int ret =
+        sslRead(ssl, (char*) (bytes + offset), len, &returnCode, &errorCode, timeout);
+
+    env->ReleaseByteArrayElements(dest, bytes, 0);
+
+    if (ret == THROW_EXCEPTION) {
+        // See sslRead() regarding improper failure to handle normal cases.
+        throwIOExceptionWithSslErrors(env, returnCode, errorCode,
+                "Read error");
+        return -1;
+    } else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
+        throwSocketTimeoutException(env, "Read timed out");
+        return -1;
+    }
+
+    return ret;
+}
+
+/**
+ * OpenSSL write function (1): only one chunk is written.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv* env, jobject object, jint b)
+{
+    SSL *ssl = getSslPointer(env, object, true);
+    if (ssl == NULL) {
+        return;
+    }
+
+    int returnCode = 0;
+    int errorCode = 0;
+    char buf[1] = { (char) b };
+    int ret = sslWrite(ssl, buf, 1, &returnCode, &errorCode);
+
+    if (ret == THROW_EXCEPTION) {
+        // See sslWrite() regarding improper failure to handle normal cases.
+        throwIOExceptionWithSslErrors(env, returnCode, errorCode,
+                "Write error");
+    } else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
+        throwSocketTimeoutException(env, "Write timed out");
+    }
+}
+
+/**
+ * OpenSSL write function (2): write into buffer at offset n chunks. 
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIEnv* env, jobject obj,
+        jbyteArray dest, jint offset, jint len)
+{
+    SSL *ssl = getSslPointer(env, obj, true);
+    if (ssl == NULL) {
+        return;
+    }
+
+    jbyte* bytes = env->GetByteArrayElements(dest, NULL);
+    int returnCode = 0;
+    int errorCode = 0;
+    int timeout = (int)env->GetIntField(obj, field_Socket_timeout);
+    int ret = sslWrite(ssl, (const char *) (bytes + offset), len, 
+            &returnCode, &errorCode);
+
+    env->ReleaseByteArrayElements(dest, bytes, 0);
+
+    if (ret == THROW_EXCEPTION) {
+        // See sslWrite() regarding improper failure to handle normal cases.
+        throwIOExceptionWithSslErrors(env, returnCode, errorCode,
+                "Write error");
+    } else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
+        throwSocketTimeoutException(env, "Write timed out");
+    }
+}
+
+/**
+ * Interrupt any pending IO before closing the socket. 
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt(
+        JNIEnv* env, jobject object) {
+    SSL *ssl = getSslPointer(env, object, false);
+    if (ssl == NULL) {
+        return;
+    }
+
+    /*
+     * Mark the connection as quasi-dead, then send something to the emergency
+     * file descriptor, so any blocking select() calls are woken up.
+     */
+    APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
+    if (data != NULL) {
+        data->aliveAndKicking = 0;
+
+        // At most two threads can be waiting.
+        sslNotify(data);
+        sslNotify(data);
+    }
+}
+
+/**
+ * OpenSSL close SSL socket function. 
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close(
+        JNIEnv* env, jobject object) {
+    SSL *ssl = getSslPointer(env, object, false);
+    if (ssl == NULL) {
+        return;
+    }
+
+    /*
+     * Try to make socket blocking again. OpenSSL literature recommends this.
+     */
+    int fd = SSL_get_fd(ssl);
+    if (fd != -1) {
+        int mode = fcntl(fd, F_GETFL);
+        if (mode == -1 || fcntl(fd, F_SETFL, mode & ~O_NONBLOCK) == -1) {
+//            throwIOExceptionStr(env, "Unable to make socket blocking again");
+//            LOGW("Unable to make socket blocking again");
+        }
+    }
+
+    int ret = SSL_shutdown(ssl);
+    switch (ret) {
+        case 0:
+            /*
+             * Shutdown was not successful (yet), but there also
+             * is no error. Since we can't know whether the remote
+             * server is actually still there, and we don't want to
+             * get stuck forever in a second SSL_shutdown() call, we
+             * simply return. This is not security a problem as long
+             * as we close the underlying socket, which we actually
+             * do, because that's where we are just coming from.
+             */
+            break;
+        case 1:
+            /*
+             * Shutdown was sucessful. We can safely return. Hooray!
+             */
+            break;
+        default:
+            /*
+             * Everything else is a real error condition. We should
+             * let the Java layer know about this by throwing an
+             * exception.
+             */ 
+            throwIOExceptionWithSslErrors(env, ret, 0, "SSL shutdown failed.");
+            break;
+    }
+
+    freeSslErrorState();
+    free_ssl(env, object);
+    free_ssl_ctx(env, object);
+}    
+
+/**
+ * OpenSSL free SSL socket function. 
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free(JNIEnv* env, jobject object)
+{
+    free_ssl(env, object);
+    free_ssl_ctx(env, object);
+}
+
+/**
+ * Verifies an RSA signature.
+ */
+static int org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignature(JNIEnv* env, jclass clazz,
+        jbyteArray msg, jbyteArray sig, jstring algorithm, jbyteArray mod, jbyteArray exp) {
+
+    // LOGD("Entering verifysignature()");
+
+    if (msg == NULL || sig == NULL || algorithm == NULL || mod == NULL || exp == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return -1;
+    }
+
+    int result = -1;
+
+    jbyte* msgBytes = env->GetByteArrayElements(msg, NULL);
+    jint msgLength = env->GetArrayLength(msg);
+
+    jbyte* sigBytes = env->GetByteArrayElements(sig, NULL);
+    jint sigLength = env->GetArrayLength(sig);
+
+    jbyte* modBytes = env->GetByteArrayElements(mod, NULL);
+    jint modLength = env->GetArrayLength(mod);
+
+    jbyte* expBytes = env->GetByteArrayElements(exp, NULL);
+    jint expLength = env->GetArrayLength(exp);
+
+    const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
+
+    RSA* rsa = rsaCreateKey((unsigned char*) modBytes, modLength, (unsigned char*) expBytes, expLength);
+    if (rsa != NULL) {
+        result = rsaVerify((unsigned char*) msgBytes, msgLength, (unsigned char*) sigBytes, sigLength,
+                (char*) algorithmChars, rsa);
+        rsaFreeKey(rsa);
+    }
+
+    env->ReleaseStringUTFChars(algorithm, algorithmChars);
+
+    env->ReleaseByteArrayElements(exp, expBytes, JNI_ABORT);
+    env->ReleaseByteArrayElements(mod, modBytes, JNI_ABORT);
+    env->ReleaseByteArrayElements(sig, sigBytes, JNI_ABORT);
+    env->ReleaseByteArrayElements(msg, msgBytes, JNI_ABORT);
+
+    if (result == -1) {
+        int error = ERR_get_error();
+        if (error != 0) {
+            char message[50];
+            ERR_error_string_n(error, message, sizeof(message));
+            jniThrowRuntimeException(env, message);
+        } else {
+            jniThrowRuntimeException(env, "Internal error during verification");
+        }
+        freeSslErrorState();
+    }
+
+    return result;
+}
+
+static JNINativeMethod sSocketImplMethods[] =
+{
+    {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic},
+    {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init},
+    {"nativeconnect", "(ILjava/net/Socket;ZI)Z", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect},
+    {"nativegetsslsession", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession},
+    {"nativeread", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read},
+    {"nativeread", "([BIII)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba},
+    {"nativewrite", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write},
+    {"nativewrite", "([BII)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba},
+    {"nativeaccept", "(Ljava/net/Socket;IZ)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept},
+    {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols},
+    {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites},
+    {"nativeGetEnabledCipherSuites", "(I)[Ljava/lang/String;", (void*) OpenSSLSocketImpl_nativeGetEnabledCipherSuites},
+    {"nativeSetEnabledCipherSuites", "(ILjava/lang/String;)V", (void*) OpenSSLSocketImpl_nativeSetEnabledCipherSuites},
+    {"nativecipherauthenticationmethod", "()Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod},
+    {"nativeinterrupt", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt},
+    {"nativeclose", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close},
+    {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free},
+    {"nativeverifysignature", "([B[BLjava/lang/String;[B[B)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignature},
+};
+
+/**
+ * Module scope variables initialized during JNI registration.
+ */
+static jfieldID field_ServerSocket_ssl_ctx;
+
+/**
+ * Initialization phase of OpenSSL: Loads the Error strings, the crypto algorithms and reset the OpenSSL library
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic(JNIEnv* env, jobject obj)
+{
+    SSL_load_error_strings();
+    ERR_load_crypto_strings();
+    SSL_library_init();
+    OpenSSL_add_all_algorithms();
+}
+
+/**
+ * Initialization phase for a server socket with OpenSSL.  The server's private key and X509 certificate are read and  
+ * the Linux /dev/random file is loaded as RNG for the session keys.
+ *  
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init(JNIEnv* env, jobject object,
+        jstring privatekey, jstring certificates, jbyteArray seed)
+{
+    SSL_CTX *ssl_ctx;
+    const char *privatekeychar;
+    const char *certificateschar;
+    EVP_PKEY * privatekeyevp;
+
+    BIO *privatekeybio;
+    BIO *certificatesbio;
+
+    // 'seed == null' when no SecureRandom Object is set
+    // in the SSLContext.
+    if (seed != NULL) {
+        jbyte* randseed = env->GetByteArrayElements(seed, NULL);
+        RAND_seed((unsigned char*) randseed, 1024);
+        env->ReleaseByteArrayElements(seed, randseed, 0);
+    } else {
+        RAND_load_file("/dev/urandom", 1024);
+    }
+
+    ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+    SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
+
+    privatekeychar = env->GetStringUTFChars((jstring)privatekey, NULL);
+    privatekeybio = BIO_new_mem_buf((void*)privatekeychar, -1);
+
+    privatekeyevp = PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
+    env->ReleaseStringUTFChars(privatekey, privatekeychar);
+
+    if (privatekeyevp == NULL) {
+        LOGE(ERR_error_string(ERR_get_error(), NULL));
+        throwIOExceptionStr(env, "Error parsing the private key");
+        return;
+    }
+
+    certificateschar = env->GetStringUTFChars((jstring)certificates, NULL);
+    certificatesbio = BIO_new_mem_buf((void*)certificateschar, -1);
+
+    X509 * certificatesx509 = PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
+    env->ReleaseStringUTFChars(certificates, certificateschar);
+
+    if (certificatesx509 == NULL) {
+        LOGE(ERR_error_string(ERR_get_error(), NULL));
+        throwIOExceptionStr(env, "Error parsing the certificates");
+        return;
+    }
+
+    if (!SSL_CTX_use_certificate(ssl_ctx, certificatesx509)) {
+        LOGE(ERR_error_string(ERR_get_error(), NULL));
+        throwIOExceptionStr(env, "Error setting the certificates");
+        return;
+    }
+
+    if (!SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp)) {
+        LOGE(ERR_error_string(ERR_get_error(), NULL));
+        throwIOExceptionStr(env, "Error setting the private key");
+        return;
+    }
+
+    if (!SSL_CTX_check_private_key(ssl_ctx)) {
+        LOGE(ERR_error_string(ERR_get_error(), NULL));
+        throwIOExceptionStr(env, "Error checking private key");
+        return;
+    }
+
+    env->SetIntField(object, field_ServerSocket_ssl_ctx, (int)ssl_ctx);
+}
+
+/**
+ * Loads the desired protocol for the OpenSSL server and enables it.  
+ * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols(JNIEnv* env,
+        jobject object, jlong protocol)
+{
+    if (protocol != 0x00000000L) {
+        if (protocol & SSL_OP_NO_SSLv3)
+            LOGD("SSL_OP_NO_SSLv3 is set");
+        if (protocol & SSL_OP_NO_TLSv1)
+            LOGD("SSL_OP_NO_TLSv1 is set");
+
+        SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_ServerSocket_ssl_ctx);
+        SSL_CTX_set_options((SSL_CTX*)ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|(long)protocol);
+    }
+}
+
+/**
+ * Loads the ciphers suites that are supported by the OpenSSL server
+ * and returns them in a string array.
+ */
+static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites(JNIEnv* env,
+        jobject object)
+{
+    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+    if (ssl_ctx == NULL) {
+        return NULL;
+    }
+    jobjectArray result = makeCipherList(env, ssl_ctx);
+    SSL_CTX_free(ssl_ctx);
+    return result;
+}
+
+/**
+ * Gives an array back containing all the X509 certificate's bytes.
+ */
+static jobjectArray getcertificatebytes(JNIEnv* env,
+        const STACK_OF(X509) *chain)
+{
+    BUF_MEM *bptr;
+    int count, i;
+    jbyteArray bytes;
+    jobjectArray joa;
+
+    if (chain == NULL) {
+        // Chain can be NULL if the associated cipher doesn't do certs.
+        return NULL;
+    }
+
+    count = sk_X509_num(chain);
+
+    if (count > 0) {
+        joa = env->NewObjectArray(count, env->FindClass("[B"), NULL);
+
+        if (joa == NULL) {
+            return NULL;
+        }
+
+        BIO *bio = BIO_new(BIO_s_mem());
+
+        // LOGD("Start fetching the certificates");
+        for (i = 0; i < count; i++) {
+            X509 *cert = sk_X509_value(chain, i);
+
+            BIO_reset(bio);
+            PEM_write_bio_X509(bio, cert);
+
+            BIO_get_mem_ptr(bio, &bptr);
+            bytes = env->NewByteArray(bptr->length);
+
+            if (bytes == NULL) {
+                /*
+                 * Indicate an error by resetting joa to NULL. It will
+                 * eventually get gc'ed.
+                 */
+                joa = NULL;
+                break;
+            } else {
+                jbyte* src = reinterpret_cast<jbyte*>(bptr->data);
+                env->SetByteArrayRegion(bytes, 0, bptr->length, src);
+                env->SetObjectArrayElement(joa, i, bytes);
+            }
+        }
+
+        // LOGD("Certificate fetching complete");
+        BIO_free(bio);
+        return joa;
+    } else {
+        return NULL;
+    }
+}
+
+/**
+ * Verify the X509 certificate.
+ */
+static int verify_callback(int preverify_ok, X509_STORE_CTX *x509_store_ctx)
+{
+    SSL *ssl;
+    jsse_ssl_app_data_t *appdata;
+    jclass cls;
+
+    jobjectArray objectArray;
+
+    /* Get the correct index to the SSLobject stored into X509_STORE_CTX. */
+    ssl = (SSL*)X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+
+    appdata = (jsse_ssl_app_data_t*)SSL_get_app_data(ssl);
+
+    cls = appdata->env->GetObjectClass(appdata->object);
+
+    jmethodID methodID = appdata->env->GetMethodID(cls, "verify_callback", "([[B)I");
+
+    objectArray = getcertificatebytes(appdata->env, x509_store_ctx->untrusted);
+
+    appdata->env->CallIntMethod(appdata->object, methodID, objectArray);
+
+    return 1;
+}
+
+/**
+ * Sets  the client's credentials and the depth of theirs verification.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth(JNIEnv* env,
+        jobject object, jint value)
+{
+    SSL_CTX *ssl_ctx = (SSL_CTX *)env->GetIntField(object, field_ServerSocket_ssl_ctx);
+    SSL_CTX_set_verify(ssl_ctx, (int)value, verify_callback);
+}
+
+/**
+ * The actual SSL context is reset.
+ */
+static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree(JNIEnv* env, jobject object)
+{
+    SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_ServerSocket_ssl_ctx);
+    SSL_CTX_free(ctx);
+    env->SetIntField(object, field_ServerSocket_ssl_ctx, 0);
+}
+
+static JNINativeMethod sServerSocketImplMethods[] =
+{
+    {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic},
+    {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init},
+    {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols},
+    {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites},
+    {"nativesetclientauth", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth},
+    {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree}
+};
+
+static jfieldID field_Session_session;
+
+static SSL_SESSION* getSslSessionPointer(JNIEnv* env, jobject object) {
+    return reinterpret_cast<SSL_SESSION*>(env->GetIntField(object, field_Session_session));
+}
+
+// Fills a byte[][] with the peer certificates in the chain.
+static jobjectArray OpenSSLSessionImpl_getPeerCertificatesImpl(JNIEnv* env,
+        jobject object, jint jssl)
+{
+    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+    SSL* ssl = SSL_new(ssl_ctx);
+
+    SSL_set_session(ssl, ssl_session);
+
+    STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
+    jobjectArray objectArray = getcertificatebytes(env, chain);
+
+    SSL_free(ssl);
+    SSL_CTX_free(ssl_ctx);
+    return objectArray;
+}
+
+/**
+ * Serializes the native state of the session (ID, cipher, and keys but
+ * not certificates). Returns a byte[] containing the DER-encoded state.
+ * See apache mod_ssl.
+ */
+static jbyteArray OpenSSLSessionImpl_getEncoded(JNIEnv* env, jobject object) {
+    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+    if (ssl_session == NULL) {
+        return NULL;
+    }
+
+    // Compute the size of the DER data
+    int size = i2d_SSL_SESSION(ssl_session, NULL);
+    if (size == 0) {
+        return NULL;
+    }
+
+    jbyteArray bytes = env->NewByteArray(size);
+    if (bytes != NULL) {
+        jbyte* tmp = env->GetByteArrayElements(bytes, NULL);
+        unsigned char* ucp = reinterpret_cast<unsigned char*>(tmp);
+        i2d_SSL_SESSION(ssl_session, &ucp);
+        env->ReleaseByteArrayElements(bytes, tmp, 0);
+    }
+
+    return bytes;
+}
+
+/**
+ * Deserialize the session.
+ */
+static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jobject object, jbyteArray bytes, jint size) {
+    if (bytes == NULL) {
+        return 0;
+    }
+
+    jbyte* tmp = env->GetByteArrayElements(bytes, NULL);
+    const unsigned char* ucp = reinterpret_cast<const unsigned char*>(tmp);
+    SSL_SESSION* ssl_session = d2i_SSL_SESSION(NULL, &ucp, size);
+    env->ReleaseByteArrayElements(bytes, tmp, 0);
+
+    return static_cast<jint>(reinterpret_cast<uintptr_t>(ssl_session));
+}
+
+/**
+ * Gets and returns in a byte array the ID of the actual SSL session.
+ */
+static jbyteArray OpenSSLSessionImpl_getId(JNIEnv* env, jobject object) {
+    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+
+    jbyteArray result = env->NewByteArray(ssl_session->session_id_length);
+    if (result != NULL) {
+        jbyte* src = reinterpret_cast<jbyte*>(ssl_session->session_id);
+        env->SetByteArrayRegion(result, 0, ssl_session->session_id_length, src);
+    }
+
+    return result;
+}
+
+/**
+ * Gets and returns in a long integer the creation's time of the
+ * actual SSL session.
+ */
+static jlong OpenSSLSessionImpl_getCreationTime(JNIEnv* env, jobject object) {
+    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+    jlong result = SSL_SESSION_get_time(ssl_session);
+    result *= 1000; // OpenSSL uses seconds, Java uses milliseconds.
+    return result;
+}
+
+/**
+ * Gets and returns in a string the version of the SSL protocol. If it
+ * returns the string "unknown" it means that no connection is established.
+ */
+static jstring OpenSSLSessionImpl_getProtocol(JNIEnv* env, jobject object) {
+    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+    SSL* ssl = SSL_new(ssl_ctx);
+
+    SSL_set_session(ssl, ssl_session);
+
+    const char* protocol = SSL_get_version(ssl);
+    jstring result = env->NewStringUTF(protocol);
+
+    SSL_free(ssl);
+    SSL_CTX_free(ssl_ctx);
+    return result;
+}
+
+/**
+ * Gets and returns in a string the set of ciphers the actual SSL session uses.
+ */
+static jstring OpenSSLSessionImpl_getCipherSuite(JNIEnv* env, jobject object) {
+    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
+    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+    SSL* ssl = SSL_new(ssl_ctx);
+
+    SSL_set_session(ssl, ssl_session);
+
+    SSL_CIPHER* cipher = SSL_get_current_cipher(ssl);
+    jstring result = env->NewStringUTF(SSL_CIPHER_get_name(cipher));
+
+    SSL_free(ssl);
+    SSL_CTX_free(ssl_ctx);
+    return result;
+}
+
+/**
+ * Frees the SSL session.
+ */
+static void OpenSSLSessionImpl_freeImpl(JNIEnv* env, jobject object, jint session) {
+    LOGD("Freeing OpenSSL session");
+    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(session);
+    SSL_SESSION_free(ssl_session);
+}
+
+static JNINativeMethod sSessionImplMethods[] = {
+    { "freeImpl", "(I)V", (void*) OpenSSLSessionImpl_freeImpl },
+    { "getCipherSuite", "()Ljava/lang/String;", (void*) OpenSSLSessionImpl_getCipherSuite },
+    { "getCreationTime", "()J", (void*) OpenSSLSessionImpl_getCreationTime },
+    { "getEncoded", "()[B", (void*) OpenSSLSessionImpl_getEncoded },
+    { "getId", "()[B", (void*) OpenSSLSessionImpl_getId },
+    { "getPeerCertificatesImpl", "()[[B", (void*) OpenSSLSessionImpl_getPeerCertificatesImpl },
+    { "getProtocol", "()Ljava/lang/String;", (void*) OpenSSLSessionImpl_getProtocol },
+    { "initializeNativeImpl", "([BI)I", (void*) OpenSSLSessionImpl_initializeNativeImpl }
+};
+
+typedef struct {
+    const char*            name;
+    const JNINativeMethod* methods;
+    jint                   nMethods;
+} JNINativeClass;
+
+static JNINativeClass sClasses[] = {
+    { "org/apache/harmony/xnet/provider/jsse/NativeCrypto", sNativeCryptoMethods, NELEM(sNativeCryptoMethods) },
+    { "org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl", sSocketImplMethods, NELEM(sSocketImplMethods) },
+    { "org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl", sServerSocketImplMethods, NELEM(sServerSocketImplMethods) },
+    { "org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl", sSessionImplMethods, NELEM(sSessionImplMethods) },
+};
+
 /*
  * Peforms the actual registration of the native methods.
  * Also looks up the fields that belong to the class (if
@@ -435,17 +2560,106 @@
  * don't need.
  */
 extern "C" int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env) {
-    int result;
-    result = jniRegisterNativeMethods(env, "org/apache/harmony/xnet/provider/jsse/NativeCrypto", methods, NELEM(methods));
-    if (result == -1) {
+
+    // Register org.apache.harmony.xnet.provider.jsse.* methods
+    for (int i = 0; i < NELEM(sClasses); i++) {
+        int result = jniRegisterNativeMethods(env,
+                                              sClasses[i].name,
+                                              sClasses[i].methods,
+                                              sClasses[i].nMethods);
+        if (result == -1) {
+          return -1;
+        }
+    }
+
+    // java.io.FileDescriptor
+    jclass fileDescriptor = env->FindClass("java/io/FileDescriptor");
+    if (fileDescriptor == NULL) {
+        LOGE("Can't find java/io/FileDescriptor");
+        return -1;
+    }
+    field_FileDescriptor_descriptor = env->GetFieldID(fileDescriptor, "descriptor", "I");
+    if (field_FileDescriptor_descriptor == NULL) {
+        LOGE("Can't find FileDescriptor.descriptor");
         return -1;
     }
 
-    jclass clazz;
-    clazz = env->FindClass("org/apache/harmony/xnet/provider/jsse/NativeCrypto");
-    if (clazz == NULL) {
+    // java.net.Socket
+    jclass socket = env->FindClass("java/net/Socket");
+    if (socket == NULL) {
+        LOGE("Can't find class java.net.Socket");
         return -1;
     }
+    field_Socket_mImpl = env->GetFieldID(socket, "impl", "Ljava/net/SocketImpl;");
+    if (field_Socket_mImpl == NULL) {
+        LOGE("Can't find field impl in class java.net.Socket");
+        return -1;
+    }
+
+    // java.net.SocketImpl
+    jclass socketImplClass = env->FindClass("java/net/SocketImpl");
+    if (socketImplClass == NULL) {
+        LOGE("Can't find class java.net.SocketImpl");
+        return -1;
+    }
+    field_Socket_mFD = env->GetFieldID(socketImplClass, "fd", "Ljava/io/FileDescriptor;");
+    if (field_Socket_mFD == NULL) {
+        LOGE("Can't find field fd in java.net.SocketImpl");
+        return -1;
+    }
+
+    // org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl
+    jclass socketImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl");
+    if (socketImpl == NULL) {
+        LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl");
+        return -1;
+    }
+    // Note: do these after the registration of native methods, because 
+    // there is a static method "initstatic" that's called when the
+    // OpenSSLSocketImpl class is first loaded, and that required
+    // a native method to be associated with it.
+    field_Socket_ssl_ctx = env->GetFieldID(socketImpl, "ssl_ctx", "I");
+    if (field_Socket_ssl_ctx == NULL) {
+      LOGE("Can't find OpenSSLSocketImpl.ssl_ctx");
+      return -1;
+    }
+    field_Socket_ssl = env->GetFieldID(socketImpl, "ssl", "I");
+    if (field_Socket_ssl == NULL) {
+      LOGE("Can't find OpenSSLSocketImpl.ssl");
+      return -1;
+    }
+    field_Socket_timeout = env->GetFieldID(socketImpl, "timeout", "I");
+    if (field_Socket_timeout == NULL) {
+      LOGE("Can't find OpenSSLSocketImpl.timeout");
+      return -1;
+    }
+
+    // org.apache.harmony.xnet.provider.jsse.OpenSSLServerSocketImpl
+    jclass serverSocketImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl");
+    if (serverSocketImpl == NULL) {
+        LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl");
+        return -1;
+    }
+    // Note: do these after the registration of native methods, because
+    // there is a static method "initstatic" that's called when the
+    // OpenSSLServerSocketImpl class is first loaded, and that required
+    // a native method to be associated with it.
+    field_ServerSocket_ssl_ctx = env->GetFieldID(serverSocketImpl, "ssl_ctx", "I");
+    if (field_ServerSocket_ssl_ctx == NULL) {
+      LOGE("Can't find OpenSSLServerSocketImpl.ssl_ctx");
+      return -1;
+    }
+
+    // org.apache.harmony.xnet.provider.jsse.OpenSSLSessionImpl
+    jclass sessionImpl = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl");
+    if (sessionImpl == NULL) {
+        return -1;
+    }
+    field_Session_session = env->GetFieldID(sessionImpl, "session", "I");
+    if (field_Session_session == NULL) {
+      LOGE("Can't find OpenSSLSessionImpl.session");
+      return -1;
+    }
 
     return 0;
 }
diff --git a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp
deleted file mode 100644
index 1e73041..0000000
--- a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "OpenSSLServerSocketImpl"
-
-#include <jni.h>
-#include <JNIHelp.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#include <openssl/ssl.h>
-
-#include "org_apache_harmony_xnet_provider_jsse_common.h"
-
-/**
- * Module scope variables initialized during JNI registration.
- */
-static jfieldID field_ssl_ctx;
-
-/**
- * Throws java.io.IOException with the provided message.
- */
-static void throwIOExceptionStr(JNIEnv* env, const char* message) {
-    jniThrowException(env, "java/io/IOException", message);
-}
-
-/**
- * Initialization phase of OpenSSL: Loads the Error strings, the crypto algorithms and reset the OpenSSL library
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic(JNIEnv* env, jobject obj)
-{
-    SSL_load_error_strings();
-    ERR_load_crypto_strings();
-    SSL_library_init();
-    OpenSSL_add_all_algorithms();
-}
-
-/**
- * Initialization phase for a server socket with OpenSSL.  The server's private key and X509 certificate are read and  
- * the Linux /dev/random file is loaded as RNG for the session keys.
- *  
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init(JNIEnv* env, jobject object,
-        jstring privatekey, jstring certificates, jbyteArray seed)
-{
-    SSL_CTX *ssl_ctx;
-    const char *privatekeychar;
-    const char *certificateschar;
-    EVP_PKEY * privatekeyevp;
-
-    BIO *privatekeybio;
-    BIO *certificatesbio;
-
-    // 'seed == null' when no SecureRandom Object is set
-    // in the SSLContext.
-    if (seed != NULL) {
-        jbyte* randseed = env->GetByteArrayElements(seed, NULL);
-        RAND_seed((unsigned char*) randseed, 1024);
-        env->ReleaseByteArrayElements(seed, randseed, 0);
-    } else {
-        RAND_load_file("/dev/urandom", 1024);
-    }
-
-    ssl_ctx = SSL_CTX_new(SSLv23_server_method());
-    SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
-
-    privatekeychar = env->GetStringUTFChars((jstring)privatekey, NULL);
-    privatekeybio = BIO_new_mem_buf((void*)privatekeychar, -1);
-
-    privatekeyevp = PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
-    env->ReleaseStringUTFChars(privatekey, privatekeychar);
-
-    if (privatekeyevp == NULL) {
-        LOGE(ERR_error_string(ERR_get_error(), NULL));
-        throwIOExceptionStr(env, "Error parsing the private key");
-        return;
-    }
-
-    certificateschar = env->GetStringUTFChars((jstring)certificates, NULL);
-    certificatesbio = BIO_new_mem_buf((void*)certificateschar, -1);
-
-    X509 * certificatesx509 = PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
-    env->ReleaseStringUTFChars(certificates, certificateschar);
-
-    if (certificatesx509 == NULL) {
-        LOGE(ERR_error_string(ERR_get_error(), NULL));
-        throwIOExceptionStr(env, "Error parsing the certificates");
-        return;
-    }
-
-    if (!SSL_CTX_use_certificate(ssl_ctx, certificatesx509)) {
-        LOGE(ERR_error_string(ERR_get_error(), NULL));
-        throwIOExceptionStr(env, "Error setting the certificates");
-        return;
-    }
-
-    if (!SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp)) {
-        LOGE(ERR_error_string(ERR_get_error(), NULL));
-        throwIOExceptionStr(env, "Error setting the private key");
-        return;
-    }
-
-    if (!SSL_CTX_check_private_key(ssl_ctx)) {
-        LOGE(ERR_error_string(ERR_get_error(), NULL));
-        throwIOExceptionStr(env, "Error checking private key");
-        return;
-    }
-
-    env->SetIntField(object, field_ssl_ctx, (int)ssl_ctx);
-}
-
-/**
- * Loads the desired protocol for the OpenSSL server and enables it.  
- * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols(JNIEnv* env,
-        jobject object, jlong protocol)
-{
-    if (protocol != 0x00000000L) {
-        if (protocol & SSL_OP_NO_SSLv3)
-            LOGD("SSL_OP_NO_SSLv3 is set");
-        if (protocol & SSL_OP_NO_TLSv1)
-            LOGD("SSL_OP_NO_TLSv1 is set");
-
-        SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx);
-        SSL_CTX_set_options((SSL_CTX*)ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2|(long)protocol);
-    }
-}
-
-/**
- * Loads the ciphers suites that are supported by the OpenSSL server
- * and returns them in a string array.
- */
-static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites(JNIEnv* env,
-        jobject object)
-{
-    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_server_method());
-    if (ssl_ctx == NULL) {
-        return NULL;
-    }
-    jobjectArray result = makeCipherList(env, ssl_ctx);
-    SSL_CTX_free(ssl_ctx);
-    return result;
-}
-
-/**
- * Sets  the client's credentials and the depth of theirs verification.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth(JNIEnv* env,
-        jobject object, jint value)
-{
-    SSL_CTX *ssl_ctx = (SSL_CTX *)env->GetIntField(object, field_ssl_ctx);
-    SSL_CTX_set_verify(ssl_ctx, (int)value, verify_callback);
-}
-
-/**
- * The actual SSL context is reset.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree(JNIEnv* env, jobject object)
-{
-    SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_ssl_ctx);
-    SSL_CTX_free(ctx);
-    env->SetIntField(object, field_ssl_ctx, 0);
-}
-
-/**
- * The actual JNI methods' mapping table for the class OpenSSLServerSocketImpl.
- */
-static JNINativeMethod sMethods[] =
-{
-    {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_initstatic},
-    {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init},
-    {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setenabledprotocols},
-    {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_getsupportedciphersuites},
-    {"nativesetclientauth", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativesetclientauth},
-    {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_nativefree}
-};
-
-/**
- * Register the native methods with JNI for the class OpenSSLServerSocketImpl.
- */
-extern "C" int register_org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl(JNIEnv* env)
-{
-    jclass clazz = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl");
-    if (clazz == NULL) {
-        LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl");
-        return -1;
-    }
-
-    int rc = jniRegisterNativeMethods(env, "org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl",
-            sMethods, NELEM(sMethods));
-    if (rc >= 0) {
-        // Note: do these after the registration of native methods, because 
-        // there is a static method "initstatic" that's called when the
-        // OpenSSLServerSocketImpl class is first loaded, and that required
-        // a native method to be associated with it.
-        field_ssl_ctx = env->GetFieldID(clazz, "ssl_ctx", "I");
-        if (field_ssl_ctx == NULL) {
-            LOGE("Can't find OpenSSLServerSocketImpl.ssl_ctx");
-            return -1;
-        }
-    }
-    return rc;
-}
diff --git a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp
deleted file mode 100644
index e0ea0b8..0000000
--- a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "OpenSSLSessionImpl"
-
-#include "AndroidSystemNatives.h"
-#include "JNIHelp.h"
-
-#include <jni.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#include <openssl/ssl.h>
-
-#include "org_apache_harmony_xnet_provider_jsse_common.h"
-
-static jfieldID field_session;
-
-static SSL_SESSION* getSslSessionPointer(JNIEnv* env, jobject object) {
-    return reinterpret_cast<SSL_SESSION*>(env->GetIntField(object, field_session));
-}
-
-// Fills a byte[][] with the peer certificates in the chain.
-static jobjectArray OpenSSLSessionImpl_getPeerCertificatesImpl(JNIEnv* env,
-        jobject object, jint jssl)
-{
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
-    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-    SSL* ssl = SSL_new(ssl_ctx);
-
-    SSL_set_session(ssl, ssl_session);
-
-    STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl);
-    jobjectArray objectArray = getcertificatebytes(env, chain);
-
-    SSL_free(ssl);
-    SSL_CTX_free(ssl_ctx);
-    return objectArray;
-}
-
-/**
- * Serializes the native state of the session (ID, cipher, and keys but
- * not certificates). Returns a byte[] containing the DER-encoded state.
- * See apache mod_ssl.
- */
-static jbyteArray OpenSSLSessionImpl_getEncoded(JNIEnv* env, jobject object) {
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
-    if (ssl_session == NULL) {
-        return NULL;
-    }
-
-    // Compute the size of the DER data
-    int size = i2d_SSL_SESSION(ssl_session, NULL);
-    if (size == 0) {
-        return NULL;
-    }
-
-    jbyteArray bytes = env->NewByteArray(size);
-    if (bytes != NULL) {
-        jbyte* tmp = env->GetByteArrayElements(bytes, NULL);
-        unsigned char* ucp = reinterpret_cast<unsigned char*>(tmp);
-        i2d_SSL_SESSION(ssl_session, &ucp);
-        env->ReleaseByteArrayElements(bytes, tmp, 0);
-    }
-
-    return bytes;
-}
-
-/**
- * Deserialize the session.
- */
-static jint OpenSSLSessionImpl_initializeNativeImpl(JNIEnv* env, jobject object, jbyteArray bytes, jint size) {
-    if (bytes == NULL) {
-        return 0;
-    }
-
-    jbyte* tmp = env->GetByteArrayElements(bytes, NULL);
-    const unsigned char* ucp = reinterpret_cast<const unsigned char*>(tmp);
-    SSL_SESSION* ssl_session = d2i_SSL_SESSION(NULL, &ucp, size);
-    env->ReleaseByteArrayElements(bytes, tmp, 0);
-
-    return static_cast<jint>(reinterpret_cast<uintptr_t>(ssl_session));
-}
-
-/**
- * Gets and returns in a byte array the ID of the actual SSL session.
- */
-static jbyteArray OpenSSLSessionImpl_getId(JNIEnv* env, jobject object) {
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
-
-    jbyteArray result = env->NewByteArray(ssl_session->session_id_length);
-    if (result != NULL) {
-        jbyte* src = reinterpret_cast<jbyte*>(ssl_session->session_id);
-        env->SetByteArrayRegion(result, 0, ssl_session->session_id_length, src);
-    }
-
-    return result;
-}
-
-/**
- * Gets and returns in a long integer the creation's time of the
- * actual SSL session.
- */
-static jlong OpenSSLSessionImpl_getCreationTime(JNIEnv* env, jobject object) {
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
-    jlong result = SSL_SESSION_get_time(ssl_session);
-    result *= 1000; // OpenSSL uses seconds, Java uses milliseconds.
-    return result;
-}
-
-/**
- * Gets and returns in a string the version of the SSL protocol. If it
- * returns the string "unknown" it means that no connection is established.
- */
-static jstring OpenSSLSessionImpl_getProtocol(JNIEnv* env, jobject object) {
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
-    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-    SSL* ssl = SSL_new(ssl_ctx);
-
-    SSL_set_session(ssl, ssl_session);
-
-    const char* protocol = SSL_get_version(ssl);
-    jstring result = env->NewStringUTF(protocol);
-
-    SSL_free(ssl);
-    SSL_CTX_free(ssl_ctx);
-    return result;
-}
-
-/**
- * Gets and returns in a string the set of ciphers the actual SSL session uses.
- */
-static jstring OpenSSLSessionImpl_getCipherSuite(JNIEnv* env, jobject object) {
-    SSL_SESSION* ssl_session = getSslSessionPointer(env, object);
-    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-    SSL* ssl = SSL_new(ssl_ctx);
-
-    SSL_set_session(ssl, ssl_session);
-
-    SSL_CIPHER* cipher = SSL_get_current_cipher(ssl);
-    jstring result = env->NewStringUTF(SSL_CIPHER_get_name(cipher));
-
-    SSL_free(ssl);
-    SSL_CTX_free(ssl_ctx);
-    return result;
-}
-
-/**
- * Frees the SSL session.
- */
-static void OpenSSLSessionImpl_freeImpl(JNIEnv* env, jobject object, jint session) {
-    LOGD("Freeing OpenSSL session");
-    SSL_SESSION* ssl_session = reinterpret_cast<SSL_SESSION*>(session);
-    SSL_SESSION_free(ssl_session);
-}
-
-static JNINativeMethod sMethods[] = {
-    { "freeImpl", "(I)V", (void*) OpenSSLSessionImpl_freeImpl },
-    { "getCipherSuite", "()Ljava/lang/String;", (void*) OpenSSLSessionImpl_getCipherSuite },
-    { "getCreationTime", "()J", (void*) OpenSSLSessionImpl_getCreationTime },
-    { "getEncoded", "()[B", (void*) OpenSSLSessionImpl_getEncoded },
-    { "getId", "()[B", (void*) OpenSSLSessionImpl_getId },
-    { "getPeerCertificatesImpl", "()[[B", (void*) OpenSSLSessionImpl_getPeerCertificatesImpl },
-    { "getProtocol", "()Ljava/lang/String;", (void*) OpenSSLSessionImpl_getProtocol },
-    { "initializeNativeImpl", "([BI)I", (void*) OpenSSLSessionImpl_initializeNativeImpl }
-};
-
-int register_org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl(JNIEnv* env) {
-    jclass clazz = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl");
-    if (clazz == NULL) {
-        return -1;
-    }
-
-    field_session = env->GetFieldID(clazz, "session", "I");
-
-    return jniRegisterNativeMethods(env, "org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl",
-            sMethods, NELEM(sMethods));
-}
diff --git a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
deleted file mode 100644
index a3c86d6..0000000
--- a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
+++ /dev/null
@@ -1,1902 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "OpenSSLSocketImpl"
-
-#include "JNIHelp.h"
-#include "LocalArray.h"
-#include "cutils/log.h"
-#include "jni.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/select.h>
-
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#include <openssl/ssl.h>
-
-#include "org_apache_harmony_xnet_provider_jsse_common.h"
-
-/**
- * Module scope variables initialized during JNI registration.
- */
-static jfieldID field_ssl_ctx;
-static jfieldID field_ssl;
-static jfieldID field_descriptor;
-static jfieldID field_mImpl;
-static jfieldID field_mFD;
-static jfieldID field_timeout;
-
-/**
- * Gets the chars of a String object as a '\0'-terminated UTF-8 string,
- * stored in a freshly-allocated BIO memory buffer.
- */
-static BIO *stringToMemBuf(JNIEnv* env, jstring string) {
-    jsize byteCount = env->GetStringUTFLength(string);
-    LocalArray<1024> buf(byteCount + 1);
-    env->GetStringUTFRegion(string, 0, env->GetStringLength(string), &buf[0]);
-
-    BIO* result = BIO_new(BIO_s_mem());
-    BIO_puts(result, &buf[0]);
-    return result;
-}
-
-/**
- * Throws an SocketTimeoutException with the given string as a message.
- */
-static void throwSocketTimeoutException(JNIEnv* env, const char* message) {
-    if (jniThrowException(env, "java/net/SocketTimeoutException", message)) {
-        LOGE("Unable to throw");
-    }
-}
-
-/**
- * Throws an IOException with the given string as a message.
- */
-static void throwIOExceptionStr(JNIEnv* env, const char* message) {
-    if (jniThrowException(env, "java/io/IOException", message)) {
-        LOGE("Unable to throw");
-    }
-}
-
-/**
- * Frees the SSL error state.
- * 
- * OpenSSL keeps an "error stack" per thread, and given that this code
- * can be called from arbitrary threads that we don't keep track of,
- * we err on the side of freeing the error state promptly (instead of,
- * say, at thread death).
- */
-static void freeSslErrorState(void) {
-    ERR_clear_error();
-    ERR_remove_state(0);
-}
-
-/**
- * Throws an IOException with a message constructed from the current
- * SSL errors. This will also log the errors.
- * 
- * @param env the JNI environment
- * @param sslReturnCode return code from failing SSL function
- * @param sslErrorCode error code returned from SSL_get_error()
- * @param message null-ok; general error message
- */
-static void throwIOExceptionWithSslErrors(JNIEnv* env, int sslReturnCode,
-        int sslErrorCode, const char* message) {
-    const char* messageStr = NULL;
-    char* str;
-    int ret;
-
-    // First consult the SSL error code for the general message. 
-    switch (sslErrorCode) {
-        case SSL_ERROR_NONE:
-            messageStr = "Ok";
-            break;
-        case SSL_ERROR_SSL:
-            messageStr = "Failure in SSL library, usually a protocol error";
-            break;
-        case SSL_ERROR_WANT_READ:
-            messageStr = "SSL_ERROR_WANT_READ occured. You should never see this.";
-            break;
-        case SSL_ERROR_WANT_WRITE:
-            messageStr = "SSL_ERROR_WANT_WRITE occured. You should never see this.";
-            break;
-        case SSL_ERROR_WANT_X509_LOOKUP:
-            messageStr = "SSL_ERROR_WANT_X509_LOOKUP occured. You should never see this.";
-            break;
-        case SSL_ERROR_SYSCALL:
-            messageStr = "I/O error during system call";
-            break;
-        case SSL_ERROR_ZERO_RETURN:
-            messageStr = "SSL_ERROR_ZERO_RETURN occured. You should never see this.";
-            break;
-        case SSL_ERROR_WANT_CONNECT:
-            messageStr = "SSL_ERROR_WANT_CONNECT occured. You should never see this.";
-            break;
-        case SSL_ERROR_WANT_ACCEPT:
-            messageStr = "SSL_ERROR_WANT_ACCEPT occured. You should never see this.";
-            break;
-        default:
-            messageStr = "Unknown SSL error";
-    }
-
-    // Prepend either our explicit message or a default one.
-    if (asprintf(&str, "%s: %s",
-            (message != NULL) ? message : "SSL error", messageStr) == 0) {
-        throwIOExceptionStr(env, messageStr);
-        LOGV("%s", messageStr);
-        freeSslErrorState();
-        return;
-    }
-
-    char* allocStr = str;
-
-    // For SSL protocol errors, SSL might have more information.
-    if (sslErrorCode == SSL_ERROR_SSL) {
-        // Append each error as an additional line to the message.
-        for (;;) {
-            char errStr[256];
-            const char* file;
-            int line;
-            const char* data;
-            int flags;
-            unsigned long err =
-                ERR_get_error_line_data(&file, &line, &data, &flags);
-
-            if (err == 0) {
-                break;
-            }
-
-            ERR_error_string_n(err, errStr, sizeof(errStr));
-
-            ret = asprintf(&str, "%s\n%s (%s:%d %p:0x%08x)",
-                    (allocStr == NULL) ? "" : allocStr,
-                    errStr,
-                    file,
-                    line,
-                    data,
-                    flags);
-
-            if (ret < 0) {
-                break;
-            }
-
-            free(allocStr);
-            allocStr = str;
-        }
-    // For errors during system calls, errno might be our friend.        
-    } else if (sslErrorCode == SSL_ERROR_SYSCALL) {
-        if (asprintf(&str, "%s, %s", allocStr, strerror(errno)) >= 0) {
-            free(allocStr);
-            allocStr = str;
-        }
-    // If the error code is invalid, print it.
-    } else if (sslErrorCode > SSL_ERROR_WANT_ACCEPT) {
-        if (asprintf(&str, ", error code is %d", sslErrorCode) >= 0) {
-            free(allocStr);
-            allocStr = str;
-        }
-    }
-
-    throwIOExceptionStr(env, allocStr);
-
-    LOGV("%s", allocStr);
-    free(allocStr);
-    freeSslErrorState();
-}
-
-/**
- * Helper function that grabs the ssl pointer out of the given object.
- * If this function returns NULL and <code>throwIfNull</code> is
- * passed as <code>true</code>, then this function will call
- * <code>throwIOExceptionStr</code> before returning, so in this case of
- * NULL, a caller of this function should simply return and allow JNI
- * to do its thing.
- * 
- * @param env non-null; the JNI environment
- * @param obj non-null; socket object
- * @param throwIfNull whether to throw if the SSL pointer is NULL
- * @returns the pointer, which may be NULL
- */
-static SSL *getSslPointer(JNIEnv* env, jobject obj, bool throwIfNull) {
-    SSL *ssl = (SSL *)env->GetIntField(obj, field_ssl);
-
-    if ((ssl == NULL) && throwIfNull) {
-        throwIOExceptionStr(env, "null SSL pointer");
-    }
-
-    return ssl;
-}
-
-// ============================================================================
-// === OpenSSL-related helper stuff begins here. ==============================
-// ============================================================================
-
-/**
- * OpenSSL locking support. Taken from the O'Reilly book by Viega et al., but I
- * suppose there are not many other ways to do this on a Linux system (modulo
- * isomorphism).
- */
-#define MUTEX_TYPE pthread_mutex_t
-#define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
-#define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
-#define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
-#define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
-#define THREAD_ID pthread_self()
-#define THROW_EXCEPTION (-2)
-#define THROW_SOCKETTIMEOUTEXCEPTION (-3)
-
-static MUTEX_TYPE *mutex_buf = NULL;
-
-static void locking_function(int mode, int n, const char * file, int line) {
-    if (mode & CRYPTO_LOCK) {
-        MUTEX_LOCK(mutex_buf[n]);
-    } else {
-        MUTEX_UNLOCK(mutex_buf[n]);
-    }
-}
-
-static unsigned long id_function(void) {
-    return ((unsigned long)THREAD_ID);
-}
-
-int THREAD_setup(void) {
-    int i;
-
-    mutex_buf = (MUTEX_TYPE *)malloc(CRYPTO_num_locks( ) * sizeof(MUTEX_TYPE));
-
-    if(!mutex_buf) {
-        return 0;
-    }
-
-    for (i = 0; i < CRYPTO_num_locks( ); i++) {
-        MUTEX_SETUP(mutex_buf[i]);
-    }
-
-    CRYPTO_set_id_callback(id_function);
-    CRYPTO_set_locking_callback(locking_function);
-
-    return 1;
-}
-
-int THREAD_cleanup(void) {
-    int i;
-
-    if (!mutex_buf) {
-      return 0;
-    }
-
-    CRYPTO_set_id_callback(NULL);
-    CRYPTO_set_locking_callback(NULL);
-
-    for (i = 0; i < CRYPTO_num_locks( ); i++) {
-        MUTEX_CLEANUP(mutex_buf[i]);
-    }
-
-    free(mutex_buf);
-    mutex_buf = NULL;
-
-    return 1;
-}
-
-int get_socket_timeout(int type, int sd) {
-    struct timeval tv;
-    socklen_t len = sizeof(tv);
-    if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
-         LOGE("getsockopt(%d, SOL_SOCKET): %s (%d)",
-              sd,
-              strerror(errno),
-              errno);
-        return 0;         
-    }
-    // LOGI("Current socket timeout (%d(s), %d(us))!",
-    //      (int)tv.tv_sec, (int)tv.tv_usec);
-    int timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
-    return timeout;
-}
-
-#ifdef TIMEOUT_DEBUG_SSL
-
-void print_socket_timeout(const char* name, int type, int sd) {
-    struct timeval tv;
-    int len = sizeof(tv);
-    if (getsockopt(sd, SOL_SOCKET, type, &tv, &len) < 0) {
-         LOGE("getsockopt(%d, SOL_SOCKET, %s): %s (%d)",
-              sd,
-              name,
-              strerror(errno),
-              errno);
-    }
-    LOGI("Current socket %s is (%d(s), %d(us))!",
-          name, (int)tv.tv_sec, (int)tv.tv_usec);
-}
-
-void print_timeout(const char* method, SSL* ssl) {    
-    LOGI("SSL_get_default_timeout %d in %s", SSL_get_default_timeout(ssl), method);
-    int fd = SSL_get_fd(ssl);
-    print_socket_timeout("SO_RCVTIMEO", SO_RCVTIMEO, fd);
-    print_socket_timeout("SO_SNDTIMEO", SO_SNDTIMEO, fd);
-}
-
-#endif
-
-/**
- * Our additional application data needed for getting synchronization right.
- * This maybe warrants a bit of lengthy prose:
- * 
- * (1) We use a flag to reflect whether we consider the SSL connection alive.
- * Any read or write attempt loops will be cancelled once this flag becomes 0.
- * 
- * (2) We use an int to count the number of threads that are blocked by the
- * underlying socket. This may be at most two (one reader and one writer), since
- * the Java layer ensures that no more threads will enter the native code at the
- * same time.
- * 
- * (3) The pipe is used primarily as a means of cancelling a blocking select()
- * when we want to close the connection (aka "emergency button"). It is also
- * necessary for dealing with a possible race condition situation: There might
- * be cases where both threads see an SSL_ERROR_WANT_READ or
- * SSL_ERROR_WANT_WRITE. Both will enter a select() with the proper argument.
- * If one leaves the select() successfully before the other enters it, the
- * "success" event is already consumed and the second thread will be blocked,
- * possibly forever (depending on network conditions).
- *  
- * The idea for solving the problem looks like this: Whenever a thread is
- * successful in moving around data on the network, and it knows there is
- * another thread stuck in a select(), it will write a byte to the pipe, waking
- * up the other thread. A thread that returned from select(), on the other hand,
- * knows whether it's been woken up by the pipe. If so, it will consume the
- * byte, and the original state of affairs has been restored.
- * 
- * The pipe may seem like a bit of overhead, but it fits in nicely with the
- * other file descriptors of the select(), so there's only one condition to wait
- * for.
- * 
- * (4) Finally, a mutex is needed to make sure that at most one thread is in
- * either SSL_read() or SSL_write() at any given time. This is an OpenSSL
- * requirement. We use the same mutex to guard the field for counting the
- * waiting threads.
- * 
- * Note: The current implementation assumes that we don't have to deal with
- * problems induced by multiple cores or processors and their respective
- * memory caches. One possible problem is that of inconsistent views on the
- * "aliveAndKicking" field. This could be worked around by also enclosing all
- * accesses to that field inside a lock/unlock sequence of our mutex, but
- * currently this seems a bit like overkill.
- */
-typedef struct app_data {
-    int aliveAndKicking;
-    int waitingThreads;
-    int fdsEmergency[2];
-    MUTEX_TYPE mutex;
-} APP_DATA;
-
-/**
- * Creates our application data and attaches it to a given SSL connection.
- * 
- * @param ssl The SSL connection to attach the data to.
- * @return 0 on success, -1 on failure.
- */
-static int sslCreateAppData(SSL* ssl) {
-    APP_DATA* data = (APP_DATA*) malloc(sizeof(APP_DATA));
-
-    memset(data, 0, sizeof(APP_DATA));
-
-    data->aliveAndKicking = 1;
-    data->waitingThreads = 0;
-    data->fdsEmergency[0] = -1;
-    data->fdsEmergency[1] = -1;
-
-    if (pipe(data->fdsEmergency) == -1) {
-        return -1;
-    }
-
-    if (MUTEX_SETUP(data->mutex) == -1) {
-        return -1;
-    }
-
-    SSL_set_app_data(ssl, (char*) data);
-
-    return 0;
-}
-
-/**
- * Destroys our application data, cleaning up everything in the process.
- * 
- * @param ssl The SSL connection to take the data from.
- */ 
-static void sslDestroyAppData(SSL* ssl) {
-    APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
-
-    if (data != NULL) {
-        SSL_set_app_data(ssl, NULL);
-
-        data -> aliveAndKicking = 0;
-
-        if (data->fdsEmergency[0] != -1) {
-            close(data->fdsEmergency[0]);
-        }
-
-        if (data->fdsEmergency[1] != -1) {
-            close(data->fdsEmergency[1]);
-        }
-
-        MUTEX_CLEANUP(data->mutex);
-
-        free(data);
-    }
-}
-
-
-/**
- * Frees the SSL_CTX struct for the given instance.
- */
-static void free_ssl_ctx(JNIEnv* env, jobject object) {
-    /*
-     * Preserve and restore the exception state around this call, so
-     * that GetIntField and SetIntField will operate without complaint.
-     */
-    jthrowable exception = env->ExceptionOccurred();
-
-    if (exception != NULL) {
-        env->ExceptionClear();
-    }
-
-    SSL_CTX *ctx = (SSL_CTX *)env->GetIntField(object, field_ssl_ctx);
-
-    if (ctx != NULL) {
-        SSL_CTX_free(ctx);
-        env->SetIntField(object, field_ssl_ctx, (int) NULL);
-    }
-
-    if (exception != NULL) {
-        env->Throw(exception);
-    }
-}
-
-/**
- * Frees the SSL struct for the given instance.
- */
-static void free_ssl(JNIEnv* env, jobject object) {
-    /*
-     * Preserve and restore the exception state around this call, so
-     * that GetIntField and SetIntField will operate without complaint.
-     */
-    jthrowable exception = env->ExceptionOccurred();
-
-    if (exception != NULL) {
-        env->ExceptionClear();
-    }
-
-    SSL *ssl = (SSL *)env->GetIntField(object, field_ssl);
-
-    if (ssl != NULL) {
-        sslDestroyAppData(ssl);
-        SSL_free(ssl);
-        env->SetIntField(object, field_ssl, (int) NULL);
-    }
-
-    if (exception != NULL) {
-        env->Throw(exception);
-    }
-}
-
-/**
- * Constructs the SSL struct for the given instance, replacing one
- * that was already made, if any.
- */
-static SSL* create_ssl(JNIEnv* env, jobject object, SSL_CTX*  ssl_ctx) {
-    free_ssl(env, object);
-
-    SSL *ssl = SSL_new(ssl_ctx);
-    env->SetIntField(object, field_ssl, (int) ssl);
-    return ssl;
-}
-
-/**
- * Dark magic helper function that checks, for a given SSL session, whether it
- * can SSL_read() or SSL_write() without blocking. Takes into account any
- * concurrent attempts to close the SSL session from the Java side. This is
- * needed to get rid of the hangs that occur when thread #1 closes the SSLSocket
- * while thread #2 is sitting in a blocking read or write. The type argument
- * specifies whether we are waiting for readability or writability. It expects
- * to be passed either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, since we
- * only need to wait in case one of these problems occurs.
- * 
- * @param type Either SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
- * @param fd The file descriptor to wait for (the underlying socket)
- * @param data The application data structure with mutex info etc. 
- * @param timeout The timeout value for select call, with the special value
- *                0 meaning no timeout at all (wait indefinitely). Note: This is
- *                the Java semantics of the timeout value, not the usual
- *                select() semantics.
- * @return The result of the inner select() call, -1 on additional errors 
- */
-static int sslSelect(int type, int fd, APP_DATA *data, int timeout) {
-    fd_set rfds;
-    fd_set wfds;
-
-    FD_ZERO(&rfds);
-    FD_ZERO(&wfds);
-
-    if (type == SSL_ERROR_WANT_READ) {
-        FD_SET(fd, &rfds);
-    } else {
-        FD_SET(fd, &wfds);
-    }
-
-    FD_SET(data->fdsEmergency[0], &rfds);
-
-    int max = fd > data->fdsEmergency[0] ? fd : data->fdsEmergency[0];
-
-    // Build a struct for the timeout data if we actually want a timeout.
-    struct timeval tv;
-    struct timeval *ptv;
-    if (timeout > 0) {
-        tv.tv_sec = timeout / 1000;
-        tv.tv_usec = 0;
-        ptv = &tv;
-    } else {
-        ptv = NULL;
-    }
-    
-    // LOGD("Doing select() for SSL_ERROR_WANT_%s...", type == SSL_ERROR_WANT_READ ? "READ" : "WRITE");
-    int result = select(max + 1, &rfds, &wfds, NULL, ptv);
-    // LOGD("Returned from select(), result is %d", result);
-    
-    // Lock
-    if (MUTEX_LOCK(data->mutex) == -1) {
-        return -1;
-    }
-    
-    // If we have been woken up by the emergency pipe, there must be a token in
-    // it. Thus we can safely read it (even in a blocking way).
-    if (FD_ISSET(data->fdsEmergency[0], &rfds)) {
-        char token;
-        do {
-            read(data->fdsEmergency[0], &token, 1);
-        } while (errno == EINTR);
-    }
-
-    // Tell the world that there is now one thread less waiting for the
-    // underlying network.
-    data->waitingThreads--;
-    
-    // Unlock
-    MUTEX_UNLOCK(data->mutex);
-    // LOGD("leave sslSelect");
-    return result;
-}
-
-/**
- * Helper function that wakes up a thread blocked in select(), in case there is
- * one. Is being called by sslRead() and sslWrite() as well as by JNI glue
- * before closing the connection.
- * 
- * @param data The application data structure with mutex info etc. 
- */
-static void sslNotify(APP_DATA *data) {
-    // Write a byte to the emergency pipe, so a concurrent select() can return.
-    // Note we have to restore the errno of the original system call, since the
-    // caller relies on it for generating error messages.
-    int errnoBackup = errno;
-    char token = '*';
-    do {
-        errno = 0;
-        write(data->fdsEmergency[1], &token, 1);
-    } while (errno == EINTR);
-    errno = errnoBackup;
-}
-
-/**
- * Helper function which does the actual reading. The Java layer guarantees that
- * at most one thread will enter this function at any given time.
- * 
- * @param ssl non-null; the SSL context
- * @param buf non-null; buffer to read into
- * @param len length of the buffer, in bytes
- * @param sslReturnCode original SSL return code
- * @param sslErrorCode filled in with the SSL error code in case of error
- * @return number of bytes read on success, -1 if the connection was
- * cleanly shut down, or THROW_EXCEPTION if an exception should be thrown.
- */
-static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode,
-        int* sslErrorCode, int timeout) {
-
-    // LOGD("Entering sslRead, caller requests to read %d bytes...", len);
-    
-    if (len == 0) {
-        // Don't bother doing anything in this case.
-        return 0;
-    }
-
-    int fd = SSL_get_fd(ssl);
-    BIO *bio = SSL_get_rbio(ssl);
-    
-    APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
-
-    while (data->aliveAndKicking) {
-        errno = 0;
-
-        // Lock
-        if (MUTEX_LOCK(data->mutex) == -1) {
-            return -1;
-        }
-
-        unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
-        
-        // LOGD("Doing SSL_Read()");
-        int result = SSL_read(ssl, buf, len);
-        int error = SSL_ERROR_NONE;
-        if (result <= 0) {
-            error = SSL_get_error(ssl, result);
-            freeSslErrorState();
-        }
-        // LOGD("Returned from SSL_Read() with result %d, error code %d", result, error);
-
-        // If we have been successful in moving data around, check whether it
-        // might make sense to wake up other blocked threads, so they can give
-        // it a try, too.
-        if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) {
-            sslNotify(data);
-        }
-        
-        // If we are blocked by the underlying socket, tell the world that
-        // there will be one more waiting thread now.
-        if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
-            data->waitingThreads++;
-        }
-        
-        // Unlock
-        MUTEX_UNLOCK(data->mutex);
-
-        switch (error) {
-             // Sucessfully read at least one byte.
-            case SSL_ERROR_NONE: {
-                return result;
-            }
-
-            // Read zero bytes. End of stream reached.
-            case SSL_ERROR_ZERO_RETURN: {
-                return -1;
-            }
-
-            // Need to wait for availability of underlying layer, then retry. 
-            case SSL_ERROR_WANT_READ:
-            case SSL_ERROR_WANT_WRITE: {
-                int selectResult = sslSelect(error, fd, data, timeout);
-                if (selectResult == -1) {
-                    *sslReturnCode = -1;
-                    *sslErrorCode = error;
-                    return THROW_EXCEPTION;
-                } else if (selectResult == 0) {
-                    return THROW_SOCKETTIMEOUTEXCEPTION;
-                }
-                
-                break;
-            }
-
-            // A problem occured during a system call, but this is not
-            // necessarily an error.
-            case SSL_ERROR_SYSCALL: {
-                // Connection closed without proper shutdown. Tell caller we
-                // have reached end-of-stream.
-                if (result == 0) {
-                    return -1;
-                }
-                
-                // System call has been interrupted. Simply retry.
-                if (errno == EINTR) {
-                    break;
-                }
-                
-                // Note that for all other system call errors we fall through
-                // to the default case, which results in an Exception. 
-            }
-            
-            // Everything else is basically an error.
-            default: {
-                *sslReturnCode = result;
-                *sslErrorCode = error;
-                return THROW_EXCEPTION;
-            }
-        }
-    }
-    
-    return -1;
-}
-
-/**
- * Helper function which does the actual writing. The Java layer guarantees that
- * at most one thread will enter this function at any given time.
- * 
- * @param ssl non-null; the SSL context
- * @param buf non-null; buffer to write
- * @param len length of the buffer, in bytes
- * @param sslReturnCode original SSL return code
- * @param sslErrorCode filled in with the SSL error code in case of error
- * @return number of bytes read on success, -1 if the connection was
- * cleanly shut down, or THROW_EXCEPTION if an exception should be thrown.
- */
-static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode,
-        int* sslErrorCode) {
-  
-    // LOGD("Entering sslWrite(), caller requests to write %d bytes...", len);
-
-    if (len == 0) {
-        // Don't bother doing anything in this case.
-        return 0;
-    }
-    
-    int fd = SSL_get_fd(ssl);
-    BIO *bio = SSL_get_wbio(ssl);
-    
-    APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
-    
-    int count = len;
-    
-    while(data->aliveAndKicking && len > 0) {
-        errno = 0;
-        if (MUTEX_LOCK(data->mutex) == -1) {
-            return -1;
-        }
-        
-        unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio);
-        
-        // LOGD("Doing SSL_write() with %d bytes to go", len);
-        int result = SSL_write(ssl, buf, len);
-        int error = SSL_ERROR_NONE;
-        if (result <= 0) {
-            error = SSL_get_error(ssl, result);
-            freeSslErrorState();
-        }
-        // LOGD("Returned from SSL_write() with result %d, error code %d", result, error);
-
-        // If we have been successful in moving data around, check whether it
-        // might make sense to wake up other blocked threads, so they can give
-        // it a try, too.
-        if (BIO_number_read(bio) + BIO_number_written(bio) != bytesMoved && data->waitingThreads > 0) {
-            sslNotify(data);
-        }
-        
-        // If we are blocked by the underlying socket, tell the world that
-        // there will be one more waiting thread now.
-        if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
-            data->waitingThreads++;
-        }
-        
-        MUTEX_UNLOCK(data->mutex);
-        
-        switch (error) {
-             // Sucessfully write at least one byte.
-            case SSL_ERROR_NONE: {
-                buf += result;
-                len -= result;
-                break;
-            }
-
-            // Wrote zero bytes. End of stream reached.
-            case SSL_ERROR_ZERO_RETURN: {
-                return -1;
-            }
-                
-            // Need to wait for availability of underlying layer, then retry.
-            // The concept of a write timeout doesn't really make sense, and
-            // it's also not standard Java behavior, so we wait forever here.
-            case SSL_ERROR_WANT_READ:
-            case SSL_ERROR_WANT_WRITE: {
-                int selectResult = sslSelect(error, fd, data, 0);
-                if (selectResult == -1) {
-                    *sslReturnCode = -1;
-                    *sslErrorCode = error;
-                    return THROW_EXCEPTION;
-                } else if (selectResult == 0) {
-                    return THROW_SOCKETTIMEOUTEXCEPTION;
-                }
-                
-                break;
-            }
-
-            // An problem occured during a system call, but this is not
-            // necessarily an error.
-            case SSL_ERROR_SYSCALL: {
-                // Connection closed without proper shutdown. Tell caller we
-                // have reached end-of-stream.
-                if (result == 0) {
-                    return -1;
-                }
-                
-                // System call has been interrupted. Simply retry.
-                if (errno == EINTR) {
-                    break;
-                }
-                
-                // Note that for all other system call errors we fall through
-                // to the default case, which results in an Exception. 
-            }
-            
-            // Everything else is basically an error.
-            default: {
-                *sslReturnCode = result;
-                *sslErrorCode = error;
-                return THROW_EXCEPTION;
-            }
-        }
-    }
-    // LOGD("Successfully wrote %d bytes", count);
-    
-    return count;
-}
-
-/**
- * Helper function that creates an RSA public key from two buffers containing
- * the big-endian bit representation of the modulus and the public exponent.
- * 
- * @param mod The data of the modulus
- * @param modLen The length of the modulus data
- * @param exp The data of the exponent
- * @param expLen The length of the exponent data
- * 
- * @return A pointer to the new RSA structure, or NULL on error 
- */
-static RSA* rsaCreateKey(unsigned char* mod, int modLen, unsigned char* exp, int expLen) {
-    // LOGD("Entering rsaCreateKey()");
-
-    RSA* rsa = RSA_new();
-
-    rsa->n = BN_bin2bn((unsigned char*) mod, modLen, NULL);
-    rsa->e = BN_bin2bn((unsigned char*) exp, expLen, NULL);
-
-    if (rsa->n == NULL || rsa->e == NULL) {
-        RSA_free(rsa);
-        return NULL;
-    }
-
-    return rsa;
-}
-
-/**
- * Helper function that frees an RSA key. Just calls the corresponding OpenSSL
- * function.
- * 
- * @param rsa The pointer to the new RSA structure to free.
- */
-static void rsaFreeKey(RSA* rsa) {
-    // LOGD("Entering rsaFreeKey()");
-
-    if (rsa != NULL) {
-        RSA_free(rsa);
-    }
-}
-
-/**
- * Helper function that verifies a given RSA signature for a given message.
- * 
- * @param msg The message to verify
- * @param msgLen The length of the message
- * @param sig The signature to verify
- * @param sigLen The length of the signature
- * @param algorithm The name of the hash/sign algorithm to use, e.g. "RSA-SHA1"
- * @param rsa The RSA public key to use
- * 
- * @return 1 on success, 0 on failure, -1 on error (check SSL errors then)
- * 
- */
-static int rsaVerify(unsigned char* msg, unsigned int msgLen, unsigned char* sig,
-                     unsigned int sigLen, char* algorithm, RSA* rsa) {
-
-    // LOGD("Entering rsaVerify(%x, %d, %x, %d, %s, %x)", msg, msgLen, sig, sigLen, algorithm, rsa);
-
-    int result = -1;
-
-    EVP_PKEY* key = EVP_PKEY_new();
-    EVP_PKEY_set1_RSA(key, rsa);
-
-    const EVP_MD *type = EVP_get_digestbyname(algorithm);
-    if (type == NULL) {
-        goto cleanup;
-    }
-
-    EVP_MD_CTX ctx;
-
-    EVP_MD_CTX_init(&ctx);    
-    if (EVP_VerifyInit_ex(&ctx, type, NULL) == 0) {
-        goto cleanup;
-    }
-
-    EVP_VerifyUpdate(&ctx, msg, msgLen);
-    result = EVP_VerifyFinal(&ctx, sig, sigLen, key);
-    EVP_MD_CTX_cleanup(&ctx);
-
-    cleanup:
-
-    if (key != NULL) {
-        EVP_PKEY_free(key);
-    }
-
-    return result;
-}
-
-// ============================================================================
-// === OpenSSL-related helper stuff ends here. JNI glue follows. ==============
-// ============================================================================
-
-/**
- * Initialization phase for every OpenSSL job: Loads the Error strings, the 
- * crypto algorithms and reset the OpenSSL library
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic(JNIEnv* env, jobject obj)
-{
-    SSL_load_error_strings();
-    ERR_load_crypto_strings();
-    SSL_library_init();
-    OpenSSL_add_all_algorithms();
-    THREAD_setup();
-}
-
-/**
- * Initialization phase for a socket with OpenSSL.  The server's private key
- * and X509 certificate are read and the Linux /dev/urandom file is loaded 
- * as RNG for the session keys.
- *  
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init(JNIEnv* env, jobject object,
-        jstring privatekey, jstring certificates, jbyteArray seed)
-{   
-    SSL_CTX* ssl_ctx;
-
-    // 'seed == null' when no SecureRandom Object is set
-    // in the SSLContext.
-    if (seed != NULL) {
-        jbyte* randseed = env->GetByteArrayElements(seed, NULL);
-        RAND_seed((unsigned char*) randseed, 1024);
-        env->ReleaseByteArrayElements(seed, randseed, 0);
-    } else {
-        RAND_load_file("/dev/urandom", 1024);
-    }
-
-    ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-
-    // Note: We explicitly do not allow SSLv2 to be used. It
-    SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
-
-    /* Java code in class OpenSSLSocketImpl does the verification. Meaning of 
-     * SSL_VERIFY_NONE flag in client mode: if not using an anonymous cipher
-     * (by default disabled), the server will send a certificate which will 
-     * be checked. The result of the certificate verification process can be  
-     * checked after the TLS/SSL handshake using the SSL_get_verify_result(3) 
-     * function. The handshake will be continued regardless of the 
-     * verification result.    
-     */
-    SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
-
-    int mode = SSL_CTX_get_mode(ssl_ctx);
-    /*
-     * Turn on "partial write" mode. This means that SSL_write() will
-     * behave like Posix write() and possibly return after only
-     * writing a partial buffer. Note: The alternative, perhaps
-     * surprisingly, is not that SSL_write() always does full writes
-     * but that it will force you to retry write calls having
-     * preserved the full state of the original call. (This is icky
-     * and undesirable.)
-     */
-    mode |= SSL_MODE_ENABLE_PARTIAL_WRITE;
-#if defined(SSL_MODE_SMALL_BUFFERS) /* not all SSL versions have this */
-    mode |= SSL_MODE_SMALL_BUFFERS;  /* lazily allocate record buffers; usually saves
-                                      * 44k over the default */
-#endif
-#if defined(SSL_MODE_HANDSHAKE_CUTTHROUGH) /* not all SSL versions have this */
-    mode |= SSL_MODE_HANDSHAKE_CUTTHROUGH;  /* enable sending of client data as soon as
-                                             * ClientCCS and ClientFinished are sent */
-#endif
-
-    SSL_CTX_set_mode(ssl_ctx, mode);
-
-    if (privatekey != NULL) {
-        BIO* privatekeybio = stringToMemBuf(env, (jstring) privatekey);
-        EVP_PKEY* privatekeyevp =
-          PEM_read_bio_PrivateKey(privatekeybio, NULL, 0, NULL);
-        BIO_free(privatekeybio);
-
-        if (privatekeyevp == NULL) {
-            throwIOExceptionWithSslErrors(env, 0, 0,
-                    "Error parsing the private key");
-            SSL_CTX_free(ssl_ctx);
-            return;
-        }
-
-        BIO* certificatesbio = stringToMemBuf(env, (jstring) certificates);
-        X509* certificatesx509 =
-          PEM_read_bio_X509(certificatesbio, NULL, 0, NULL);
-        BIO_free(certificatesbio);
-
-        if (certificatesx509 == NULL) {
-            throwIOExceptionWithSslErrors(env, 0, 0,
-                    "Error parsing the certificates");
-            EVP_PKEY_free(privatekeyevp);
-            SSL_CTX_free(ssl_ctx);
-            return;
-        }
-
-        int ret = SSL_CTX_use_certificate(ssl_ctx, certificatesx509);
-        if (ret != 1) {
-            throwIOExceptionWithSslErrors(env, ret, 0,
-                    "Error setting the certificates");
-            X509_free(certificatesx509);
-            EVP_PKEY_free(privatekeyevp);
-            SSL_CTX_free(ssl_ctx);
-            return;
-        }
-
-        ret = SSL_CTX_use_PrivateKey(ssl_ctx, privatekeyevp);
-        if (ret != 1) {
-            throwIOExceptionWithSslErrors(env, ret, 0,
-                    "Error setting the private key");
-            X509_free(certificatesx509);
-            EVP_PKEY_free(privatekeyevp);
-            SSL_CTX_free(ssl_ctx);
-            return;
-        }
-
-        ret = SSL_CTX_check_private_key(ssl_ctx);
-        if (ret != 1) {
-            throwIOExceptionWithSslErrors(env, ret, 0,
-                    "Error checking the private key");
-            X509_free(certificatesx509);
-            EVP_PKEY_free(privatekeyevp);
-            SSL_CTX_free(ssl_ctx);
-            return;
-        }
-    }
-
-    env->SetIntField(object, field_ssl_ctx, (int)ssl_ctx);
-}
-
-/**
- * A connection within an OpenSSL context is established. (1) A new socket is
- * constructed, (2) the TLS/SSL handshake with a server is initiated. 
- */
-static jboolean org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect(JNIEnv* env, jobject object,
-        jint ctx, jobject socketObject, jboolean client_mode, jint session)
-{
-    // LOGD("ENTER connect");
-    int ret, fd;
-    SSL_CTX* ssl_ctx;
-    SSL* ssl;
-    SSL_SESSION* ssl_session;
-
-    ssl_ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx);
-
-    ssl = create_ssl(env, object, ssl_ctx);
-    if (ssl == NULL) {
-        throwIOExceptionWithSslErrors(env, 0, 0,
-                "Unable to create SSL structure");
-        free_ssl_ctx(env, object);
-        return (jboolean) false;
-    }
-
-    jobject socketImplObject = env->GetObjectField(socketObject, field_mImpl);
-    if (socketImplObject == NULL) {
-        free_ssl(env, object);
-        free_ssl_ctx(env, object);
-        throwIOExceptionStr(env,
-            "couldn't get the socket impl from the socket");
-        return (jboolean) false;
-    }
-
-    jobject fdObject = env->GetObjectField(socketImplObject, field_mFD);
-    if (fdObject == NULL) {
-        free_ssl(env, object);
-        free_ssl_ctx(env, object);
-        throwIOExceptionStr(env,
-            "couldn't get the file descriptor from the socket impl");
-        return (jboolean) false;
-    }
-
-    fd = jniGetFDFromFileDescriptor(env, fdObject);
-
-    ssl_session = (SSL_SESSION *) session;
-
-    ret = SSL_set_fd(ssl, fd);
-
-    if (ret != 1) {
-        throwIOExceptionWithSslErrors(env, ret, 0,
-                "Error setting the file descriptor");
-        free_ssl(env, object);
-        free_ssl_ctx(env, object);
-        return (jboolean) false;
-    }
-
-    if (ssl_session != NULL) {
-        ret = SSL_set_session(ssl, ssl_session);
-
-        if (ret != 1) {
-            /*
-             * Translate the error, and throw if it turns out to be a real
-             * problem.
-             */
-            int sslErrorCode = SSL_get_error(ssl, ret);
-            if (sslErrorCode != SSL_ERROR_ZERO_RETURN) {
-                throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
-                        "SSL session set");
-                free_ssl(env, object);
-                free_ssl_ctx(env, object);
-                return (jboolean) false;
-            }
-        }
-    }
-
-    /*
-     * Make socket non-blocking, so SSL_connect SSL_read() and SSL_write() don't hang
-     * forever and we can use select() to find out if the socket is ready.
-     */
-    int mode = fcntl(fd, F_GETFL);
-    if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) {
-        throwIOExceptionStr(env, "Unable to make socket non blocking");
-        free_ssl(env, object);
-        free_ssl_ctx(env, object);
-        return (jboolean) false;
-    }
-
-    /*
-     * Create our special application data.
-     */
-    if (sslCreateAppData(ssl) == -1) {
-        throwIOExceptionStr(env, "Unable to create application data");
-        free_ssl(env, object);
-        free_ssl_ctx(env, object);
-        // TODO
-        return (jboolean) false;
-    }
-    
-    APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
-    env->SetIntField(object, field_ssl, (int)ssl);
-    
-    int timeout = (int)env->GetIntField(object, field_timeout);
-    
-    while (data->aliveAndKicking) {
-        errno = 0;        
-        ret = SSL_connect(ssl);
-        if (ret == 1) {
-            break;
-        } else if (errno == EINTR) {
-            continue;
-        } else {
-            // LOGD("SSL_connect: result %d, errno %d, timeout %d", ret, errno, timeout);
-            int error = SSL_get_error(ssl, ret);
-
-            /*
-             * If SSL_connect doesn't succeed due to the socket being
-             * either unreadable or unwritable, we use sslSelect to
-             * wait for it to become ready. If that doesn't happen
-             * before the specified timeout or an error occurs, we
-             * cancel the handshake. Otherwise we try the SSL_connect
-             * again.
-             */
-            if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
-                data->waitingThreads++;
-                int selectResult = sslSelect(error, fd, data, timeout);
-                
-                if (selectResult == -1) {
-                    throwIOExceptionWithSslErrors(env, -1, error,
-                        "Connect error");
-                    free_ssl(env, object);
-                    free_ssl_ctx(env, object);
-                    return (jboolean) false;
-                } else if (selectResult == 0) {
-                    throwSocketTimeoutException(env, "SSL handshake timed out");
-                    freeSslErrorState();
-                    free_ssl(env, object);
-                    free_ssl_ctx(env, object);
-                    return (jboolean) false;
-                }
-            } else {
-                LOGE("Unknown error %d during connect", error);
-                break;
-            }
-        }        
-    } 
-
-    if (ret != 1) {
-        /*
-         * Translate the error, and throw if it turns out to be a real
-         * problem.
-         */
-        int sslErrorCode = SSL_get_error(ssl, ret);
-        if (sslErrorCode != SSL_ERROR_ZERO_RETURN) {
-            throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
-                    "SSL handshake failure");
-            free_ssl(env, object);
-            free_ssl_ctx(env, object);
-            return (jboolean) false;
-        }
-    }
-
-    if (ssl_session != NULL) {
-        ret = SSL_session_reused(ssl);
-        // if (ret == 1) LOGD("A session was reused");
-        // else LOGD("A new session was negotiated");
-        return (jboolean) ret;
-    } else {
-        // LOGD("A new session was negotiated");
-        return (jboolean) 0;
-    }
-    // LOGD("LEAVE connect");
-}
-
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession(JNIEnv* env, jobject object,
-        jint jssl)
-{
-    return (jint) SSL_get1_session((SSL *) jssl);
-}
-
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept(JNIEnv* env, jobject object,
-        jobject socketObject, jint jssl_ctx, jboolean client_mode)
-{
-    int sd, ret;
-    BIO *bio;
-    SSL *ssl;
-    SSL_CTX *ssl_ctx;
-    mydata_t mydata;
-
-    ssl_ctx = (SSL_CTX *)jssl_ctx;
-
-    ssl = create_ssl(env, object, ssl_ctx);
-    if (ssl == NULL) {
-        throwIOExceptionWithSslErrors(env, 0, 0,
-                "Unable to create SSL structure");
-        return;
-    }
-
-    jobject socketImplObject = env->GetObjectField(socketObject, field_mImpl);
-    if (socketImplObject == NULL) {
-        free_ssl(env, object);
-        throwIOExceptionStr(env, "couldn't get the socket impl from the socket");
-        return;
-    }
-
-    jobject fdObject = env->GetObjectField(socketImplObject, field_mFD);
-    if (fdObject == NULL) {
-        free_ssl(env, object);
-        throwIOExceptionStr(env, "couldn't get the file descriptor from the socket impl");
-        return;
-    }
-
-
-    sd = jniGetFDFromFileDescriptor(env, fdObject);
-
-    bio = BIO_new_socket(sd, BIO_NOCLOSE);
-
-    /* The parameter client_mode must be 1 */
-    if (client_mode != 0)
-        client_mode = 1;
-    BIO_set_ssl_mode(bio, client_mode);
-
-    SSL_set_bio(ssl, bio, bio);
-
-    /*
-     * Fill in the mydata structure needed for the certificate callback and
-     * store this in the SSL application data slot.
-     */
-    mydata.env = env;
-    mydata.object = object;
-    SSL_set_app_data(ssl, &mydata);
-
-    /*
-     * Do the actual SSL_accept(). It is possible this code is insufficient.
-     * Maybe we need to deal with all the special SSL error cases (WANT_*),
-     * just like we do for SSL_connect(). But currently it is looking ok.
-     */
-    ret = SSL_accept(ssl);
-
-    /*
-     * Clear the SSL application data slot again, so we can safely use it for
-     * our ordinary synchronization structure afterwards. Also, we don't want
-     * sslDestroyAppData() to think that there is something that needs to be
-     * freed right now (in case of an error).
-     */
-    SSL_set_app_data(ssl, NULL);
-
-    if (ret == 0) {
-        /*
-         * The other side closed the socket before the handshake could be
-         * completed, but everything is within the bounds of the TLS protocol.
-         * We still might want to find out the real reason of the failure.
-         */
-        int sslErrorCode = SSL_get_error(ssl, ret);
-        if (sslErrorCode == SSL_ERROR_NONE ||
-                sslErrorCode == SSL_ERROR_SYSCALL && errno == 0) {
-          throwIOExceptionStr(env, "Connection closed by peer");
-        } else {
-          throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
-              "Trouble accepting connection");
-    	}
-        free_ssl(env, object);
-        return;
-    } else if (ret < 0) {
-        /*
-         * Translate the error and throw exception. We are sure it is an error
-         * at this point.
-         */
-        int sslErrorCode = SSL_get_error(ssl, ret);
-        throwIOExceptionWithSslErrors(env, ret, sslErrorCode,
-                "Trouble accepting connection");
-        free_ssl(env, object);
-        return;
-    }
-
-    /*
-     * Make socket non-blocking, so SSL_read() and SSL_write() don't hang
-     * forever and we can use select() to find out if the socket is ready.
-     */
-    int fd = SSL_get_fd(ssl);
-    int mode = fcntl(fd, F_GETFL);
-    if (mode == -1 || fcntl(fd, F_SETFL, mode | O_NONBLOCK) == -1) {
-        throwIOExceptionStr(env, "Unable to make socket non blocking");
-        free_ssl(env, object);
-        return;
-    }
-
-    /*
-     * Create our special application data.
-     */
-    if (sslCreateAppData(ssl) == -1) {
-        throwIOExceptionStr(env, "Unable to create application data");
-        free_ssl(env, object);
-        return;
-    }
-}
-
-/**
- * Loads the desired protocol for the OpenSSL client and enables it.  
- * For example SSL_OP_NO_TLSv1 means do not use TLS v. 1.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols(JNIEnv* env, jobject object,
-        jlong protocol)
-{
-    if (protocol != 0x00000000L) {
-        if (protocol & SSL_OP_NO_SSLv3)
-            LOGD("SSL_OP_NO_SSLv3 is set");
-        if (protocol & SSL_OP_NO_TLSv1)
-            LOGD("SSL_OP_NO_TLSv1 is set");
-
-        SSL_CTX* ctx = (SSL_CTX*)env->GetIntField(object, field_ssl_ctx);
-        int options = SSL_CTX_get_options(ctx);
-        options |= protocol; // Note: SSLv2 disabled earlier.
-        SSL_CTX_set_options(ctx, options);
-    }
-}
-
-/**
- * Loads the ciphers suites that are supported by the OpenSSL client
- * and returns them in a string array.
- */
-static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites(JNIEnv* env,
-        jobject object)
-{
-    SSL_CTX* ssl_ctx = SSL_CTX_new(SSLv23_client_method());
-    if (ssl_ctx == NULL) {
-        return NULL;
-    }
-    jobjectArray result = makeCipherList(env, ssl_ctx);
-    SSL_CTX_free(ssl_ctx);
-    return result;
-}
-
-/**
- * Loads the ciphers suites that are enabled in the OpenSSL client
- * and returns them in a string array.
- */
-static jobjectArray OpenSSLSocketImpl_nativeGetEnabledCipherSuites(JNIEnv* env,
-        jclass, jint ssl_ctx_address)
-{
-    SSL_CTX* ssl_ctx =
-            reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
-    return makeCipherList(env, ssl_ctx);
-}
-
-/**
- * Sets the ciphers suites that are enabled in the OpenSSL client.
- */
-static void OpenSSLSocketImpl_nativeSetEnabledCipherSuites(JNIEnv* env, jclass,
-        jint ssl_ctx_address, jstring controlString)
-{
-    SSL_CTX* ssl_ctx =
-            reinterpret_cast<SSL_CTX*>(static_cast<uintptr_t>(ssl_ctx_address));
-    setEnabledCipherSuites(env, controlString, ssl_ctx);
-}
-
-static jobjectArray makeCipherList(JNIEnv* env, SSL* ssl) {
-    // Count the ciphers.
-    int cipherCount = 0;
-    while (SSL_get_cipher_list(ssl, cipherCount) != NULL) {
-        ++cipherCount;
-    }
-
-    // Create a String[].
-    jclass stringClass = env->FindClass("java/lang/String");
-    if (stringClass == NULL) {
-        return NULL;
-    }
-    jobjectArray array = env->NewObjectArray(cipherCount, stringClass, NULL);
-    if (array == NULL) {
-        return NULL;
-    }
-
-    // Fill in the cipher names.
-    for (int i = 0; i < cipherCount; ++i) {
-        const char* c = SSL_get_cipher_list(ssl, i);
-        env->SetObjectArrayElement(array, i, env->NewStringUTF(c));
-    }
-    return array;
-}
-
-jobjectArray makeCipherList(JNIEnv* env, SSL_CTX* ssl_ctx) {
-    SSL* ssl = SSL_new(ssl_ctx);
-    if (ssl == NULL) {
-        return NULL;
-    }
-    jobjectArray result = makeCipherList(env, ssl);
-    SSL_free(ssl);
-    return result;
-}
-
-void setEnabledCipherSuites(JNIEnv* env, jstring controlString, SSL_CTX* ssl_ctx) {
-    const char* str = env->GetStringUTFChars(controlString, NULL);
-    int rc = SSL_CTX_set_cipher_list(ssl_ctx, str);
-    env->ReleaseStringUTFChars(controlString, str);
-    if (rc == 0) {
-        freeSslErrorState();
-        jniThrowException(env, "java/lang/IllegalArgumentException",
-                          "Illegal cipher suite strings.");
-    }
-}
-
-#define SSL_AUTH_MASK           0x00007F00L
-#define SSL_aRSA                0x00000100L /* Authenticate with RSA */
-#define SSL_aDSS                0x00000200L /* Authenticate with DSS */
-#define SSL_DSS                 SSL_aDSS
-#define SSL_aFZA                0x00000400L
-#define SSL_aNULL               0x00000800L /* no Authenticate, ADH */
-#define SSL_aDH                 0x00001000L /* no Authenticate, ADH */
-#define SSL_aKRB5               0x00002000L /* Authenticate with KRB5 */
-#define SSL_aECDSA              0x00004000L /* Authenticate with ECDSA */
-
-/**
- * Sets  the client's crypto algorithms and authentication methods.
- */
-static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod(JNIEnv* env,
-        jobject object)
-{
-    SSL* ssl;
-    SSL_CIPHER *cipher;
-    jstring ret;
-    char buf[512];
-    unsigned long alg;
-    const char *au;
-
-    ssl = getSslPointer(env, object, true);
-    if (ssl == NULL) {
-        return NULL;
-    }
-
-    cipher = SSL_get_current_cipher(ssl);
-
-    alg = cipher->algorithms;
-
-    switch (alg&SSL_AUTH_MASK) {
-        case SSL_aRSA:
-            au="RSA";
-            break;
-        case SSL_aDSS:
-            au="DSS";
-            break;
-        case SSL_aDH:
-            au="DH";
-            break;
-        case SSL_aFZA:
-            au = "FZA";
-            break;
-        case SSL_aNULL:
-            au="None";
-            break;
-        case SSL_aECDSA:
-            au="ECDSA";
-            break;
-        default:
-            au="unknown";
-            break;
-    }
-
-    ret = env->NewStringUTF(au);
-
-    return ret;
-}
-
-/**
- * OpenSSL read function (1): only one chunk is read (returned as jint).
- */
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read(JNIEnv* env, jobject object, jint timeout)
-{
-    SSL *ssl = getSslPointer(env, object, true);
-    if (ssl == NULL) {
-        return 0;
-    }
-
-    unsigned char byteRead;
-    int returnCode = 0;
-    int errorCode = 0;
-
-    int ret = sslRead(ssl, (char *) &byteRead, 1, &returnCode, &errorCode, timeout);
-
-    switch (ret) {
-        case THROW_EXCEPTION:
-            // See sslRead() regarding improper failure to handle normal cases.
-            throwIOExceptionWithSslErrors(env, returnCode, errorCode,
-                    "Read error");
-            return -1;
-        case THROW_SOCKETTIMEOUTEXCEPTION:
-            throwSocketTimeoutException(env, "Read timed out");
-            return -1;
-        case -1:
-            // Propagate EOF upwards.
-            return -1;
-        default:
-            // Return the actual char read, make sure it stays 8 bits wide.
-            return ((jint) byteRead) & 0xFF;
-    }
-}
-
-/**
- * OpenSSL read function (2): read into buffer at offset n chunks. 
- * Returns 1 (success) or value <= 0 (failure).
- */
-static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba(JNIEnv* env, jobject obj, jbyteArray dest, jint offset, jint len, jint timeout)
-{
-    SSL *ssl = getSslPointer(env, obj, true);
-    if (ssl == NULL) {
-        return 0;
-    }
-
-    jbyte* bytes = env->GetByteArrayElements(dest, NULL);
-    int returnCode = 0;
-    int errorCode = 0;
-
-    int ret =
-        sslRead(ssl, (char*) (bytes + offset), len, &returnCode, &errorCode, timeout);
-
-    env->ReleaseByteArrayElements(dest, bytes, 0);
-
-    if (ret == THROW_EXCEPTION) {
-        // See sslRead() regarding improper failure to handle normal cases.
-        throwIOExceptionWithSslErrors(env, returnCode, errorCode,
-                "Read error");
-        return -1;
-    } else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
-        throwSocketTimeoutException(env, "Read timed out");
-        return -1;
-    }
-
-    return ret;
-}
-
-/**
- * OpenSSL write function (1): only one chunk is written.
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write(JNIEnv* env, jobject object, jint b)
-{
-    SSL *ssl = getSslPointer(env, object, true);
-    if (ssl == NULL) {
-        return;
-    }
-
-    int returnCode = 0;
-    int errorCode = 0;
-    char buf[1] = { (char) b };
-    int ret = sslWrite(ssl, buf, 1, &returnCode, &errorCode);
-
-    if (ret == THROW_EXCEPTION) {
-        // See sslWrite() regarding improper failure to handle normal cases.
-        throwIOExceptionWithSslErrors(env, returnCode, errorCode,
-                "Write error");
-    } else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
-        throwSocketTimeoutException(env, "Write timed out");
-    }
-}
-
-/**
- * OpenSSL write function (2): write into buffer at offset n chunks. 
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba(JNIEnv* env, jobject obj,
-        jbyteArray dest, jint offset, jint len)
-{
-    SSL *ssl = getSslPointer(env, obj, true);
-    if (ssl == NULL) {
-        return;
-    }
-
-    jbyte* bytes = env->GetByteArrayElements(dest, NULL);
-    int returnCode = 0;
-    int errorCode = 0;
-    int timeout = (int)env->GetIntField(obj, field_timeout);
-    int ret = sslWrite(ssl, (const char *) (bytes + offset), len, 
-            &returnCode, &errorCode);
-
-    env->ReleaseByteArrayElements(dest, bytes, 0);
-
-    if (ret == THROW_EXCEPTION) {
-        // See sslWrite() regarding improper failure to handle normal cases.
-        throwIOExceptionWithSslErrors(env, returnCode, errorCode,
-                "Write error");
-    } else if(ret == THROW_SOCKETTIMEOUTEXCEPTION) {
-        throwSocketTimeoutException(env, "Write timed out");
-    }
-}
-
-/**
- * Interrupt any pending IO before closing the socket. 
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt(
-        JNIEnv* env, jobject object) {
-    SSL *ssl = getSslPointer(env, object, false);
-    if (ssl == NULL) {
-        return;
-    }
-
-    /*
-     * Mark the connection as quasi-dead, then send something to the emergency
-     * file descriptor, so any blocking select() calls are woken up.
-     */
-    APP_DATA* data = (APP_DATA*) SSL_get_app_data(ssl);
-    if (data != NULL) {
-        data->aliveAndKicking = 0;
-
-        // At most two threads can be waiting.
-        sslNotify(data);
-        sslNotify(data);
-    }
-}
-
-/**
- * OpenSSL close SSL socket function. 
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close(
-        JNIEnv* env, jobject object) {
-    SSL *ssl = getSslPointer(env, object, false);
-    if (ssl == NULL) {
-        return;
-    }
-
-    /*
-     * Try to make socket blocking again. OpenSSL literature recommends this.
-     */
-    int fd = SSL_get_fd(ssl);
-    if (fd != -1) {
-        int mode = fcntl(fd, F_GETFL);
-        if (mode == -1 || fcntl(fd, F_SETFL, mode & ~O_NONBLOCK) == -1) {
-//            throwIOExceptionStr(env, "Unable to make socket blocking again");
-//            LOGW("Unable to make socket blocking again");
-        }
-    }
-
-    int ret = SSL_shutdown(ssl);
-    switch (ret) {
-        case 0:
-            /*
-             * Shutdown was not successful (yet), but there also
-             * is no error. Since we can't know whether the remote
-             * server is actually still there, and we don't want to
-             * get stuck forever in a second SSL_shutdown() call, we
-             * simply return. This is not security a problem as long
-             * as we close the underlying socket, which we actually
-             * do, because that's where we are just coming from.
-             */
-            break;
-        case 1:
-            /*
-             * Shutdown was sucessful. We can safely return. Hooray!
-             */
-            break;
-        default:
-            /*
-             * Everything else is a real error condition. We should
-             * let the Java layer know about this by throwing an
-             * exception.
-             */ 
-            throwIOExceptionWithSslErrors(env, ret, 0, "SSL shutdown failed.");
-            break;
-    }
-
-    freeSslErrorState();
-    free_ssl(env, object);
-    free_ssl_ctx(env, object);
-}    
-
-/**
- * OpenSSL free SSL socket function. 
- */
-static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free(JNIEnv* env, jobject object)
-{
-    free_ssl(env, object);
-    free_ssl_ctx(env, object);
-}
-
-/**
- * Verifies an RSA signature.
- */
-static int org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignature(JNIEnv* env, jclass clazz,
-        jbyteArray msg, jbyteArray sig, jstring algorithm, jbyteArray mod, jbyteArray exp) {
-
-    // LOGD("Entering verifysignature()");
-
-    if (msg == NULL || sig == NULL || algorithm == NULL || mod == NULL || exp == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return -1;
-    }
-
-    int result = -1;
-
-    jbyte* msgBytes = env->GetByteArrayElements(msg, NULL);
-    jint msgLength = env->GetArrayLength(msg);
-
-    jbyte* sigBytes = env->GetByteArrayElements(sig, NULL);
-    jint sigLength = env->GetArrayLength(sig);
-
-    jbyte* modBytes = env->GetByteArrayElements(mod, NULL);
-    jint modLength = env->GetArrayLength(mod);
-
-    jbyte* expBytes = env->GetByteArrayElements(exp, NULL);
-    jint expLength = env->GetArrayLength(exp);
-
-    const char* algorithmChars = env->GetStringUTFChars(algorithm, NULL);
-
-    RSA* rsa = rsaCreateKey((unsigned char*) modBytes, modLength, (unsigned char*) expBytes, expLength);
-    if (rsa != NULL) {
-        result = rsaVerify((unsigned char*) msgBytes, msgLength, (unsigned char*) sigBytes, sigLength,
-                (char*) algorithmChars, rsa);
-        rsaFreeKey(rsa);
-    }
-
-    env->ReleaseStringUTFChars(algorithm, algorithmChars);
-
-    env->ReleaseByteArrayElements(exp, expBytes, JNI_ABORT);
-    env->ReleaseByteArrayElements(mod, modBytes, JNI_ABORT);
-    env->ReleaseByteArrayElements(sig, sigBytes, JNI_ABORT);
-    env->ReleaseByteArrayElements(msg, msgBytes, JNI_ABORT);
-
-    if (result == -1) {
-        int error = ERR_get_error();
-        if (error != 0) {
-            char message[50];
-            ERR_error_string_n(error, message, sizeof(message));
-            jniThrowRuntimeException(env, message);
-        } else {
-            jniThrowRuntimeException(env, "Internal error during verification");
-        }
-        freeSslErrorState();
-    }
-
-    return result;
-}
-
-/**
- * The actual JNI methods' mapping table for the class OpenSSLSocketImpl.
- */
-static JNINativeMethod sMethods[] =
-{
-    {"nativeinitstatic", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_initstatic},
-    {"nativeinit", "(Ljava/lang/String;Ljava/lang/String;[B)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init},
-    {"nativeconnect", "(ILjava/net/Socket;ZI)Z", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_connect},
-    {"nativegetsslsession", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsslsession},
-    {"nativeread", "(I)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_read},
-    {"nativeread", "([BIII)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_readba},
-    {"nativewrite", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_write},
-    {"nativewrite", "([BII)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_writeba},
-    {"nativeaccept", "(Ljava/net/Socket;IZ)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_accept},
-    {"nativesetenabledprotocols", "(J)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledprotocols},
-    {"nativegetsupportedciphersuites", "()[Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_getsupportedciphersuites},
-    {"nativeGetEnabledCipherSuites", "(I)[Ljava/lang/String;", (void*) OpenSSLSocketImpl_nativeGetEnabledCipherSuites},
-    {"nativeSetEnabledCipherSuites", "(ILjava/lang/String;)V", (void*) OpenSSLSocketImpl_nativeSetEnabledCipherSuites},
-    {"nativecipherauthenticationmethod", "()Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_cipherauthenticationmethod},
-    {"nativeinterrupt", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_interrupt},
-    {"nativeclose", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_close},
-    {"nativefree", "()V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_free},
-    {"nativeverifysignature", "([B[BLjava/lang/String;[B[B)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_verifysignature},
-};
-
-/**
- * Register the native methods with JNI for the class OpenSSLSocketImpl.
- */
-extern "C" int register_org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl(JNIEnv* env)
-{
-    jclass clazz = env->FindClass("org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl");
-    if (clazz == NULL) {
-        LOGE("Can't find org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl");
-        return -1;
-    }
-
-    jclass socketClass = env->FindClass("java/net/Socket");
-    if (socketClass == NULL) {
-        LOGE("Can't find class java.net.Socket");
-        return -1;
-    }
-
-    field_mImpl = env->GetFieldID(socketClass, "impl", "Ljava/net/SocketImpl;");
-    if (field_mImpl == NULL) {
-        LOGE("Can't find field impl in class java.net.Socket");
-        return -1;
-    }
-
-    jclass socketImplClass = env->FindClass("java/net/SocketImpl");
-    if (socketImplClass == NULL) {
-        LOGE("Can't find class java.net.SocketImpl");
-        return -1;
-    }
-
-    field_mFD = env->GetFieldID(socketImplClass, "fd", "Ljava/io/FileDescriptor;");
-    if (field_mFD == NULL) {
-        LOGE("Can't find field fd in java.net.SocketImpl");
-        return -1;
-    }
-
-    jclass fdclazz = env->FindClass("java/io/FileDescriptor");
-    if (fdclazz == NULL) {
-        LOGE("Can't find java/io/FileDescriptor");
-        return -1;
-    }
-
-    field_descriptor = env->GetFieldID(fdclazz, "descriptor", "I");
-    if (field_descriptor == NULL) {
-        LOGE("Can't find FileDescriptor.descriptor");
-        return -1;
-    }
-
-    int rc = jniRegisterNativeMethods(env, "org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl",
-            sMethods, NELEM(sMethods));
-    if (rc >= 0) {
-        // Note: do these after the registration of native methods, because 
-        // there is a static method "initstatic" that's called when the
-        // OpenSSLSocketImpl class is first loaded, and that required
-        // a native method to be associated with it.
-        field_ssl_ctx = env->GetFieldID(clazz, "ssl_ctx", "I");
-        if (field_ssl_ctx == NULL) {
-            LOGE("Can't find OpenSSLSocketImpl.ssl_ctx");
-            return -1;
-        }
-
-        field_ssl = env->GetFieldID(clazz, "ssl", "I");
-        if (field_ssl == NULL) {
-            LOGE("Can't find OpenSSLSocketImpl.ssl");
-            return -1;
-        }
-
-        field_timeout = env->GetFieldID(clazz, "timeout", "I");
-        if (field_timeout == NULL) {
-            LOGE("Can't find OpenSSLSocketImpl.timeout");
-            return -1;
-        }
-    }
-    return rc;
-}
diff --git a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h
deleted file mode 100644
index e78cdd8..0000000
--- a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef org_apache_harmony_xnet_provider_jsse_common_h
-#define org_apache_harmony_xnet_provider_jsse_common_h
-
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#include <openssl/ssl.h>
-
-#include <stdio.h>
-
-/**
- * Structure to hold together useful JNI variables.
- */
-struct mydata_t {
-    JNIEnv* env;
-    jobject object;
-};
-
-/**
- * Gives an array back containing all the X509 certificate's bytes.
- */
-static jobjectArray getcertificatebytes(JNIEnv* env,
-        const STACK_OF(X509) *chain)
-{
-    BUF_MEM *bptr;
-    int count, i;
-    jbyteArray bytes;
-    jobjectArray joa;
-
-    if (chain == NULL) {
-        // Chain can be NULL if the associated cipher doesn't do certs.
-        return NULL;
-    }
-    
-    count = sk_X509_num(chain);
-
-    if (count > 0) {
-        joa = env->NewObjectArray(count, env->FindClass("[B"), NULL);
-
-        if (joa == NULL) {
-            return NULL;
-        }
-
-        BIO *bio = BIO_new(BIO_s_mem());
-
-        // LOGD("Start fetching the certificates");
-        for (i = 0; i < count; i++) {
-            X509 *cert = sk_X509_value(chain, i);
-
-            BIO_reset(bio);
-            PEM_write_bio_X509(bio, cert);
-
-            BIO_get_mem_ptr(bio, &bptr);
-            bytes = env->NewByteArray(bptr->length);
-
-            if (bytes == NULL) {
-                /*
-                 * Indicate an error by resetting joa to NULL. It will
-                 * eventually get gc'ed.
-                 */
-                joa = NULL;
-                break;
-            } else {
-                jbyte* src = reinterpret_cast<jbyte*>(bptr->data);
-                env->SetByteArrayRegion(bytes, 0, bptr->length, src);
-                env->SetObjectArrayElement(joa, i, bytes);
-            }
-        }
-
-        // LOGD("Certificate fetching complete");
-        BIO_free(bio);
-        return joa;
-    } else {
-        return NULL;
-    }
-}
-
-/**
- * Verify the X509 certificate.
- */
-static int verify_callback(int preverify_ok, X509_STORE_CTX *x509_store_ctx)
-{
-    SSL *ssl;
-    mydata_t *mydata;
-    jclass cls;
-
-    jobjectArray objectArray;
-
-    /* Get the correct index to the SSLobject stored into X509_STORE_CTX. */
-    ssl = (SSL*)X509_STORE_CTX_get_ex_data(x509_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
-
-    mydata = (mydata_t*)SSL_get_app_data(ssl);
-
-    cls = mydata->env->GetObjectClass(mydata->object);
-
-    jmethodID methodID = mydata->env->GetMethodID(cls, "verify_callback", "([[B)I");
-
-    objectArray = getcertificatebytes(mydata->env, x509_store_ctx->untrusted);
-
-    mydata->env->CallIntMethod(mydata->object, methodID, objectArray);
-
-    return 1;
-}
-
-extern jobjectArray makeCipherList(JNIEnv* env, SSL_CTX* ssl);
-extern void setEnabledCipherSuites(JNIEnv* env, jstring controlString, SSL_CTX* ssl_ctx);
-
-#endif
diff --git a/libcore/x-net/src/main/native/sub.mk b/libcore/x-net/src/main/native/sub.mk
index 4aeb41e..f5aa84a 100644
--- a/libcore/x-net/src/main/native/sub.mk
+++ b/libcore/x-net/src/main/native/sub.mk
@@ -3,10 +3,7 @@
 # or BUILD_*_LIBRARY.
 
 LOCAL_SRC_FILES := \
-	org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp \
-	org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp \
-	org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp \
-	org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
+	org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp
 
 LOCAL_C_INCLUDES += \
 	external/openssl/include
@@ -19,6 +16,6 @@
 LOCAL_SHARED_LIBRARIES += \
 	libcrypto \
 	libssl \
-    libutils
-    
+	libutils
+
 LOCAL_STATIC_LIBRARIES +=
diff --git a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java
index 1283eeb..991a707 100644
--- a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java
+++ b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java
@@ -41,7 +41,7 @@
 
     public Document createDocument(String namespaceURI, String qualifiedName,
             DocumentType doctype) throws DOMException {
-        return new DocumentImpl(this, namespaceURI, qualifiedName, doctype);
+        return new DocumentImpl(this, namespaceURI, qualifiedName, doctype, null);
     }
 
     public DocumentType createDocumentType(String qualifiedName,
diff --git a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java
index b2f16d1..e297280 100644
--- a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java
+++ b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java
@@ -48,11 +48,23 @@
 
     private DOMImplementation domImplementation;
 
+    /*
+     * The default values of these fields are specified by the Document
+     * interface.
+     */
+    private String documentUri;
+    private String inputEncoding;
+    private String xmlEncoding;
+    private String xmlVersion = "1.0";
+    private boolean xmlStandalone = false;
+    private boolean strictErrorChecking = true;
+
     public DocumentImpl(DOMImplementationImpl impl, String namespaceURI,
-            String qualifiedName, DocumentType doctype) {
+            String qualifiedName, DocumentType doctype, String inputEncoding) {
         super(null);
 
         this.domImplementation = impl;
+        this.inputEncoding = inputEncoding;
         // this.document = this;
         
         if (doctype != null) {
@@ -304,43 +316,43 @@
     }
 
     public String getInputEncoding() {
-        throw new UnsupportedOperationException(); // TODO
+        return inputEncoding;
     }
 
     public String getXmlEncoding() {
-        throw new UnsupportedOperationException(); // TODO
+        return xmlEncoding;
     }
 
     public boolean getXmlStandalone() {
-        throw new UnsupportedOperationException(); // TODO
+        return xmlStandalone;
     }
 
     public void setXmlStandalone(boolean xmlStandalone) throws DOMException {
-        throw new UnsupportedOperationException(); // TODO
+        this.xmlStandalone = xmlStandalone;
     }
 
     public String getXmlVersion() {
-        throw new UnsupportedOperationException(); // TODO
+        return xmlVersion;
     }
 
     public void setXmlVersion(String xmlVersion) throws DOMException {
-        throw new UnsupportedOperationException(); // TODO
+        this.xmlVersion = xmlVersion;
     }
 
     public boolean getStrictErrorChecking() {
-        throw new UnsupportedOperationException(); // TODO
+        return strictErrorChecking;
     }
 
     public void setStrictErrorChecking(boolean strictErrorChecking) {
-        throw new UnsupportedOperationException(); // TODO
+        this.strictErrorChecking = strictErrorChecking;
     }
 
     public String getDocumentURI() {
-        throw new UnsupportedOperationException(); // TODO
+        return documentUri;
     }
 
-    public void setDocumentURI(String documentURI) {
-        throw new UnsupportedOperationException(); // TODO
+    public void setDocumentURI(String documentUri) {
+        this.documentUri = documentUri;
     }
 
     public Node adoptNode(Node source) throws DOMException {
diff --git a/libcore/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java b/libcore/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
index ca2ff98..4b273fe 100644
--- a/libcore/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
+++ b/libcore/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
@@ -100,25 +100,27 @@
         String namespaceURI = null;
         String qualifiedName = null;
         DocumentType doctype = null;
-        DocumentImpl document = new DocumentImpl(dom, namespaceURI, qualifiedName, doctype);
+        String inputEncoding = source.getEncoding();
+        String systemId = source.getSystemId();
+        DocumentImpl document = new DocumentImpl(
+                dom, namespaceURI, qualifiedName, doctype, inputEncoding);
+        document.setDocumentURI(systemId);
 
         try {
             KXmlParser parser = new KXmlParser();
             parser.keepNamespaceAttributes();
-            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES,
-                    namespaceAware);
-            
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, namespaceAware);
+
             if (source.getByteStream() != null) {
-                parser.setInput(source.getByteStream(), source.getEncoding());
+                parser.setInput(source.getByteStream(), inputEncoding);
             } else if (source.getCharacterStream() != null) {
                 parser.setInput(source.getCharacterStream());
-            } else if (source.getSystemId() != null) {
-                URL url = new URL(source.getSystemId());
+            } else if (systemId != null) {
+                URL url = new URL(systemId);
                 URLConnection urlConnection = url.openConnection();
                 urlConnection.connect();
-                String encoding = source.getEncoding();
-                // TODO: if null, extract the encoding from the Content-Type header?
-                parser.setInput(urlConnection.getInputStream(), encoding);
+                // TODO: if null, extract the inputEncoding from the Content-Type header?
+                parser.setInput(urlConnection.getInputStream(), inputEncoding);
             } else {
                 throw new SAXParseException(
                         "InputSource needs a stream, reader or URI", null);
@@ -143,7 +145,7 @@
             LocatorImpl locator = new LocatorImpl();
 
             locator.setPublicId(source.getPublicId());
-            locator.setSystemId(source.getSystemId());
+            locator.setSystemId(systemId);
             locator.setLineNumber(ex.getLineNumber());
             locator.setColumnNumber(ex.getColumnNumber());
 
diff --git a/libcore/xml/src/test/java/tests/xml/AllTests.java b/libcore/xml/src/test/java/tests/xml/AllTests.java
index 597e35e..beabd08 100644
--- a/libcore/xml/src/test/java/tests/xml/AllTests.java
+++ b/libcore/xml/src/test/java/tests/xml/AllTests.java
@@ -24,6 +24,7 @@
     public static Test suite() {
         TestSuite suite = tests.TestSuiteFactory.createTestSuite();
 
+        suite.addTestSuite(DeclarationTest.class);
         suite.addTestSuite(DomTest.class);
         suite.addTestSuite(SimpleParserTest.class);
         suite.addTestSuite(SimpleBuilderTest.class);
diff --git a/libcore/xml/src/test/java/tests/xml/DeclarationTest.java b/libcore/xml/src/test/java/tests/xml/DeclarationTest.java
new file mode 100644
index 0000000..8fea844
--- /dev/null
+++ b/libcore/xml/src/test/java/tests/xml/DeclarationTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2010 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 tests.xml;
+
+import junit.framework.TestCase;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Test the parsing of the XML declaration, plus the additional document fields
+ * captured during parsing.
+ */
+public class DeclarationTest extends TestCase {
+
+    private String systemIdA;
+    private Document documentA;
+
+    private String systemIdB;
+    private Document documentB;
+
+    @Override protected void setUp() throws Exception {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        DocumentBuilder builder = factory.newDocumentBuilder();
+
+        systemIdA = stringToSystemId(
+                "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"no\" ?><foo />");
+        InputSource inputSourceA = new InputSource(systemIdA);
+        inputSourceA.setEncoding("US-ASCII");
+        documentA = builder.parse(inputSourceA);
+
+        systemIdB = stringToSystemId(
+                "<?xml version=\"1.1\" encoding=\"US-ASCII\" standalone=\"yes\" ?><foo />");
+        InputSource inputSourceB = new InputSource(systemIdB);
+        inputSourceB.setEncoding("ISO-8859-1");
+        documentB = builder.parse(inputSourceB);
+    }
+
+    private String stringToSystemId(String contents) throws IOException {
+        File file = File.createTempFile("temp", "xml");
+        file.deleteOnExit();
+        OutputStream out = new FileOutputStream(file);
+        out.write(contents.getBytes("UTF-8"));
+        out.close();
+        return "file:" + file;
+    }
+
+    /**
+     * XML parsers are advised of the document's character set via two channels:
+     * via the declaration and also the document's input source. To test that
+     * each of these winds up in the correct location in the document model, we
+     * supply different names for each. This is only safe because for the subset
+     * of characters in the document, the character sets are equivalent.
+     */
+    public void testGetInputEncoding() throws Exception {
+        assertEquals("US-ASCII", documentA.getInputEncoding());
+        assertEquals("ISO-8859-1", documentB.getInputEncoding());
+    }
+
+    public void testGetXmlEncoding() throws Exception {
+        String message = "This implementation doesn't parse the encoding from the XML declaration";
+        assertEquals(message, "ISO-8859-1", documentA.getXmlEncoding());
+        assertEquals(message, "US-ASCII", documentB.getXmlEncoding());
+    }
+
+    public void testGetXmlVersion() throws Exception {
+        String message = "This implementation doesn't parse the version from the XML declaration";
+        assertEquals(message, "1.0", documentA.getXmlVersion());
+        assertEquals(message, "1.1", documentB.getXmlVersion());
+    }
+
+    public void testGetXmlStandalone() throws Exception {
+        String message = "This implementation doesn't parse standalone from the XML declaration";
+        assertEquals(message, false, documentA.getXmlStandalone());
+        assertEquals(message, true, documentB.getXmlStandalone());
+    }
+
+    public void testGetDocumentUri() throws Exception {
+        assertEquals(systemIdA, documentA.getDocumentURI());
+        assertEquals(systemIdB, documentB.getDocumentURI());
+    }
+}
diff --git a/libnativehelper/Register.c b/libnativehelper/Register.c
index 7427972..92c0e9d 100644
--- a/libnativehelper/Register.c
+++ b/libnativehelper/Register.c
@@ -59,13 +59,6 @@
     if (register_org_apache_harmony_text_BidiWrapper(env) != 0)
         goto bail;
 
-    if (register_org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl(env) != 0)
-        goto bail;
-    if (register_org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl(env) != 0)
-        goto bail;
-    if (register_org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl(env) != 0)
-        goto bail;
-
     if (register_org_openssl_NativeBN(env) != 0)
         goto bail;
     if (register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(env) != 0)
diff --git a/vm/Dvm.mk b/vm/Dvm.mk
index 1504a02..baf41c6 100644
--- a/vm/Dvm.mk
+++ b/vm/Dvm.mk
@@ -290,11 +290,6 @@
 
 ifeq ($(dvm_arch),x86)
   ifeq ($(dvm_os),linux)
-
-# TODO: Remove this once build core defines a default arch variant for x86
-    ifneq ($(dvm_arch_variant),x86-atom)
-        dvm_arch_variant := x86
-    endif
     MTERP_ARCH_KNOWN := true
     LOCAL_SRC_FILES += \
 		arch/$(dvm_arch_variant)/Call386ABI.S \
diff --git a/vm/compiler/Compiler.h b/vm/compiler/Compiler.h
index 3f60a5e..932f41d 100644
--- a/vm/compiler/Compiler.h
+++ b/vm/compiler/Compiler.h
@@ -76,12 +76,13 @@
     kJitOff = 0,
     kJitNormal = 1,            // Profiling in mterp or running native
     kJitTSelectRequest = 2,    // Transition state - start trace selection
-    kJitTSelect = 3,           // Actively selecting trace in dbg interp
-    kJitTSelectAbort = 4,      // Something threw during selection - abort
-    kJitTSelectEnd = 5,        // Done with the trace - wrap it up
-    kJitSingleStep = 6,        // Single step interpretation
-    kJitSingleStepEnd = 7,     // Done with single step, return to mterp
-    kJitSelfVerification = 8,  // Self Verification Mode
+    kJitTSelectRequestHot = 3, // Transition state - start hot trace selection
+    kJitTSelect = 4,           // Actively selecting trace in dbg interp
+    kJitTSelectAbort = 5,      // Something threw during selection - abort
+    kJitTSelectEnd = 6,        // Done with the trace - wrap it up
+    kJitSingleStep = 7,        // Single step interpretation
+    kJitSingleStepEnd = 8,     // Done with single step, return to mterp
+    kJitSelfVerification = 9,  // Self Verification Mode
 } JitState;
 
 #if defined(WITH_SELF_VERIFICATION)
@@ -90,11 +91,12 @@
     kSVSStart = 1,          // Shadow space set up, running compiled code
     kSVSPunt = 2,           // Exiting compiled code by punting
     kSVSSingleStep = 3,     // Exiting compiled code by single stepping
-    kSVSTraceSelect = 4,    // Exiting compiled code by trace select
-    kSVSNormal = 5,         // Exiting compiled code normally
-    kSVSNoChain = 6,        // Exiting compiled code by no chain
-    kSVSBackwardBranch = 7, // Exiting compiled code with backward branch trace
-    kSVSDebugInterp = 8,    // Normal state restored, running debug interpreter
+    kSVSTraceSelectNoChain = 4,// Exiting compiled code by trace select no chain
+    kSVSTraceSelect = 5,    // Exiting compiled code by trace select
+    kSVSNormal = 6,         // Exiting compiled code normally
+    kSVSNoChain = 7,        // Exiting compiled code by no chain
+    kSVSBackwardBranch = 8, // Exiting compiled code with backward branch trace
+    kSVSDebugInterp = 9,    // Normal state restored, running debug interpreter
 } SelfVerificationState;
 #endif
 
diff --git a/vm/compiler/codegen/arm/Assemble.c b/vm/compiler/codegen/arm/Assemble.c
index 7f8ad9e..2ad0842 100644
--- a/vm/compiler/codegen/arm/Assemble.c
+++ b/vm/compiler/codegen/arm/Assemble.c
@@ -1597,7 +1597,7 @@
                 case kChainingCellHot:
                 case kChainingCellInvokeSingleton:
                     targetOffset = offsetof(InterpState,
-                          jitToInterpEntries.dvmJitToTraceSelect);
+                          jitToInterpEntries.dvmJitToInterpTraceSelect);
                     break;
                 case kChainingCellInvokePredicted:
                     targetOffset = 0;
@@ -1614,7 +1614,7 @@
 #if defined(WITH_SELF_VERIFICATION)
                 case kChainingCellBackwardBranch:
                     targetOffset = offsetof(InterpState,
-                          jitToInterpEntries.dvmJitToBackwardBranch);
+                          jitToInterpEntries.dvmJitToInterpBackwardBranch);
                     break;
 #elif defined(WITH_JIT_TUNING)
                 case kChainingCellBackwardBranch:
diff --git a/vm/compiler/codegen/arm/CodegenDriver.c b/vm/compiler/codegen/arm/CodegenDriver.c
index d3b10a4..a6aafd1 100644
--- a/vm/compiler/codegen/arm/CodegenDriver.c
+++ b/vm/compiler/codegen/arm/CodegenDriver.c
@@ -3094,7 +3094,7 @@
                                   unsigned int offset)
 {
     loadWordDisp(cUnit, rGLUE, offsetof(InterpState,
-                 jitToInterpEntries.dvmJitToTraceSelect), r0);
+                 jitToInterpEntries.dvmJitToInterpTraceSelect), r0);
     opReg(cUnit, kOpBlx, r0);
     addWordData(cUnit, (int) (cUnit->method->insns + offset), true);
 }
@@ -3106,7 +3106,8 @@
 {
 #if defined(WITH_SELF_VERIFICATION)
     newLIR3(cUnit, kThumbLdrRRI5, r0, rGLUE,
-        offsetof(InterpState, jitToInterpEntries.dvmJitToBackwardBranch) >> 2);
+        offsetof(InterpState,
+                 jitToInterpEntries.dvmJitToInterpBackwardBranch) >> 2);
 #else
     newLIR3(cUnit, kThumbLdrRRI5, r0, rGLUE,
         offsetof(InterpState, jitToInterpEntries.dvmJitToInterpNormal) >> 2);
@@ -3121,7 +3122,7 @@
                                               const Method *callee)
 {
     loadWordDisp(cUnit, rGLUE, offsetof(InterpState,
-                 jitToInterpEntries.dvmJitToTraceSelect), r0);
+                 jitToInterpEntries.dvmJitToInterpTraceSelect), r0);
     opReg(cUnit, kOpBlx, r0);
     addWordData(cUnit, (int) (callee->insns), true);
 }
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S
index 945203e..be14a5c 100644
--- a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S
@@ -62,7 +62,7 @@
     bxne    r2                          @ yes - go ahead
 
     @ continue executing the next instruction through the interpreter
-    ldr     r1, .LdvmJitToInterpNoChain @ defined in footer.S
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
     add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
 #if defined(EXIT_STATS)
     mov     r0, #kCallsiteInterpreted
diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S
index c4f766a..c08e556 100644
--- a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S
+++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S
@@ -37,7 +37,7 @@
     bxne    lr                          @ bail to the interpreter
 #endif
 
-    ldr     r10, .LdvmJitToInterpNoChain
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
     ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
     ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
 
@@ -51,4 +51,4 @@
 #if defined(EXIT_STATS)
     mov     r0, #kInlineCacheMiss
 #endif
-    mov     pc, r10                         @ dvmJitToInterpNoChain
+    mov     pc, r10                         @ dvmJitToInterpTraceSelectNoChain
diff --git a/vm/compiler/template/armv5te/footer.S b/vm/compiler/template/armv5te/footer.S
index 1709bf3..8937495 100644
--- a/vm/compiler/template/armv5te/footer.S
+++ b/vm/compiler/template/armv5te/footer.S
@@ -48,7 +48,7 @@
     bxne    r2                          @ yes - go ahead
 
     @ continue executing the next instruction through the interpreter
-    ldr     r1, .LdvmJitToInterpNoChain @ defined in footer.S
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
     add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
 #if defined(EXIT_STATS)
     mov     r0, #kCallsiteInterpreted
@@ -71,6 +71,8 @@
     .align  2
 .LdvmAsmInstructionStart:
     .word   dvmAsmInstructionStart
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
 .LdvmJitToInterpNoChain:
     .word   dvmJitToInterpNoChain
 .LdvmMterpStdBail:
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
index 71cb120..ac3455a 100644
--- a/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S
@@ -267,7 +267,7 @@
     bxne    lr                          @ bail to the interpreter
 #endif
 
-    ldr     r10, .LdvmJitToInterpNoChain
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
     ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
     ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
 
@@ -281,7 +281,7 @@
 #if defined(EXIT_STATS)
     mov     r0, #kInlineCacheMiss
 #endif
-    mov     pc, r10                         @ dvmJitToInterpNoChain
+    mov     pc, r10                         @ dvmJitToInterpTraceSelectNoChain
 
 /* ------------------------------ */
     .balign 4
@@ -453,7 +453,7 @@
     bxne    r2                          @ yes - go ahead
 
     @ continue executing the next instruction through the interpreter
-    ldr     r1, .LdvmJitToInterpNoChain @ defined in footer.S
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
     add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
 #if defined(EXIT_STATS)
     mov     r0, #kCallsiteInterpreted
@@ -1489,7 +1489,7 @@
     bxne    r2                          @ yes - go ahead
 
     @ continue executing the next instruction through the interpreter
-    ldr     r1, .LdvmJitToInterpNoChain @ defined in footer.S
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
     add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
 #if defined(EXIT_STATS)
     mov     r0, #kCallsiteInterpreted
@@ -1512,6 +1512,8 @@
     .align  2
 .LdvmAsmInstructionStart:
     .word   dvmAsmInstructionStart
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
 .LdvmJitToInterpNoChain:
     .word   dvmJitToInterpNoChain
 .LdvmMterpStdBail:
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
index 1da4cb8..4863141 100644
--- a/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S
@@ -267,7 +267,7 @@
     bxne    lr                          @ bail to the interpreter
 #endif
 
-    ldr     r10, .LdvmJitToInterpNoChain
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
     ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
     ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
 
@@ -281,7 +281,7 @@
 #if defined(EXIT_STATS)
     mov     r0, #kInlineCacheMiss
 #endif
-    mov     pc, r10                         @ dvmJitToInterpNoChain
+    mov     pc, r10                         @ dvmJitToInterpTraceSelectNoChain
 
 /* ------------------------------ */
     .balign 4
@@ -453,7 +453,7 @@
     bxne    r2                          @ yes - go ahead
 
     @ continue executing the next instruction through the interpreter
-    ldr     r1, .LdvmJitToInterpNoChain @ defined in footer.S
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
     add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
 #if defined(EXIT_STATS)
     mov     r0, #kCallsiteInterpreted
@@ -1217,7 +1217,7 @@
     bxne    r2                          @ yes - go ahead
 
     @ continue executing the next instruction through the interpreter
-    ldr     r1, .LdvmJitToInterpNoChain @ defined in footer.S
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
     add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
 #if defined(EXIT_STATS)
     mov     r0, #kCallsiteInterpreted
@@ -1240,6 +1240,8 @@
     .align  2
 .LdvmAsmInstructionStart:
     .word   dvmAsmInstructionStart
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
 .LdvmJitToInterpNoChain:
     .word   dvmJitToInterpNoChain
 .LdvmMterpStdBail:
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
index e83e773..0b06826 100644
--- a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
@@ -267,7 +267,7 @@
     bxne    lr                          @ bail to the interpreter
 #endif
 
-    ldr     r10, .LdvmJitToInterpNoChain
+    ldr     r10, .LdvmJitToInterpTraceSelectNoChain
     ldr     r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex
     ldr     r2, [rGLUE, #offGlue_self]      @ r2<- glue->self
 
@@ -281,7 +281,7 @@
 #if defined(EXIT_STATS)
     mov     r0, #kInlineCacheMiss
 #endif
-    mov     pc, r10                         @ dvmJitToInterpNoChain
+    mov     pc, r10                         @ dvmJitToInterpTraceSelectNoChain
 
 /* ------------------------------ */
     .balign 4
@@ -453,7 +453,7 @@
     bxne    r2                          @ yes - go ahead
 
     @ continue executing the next instruction through the interpreter
-    ldr     r1, .LdvmJitToInterpNoChain @ defined in footer.S
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
     add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
 #if defined(EXIT_STATS)
     mov     r0, #kCallsiteInterpreted
@@ -1489,7 +1489,7 @@
     bxne    r2                          @ yes - go ahead
 
     @ continue executing the next instruction through the interpreter
-    ldr     r1, .LdvmJitToInterpNoChain @ defined in footer.S
+    ldr     r1, .LdvmJitToInterpTraceSelectNoChain @ defined in footer.S
     add     rPC, r0, #6                 @ reconstruct new rPC (advance 6 bytes)
 #if defined(EXIT_STATS)
     mov     r0, #kCallsiteInterpreted
@@ -1512,6 +1512,8 @@
     .align  2
 .LdvmAsmInstructionStart:
     .word   dvmAsmInstructionStart
+.LdvmJitToInterpTraceSelectNoChain:
+    .word   dvmJitToInterpTraceSelectNoChain
 .LdvmJitToInterpNoChain:
     .word   dvmJitToInterpNoChain
 .LdvmMterpStdBail:
diff --git a/vm/interp/Interp.c b/vm/interp/Interp.c
index 6c6abce..4e7a7e3 100644
--- a/vm/interp/Interp.c
+++ b/vm/interp/Interp.c
@@ -1227,10 +1227,11 @@
     extern void dvmJitToInterpNoChain();
     extern void dvmJitToInterpPunt();
     extern void dvmJitToInterpSingleStep();
-    extern void dvmJitToTraceSelect();
+    extern void dvmJitToInterpTraceSelectNoChain();
+    extern void dvmJitToInterpTraceSelect();
     extern void dvmJitToPatchPredictedChain();
 #if defined(WITH_SELF_VERIFICATION)
-    extern void dvmJitToBackwardBranch();
+    extern void dvmJitToInterpBackwardBranch();
 #endif
 
     /*
@@ -1242,10 +1243,11 @@
         dvmJitToInterpNoChain,
         dvmJitToInterpPunt,
         dvmJitToInterpSingleStep,
-        dvmJitToTraceSelect,
+        dvmJitToInterpTraceSelectNoChain,
+        dvmJitToInterpTraceSelect,
         dvmJitToPatchPredictedChain,
 #if defined(WITH_SELF_VERIFICATION)
-        dvmJitToBackwardBranch,
+        dvmJitToInterpBackwardBranch,
 #endif
     };
 
diff --git a/vm/interp/InterpDefs.h b/vm/interp/InterpDefs.h
index fc9c3fe..31856df 100644
--- a/vm/interp/InterpDefs.h
+++ b/vm/interp/InterpDefs.h
@@ -73,10 +73,11 @@
     void *dvmJitToInterpNoChain;
     void *dvmJitToInterpPunt;
     void *dvmJitToInterpSingleStep;
-    void *dvmJitToTraceSelect;
+    void *dvmJitToInterpTraceSelectNoChain;
+    void *dvmJitToInterpTraceSelect;
     void *dvmJitToPatchPredictedChain;
 #if defined(WITH_SELF_VERIFICATION)
-    void *dvmJitToBackwardBranch;
+    void *dvmJitToInterpBackwardBranch;
 #endif
 };
 
@@ -88,7 +89,10 @@
  */
 #define JIT_CALLEE_SAVE_DOUBLE_COUNT 8
 
-#define JIT_TRACE_THRESH_FILTER_SIZE  16
+/* Number of entries in the 2nd level JIT profiler filter cache */
+#define JIT_TRACE_THRESH_FILTER_SIZE 32
+/* Granularity of coverage (power of 2) by each cached entry */
+#define JIT_TRACE_THRESH_FILTER_GRAN_LOG2 6
 #endif
 
 /*
@@ -173,7 +177,7 @@
     int currRunLen;           // Length of run in 16-bit words
     int lastThreshFilter;
     const u2* lastPC;         // Stage the PC first for the threaded interpreter
-    const u2* threshFilter[JIT_TRACE_THRESH_FILTER_SIZE];
+    intptr_t threshFilter[JIT_TRACE_THRESH_FILTER_SIZE];
     JitTraceRun trace[MAX_JIT_RUN_LEN];
     double calleeSave[JIT_CALLEE_SAVE_DOUBLE_COUNT];
 #endif
diff --git a/vm/interp/Jit.c b/vm/interp/Jit.c
index 198e1a2..3c0082b 100644
--- a/vm/interp/Jit.c
+++ b/vm/interp/Jit.c
@@ -753,6 +753,7 @@
             switchInterp = !debugOrProfile;
             break;
         case kJitTSelectRequest:
+        case kJitTSelectRequestHot:
         case kJitTSelectAbort:
 #if defined(SHOW_TRACE)
             LOGD("TraceGen:  trace abort");
@@ -875,29 +876,36 @@
 {
     bool res = false;         /* Assume success */
     int i;
+    intptr_t filterKey = ((intptr_t) interpState->pc) >>
+                         JIT_TRACE_THRESH_FILTER_GRAN_LOG2;
+
     /*
      * If previous trace-building attempt failed, force it's head to be
      * interpret-only.
      */
     if (gDvmJit.pJitEntryTable != NULL) {
-        /* Two-level filtering scheme */
-        for (i=0; i< JIT_TRACE_THRESH_FILTER_SIZE; i++) {
-            if (interpState->pc == interpState->threshFilter[i]) {
-                break;
+        /* Bypass the filter for hot trace requests */
+        if (interpState->jitState != kJitTSelectRequestHot) {
+            /* Two-level filtering scheme */
+            for (i=0; i< JIT_TRACE_THRESH_FILTER_SIZE; i++) {
+                if (filterKey == interpState->threshFilter[i]) {
+                    break;
+                }
             }
-        }
-        if (i == JIT_TRACE_THRESH_FILTER_SIZE) {
-            /*
-             * Use random replacement policy - otherwise we could miss a large
-             * loop that contains more traces than the size of our filter array.
-             */
-            i = rand() % JIT_TRACE_THRESH_FILTER_SIZE;
-            interpState->threshFilter[i] = interpState->pc;
-            res = true;
-        }
+            if (i == JIT_TRACE_THRESH_FILTER_SIZE) {
+                /*
+                 * Use random replacement policy - otherwise we could miss a
+                 * large loop that contains more traces than the size of our
+                 * filter array.
+                 */
+                i = rand() % JIT_TRACE_THRESH_FILTER_SIZE;
+                interpState->threshFilter[i] = filterKey;
+                res = true;
+            }
 
-        /* If stress mode (threshold <= 6), always translate */
-        res &= (gDvmJit.threshold > 6);
+            /* If stress mode (threshold <= 6), always translate */
+            res &= (gDvmJit.threshold > 6);
+        }
 
         /*
          * If the compiler is backlogged, or if a debugger or profiler is
@@ -912,7 +920,8 @@
             if (interpState->jitState != kJitOff) {
                 interpState->jitState = kJitNormal;
             }
-        } else if (interpState->jitState == kJitTSelectRequest) {
+        } else if (interpState->jitState == kJitTSelectRequest ||
+                   interpState->jitState == kJitTSelectRequestHot) {
             JitEntry *slot = lookupAndAdd(interpState->pc, false);
             if (slot == NULL) {
                 /*
@@ -954,6 +963,7 @@
         }
         switch (interpState->jitState) {
             case kJitTSelectRequest:
+            case kJitTSelectRequestHot:
                  interpState->jitState = kJitTSelect;
                  interpState->currTraceHead = interpState->pc;
                  interpState->currTraceRun = 0;
diff --git a/vm/mterp/armv5te/footer.S b/vm/mterp/armv5te/footer.S
index 27129fd..ed77978 100644
--- a/vm/mterp/armv5te/footer.S
+++ b/vm/mterp/armv5te/footer.S
@@ -22,14 +22,20 @@
     mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
 
-    .global dvmJitToTraceSelect
-dvmJitToTraceSelect:
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelectNoChain   @ r2<- interpreter entry point
+    b      dvmJitSelfVerificationEnd    @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
     ldr    r0,[lr, #-1]                 @ pass our target PC
     mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
 
-    .global dvmJitToBackwardBranch
-dvmJitToBackwardBranch:
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
     ldr    r0,[lr, #-1]                 @ pass our target PC
     mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
@@ -92,14 +98,32 @@
     mov    r1,#1                  @ set changeInterp to bail to debug interp
     b      common_gotoBail
 
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#ifdef EXIT_STATS
+    bl     dvmBumpNoChain
+#endif
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    b      2f
 
 /*
  * Return from the translation cache and immediately request
  * a translation for the exit target.  Commonly used following
  * invokes.
  */
-    .global dvmJitToTraceSelect
-dvmJitToTraceSelect:
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
     ldr    rPC,[lr, #-1]           @ get our target PC
     ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
     add    rINST,lr,#-5            @ save start of chain branch
@@ -122,6 +146,7 @@
     GET_JIT_PROF_TABLE(r0)
     FETCH_INST()
     cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
     bne    common_selectTrace
     GET_INST_OPCODE(ip)
     GOTO_OPCODE(ip)
@@ -231,7 +256,10 @@
     cmp     r0,#0
 #if !defined(WITH_SELF_VERIFICATION)
     bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
 #else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
     beq     common_selectTrace
     /*
      * At this point, we have a target translation.  However, if
@@ -247,8 +275,11 @@
     /* no return */
 #endif
 
+/*
+ * On entry:
+ *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
 common_selectTrace:
-    mov     r2,#kJitTSelectRequest      @ ask for trace selection
     str     r2,[rGLUE,#offGlue_jitState]
     mov     r2,#kInterpEntryInstr       @ normal entry reason
     str     r2,[rGLUE,#offGlue_entryPoint]
diff --git a/vm/mterp/common/asm-constants.h b/vm/mterp/common/asm-constants.h
index f64aeee..b9e21ec 100644
--- a/vm/mterp/common/asm-constants.h
+++ b/vm/mterp/common/asm-constants.h
@@ -293,26 +293,28 @@
 #endif
 
 #if defined(WITH_JIT)
-MTERP_CONSTANT(kJitOff,             0)
-MTERP_CONSTANT(kJitNormal,          1)
-MTERP_CONSTANT(kJitTSelectRequest,  2)
-MTERP_CONSTANT(kJitTSelect,         3)
-MTERP_CONSTANT(kJitTSelectAbort,    4)
-MTERP_CONSTANT(kJitTSelectEnd,      5)
-MTERP_CONSTANT(kJitSingleStep,      6)
-MTERP_CONSTANT(kJitSingleStepEnd,   7)
-MTERP_CONSTANT(kJitSelfVerification, 8)
+MTERP_CONSTANT(kJitOff,                 0)
+MTERP_CONSTANT(kJitNormal,              1)
+MTERP_CONSTANT(kJitTSelectRequest,      2)
+MTERP_CONSTANT(kJitTSelectRequestHot,   3)
+MTERP_CONSTANT(kJitTSelect,             4)
+MTERP_CONSTANT(kJitTSelectAbort,        5)
+MTERP_CONSTANT(kJitTSelectEnd,          6)
+MTERP_CONSTANT(kJitSingleStep,          7)
+MTERP_CONSTANT(kJitSingleStepEnd,       8)
+MTERP_CONSTANT(kJitSelfVerification,    9)
 
 #if defined(WITH_SELF_VERIFICATION)
 MTERP_CONSTANT(kSVSIdle, 0)
 MTERP_CONSTANT(kSVSStart, 1)
 MTERP_CONSTANT(kSVSPunt, 2)
 MTERP_CONSTANT(kSVSSingleStep, 3)
-MTERP_CONSTANT(kSVSTraceSelect, 4)
-MTERP_CONSTANT(kSVSNormal, 5)
-MTERP_CONSTANT(kSVSNoChain, 6)
-MTERP_CONSTANT(kSVSBackwardBranch, 7)
-MTERP_CONSTANT(kSVSDebugInterp, 8)
+MTERP_CONSTANT(kSVSTraceSelectNoChain, 4)
+MTERP_CONSTANT(kSVSTraceSelect, 5)
+MTERP_CONSTANT(kSVSNormal, 6)
+MTERP_CONSTANT(kSVSNoChain, 7)
+MTERP_CONSTANT(kSVSBackwardBranch, 8)
+MTERP_CONSTANT(kSVSDebugInterp, 9)
 #endif
 #endif
 
diff --git a/vm/mterp/config-x86-atom b/vm/mterp/config-x86-atom
index 2470b33..a8acc65 100644
--- a/vm/mterp/config-x86-atom
+++ b/vm/mterp/config-x86-atom
@@ -266,9 +266,6 @@
 #op OP_UNUSED_E9 c
 #op OP_UNUSED_EA c
 #op OP_UNUSED_EB c
-#op OP_UNUSED_EC c
-#op OP_UNUSED_ED c
-#op OP_UNUSED_EF c
 #op OP_UNUSED_F1 c
 #op OP_UNUSED_FC c
 #op OP_UNUSED_FD c
@@ -286,6 +283,10 @@
 #op OP_XOR_LONG_2ADDR c
 #op OP_XOR_LONG c
 
+# TODO: provide native implementations
+op OP_BREAKPOINT c
+op OP_EXECUTE_INLINE_RANGE c
+
 op-end
 
 # arch-specific entry point to interpreter
diff --git a/vm/mterp/out/InterpAsm-armv4t.S b/vm/mterp/out/InterpAsm-armv4t.S
index c3cebb7..c9f69cb 100644
--- a/vm/mterp/out/InterpAsm-armv4t.S
+++ b/vm/mterp/out/InterpAsm-armv4t.S
@@ -9555,14 +9555,20 @@
     mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
 
-    .global dvmJitToTraceSelect
-dvmJitToTraceSelect:
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelectNoChain   @ r2<- interpreter entry point
+    b      dvmJitSelfVerificationEnd    @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
     ldr    r0,[lr, #-1]                 @ pass our target PC
     mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
 
-    .global dvmJitToBackwardBranch
-dvmJitToBackwardBranch:
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
     ldr    r0,[lr, #-1]                 @ pass our target PC
     mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
@@ -9625,14 +9631,32 @@
     mov    r1,#1                  @ set changeInterp to bail to debug interp
     b      common_gotoBail
 
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#ifdef EXIT_STATS
+    bl     dvmBumpNoChain
+#endif
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    b      2f
 
 /*
  * Return from the translation cache and immediately request
  * a translation for the exit target.  Commonly used following
  * invokes.
  */
-    .global dvmJitToTraceSelect
-dvmJitToTraceSelect:
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
     ldr    rPC,[lr, #-1]           @ get our target PC
     ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
     add    rINST,lr,#-5            @ save start of chain branch
@@ -9655,6 +9679,7 @@
     GET_JIT_PROF_TABLE(r0)
     FETCH_INST()
     cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
     bne    common_selectTrace
     GET_INST_OPCODE(ip)
     GOTO_OPCODE(ip)
@@ -9764,7 +9789,10 @@
     cmp     r0,#0
 #if !defined(WITH_SELF_VERIFICATION)
     bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
 #else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
     beq     common_selectTrace
     /*
      * At this point, we have a target translation.  However, if
@@ -9780,8 +9808,11 @@
     /* no return */
 #endif
 
+/*
+ * On entry:
+ *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
 common_selectTrace:
-    mov     r2,#kJitTSelectRequest      @ ask for trace selection
     str     r2,[rGLUE,#offGlue_jitState]
     mov     r2,#kInterpEntryInstr       @ normal entry reason
     str     r2,[rGLUE,#offGlue_entryPoint]
diff --git a/vm/mterp/out/InterpAsm-armv5te-vfp.S b/vm/mterp/out/InterpAsm-armv5te-vfp.S
index 2064668..d2f3a79 100644
--- a/vm/mterp/out/InterpAsm-armv5te-vfp.S
+++ b/vm/mterp/out/InterpAsm-armv5te-vfp.S
@@ -9073,14 +9073,20 @@
     mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
 
-    .global dvmJitToTraceSelect
-dvmJitToTraceSelect:
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelectNoChain   @ r2<- interpreter entry point
+    b      dvmJitSelfVerificationEnd    @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
     ldr    r0,[lr, #-1]                 @ pass our target PC
     mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
 
-    .global dvmJitToBackwardBranch
-dvmJitToBackwardBranch:
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
     ldr    r0,[lr, #-1]                 @ pass our target PC
     mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
@@ -9143,14 +9149,32 @@
     mov    r1,#1                  @ set changeInterp to bail to debug interp
     b      common_gotoBail
 
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#ifdef EXIT_STATS
+    bl     dvmBumpNoChain
+#endif
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    b      2f
 
 /*
  * Return from the translation cache and immediately request
  * a translation for the exit target.  Commonly used following
  * invokes.
  */
-    .global dvmJitToTraceSelect
-dvmJitToTraceSelect:
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
     ldr    rPC,[lr, #-1]           @ get our target PC
     ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
     add    rINST,lr,#-5            @ save start of chain branch
@@ -9173,6 +9197,7 @@
     GET_JIT_PROF_TABLE(r0)
     FETCH_INST()
     cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
     bne    common_selectTrace
     GET_INST_OPCODE(ip)
     GOTO_OPCODE(ip)
@@ -9282,7 +9307,10 @@
     cmp     r0,#0
 #if !defined(WITH_SELF_VERIFICATION)
     bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
 #else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
     beq     common_selectTrace
     /*
      * At this point, we have a target translation.  However, if
@@ -9298,8 +9326,11 @@
     /* no return */
 #endif
 
+/*
+ * On entry:
+ *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
 common_selectTrace:
-    mov     r2,#kJitTSelectRequest      @ ask for trace selection
     str     r2,[rGLUE,#offGlue_jitState]
     mov     r2,#kInterpEntryInstr       @ normal entry reason
     str     r2,[rGLUE,#offGlue_entryPoint]
diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S
index 3a41054..919c79d 100644
--- a/vm/mterp/out/InterpAsm-armv5te.S
+++ b/vm/mterp/out/InterpAsm-armv5te.S
@@ -9549,14 +9549,20 @@
     mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
 
-    .global dvmJitToTraceSelect
-dvmJitToTraceSelect:
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelectNoChain   @ r2<- interpreter entry point
+    b      dvmJitSelfVerificationEnd    @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
     ldr    r0,[lr, #-1]                 @ pass our target PC
     mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
 
-    .global dvmJitToBackwardBranch
-dvmJitToBackwardBranch:
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
     ldr    r0,[lr, #-1]                 @ pass our target PC
     mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
@@ -9619,14 +9625,32 @@
     mov    r1,#1                  @ set changeInterp to bail to debug interp
     b      common_gotoBail
 
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#ifdef EXIT_STATS
+    bl     dvmBumpNoChain
+#endif
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    b      2f
 
 /*
  * Return from the translation cache and immediately request
  * a translation for the exit target.  Commonly used following
  * invokes.
  */
-    .global dvmJitToTraceSelect
-dvmJitToTraceSelect:
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
     ldr    rPC,[lr, #-1]           @ get our target PC
     ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
     add    rINST,lr,#-5            @ save start of chain branch
@@ -9649,6 +9673,7 @@
     GET_JIT_PROF_TABLE(r0)
     FETCH_INST()
     cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
     bne    common_selectTrace
     GET_INST_OPCODE(ip)
     GOTO_OPCODE(ip)
@@ -9758,7 +9783,10 @@
     cmp     r0,#0
 #if !defined(WITH_SELF_VERIFICATION)
     bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
 #else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
     beq     common_selectTrace
     /*
      * At this point, we have a target translation.  However, if
@@ -9774,8 +9802,11 @@
     /* no return */
 #endif
 
+/*
+ * On entry:
+ *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
 common_selectTrace:
-    mov     r2,#kJitTSelectRequest      @ ask for trace selection
     str     r2,[rGLUE,#offGlue_jitState]
     mov     r2,#kInterpEntryInstr       @ normal entry reason
     str     r2,[rGLUE,#offGlue_entryPoint]
diff --git a/vm/mterp/out/InterpAsm-armv7-a.S b/vm/mterp/out/InterpAsm-armv7-a.S
index bc3b7eb..62beb43 100644
--- a/vm/mterp/out/InterpAsm-armv7-a.S
+++ b/vm/mterp/out/InterpAsm-armv7-a.S
@@ -9009,14 +9009,20 @@
     mov    r2,#kSVSSingleStep           @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
 
-    .global dvmJitToTraceSelect
-dvmJitToTraceSelect:
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+    mov    r0,rPC                       @ pass our target PC
+    mov    r2,#kSVSTraceSelectNoChain   @ r2<- interpreter entry point
+    b      dvmJitSelfVerificationEnd    @ doesn't return
+
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
     ldr    r0,[lr, #-1]                 @ pass our target PC
     mov    r2,#kSVSTraceSelect          @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
 
-    .global dvmJitToBackwardBranch
-dvmJitToBackwardBranch:
+    .global dvmJitToInterpBackwardBranch
+dvmJitToInterpBackwardBranch:
     ldr    r0,[lr, #-1]                 @ pass our target PC
     mov    r2,#kSVSBackwardBranch       @ r2<- interpreter entry point
     b      dvmJitSelfVerificationEnd    @ doesn't return
@@ -9079,14 +9085,32 @@
     mov    r1,#1                  @ set changeInterp to bail to debug interp
     b      common_gotoBail
 
+/*
+ * Return from the translation cache and immediately request
+ * a translation for the exit target.  Commonly used for callees.
+ */
+    .global dvmJitToInterpTraceSelectNoChain
+dvmJitToInterpTraceSelectNoChain:
+#ifdef EXIT_STATS
+    bl     dvmBumpNoChain
+#endif
+    ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
+    mov    r0,rPC
+    bl     dvmJitGetCodeAddr        @ Is there a translation?
+    str    r0, [r10, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
+    mov    r1, rPC                  @ arg1 of translation may need this
+    mov    lr, #0                   @  in case target is HANDLER_INTERPRET
+    cmp    r0,#0
+    bxne   r0                       @ continue native execution if so
+    b      2f
 
 /*
  * Return from the translation cache and immediately request
  * a translation for the exit target.  Commonly used following
  * invokes.
  */
-    .global dvmJitToTraceSelect
-dvmJitToTraceSelect:
+    .global dvmJitToInterpTraceSelect
+dvmJitToInterpTraceSelect:
     ldr    rPC,[lr, #-1]           @ get our target PC
     ldr    r10, [rGLUE, #offGlue_self]  @ callee saved r10 <- glue->self
     add    rINST,lr,#-5            @ save start of chain branch
@@ -9109,6 +9133,7 @@
     GET_JIT_PROF_TABLE(r0)
     FETCH_INST()
     cmp    r0, #0
+    movne  r2,#kJitTSelectRequestHot   @ ask for trace selection
     bne    common_selectTrace
     GET_INST_OPCODE(ip)
     GOTO_OPCODE(ip)
@@ -9218,7 +9243,10 @@
     cmp     r0,#0
 #if !defined(WITH_SELF_VERIFICATION)
     bxne    r0                          @ jump to the translation
+    mov     r2,#kJitTSelectRequest      @ ask for trace selection
+    @ fall-through to common_selectTrace
 #else
+    moveq   r2,#kJitTSelectRequest      @ ask for trace selection
     beq     common_selectTrace
     /*
      * At this point, we have a target translation.  However, if
@@ -9234,8 +9262,11 @@
     /* no return */
 #endif
 
+/*
+ * On entry:
+ *  r2 is jit state, e.g. kJitTSelectRequest or kJitTSelectRequestHot
+ */
 common_selectTrace:
-    mov     r2,#kJitTSelectRequest      @ ask for trace selection
     str     r2,[rGLUE,#offGlue_jitState]
     mov     r2,#kInterpEntryInstr       @ normal entry reason
     str     r2,[rGLUE,#offGlue_entryPoint]
diff --git a/vm/mterp/out/InterpAsm-x86-atom.S b/vm/mterp/out/InterpAsm-x86-atom.S
index 8b2a694..778fa48 100644
--- a/vm/mterp/out/InterpAsm-x86-atom.S
+++ b/vm/mterp/out/InterpAsm-x86-atom.S
@@ -14866,8 +14866,7 @@
 
 /* ------------------------------ */
     .balign 64
-.L_OP_UNUSED_EC: /* 0xec */
-/* File: x86-atom/OP_UNUSED_EC.S */
+.L_OP_BREAKPOINT: /* 0xec */
    /* Copyright (C) 2008 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
@@ -14884,43 +14883,15 @@
     */
 
    /*
-    * File: OP_UNUSED_EC.S
+    * File: stub.S
     */
 
-/* File: x86-atom/unused.S */
-   /* 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.
-    */
-
-   /*
-    * File: unused.S
-    *
-    * Code: Common code for unused bytecodes. Uses no subtitutions.
-    *
-    * For: all unused bytecodes
-    *
-    * Description: aborts if executed.
-    *
-    * Format: ØØ|op (10x)
-    *
-    * Syntax: op
-    */
-
-    call        common_abort
-
-
-
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_BREAKPOINT      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
 /* ------------------------------ */
     .balign 64
 .L_OP_THROW_VERIFICATION_ERROR: /* 0xed */
@@ -15011,8 +14982,7 @@
 
 /* ------------------------------ */
     .balign 64
-.L_OP_UNUSED_EF: /* 0xef */
-/* File: x86-atom/OP_UNUSED_EF.S */
+.L_OP_EXECUTE_INLINE_RANGE: /* 0xef */
    /* Copyright (C) 2008 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
@@ -15029,43 +14999,15 @@
     */
 
    /*
-    * File: OP_UNUSED_EF.S
+    * File: stub.S
     */
 
-/* File: x86-atom/unused.S */
-   /* 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.
-    */
-
-   /*
-    * File: unused.S
-    *
-    * Code: Common code for unused bytecodes. Uses no subtitutions.
-    *
-    * For: all unused bytecodes
-    *
-    * Description: aborts if executed.
-    *
-    * Format: ØØ|op (10x)
-    *
-    * Syntax: op
-    */
-
-    call        common_abort
-
-
-
+    SAVE_PC_FP_TO_GLUE %edx             # save program counter and frame pointer
+    pushl       rGLUE                   # push parameter glue
+    call        dvmMterp_OP_EXECUTE_INLINE_RANGE      # call c-based implementation
+    lea         4(%esp), %esp
+    LOAD_PC_FP_FROM_GLUE                # restore program counter and frame pointer
+    FINISH_A                            # jump to next instruction
 /* ------------------------------ */
     .balign 64
 .L_OP_INVOKE_DIRECT_EMPTY: /* 0xf0 */
diff --git a/vm/mterp/out/InterpC-x86-atom.c b/vm/mterp/out/InterpC-x86-atom.c
index 5e1309d..1a8a61a 100644
--- a/vm/mterp/out/InterpC-x86-atom.c
+++ b/vm/mterp/out/InterpC-x86-atom.c
@@ -302,6 +302,11 @@
 #define INST_INST(_inst)    ((_inst) & 0xff)
 
 /*
+ * Replace the opcode (used when handling breakpoints).  _opcode is a u1.
+ */
+#define INST_REPLACE_OP(_inst, _opcode) (((_inst) & 0xff00) | _opcode)
+
+/*
  * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
  */
 #define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
@@ -338,8 +343,7 @@
 #if defined(WITH_JIT)
 # define NEED_INTERP_SWITCH(_current) (                                     \
     (_current == INTERP_STD) ?                                              \
-        dvmJitDebuggerOrProfilerActive(interpState->jitState) :             \
-        !dvmJitDebuggerOrProfilerActive(interpState->jitState) )
+        dvmJitDebuggerOrProfilerActive() : !dvmJitDebuggerOrProfilerActive() )
 #else
 # define NEED_INTERP_SWITCH(_current) (                                     \
     (_current == INTERP_STD) ?                                              \
@@ -418,6 +422,10 @@
 #define INTERP_TYPE INTERP_STD
 #define CHECK_DEBUG_AND_PROF() ((void)0)
 # define CHECK_TRACKED_REFS() ((void)0)
+#if defined(WITH_JIT)
+#define CHECK_JIT() (0)
+#define ABORT_JIT_TSELECT() ((void)0)
+#endif
 
 /*
  * In the C mterp stubs, "goto" is a function call followed immediately
@@ -536,7 +544,6 @@
         }                                                                   \
     }
 
-
 /* File: c/opcommon.c */
 /* forward declarations of goto targets */
 GOTO_TARGET_DECL(filledNewArray, bool methodCallRange);
@@ -1185,6 +1192,82 @@
     FINISH(2);
 
 
+/* File: c/OP_BREAKPOINT.c */
+HANDLE_OPCODE(OP_BREAKPOINT)
+#if (INTERP_TYPE == INTERP_DBG) && defined(WITH_DEBUGGER)
+    {
+        /*
+         * Restart this instruction with the original opcode.  We do
+         * this by simply jumping to the handler.
+         *
+         * It's probably not necessary to update "inst", but we do it
+         * for the sake of anything that needs to do disambiguation in a
+         * common handler with INST_INST.
+         *
+         * The breakpoint itself is handled over in updateDebugger(),
+         * because we need to detect other events (method entry, single
+         * step) and report them in the same event packet, and we're not
+         * yet handling those through breakpoint instructions.  By the
+         * time we get here, the breakpoint has already been handled and
+         * the thread resumed.
+         */
+        u1 originalOpCode = dvmGetOriginalOpCode(pc);
+        LOGV("+++ break 0x%02x (0x%04x -> 0x%04x)\n", originalOpCode, inst,
+            INST_REPLACE_OP(inst, originalOpCode));
+        inst = INST_REPLACE_OP(inst, originalOpCode);
+        FINISH_BKPT(originalOpCode);
+    }
+#else
+    LOGE("Breakpoint hit in non-debug interpreter\n");
+    dvmAbort();
+#endif
+OP_END
+
+/* File: c/OP_EXECUTE_INLINE_RANGE.c */
+HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/)
+    {
+        u4 arg0, arg1, arg2, arg3;
+        arg0 = arg1 = arg2 = arg3 = 0;      /* placate gcc */
+
+        EXPORT_PC();
+
+        vsrc1 = INST_AA(inst);      /* #of args */
+        ref = FETCH(1);             /* inline call "ref" */
+        vdst = FETCH(2);            /* range base */
+        ILOGV("|execute-inline-range args=%d @%d {regs=v%d-v%d}",
+            vsrc1, ref, vdst, vdst+vsrc1-1);
+
+        assert((vdst >> 16) == 0);  // 16-bit type -or- high 16 bits clear
+        assert(vsrc1 <= 4);
+
+        switch (vsrc1) {
+        case 4:
+            arg3 = GET_REGISTER(vdst+3);
+            /* fall through */
+        case 3:
+            arg2 = GET_REGISTER(vdst+2);
+            /* fall through */
+        case 2:
+            arg1 = GET_REGISTER(vdst+1);
+            /* fall through */
+        case 1:
+            arg0 = GET_REGISTER(vdst+0);
+            /* fall through */
+        default:        // case 0
+            ;
+        }
+
+#if INTERP_TYPE == INTERP_DBG
+        if (!dvmPerformInlineOp4Dbg(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#else
+        if (!dvmPerformInlineOp4Std(arg0, arg1, arg2, arg3, &retval, ref))
+            GOTO_exceptionThrown();
+#endif
+    }
+    FINISH(3);
+OP_END
+
 /* File: c/gotoTargets.c */
 /*
  * C footer.  This has some common code shared by the various targets.
@@ -1720,6 +1803,10 @@
         if (dvmIsBreakFrame(fp)) {
             /* bail without popping the method frame from stack */
             LOGVV("+++ returned into break frame\n");
+#if defined(WITH_JIT)
+            /* Let the Jit know the return is terminating normally */
+            CHECK_JIT();
+#endif
             GOTO_bail();
         }
 
@@ -1764,6 +1851,10 @@
          */
         PERIODIC_CHECKS(kInterpEntryThrow, 0);
 
+#if defined(WITH_JIT)
+        // Something threw during trace selection - abort the current trace
+        ABORT_JIT_TSELECT();
+#endif
         /*
          * We save off the exception and clear the exception status.  While
          * processing the exception we might need to load some Throwable
@@ -1814,6 +1905,9 @@
          *
          * If we do find a catch block, we want to transfer execution to
          * that point.
+         *
+         * Note this can cause an exception while resolving classes in
+         * the "catch" blocks.
          */
         catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
                     exception, false, (void*)&fp);
@@ -1828,9 +1922,19 @@
          * Note we want to do this *after* the call to dvmFindCatchBlock,
          * because that may need extra stack space to resolve exception
          * classes (e.g. through a class loader).
+         *
+         * It's possible for the stack overflow handling to cause an
+         * exception (specifically, class resolution in a "catch" block
+         * during the call above), so we could see the thread's overflow
+         * flag raised but actually be running in a "nested" interpreter
+         * frame.  We don't allow doubled-up StackOverflowErrors, so
+         * we can check for this by just looking at the exception type
+         * in the cleanup function.  Also, we won't unroll past the SOE
+         * point because the more-recent exception will hit a break frame
+         * as it unrolls to here.
          */
         if (self->stackOverflowed)
-            dvmCleanupStackOverflow(self);
+            dvmCleanupStackOverflow(self, exception);
 
         if (catchRelPc < 0) {
             /* falling through to JNI code or off the bottom of the stack */
@@ -1995,10 +2099,11 @@
             bottom = (u1*) newSaveArea - methodToCall->outsSize * sizeof(u4);
             if (bottom < self->interpStackEnd) {
                 /* stack overflow */
-                LOGV("Stack overflow on method call (start=%p end=%p newBot=%p size=%d '%s')\n",
+                LOGV("Stack overflow on method call (start=%p end=%p newBot=%p(%d) size=%d '%s')\n",
                     self->interpStackStart, self->interpStackEnd, bottom,
-                    self->interpStackSize, methodToCall->name);
-                dvmHandleStackOverflow(self);
+                    (u1*) fp - bottom, self->interpStackSize,
+                    methodToCall->name);
+                dvmHandleStackOverflow(self, methodToCall);
                 assert(dvmCheckException(self));
                 GOTO_exceptionThrown();
             }
@@ -2073,6 +2178,11 @@
             ILOGD("> native <-- %s.%s %s", methodToCall->clazz->descriptor,
                 methodToCall->name, methodToCall->shorty);
 
+#if defined(WITH_JIT)
+            /* Allow the Jit to end any pending trace building */
+            CHECK_JIT();
+#endif
+
             /*
              * Jump through native call bridge.  Because we leave no
              * space for locals on native calls, "newFp" points directly
diff --git a/vm/mterp/x86-atom/OP_UNUSED_EC.S b/vm/mterp/x86-atom/OP_UNUSED_EC.S
deleted file mode 100644
index 15e5675..0000000
--- a/vm/mterp/x86-atom/OP_UNUSED_EC.S
+++ /dev/null
@@ -1,20 +0,0 @@
-   /* 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.
-    */
-
-   /*
-    * File: OP_UNUSED_EC.S
-    */
-
-%include "x86-atom/unused.S"
diff --git a/vm/mterp/x86-atom/OP_UNUSED_EF.S b/vm/mterp/x86-atom/OP_UNUSED_EF.S
deleted file mode 100644
index f13ad25..0000000
--- a/vm/mterp/x86-atom/OP_UNUSED_EF.S
+++ /dev/null
@@ -1,20 +0,0 @@
-   /* 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.
-    */
-
-   /*
-    * File: OP_UNUSED_EF.S
-    */
-
-%include "x86-atom/unused.S"