Merge upstream master

The only significant change is implementing clean processing of close_notify
alerts, which lets users cleanly shutdown an SSLEngine.

Bug: 23665450
Test: cts -m CtsLibcoreTestCases
Test: cts -m CtsLibcoreOkHttpTestCases
Test: cts -m CtsLibcoreWycheproofConscryptTestCases
Change-Id: If7e11108984b9d3271bff3daae3735713cabd4e0
diff --git a/.travis.yml b/.travis.yml
index 2b35916..5020f8a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,6 +26,7 @@
         - ANDROID_TOOLS_URL="https://dl.google.com/android/repository/tools_r25.2.3-linux.zip"
         - ANDROID_HOME="$HOME/android-sdk-linux"
         - ANDROID_NDK_HOME="$ANDROID_HOME/ndk-bundle"
+        - JAVA6_HOME=/usr/lib/jvm/java-6-openjdk-amd64
 
       before_install:
         - curl -L $ANDROID_TOOLS_URL -o $HOME/tools.zip
@@ -56,6 +57,7 @@
             - libc6-dev-i386
             - libc6-dev:i386
             - ninja-build
+            - openjdk-6-jre # for running tests with Java 6
 
     ###
     ### MacOS build only does x86-64.
@@ -91,17 +93,6 @@
   - chmod 0755 $HOME/bin/git-clang-format
   - export PATH="$HOME/bin:$PATH"
 
-  # TODO(nathanmittler): Need to figure out how to make 32-bit builds work
-  # Build BoringSSL for 32-bit
-  # - if [[ "$TRAVIS_OS_NAME" == "linux" ]];
-  #  then
-  #      mkdir $BORINGSSL_HOME/build32;
-  #      pushd $BORINGSSL_HOME/build32;
-  #      cmake -DCMAKE_TOOLCHAIN_FILE=../util/32-bit-toolchain.cmake -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -DCMAKE_BUILD_TYPE=Release -DCMAKE_ASM_FLAGS="-Wa,--noexecstack -m32 -msse2" -GNinja ..;
-  #      ninja;
-  #      popd;
-  #  fi
-
   # We need this to find the merge-base
   - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" != "false" ]];
     then
@@ -121,5 +112,15 @@
 
   - ./gradlew build
 
+  # Also test with Java 6 on linux
+  - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" != "false" ]];
+      then
+        ${JAVA6_HOME}/bin/java -version;
+      fi
+  - if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" != "false" ]];
+    then
+      ./gradlew check -DjavaExecutable64=${JAVA6_HOME}/bin/java;
+    fi
+
 after_script:
   - "[ -f android/build/outputs/lint-results-debug.xml ] && cat android/build/outputs/lint-results-debug.xml"
diff --git a/android/src/main/java/org/conscrypt/Platform.java b/android/src/main/java/org/conscrypt/Platform.java
index 48467f7..06f1f0a 100644
--- a/android/src/main/java/org/conscrypt/Platform.java
+++ b/android/src/main/java/org/conscrypt/Platform.java
@@ -69,6 +69,14 @@
 
     public static void setup() {}
 
+    /**
+     * Default name used in the {@link java.security.Security JCE system} by {@code OpenSSLProvider}
+     * if the default constructor is used.
+     */
+    public static String getDefaultProviderName() {
+        return "Conscrypt";
+    }
+
     public static FileDescriptor getFileDescriptor(Socket s) {
         try {
             Field f_impl = Socket.class.getDeclaredField("impl");
@@ -426,10 +434,8 @@
         }
         if (!superClass.isInstance(javaKey)) {
             // This may happen if the PrivateKey was not created by the
-            // "AndroidOpenSSL"
-            // provider, which should be the default. That could happen if an
-            // OEM decided
-            // to implement a different default provider. Also highly unlikely.
+            // Conscrypt provider, which should be the default. That could happen if an
+            // OEM decided to implement a different default provider. Also highly unlikely.
             Log.e(TAG,
                     "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:"
                             + javaKey.getClass().getCanonicalName());
diff --git a/benchmark-android/src/main/java/org/conscrypt/AndroidEngineFactory.java b/benchmark-android/src/main/java/org/conscrypt/AndroidEngineFactory.java
index 65dcf68..42d9a22 100644
--- a/benchmark-android/src/main/java/org/conscrypt/AndroidEngineFactory.java
+++ b/benchmark-android/src/main/java/org/conscrypt/AndroidEngineFactory.java
@@ -35,8 +35,7 @@
         public SSLEngine newClientEngine(String cipher, boolean useAlpn) {
             SSLEngine engine = initEngine(clientContext.createSSLEngine(), cipher, true);
             if (useAlpn) {
-                Conscrypt.Engines.setAlpnProtocols(
-                        engine, new String[] {"h2"});
+                Conscrypt.setAlpnProtocols(engine, new String[] {"h2"});
             }
             return engine;
         }
@@ -45,8 +44,7 @@
         public SSLEngine newServerEngine(String cipher, boolean useAlpn) {
             SSLEngine engine = initEngine(serverContext.createSSLEngine(), cipher, false);
             if (useAlpn) {
-                Conscrypt.Engines.setAlpnProtocols(
-                        engine, new String[] {"h2"});
+                Conscrypt.setAlpnProtocols(engine, new String[] {"h2"});
             }
             return engine;
         }
diff --git a/common/src/jni/main/cpp/conscrypt/native_crypto.cc b/common/src/jni/main/cpp/conscrypt/native_crypto.cc
index f97862a..d66fddf 100644
--- a/common/src/jni/main/cpp/conscrypt/native_crypto.cc
+++ b/common/src/jni/main/cpp/conscrypt/native_crypto.cc
@@ -6076,7 +6076,15 @@
     return 0;
 }
 
-static SSL_SESSION* server_session_requested_callback(SSL* ssl, uint8_t* id, int id_len,
+// TODO(davidben): Remove the version check once BoringSSL has switched to
+// advertising OpenSSL 1.1.0.
+static SSL_SESSION* server_session_requested_callback(SSL* ssl,
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+                                                      uint8_t* id,
+#else
+                                                      const uint8_t* id,
+#endif
+                                                      int id_len,
                                                       int* out_copy) {
     JNI_TRACE("ssl=%p server_session_requested_callback", ssl);
 
@@ -7924,7 +7932,6 @@
     }
 
     ERR_clear_error();
-    safeSslClear(ssl);
 }
 
 static jint NativeCrypto_SSL_get_shutdown(JNIEnv* env, jclass, jlong ssl_address) {
@@ -7965,13 +7972,15 @@
     if (ssl_session == nullptr) {
         return nullptr;
     }
-    jbyteArray result = env->NewByteArray(static_cast<jsize>(ssl_session->session_id_length));
+    unsigned length;
+    const uint8_t* id = SSL_SESSION_get_id(ssl_session, &length);
+    jbyteArray result = env->NewByteArray(static_cast<jsize>(length));
     if (result != nullptr) {
-        jbyte* src = reinterpret_cast<jbyte*>(ssl_session->session_id);
-        env->SetByteArrayRegion(result, 0, static_cast<jsize>(ssl_session->session_id_length), src);
+        const jbyte* src = reinterpret_cast<const jbyte*>(id);
+        env->SetByteArrayRegion(result, 0, static_cast<jsize>(length), src);
     }
-    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_session_id => %p session_id_length=%d",
-              ssl_session, result, ssl_session->session_id_length);
+    JNI_TRACE("ssl_session=%p NativeCrypto_SSL_SESSION_session_id => %p length=%d", ssl_session,
+              result, length);
     return result;
 }
 
@@ -8779,7 +8788,6 @@
     }
 
     ERR_clear_error();
-    safeSslClear(ssl);
 }
 
 static jint NativeCrypto_ENGINE_SSL_read_direct(JNIEnv* env, jclass, jlong sslRef, jlong address,
@@ -8831,10 +8839,8 @@
             break;
         }
         case SSL_ERROR_ZERO_RETURN: {
-            // TODO(nmittler): Can this happen with memory BIOs?
-            // Read zero bytes. End of stream reached.
-            conscrypt::jniutil::jniThrowException(env, "java/io/EOFException", "Read error");
-            break;
+            // A close_notify was received, this stream is finished.
+            return -SSL_ERROR_ZERO_RETURN;
         }
         case SSL_ERROR_WANT_READ:
         case SSL_ERROR_WANT_WRITE: {
diff --git a/common/src/main/java/org/conscrypt/ClientSessionContext.java b/common/src/main/java/org/conscrypt/ClientSessionContext.java
index 9465b27..66007f9 100644
--- a/common/src/main/java/org/conscrypt/ClientSessionContext.java
+++ b/common/src/main/java/org/conscrypt/ClientSessionContext.java
@@ -43,7 +43,7 @@
 
     /**
      * Applications should not use this method. Instead use {@link
-     * Conscrypt.Contexts#setClientSessionCache(SSLContext, SSLClientSessionCache)}.
+     * Conscrypt#setClientSessionCache(SSLContext, SSLClientSessionCache)}.
      */
     public void setPersistentCache(SSLClientSessionCache persistentCache) {
         this.persistentCache = persistentCache;
diff --git a/common/src/main/java/org/conscrypt/Conscrypt.java b/common/src/main/java/org/conscrypt/Conscrypt.java
index 087eedd..5a91aaf 100644
--- a/common/src/main/java/org/conscrypt/Conscrypt.java
+++ b/common/src/main/java/org/conscrypt/Conscrypt.java
@@ -34,6 +34,7 @@
 /**
  * Core API for creating and configuring all Conscrypt types.
  */
+@SuppressWarnings("unused")
 public final class Conscrypt {
     private Conscrypt() {}
 
@@ -59,6 +60,13 @@
     }
 
     /**
+     * Indicates whether the given {@link Provider} was created by this distribution of Conscrypt.
+     */
+    public static boolean isConscrypt(Provider provider) {
+        return provider instanceof OpenSSLProvider;
+    }
+
+    /**
      * Constructs a new {@link Provider} with the default name.
      */
     public static Provider newProvider() {
@@ -75,11 +83,10 @@
     }
 
     /**
-     * Constructs a new instance of the preferred {@link SSLContextSpi}.
+     * Returns the maximum length (in bytes) of an encrypted packet.
      */
-    public static SSLContextSpi newPreferredSSLContextSpi() {
-        checkAvailability();
-        return OpenSSLContextImpl.getPreferred();
+    public static int maxEncryptedPacketLength() {
+        return NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
     }
 
     /**
@@ -92,434 +99,384 @@
     }
 
     /**
-     * Utility that exposes common TLS constants.
+     * Indicates whether the given {@link SSLContext} was created by this distribution of Conscrypt.
+     */
+    public static boolean isConscrypt(SSLContext context) {
+        return context.getProvider() instanceof OpenSSLProvider;
+    }
+
+    /**
+     * Constructs a new instance of the preferred {@link SSLContextSpi}.
+     */
+    public static SSLContextSpi newPreferredSSLContextSpi() {
+        checkAvailability();
+        return OpenSSLContextImpl.getPreferred();
+    }
+
+    /**
+     * Sets the client-side persistent cache to be used by the context.
+     */
+    public static void setClientSessionCache(SSLContext context, SSLClientSessionCache cache) {
+        SSLSessionContext clientContext = context.getClientSessionContext();
+        if (!(clientContext instanceof ClientSessionContext)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt client context: " + clientContext.getClass().getName());
+        }
+        ((ClientSessionContext) clientContext).setPersistentCache(cache);
+    }
+
+    /**
+     * Sets the server-side persistent cache to be used by the context.
+     */
+    public static void setServerSessionCache(SSLContext context, SSLServerSessionCache cache) {
+        SSLSessionContext serverContext = context.getServerSessionContext();
+        if (!(serverContext instanceof ServerSessionContext)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt client context: " + serverContext.getClass().getName());
+        }
+        ((ServerSessionContext) serverContext).setPersistentCache(cache);
+    }
+
+    /**
+     * Indicates whether the given {@link SSLSocketFactory} was created by this distribution of
+     * Conscrypt.
+     */
+    public static boolean isConscrypt(SSLSocketFactory factory) {
+        return factory instanceof OpenSSLSocketFactoryImpl;
+    }
+
+    private static OpenSSLSocketFactoryImpl toConscrypt(SSLSocketFactory factory) {
+        if (!isConscrypt(factory)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt socket factory: " + factory.getClass().getName());
+        }
+        return (OpenSSLSocketFactoryImpl) factory;
+    }
+
+    /**
+     * Configures the default socket to be created for all socket factory instances.
      */
     @ExperimentalApi
-    public static final class Constants {
-        private Constants() {}
-
-        /**
-         * Returns the maximum length (in bytes) of an encrypted packet.
-         */
-        public static int maxEncryptedPacketLength() {
-            return NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
-        }
+    public static void setUseEngineSocketByDefault(boolean useEngineSocket) {
+        OpenSSLSocketFactoryImpl.setUseEngineSocketByDefault(useEngineSocket);
+        OpenSSLServerSocketFactoryImpl.setUseEngineSocketByDefault(useEngineSocket);
     }
 
     /**
-     * Utility methods for configuring Conscrypt {@link SSLContext} instances.
+     * Configures the socket to be created for the given socket factory instance.
      */
-    public static final class Contexts {
-        private Contexts() {}
-
-        /**
-         * Indicates whether the given object is a Conscrypt client-side session context.
-         */
-        public static boolean isConscrypt(SSLContext context) {
-            return context.getProvider() instanceof OpenSSLProvider;
-        }
-
-        /**
-         * Sets the client-side persistent cache to be used by the context.
-         */
-        public static void setClientSessionCache(SSLContext context, SSLClientSessionCache cache) {
-            SSLSessionContext clientContext = context.getClientSessionContext();
-            if (!(clientContext instanceof ClientSessionContext)) {
-                throw new IllegalArgumentException(
-                        "Not a conscrypt client context: " + clientContext.getClass().getName());
-            }
-            ((ClientSessionContext) clientContext).setPersistentCache(cache);
-        }
-
-        /**
-         * Sets the server-side persistent cache to be used by the context.
-         */
-        public static void setServerSessionCache(SSLContext context, SSLServerSessionCache cache) {
-            SSLSessionContext serverContext = context.getServerSessionContext();
-            if (!(serverContext instanceof ServerSessionContext)) {
-                throw new IllegalArgumentException(
-                        "Not a conscrypt client context: " + serverContext.getClass().getName());
-            }
-            ((ServerSessionContext) serverContext).setPersistentCache(cache);
-        }
+    @ExperimentalApi
+    public static void setUseEngineSocket(SSLSocketFactory factory, boolean useEngineSocket) {
+        toConscrypt(factory).setUseEngineSocket(useEngineSocket);
     }
 
     /**
-     * Utility methods for configuring Conscrypt socket factories.
+     * Indicates whether the given {@link SSLServerSocketFactory} was created by this distribution
+     * of Conscrypt.
      */
-    public static final class SocketFactories {
-        private SocketFactories() {}
+    public static boolean isConscrypt(SSLServerSocketFactory factory) {
+        return factory instanceof OpenSSLServerSocketFactoryImpl;
+    }
 
-        /**
-         * Indicates whether the given object is a Conscrypt socket factory.
-         */
-        public static boolean isConscrypt(SSLSocketFactory factory) {
-            return factory instanceof OpenSSLSocketFactoryImpl;
+    private static OpenSSLServerSocketFactoryImpl toConscrypt(SSLServerSocketFactory factory) {
+        if (!isConscrypt(factory)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt server socket factory: " + factory.getClass().getName());
         }
-
-        private static OpenSSLSocketFactoryImpl toConscrypt(SSLSocketFactory factory) {
-            if (!isConscrypt(factory)) {
-                throw new IllegalArgumentException(
-                        "Not a conscrypt socket factory: " + factory.getClass().getName());
-            }
-            return (OpenSSLSocketFactoryImpl) factory;
-        }
-
-        /**
-         * Configures the default socket to be created for all socket factory instances.
-         */
-        @ExperimentalApi
-        public static void setUseEngineSocketByDefault(boolean useEngineSocket) {
-            OpenSSLSocketFactoryImpl.setUseEngineSocketByDefault(useEngineSocket);
-        }
-
-        /**
-         * Configures the socket to be created for the given socket factory instance.
-         */
-        @ExperimentalApi
-        public static void setUseEngineSocket(SSLSocketFactory factory, boolean useEngineSocket) {
-            toConscrypt(factory).setUseEngineSocket(useEngineSocket);
-        }
+        return (OpenSSLServerSocketFactoryImpl) factory;
     }
 
     /**
-     * Utility methods for configuring Conscrypt server socket factories.
+     * Configures the socket to be created for the given server socket factory instance.
      */
-    public static final class ServerSocketFactories {
-        private ServerSocketFactories() {}
-
-        /**
-         * Indicates whether the given object is a Conscrypt socket factory.
-         */
-        public static boolean isConscrypt(SSLServerSocketFactory factory) {
-            return factory instanceof OpenSSLServerSocketFactoryImpl;
-        }
-
-        private static OpenSSLServerSocketFactoryImpl toConscrypt(SSLServerSocketFactory factory) {
-            if (!isConscrypt(factory)) {
-                throw new IllegalArgumentException(
-                        "Not a conscrypt server socket factory: " + factory.getClass().getName());
-            }
-            return (OpenSSLServerSocketFactoryImpl) factory;
-        }
-
-        /**
-         * Configures the default socket to be created for all server socket factory instances.
-         */
-        @ExperimentalApi
-        public static void setUseEngineSocketByDefault(boolean useEngineSocket) {
-            OpenSSLServerSocketFactoryImpl.setUseEngineSocketByDefault(useEngineSocket);
-        }
-
-        /**
-         * Configures the socket to be created for the given server socket factory instance.
-         */
-        @ExperimentalApi
-        public static void setUseEngineSocket(
-                SSLServerSocketFactory factory, boolean useEngineSocket) {
-            toConscrypt(factory).setUseEngineSocket(useEngineSocket);
-        }
+    @ExperimentalApi
+    public static void setUseEngineSocket(SSLServerSocketFactory factory, boolean useEngineSocket) {
+        toConscrypt(factory).setUseEngineSocket(useEngineSocket);
     }
 
     /**
-     * Utility methods for configuring Conscrypt sockets.
+     * Indicates whether the given {@link SSLSocket} was created by this distribution of Conscrypt.
      */
-    public static final class Sockets {
-        private Sockets() {}
+    public static boolean isConscrypt(SSLSocket socket) {
+        return socket instanceof AbstractConscryptSocket;
+    }
 
-        /**
-         * Indicates whether the given socket is a Conscrypt socket.
-         */
-        public static boolean isConscrypt(SSLSocket socket) {
-            return socket instanceof AbstractConscryptSocket;
+    private static AbstractConscryptSocket toConscrypt(SSLSocket socket) {
+        if (!isConscrypt(socket)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt socket: " + socket.getClass().getName());
         }
-
-        private static AbstractConscryptSocket toConscrypt(SSLSocket socket) {
-            if (!isConscrypt(socket)) {
-                throw new IllegalArgumentException(
-                        "Not a conscrypt socket: " + socket.getClass().getName());
-            }
-            return (AbstractConscryptSocket) socket;
-        }
-
-        /**
-         * This method enables Server Name Indication (SNI) and overrides the hostname supplied
-         * during socket creation.
-         *
-         * @param socket the socket
-         * @param hostname the desired SNI hostname, or null to disable
-         */
-        public static void setHostname(SSLSocket socket, String hostname) {
-            toConscrypt(socket).setHostname(hostname);
-        }
-
-        /**
-         * Returns either the hostname supplied during socket creation or via
-         * {@link #setHostname(SSLSocket, String)}. No DNS resolution is attempted before
-         * returning the hostname.
-         */
-        public static String getHostname(SSLSocket socket) {
-            return toConscrypt(socket).getHostname();
-        }
-
-        /**
-         * This method attempts to create a textual representation of the peer host or IP. Does
-         * not perform a reverse DNS lookup. This is typically used during session creation.
-         */
-        public static String getHostnameOrIP(SSLSocket socket) {
-            return toConscrypt(socket).getHostnameOrIP();
-        }
-
-        /**
-         * This method enables session ticket support.
-         *
-         * @param socket the socket
-         * @param useSessionTickets True to enable session tickets
-         */
-        public static void setUseSessionTickets(SSLSocket socket, boolean useSessionTickets) {
-            toConscrypt(socket).setUseSessionTickets(useSessionTickets);
-        }
-
-        /**
-         * Enables/disables TLS Channel ID for the given server-side socket.
-         *
-         * <p>This method needs to be invoked before the handshake starts.
-         *
-         * @param socket the socket
-         * @param enabled Whether to enable channel ID.
-         * @throws IllegalStateException if this is a client socket or if the handshake has already
-         * started.
-         */
-        public static void setChannelIdEnabled(SSLSocket socket, boolean enabled) {
-            toConscrypt(socket).setChannelIdEnabled(enabled);
-        }
-
-        /**
-         * Gets the TLS Channel ID for the given server-side socket. Channel ID is only available
-         * once the handshake completes.
-         *
-         * @param socket the socket
-         * @return channel ID or {@code null} if not available.
-         * @throws IllegalStateException if this is a client socket or if the handshake has not yet
-         * completed.
-         * @throws SSLException if channel ID is available but could not be obtained.
-         */
-        public static byte[] getChannelId(SSLSocket socket) throws SSLException {
-            return toConscrypt(socket).getChannelId();
-        }
-
-        /**
-         * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket.
-         *
-         * <p>This method needs to be invoked before the handshake starts.
-         *
-         * @param socket the socket
-         * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key
-         * (disables TLS Channel ID).
-         * The private key must be an Elliptic Curve (EC) key based on the NIST P-256 curve (aka
-         * SECG secp256r1 or ANSI
-         * X9.62 prime256v1).
-         * @throws IllegalStateException if this is a server socket or if the handshake has already
-         * started.
-         */
-        public static void setChannelIdPrivateKey(SSLSocket socket, PrivateKey privateKey) {
-            toConscrypt(socket).setChannelIdPrivateKey(privateKey);
-        }
-
-        /**
-         * Returns the ALPN protocol agreed upon by client and server.
-         *
-         * @param socket the socket
-         * @return the selected protocol or {@code null} if no protocol was agreed upon.
-         */
-        public static String getAlpnSelectedProtocol(SSLSocket socket) {
-            return toProtocolString(toConscrypt(socket).getAlpnSelectedProtocol());
-        }
-
-        /**
-         * Sets the list of ALPN protocols supported by the socket.
-         *
-         * @param socket the socket
-         * @param alpnProtocols the list of ALPN protocols
-         */
-        public static void setAlpnProtocols(SSLSocket socket, String[] alpnProtocols) {
-            toConscrypt(socket).setAlpnProtocols(alpnProtocols);
-        }
+        return (AbstractConscryptSocket) socket;
     }
 
     /**
-     * Utility methods for configuring Conscrypt engines.
+     * This method enables Server Name Indication (SNI) and overrides the hostname supplied
+     * during socket creation.
+     *
+     * @param socket the socket
+     * @param hostname the desired SNI hostname, or null to disable
      */
-    public static final class Engines {
-        private Engines() {}
+    public static void setHostname(SSLSocket socket, String hostname) {
+        toConscrypt(socket).setHostname(hostname);
+    }
 
-        /**
-         * Indicates whether the given engine is a Conscrypt engine.
-         */
-        public static boolean isConscrypt(SSLEngine engine) {
-            return engine instanceof ConscryptEngine;
-        }
+    /**
+     * Returns either the hostname supplied during socket creation or via
+     * {@link #setHostname(SSLSocket, String)}. No DNS resolution is attempted before
+     * returning the hostname.
+     */
+    public static String getHostname(SSLSocket socket) {
+        return toConscrypt(socket).getHostname();
+    }
 
-        private static ConscryptEngine toConscrypt(SSLEngine engine) {
-            if (!isConscrypt(engine)) {
-                throw new IllegalArgumentException(
-                        "Not a conscrypt engine: " + engine.getClass().getName());
-            }
-            return (ConscryptEngine) engine;
-        }
+    /**
+     * This method attempts to create a textual representation of the peer host or IP. Does
+     * not perform a reverse DNS lookup. This is typically used during session creation.
+     */
+    public static String getHostnameOrIP(SSLSocket socket) {
+        return toConscrypt(socket).getHostnameOrIP();
+    }
 
-        /**
-         * Provides the given engine with the provided bufferAllocator.
-         * @param engine
-         * @param bufferAllocator
-         */
-        public static void setBufferAllocator(SSLEngine engine, BufferAllocator bufferAllocator) {
-            toConscrypt(engine).setBufferAllocator(bufferAllocator);
-        }
+    /**
+     * This method enables session ticket support.
+     *
+     * @param socket the socket
+     * @param useSessionTickets True to enable session tickets
+     */
+    public static void setUseSessionTickets(SSLSocket socket, boolean useSessionTickets) {
+        toConscrypt(socket).setUseSessionTickets(useSessionTickets);
+    }
 
-        /**
-         * This method enables Server Name Indication (SNI) and overrides the hostname supplied
-         * during engine creation.
-         *
-         * @param engine the engine
-         * @param hostname the desired SNI hostname, or {@code null} to disable
-         */
-        public static void setHostname(SSLEngine engine, String hostname) {
-            toConscrypt(engine).setHostname(hostname);
-        }
+    /**
+     * Enables/disables TLS Channel ID for the given server-side socket.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param socket the socket
+     * @param enabled Whether to enable channel ID.
+     * @throws IllegalStateException if this is a client socket or if the handshake has already
+     * started.
+     */
+    public static void setChannelIdEnabled(SSLSocket socket, boolean enabled) {
+        toConscrypt(socket).setChannelIdEnabled(enabled);
+    }
 
-        /**
-         * Returns either the hostname supplied during socket creation or via
-         * {@link #setHostname(SSLEngine, String)}. No DNS resolution is attempted before
-         * returning the hostname.
-         */
-        public static String getHostname(SSLEngine engine) {
-            return toConscrypt(engine).getHostname();
-        }
+    /**
+     * Gets the TLS Channel ID for the given server-side socket. Channel ID is only available
+     * once the handshake completes.
+     *
+     * @param socket the socket
+     * @return channel ID or {@code null} if not available.
+     * @throws IllegalStateException if this is a client socket or if the handshake has not yet
+     * completed.
+     * @throws SSLException if channel ID is available but could not be obtained.
+     */
+    public static byte[] getChannelId(SSLSocket socket) throws SSLException {
+        return toConscrypt(socket).getChannelId();
+    }
 
-        /**
-         * Returns the maximum overhead, in bytes, of sealing a record with SSL.
-         */
-        public static int maxSealOverhead(SSLEngine engine) {
-            return toConscrypt(engine).maxSealOverhead();
-        }
+    /**
+     * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client socket.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param socket the socket
+     * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key
+     * (disables TLS Channel ID).
+     * The private key must be an Elliptic Curve (EC) key based on the NIST P-256 curve (aka
+     * SECG secp256r1 or ANSI
+     * X9.62 prime256v1).
+     * @throws IllegalStateException if this is a server socket or if the handshake has already
+     * started.
+     */
+    public static void setChannelIdPrivateKey(SSLSocket socket, PrivateKey privateKey) {
+        toConscrypt(socket).setChannelIdPrivateKey(privateKey);
+    }
 
-        /**
-         * Sets a listener on the given engine for completion of the TLS handshake
-         */
-        public static void setHandshakeListener(
-                SSLEngine engine, HandshakeListener handshakeListener) {
-            toConscrypt(engine).setHandshakeListener(handshakeListener);
-        }
+    /**
+     * Returns the ALPN protocol agreed upon by client and server.
+     *
+     * @param socket the socket
+     * @return the selected protocol or {@code null} if no protocol was agreed upon.
+     */
+    public static String getAlpnSelectedProtocol(SSLSocket socket) {
+        return toProtocolString(toConscrypt(socket).getAlpnSelectedProtocol());
+    }
 
-        /**
-         * Enables/disables TLS Channel ID for the given server-side engine.
-         *
-         * <p>This method needs to be invoked before the handshake starts.
-         *
-         * @param engine the engine
-         * @param enabled Whether to enable channel ID.
-         * @throws IllegalStateException if this is a client engine or if the handshake has already
-         * started.
-         */
-        public static void setChannelIdEnabled(SSLEngine engine, boolean enabled) {
-            toConscrypt(engine).setChannelIdEnabled(enabled);
-        }
+    /**
+     * Sets the list of ALPN protocols supported by the socket.
+     *
+     * @param socket the socket
+     * @param alpnProtocols the list of ALPN protocols
+     */
+    public static void setAlpnProtocols(SSLSocket socket, String[] alpnProtocols) {
+        toConscrypt(socket).setAlpnProtocols(alpnProtocols);
+    }
 
-        /**
-         * Gets the TLS Channel ID for the given server-side engine. Channel ID is only available
-         * once the handshake completes.
-         *
-         * @param engine the engine
-         * @return channel ID or {@code null} if not available.
-         * @throws IllegalStateException if this is a client engine or if the handshake has not yet
-         * completed.
-         * @throws SSLException if channel ID is available but could not be obtained.
-         */
-        public static byte[] getChannelId(SSLEngine engine) throws SSLException {
-            return toConscrypt(engine).getChannelId();
-        }
+    /**
+     * Indicates whether the given {@link SSLEngine} was created by this distribution of Conscrypt.
+     */
+    public static boolean isConscrypt(SSLEngine engine) {
+        return engine instanceof ConscryptEngine;
+    }
 
-        /**
-         * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client engine.
-         *
-         * <p>This method needs to be invoked before the handshake starts.
-         *
-         * @param engine the engine
-         * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key
-         * (disables TLS Channel ID).
-         * The private key must be an Elliptic Curve (EC) key based on the NIST P-256 curve (aka
-         * SECG secp256r1 or ANSI X9.62 prime256v1).
-         * @throws IllegalStateException if this is a server engine or if the handshake has already
-         * started.
-         */
-        public static void setChannelIdPrivateKey(SSLEngine engine, PrivateKey privateKey) {
-            toConscrypt(engine).setChannelIdPrivateKey(privateKey);
+    private static ConscryptEngine toConscrypt(SSLEngine engine) {
+        if (!isConscrypt(engine)) {
+            throw new IllegalArgumentException(
+                    "Not a conscrypt engine: " + engine.getClass().getName());
         }
+        return (ConscryptEngine) engine;
+    }
 
-        /**
-         * Extended unwrap method for multiple source and destination buffers.
-         *
-         * @param engine the target engine for the unwrap
-         * @param srcs the source buffers
-         * @param dsts the destination buffers
-         * @return the result of the unwrap operation
-         * @throws SSLException thrown if an SSL error occurred
-         */
-        public static SSLEngineResult unwrap(SSLEngine engine, final ByteBuffer[] srcs,
-                final ByteBuffer[] dsts) throws SSLException {
-            return toConscrypt(engine).unwrap(srcs, dsts);
-        }
+    /**
+     * Provides the given engine with the provided bufferAllocator.
+     */
+    @ExperimentalApi
+    public static void setBufferAllocator(SSLEngine engine, BufferAllocator bufferAllocator) {
+        toConscrypt(engine).setBufferAllocator(bufferAllocator);
+    }
 
-        /**
-         * Exteneded unwrap method for multiple source and destination buffers.
-         *
-         * @param engine the target engine for the unwrap.
-         * @param srcs the source buffers
-         * @param srcsOffset the offset in the {@code srcs} array of the first source buffer
-         * @param srcsLength the number of source buffers starting at {@code srcsOffset}
-         * @param dsts the destination buffers
-         * @param dstsOffset the offset in the {@code dsts} array of the first destination buffer
-         * @param dstsLength the number of destination buffers starting at {@code dstsOffset}
-         * @return the result of the unwrap operation
-         * @throws SSLException thrown if an SSL error occurred
-         */
-        public static SSLEngineResult unwrap(SSLEngine engine, final ByteBuffer[] srcs,
-                int srcsOffset, final int srcsLength, final ByteBuffer[] dsts, final int dstsOffset,
-                final int dstsLength) throws SSLException {
-            return toConscrypt(engine).unwrap(
-                    srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
-        }
+    /**
+     * This method enables Server Name Indication (SNI) and overrides the hostname supplied
+     * during engine creation.
+     *
+     * @param engine the engine
+     * @param hostname the desired SNI hostname, or {@code null} to disable
+     */
+    public static void setHostname(SSLEngine engine, String hostname) {
+        toConscrypt(engine).setHostname(hostname);
+    }
 
-        /**
-         * This method enables session ticket support.
-         *
-         * @param engine the engine
-         * @param useSessionTickets True to enable session tickets
-         */
-        public static void setUseSessionTickets(SSLEngine engine, boolean useSessionTickets) {
-            toConscrypt(engine).setUseSessionTickets(useSessionTickets);
-        }
+    /**
+     * Returns either the hostname supplied during socket creation or via
+     * {@link #setHostname(SSLEngine, String)}. No DNS resolution is attempted before
+     * returning the hostname.
+     */
+    public static String getHostname(SSLEngine engine) {
+        return toConscrypt(engine).getHostname();
+    }
 
-        /**
-         * Sets the list of ALPN protocols supported by the engine.
-         *
-         * @param engine the engine
-         * @param alpnProtocols the list of ALPN protocols
-         */
-        public static void setAlpnProtocols(SSLEngine engine, String[] alpnProtocols) {
-            toConscrypt(engine).setAlpnProtocols(alpnProtocols);
-        }
+    /**
+     * Returns the maximum overhead, in bytes, of sealing a record with SSL.
+     */
+    public static int maxSealOverhead(SSLEngine engine) {
+        return toConscrypt(engine).maxSealOverhead();
+    }
 
-        /**
-         * Returns the ALPN protocol agreed upon by client and server.
-         *
-         * @param engine the engine
-         * @return the selected protocol or {@code null} if no protocol was agreed upon.
-         */
-        public static String getAlpnSelectedProtocol(SSLEngine engine) {
-            return toProtocolString(toConscrypt(engine).getAlpnSelectedProtocol());
-        }
+    /**
+     * Sets a listener on the given engine for completion of the TLS handshake
+     */
+    public static void setHandshakeListener(SSLEngine engine, HandshakeListener handshakeListener) {
+        toConscrypt(engine).setHandshakeListener(handshakeListener);
+    }
+
+    /**
+     * Enables/disables TLS Channel ID for the given server-side engine.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param engine the engine
+     * @param enabled Whether to enable channel ID.
+     * @throws IllegalStateException if this is a client engine or if the handshake has already
+     * started.
+     */
+    public static void setChannelIdEnabled(SSLEngine engine, boolean enabled) {
+        toConscrypt(engine).setChannelIdEnabled(enabled);
+    }
+
+    /**
+     * Gets the TLS Channel ID for the given server-side engine. Channel ID is only available
+     * once the handshake completes.
+     *
+     * @param engine the engine
+     * @return channel ID or {@code null} if not available.
+     * @throws IllegalStateException if this is a client engine or if the handshake has not yet
+     * completed.
+     * @throws SSLException if channel ID is available but could not be obtained.
+     */
+    public static byte[] getChannelId(SSLEngine engine) throws SSLException {
+        return toConscrypt(engine).getChannelId();
+    }
+
+    /**
+     * Sets the {@link PrivateKey} to be used for TLS Channel ID by this client engine.
+     *
+     * <p>This method needs to be invoked before the handshake starts.
+     *
+     * @param engine the engine
+     * @param privateKey private key (enables TLS Channel ID) or {@code null} for no key
+     * (disables TLS Channel ID).
+     * The private key must be an Elliptic Curve (EC) key based on the NIST P-256 curve (aka
+     * SECG secp256r1 or ANSI X9.62 prime256v1).
+     * @throws IllegalStateException if this is a server engine or if the handshake has already
+     * started.
+     */
+    public static void setChannelIdPrivateKey(SSLEngine engine, PrivateKey privateKey) {
+        toConscrypt(engine).setChannelIdPrivateKey(privateKey);
+    }
+
+    /**
+     * Extended unwrap method for multiple source and destination buffers.
+     *
+     * @param engine the target engine for the unwrap
+     * @param srcs the source buffers
+     * @param dsts the destination buffers
+     * @return the result of the unwrap operation
+     * @throws SSLException thrown if an SSL error occurred
+     */
+    public static SSLEngineResult unwrap(SSLEngine engine, final ByteBuffer[] srcs,
+            final ByteBuffer[] dsts) throws SSLException {
+        return toConscrypt(engine).unwrap(srcs, dsts);
+    }
+
+    /**
+     * Exteneded unwrap method for multiple source and destination buffers.
+     *
+     * @param engine the target engine for the unwrap.
+     * @param srcs the source buffers
+     * @param srcsOffset the offset in the {@code srcs} array of the first source buffer
+     * @param srcsLength the number of source buffers starting at {@code srcsOffset}
+     * @param dsts the destination buffers
+     * @param dstsOffset the offset in the {@code dsts} array of the first destination buffer
+     * @param dstsLength the number of destination buffers starting at {@code dstsOffset}
+     * @return the result of the unwrap operation
+     * @throws SSLException thrown if an SSL error occurred
+     */
+    public static SSLEngineResult unwrap(SSLEngine engine, final ByteBuffer[] srcs, int srcsOffset,
+            final int srcsLength, final ByteBuffer[] dsts, final int dstsOffset,
+            final int dstsLength) throws SSLException {
+        return toConscrypt(engine).unwrap(
+                srcs, srcsOffset, srcsLength, dsts, dstsOffset, dstsLength);
+    }
+
+    /**
+     * This method enables session ticket support.
+     *
+     * @param engine the engine
+     * @param useSessionTickets True to enable session tickets
+     */
+    public static void setUseSessionTickets(SSLEngine engine, boolean useSessionTickets) {
+        toConscrypt(engine).setUseSessionTickets(useSessionTickets);
+    }
+
+    /**
+     * Sets the list of ALPN protocols supported by the engine.
+     *
+     * @param engine the engine
+     * @param alpnProtocols the list of ALPN protocols
+     */
+    public static void setAlpnProtocols(SSLEngine engine, String[] alpnProtocols) {
+        toConscrypt(engine).setAlpnProtocols(alpnProtocols);
+    }
+
+    /**
+     * Returns the ALPN protocol agreed upon by client and server.
+     *
+     * @param engine the engine
+     * @return the selected protocol or {@code null} if no protocol was agreed upon.
+     */
+    public static String getAlpnSelectedProtocol(SSLEngine engine) {
+        return toProtocolString(toConscrypt(engine).getAlpnSelectedProtocol());
     }
 
     private static String toProtocolString(byte[] bytes) {
diff --git a/common/src/main/java/org/conscrypt/ConscryptEngine.java b/common/src/main/java/org/conscrypt/ConscryptEngine.java
index 146ded2..60967be 100644
--- a/common/src/main/java/org/conscrypt/ConscryptEngine.java
+++ b/common/src/main/java/org/conscrypt/ConscryptEngine.java
@@ -806,6 +806,16 @@
                                 case -SSL_ERROR_WANT_WRITE: {
                                     return newResult(bytesConsumed, bytesProduced, handshakeStatus);
                                 }
+                                case -SSL_ERROR_ZERO_RETURN: {
+                                    // We received a close_notify from the peer, so mark the
+                                    // inbound direction as closed and shut down the SSL object
+                                    closeInbound();
+                                    sendSSLShutdown();
+                                    return new SSLEngineResult(Status.CLOSED,
+                                            pendingOutboundEncryptedBytes() > 0
+                                                    ? NEED_WRAP : NOT_HANDSHAKING,
+                                            bytesConsumed, bytesProduced);
+                                }
                                 default: {
                                     // Should never get here.
                                     sendSSLShutdown();
@@ -1338,6 +1348,13 @@
                     break;
                 case STATE_CLOSED_OUTBOUND:
                 case STATE_CLOSED:
+                    // We may have pending encrypted bytes from a close_notify alert, so
+                    // try to read them out
+                    SSLEngineResult pendingNetResult =
+                            readPendingBytesFromBIO(dst, 0, 0, HandshakeStatus.NOT_HANDSHAKING);
+                    if (pendingNetResult != null) {
+                        return pendingNetResult;
+                    }
                     return new SSLEngineResult(Status.CLOSED, getHandshakeStatusInternal(), 0, 0);
                 case STATE_NEW:
                     throw new IllegalStateException(
diff --git a/common/src/main/java/org/conscrypt/OpenSSLBIOSource.java b/common/src/main/java/org/conscrypt/OpenSSLBIOSource.java
index 314d494..fd3437d 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLBIOSource.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLBIOSource.java
@@ -98,7 +98,7 @@
 
         @Override
         public long skip(long byteCount) throws IOException {
-            int originalPosition = source.position();
+            long originalPosition = source.position();
             source.position((int) (originalPosition + byteCount));
             return source.position() - originalPosition;
         }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLProvider.java b/common/src/main/java/org/conscrypt/OpenSSLProvider.java
index f6e9eb0..9a46abf 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLProvider.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLProvider.java
@@ -36,12 +36,6 @@
 public final class OpenSSLProvider extends Provider {
     private static final long serialVersionUID = 2996752495318905136L;
 
-    /**
-     * Default name used in the {@link java.security.Security JCE system} by {@code OpenSSLProvider}
-     * if the {@link #OpenSSLProvider() default constructor} is used.
-     */
-    private static final String PROVIDER_NAME = "AndroidOpenSSL";
-
     private static final String PREFIX = OpenSSLProvider.class.getPackage().getName() + ".";
 
     private static final String STANDARD_EC_PRIVATE_KEY_INTERFACE_CLASS_NAME =
@@ -52,7 +46,7 @@
             "java.security.interfaces.RSAPublicKey";
 
     public OpenSSLProvider() {
-        this(PROVIDER_NAME);
+        this(Platform.getDefaultProviderName());
     }
 
     public OpenSSLProvider(String providerName) {
diff --git a/common/src/main/java/org/conscrypt/ServerSessionContext.java b/common/src/main/java/org/conscrypt/ServerSessionContext.java
index dc4cafb..331f2b2 100644
--- a/common/src/main/java/org/conscrypt/ServerSessionContext.java
+++ b/common/src/main/java/org/conscrypt/ServerSessionContext.java
@@ -49,7 +49,7 @@
 
     /**
      * Applications should not use this method. Instead use {@link
-     * Conscrypt.Contexts#setServerSessionCache(SSLContext, SSLServerSessionCache)}.
+     * Conscrypt#setServerSessionCache(SSLContext, SSLServerSessionCache)}.
      */
     public void setPersistentCache(SSLServerSessionCache persistentCache) {
         this.persistentCache = persistentCache;
diff --git a/libcore-stub/src/main/java/libcore/java/security/StandardNames.java b/libcore-stub/src/main/java/libcore/java/security/StandardNames.java
index 3e305a1..c5ddf2b 100644
--- a/libcore-stub/src/main/java/libcore/java/security/StandardNames.java
+++ b/libcore-stub/src/main/java/libcore/java/security/StandardNames.java
@@ -69,7 +69,7 @@
 public final class StandardNames {
     public static final boolean IS_RI =
             !"Dalvik Core Library".equals(System.getProperty("java.specification.name"));
-    public static final String JSSE_PROVIDER_NAME = "AndroidOpenSSL";
+    public static final String JSSE_PROVIDER_NAME = (IS_RI) ? "Conscrypt" : "AndroidOpenSSL";
     public static final String SECURITY_PROVIDER_NAME = (IS_RI) ? "SUN" : "BC";
 
     public static final String KEY_MANAGER_FACTORY_DEFAULT = (IS_RI) ? "SunX509" : "PKIX";
diff --git a/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java b/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
index 224ff33..e3b8ee6 100644
--- a/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
+++ b/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLEngineTest.java
@@ -39,6 +39,7 @@
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLEngineResult;
 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLParameters;
@@ -882,6 +883,54 @@
         }
     }
 
+    @Test
+    public void test_SSLEngine_CloseOutbound() throws Exception {
+        final TestSSLEnginePair pair = TestSSLEnginePair.create();
+        try {
+            assertConnected(pair);
+
+            // Closing the outbound direction should cause a close_notify to be sent
+            pair.client.closeOutbound();
+            ByteBuffer clientOut = ByteBuffer
+                    .allocate(pair.client.getSession().getPacketBufferSize());
+            SSLEngineResult res = pair.client.wrap(ByteBuffer.wrap(new byte[0]), clientOut);
+            assertEquals(Status.CLOSED, res.getStatus());
+            assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus());
+            assertTrue(res.bytesProduced() > 0);
+
+            // Read the close_notify in the server
+            clientOut.flip();
+            ByteBuffer serverIn = ByteBuffer
+                    .allocate(pair.server.getSession().getApplicationBufferSize());
+            res = pair.server.unwrap(clientOut, serverIn);
+            assertEquals(Status.CLOSED, res.getStatus());
+            assertEquals(HandshakeStatus.NEED_WRAP, res.getHandshakeStatus());
+
+            // Reading the close_notify should cause a close_notify to be sent back
+            ByteBuffer serverOut = ByteBuffer
+                    .allocate(pair.server.getSession().getPacketBufferSize());
+            res = pair.server.wrap(ByteBuffer.wrap(new byte[0]), serverOut);
+            assertEquals(Status.CLOSED, res.getStatus());
+            assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus());
+            assertTrue(res.bytesProduced() > 0);
+
+            // Read the close_notify in the client
+            serverOut.flip();
+            ByteBuffer clientIn = ByteBuffer
+                    .allocate(pair.client.getSession().getApplicationBufferSize());
+            res = pair.client.unwrap(serverOut, clientIn);
+            assertEquals(Status.CLOSED, res.getStatus());
+            assertEquals(HandshakeStatus.NOT_HANDSHAKING, res.getHandshakeStatus());
+
+            // Both sides have received close_notify messages, so both peers should have
+            // registered that they're finished
+            assertTrue(pair.client.isInboundDone() && pair.client.isOutboundDone());
+            assertTrue(pair.server.isInboundDone() && pair.server.isOutboundDone());
+        } finally {
+            pair.close();
+        }
+    }
+
     private void assertConnected(TestSSLEnginePair e) {
         assertConnected(e.client, e.server);
     }
diff --git a/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSessionContextTest.java b/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSessionContextTest.java
index fdc95d5..8d0427f 100644
--- a/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSessionContextTest.java
+++ b/openjdk-integ-tests/src/test/java/libcore/javax/net/ssl/SSLSessionContextTest.java
@@ -336,6 +336,7 @@
     }
 
     private boolean isConscrypt(Provider provider) {
-        return "AndroidOpenSSL".equals(provider.getName());
+        String name = provider.getName();
+        return "AndroidOpenSSL".equals(name) || "Conscrypt".equals(name);
     }
 }
diff --git a/openjdk/src/main/java/org/conscrypt/Platform.java b/openjdk/src/main/java/org/conscrypt/Platform.java
index 5d9f7f4..25b8697 100644
--- a/openjdk/src/main/java/org/conscrypt/Platform.java
+++ b/openjdk/src/main/java/org/conscrypt/Platform.java
@@ -72,7 +72,7 @@
  * Uses reflection to implement Java 8 SSL features for backwards compatibility.
  */
 final class Platform {
-    private static final Logger logger = Logger.getLogger(NativeLibraryLoader.class.getName());
+    private static final Logger logger = Logger.getLogger(Platform.class.getName());
 
     private static final int JAVA_VERSION = javaVersion0();
     private static final String TEMP_DIR_PROPERTY_NAME = "org.conscrypt.tmpdir";
@@ -252,6 +252,14 @@
         }
     }
 
+    /**
+     * Default name used in the {@link java.security.Security JCE system} by {@code OpenSSLProvider}
+     * if the default constructor is used.
+     */
+    static String getDefaultProviderName() {
+        return "Conscrypt";
+    }
+
     static boolean canExecuteExecutable(File file) throws IOException {
         if (JAVA_VERSION >= 7) {
             return Java7PlatformUtil.canExecuteExecutable(file);
@@ -535,9 +543,12 @@
             return originalHostName;
         } catch (InvocationTargetException e) {
             throw new RuntimeException("Failed to get originalHostName", e);
-        } catch (ReflectiveOperationException ignore) {
+        } catch (ClassNotFoundException ignore) {
             // passthrough and return addr.getHostAddress()
+        } catch (IllegalAccessException ignore) {
+        } catch (NoSuchMethodException ignore) {
         }
+
         return addr.getHostAddress();
     }
 
diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptEngineTest.java b/openjdk/src/test/java/org/conscrypt/ConscryptEngineTest.java
index 05f7a5d..b566103 100644
--- a/openjdk/src/test/java/org/conscrypt/ConscryptEngineTest.java
+++ b/openjdk/src/test/java/org/conscrypt/ConscryptEngineTest.java
@@ -16,7 +16,7 @@
 
 package org.conscrypt;
 
-import static org.conscrypt.Conscrypt.Engines.setBufferAllocator;
+import static org.conscrypt.Conscrypt.setBufferAllocator;
 import static org.conscrypt.TestUtils.PROTOCOL_TLS_V1_2;
 import static org.conscrypt.TestUtils.TEST_CIPHER;
 import static org.conscrypt.TestUtils.initEngine;
@@ -252,7 +252,7 @@
         // Unwrap the all of the encrypted messages.
         for (int i = 0; i < numMessages; ++i) {
             ByteBuffer out = bufferType.newBuffer(2 * MESSAGE_SIZE);
-            SSLEngineResult unwrapResult = Conscrypt.Engines.unwrap(
+            SSLEngineResult unwrapResult = Conscrypt.unwrap(
                     serverEngine, encryptedBuffers, new ByteBuffer[] {out});
             assertEquals(SSLEngineResult.Status.OK, unwrapResult.getStatus());
             assertEquals(MESSAGE_SIZE, unwrapResult.bytesProduced());
@@ -299,7 +299,7 @@
                     decryptedBuffer.clear();
                 }
                 int prevPos = decryptedBuffer.position();
-                SSLEngineResult unwrapResult = Conscrypt.Engines.unwrap(
+                SSLEngineResult unwrapResult = Conscrypt.unwrap(
                         serverEngine, encryptedBuffers, new ByteBuffer[] {decryptedBuffer});
                 status = unwrapResult.getStatus();
                 int newPos = decryptedBuffer.position();
@@ -332,8 +332,8 @@
     public void handshakeWithAlpnShouldSucceed() throws Exception {
         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer(), true /* useAlpn */);
         doHandshake();
-        assertEquals(ALPN_PROTOCOL, Conscrypt.Engines.getAlpnSelectedProtocol(clientEngine));
-        assertEquals(ALPN_PROTOCOL, Conscrypt.Engines.getAlpnSelectedProtocol(serverEngine));
+        assertEquals(ALPN_PROTOCOL, Conscrypt.getAlpnSelectedProtocol(clientEngine));
+        assertEquals(ALPN_PROTOCOL, Conscrypt.getAlpnSelectedProtocol(serverEngine));
     }
 
     private void doMutualAuthHandshake(
@@ -360,8 +360,8 @@
         setBufferAllocator(clientEngine, bufferType.allocator);
         setBufferAllocator(serverEngine, bufferType.allocator);
         if (useAlpn) {
-            Conscrypt.Engines.setAlpnProtocols(clientEngine, SUPPORTED_ALPN_PROTOCOLS);
-            Conscrypt.Engines.setAlpnProtocols(serverEngine, SUPPORTED_ALPN_PROTOCOLS);
+            Conscrypt.setAlpnProtocols(clientEngine, SUPPORTED_ALPN_PROTOCOLS);
+            Conscrypt.setAlpnProtocols(serverEngine, SUPPORTED_ALPN_PROTOCOLS);
         }
 
         // Create the application and packet buffers for both endpoints.
diff --git a/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java
index 27bd7ff..f65ccff 100644
--- a/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java
+++ b/openjdk/src/test/java/org/conscrypt/ConscryptSocketTest.java
@@ -172,7 +172,7 @@
 
         private SSLSocketFactory socketFactory(OpenSSLContextImpl context) {
             SSLSocketFactory factory = context.engineGetSocketFactory();
-            Conscrypt.SocketFactories.setUseEngineSocket(factory, useEngineSocket);
+            Conscrypt.setUseEngineSocket(factory, useEngineSocket);
             return factory;
         }
 
diff --git a/platform/src/main/java/org/conscrypt/Platform.java b/platform/src/main/java/org/conscrypt/Platform.java
index 977f1a4..13f7a91 100644
--- a/platform/src/main/java/org/conscrypt/Platform.java
+++ b/platform/src/main/java/org/conscrypt/Platform.java
@@ -71,6 +71,14 @@
 
     private Platform() {}
 
+    /**
+     * Default name used in the {@link java.security.Security JCE system} by {@code OpenSSLProvider}
+     * if the default constructor is used.
+     */
+    static String getDefaultProviderName() {
+        return "AndroidOpenSSL";
+    }
+
     static FileDescriptor getFileDescriptor(Socket s) {
         return s.getFileDescriptor$();
     }
@@ -358,8 +366,10 @@
             return originalHostName;
         } catch (InvocationTargetException e) {
             throw new RuntimeException("Failed to get originalHostName", e);
-        } catch (ReflectiveOperationException ignore) {
+        } catch (ClassNotFoundException ignore) {
             // passthrough and return addr.getHostAddress()
+        } catch (IllegalAccessException ignore) {
+        } catch (NoSuchMethodException ignore) {
         }
         return addr.getHostAddress();
     }
diff --git a/platform/src/main/java/org/conscrypt/ct/Serialization.java b/platform/src/main/java/org/conscrypt/ct/Serialization.java
index 31657c8..6881b83 100644
--- a/platform/src/main/java/org/conscrypt/ct/Serialization.java
+++ b/platform/src/main/java/org/conscrypt/ct/Serialization.java
@@ -217,22 +217,22 @@
         if (width < 0) {
             throw new SerializationException("Negative width: " + width);
         }
-        if (width < 8 && value >= (1L << (8*width))) {
-            throw new SerializationException("Number too large, " + value +
-                                             " does not fit in " + width + " bytes");
+        if (width < 8 && value >= (1L << (8 * width))) {
+            throw new SerializationException(
+                    "Number too large, " + value + " does not fit in " + width + " bytes");
         }
 
         try {
             while (width > 0) {
-                long shift = (width - 1) * 8;
+                long shift = (width - 1) * 8L;
                 // Java behaves weirdly if shifting by more than the variable's size
                 if (shift < Long.SIZE) {
-                    output.write((byte)((value >> shift) & 0xFF));
+                    output.write((byte) ((value >> shift) & 0xFF));
                 } else {
                     output.write(0);
                 }
 
-                width --;
+                width--;
             }
         } catch (IOException e) {
             throw new SerializationException(e);
diff --git a/testing/src/main/java/org/conscrypt/TestUtils.java b/testing/src/main/java/org/conscrypt/TestUtils.java
index b134963..c696833 100644
--- a/testing/src/main/java/org/conscrypt/TestUtils.java
+++ b/testing/src/main/java/org/conscrypt/TestUtils.java
@@ -179,10 +179,12 @@
         return getServerSocketFactory(JDK_PROVIDER);
     }
 
-    static SSLSocketFactory setUseEngineSocket(SSLSocketFactory conscryptFactory, boolean useEngineSocket) {
+    static SSLSocketFactory setUseEngineSocket(
+            SSLSocketFactory conscryptFactory, boolean useEngineSocket) {
         try {
-            Class<?> clazz = conscryptClass("Conscrypt$SocketFactories");
-            Method method = clazz.getMethod("setUseEngineSocket", SSLSocketFactory.class, boolean.class);
+            Class<?> clazz = conscryptClass("Conscrypt");
+            Method method =
+                    clazz.getMethod("setUseEngineSocket", SSLSocketFactory.class, boolean.class);
             method.invoke(null, conscryptFactory, useEngineSocket);
             return conscryptFactory;
         } catch (Exception e) {
@@ -190,10 +192,12 @@
         }
     }
 
-    static SSLServerSocketFactory setUseEngineSocket(SSLServerSocketFactory conscryptFactory, boolean useEngineSocket) {
+    static SSLServerSocketFactory setUseEngineSocket(
+            SSLServerSocketFactory conscryptFactory, boolean useEngineSocket) {
         try {
-            Class<?> clazz = conscryptClass("Conscrypt$ServerSocketFactories");
-            Method method = clazz.getMethod("setUseEngineSocket", SSLServerSocketFactory.class, boolean.class);
+            Class<?> clazz = conscryptClass("Conscrypt");
+            Method method = clazz.getMethod(
+                    "setUseEngineSocket", SSLServerSocketFactory.class, boolean.class);
             method.invoke(null, conscryptFactory, useEngineSocket);
             return conscryptFactory;
         } catch (Exception e) {