Revert "Merge upstream master". DO NOT MERGE ANYWHERE.

The CL broke Android O release CTS tests since it changed internal APIs that
those tests depended on.

This reverts commit 366b7587d9da3e4b2a758404938e70aff878a00c, reversing
changes made to 5d2e92fa27ac9e169ec52a2cd6ae518bd95ce656.

This is a clean revert. The upload hook to fix lint errors was not run
(this CL was uploaded with --no-verify).

Test: Treehugger
Test: make droid cts
Bug: 62424503
Change-Id: If90ad073398436cb36f266fecc509b5181aed1ba
diff --git a/Android.mk b/Android.mk
index 5b8a7e7..b682e94 100644
--- a/Android.mk
+++ b/Android.mk
@@ -72,14 +72,15 @@
 	mkdir -p $(dir $@)
 	$< > $@
 
-common_java_files := $(call all-java-files-under,common/src/main/java)
-
-bundled_main_java_files := $(common_java_files)
-bundled_main_java_files += $(call all-java-files-under,platform/src/main/java)
+common_java_files := $(filter-out \
+	%/org/conscrypt/Platform.java \
+	%/org/conscrypt/NativeCryptoJni.java \
+	, $(call all-java-files-under,common/src/main/java))
 
 # Create the conscrypt library
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(bundled_main_java_files)
+LOCAL_SRC_FILES := $(common_java_files)
+LOCAL_SRC_FILES += $(call all-java-files-under,platform/src/main/java)
 LOCAL_GENERATED_SOURCES := $(conscrypt_gen_java_files)
 LOCAL_JAVA_LIBRARIES := core-oj core-libart
 LOCAL_NO_STANDARD_LIBRARIES := true
@@ -95,7 +96,8 @@
 # The build system may or may not strip the conscrypt jar, but this one will
 # not be stripped. See b/24535627.
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(bundled_main_java_files)
+LOCAL_SRC_FILES := $(common_java_files)
+LOCAL_SRC_FILES += $(call all-java-files-under,platform/src/main/java)
 LOCAL_GENERATED_SOURCES := $(conscrypt_gen_java_files)
 LOCAL_JAVA_LIBRARIES := core-oj core-libart
 LOCAL_NO_STANDARD_LIBRARIES := true
@@ -109,7 +111,8 @@
 
 # Create the conscrypt library without jarjar for tests
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(bundled_main_java_files)
+LOCAL_SRC_FILES := $(common_java_files)
+LOCAL_SRC_FILES += $(call all-java-files-under,platform/src/main/java)
 LOCAL_GENERATED_SOURCES := $(conscrypt_gen_java_files)
 LOCAL_JAVA_LIBRARIES := core-oj core-libart
 LOCAL_NO_STANDARD_LIBRARIES := true
@@ -119,7 +122,11 @@
 LOCAL_JAVA_LANGUAGE_VERSION := 1.7
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
-bundled_test_java_files := $(call all-java-files-under,platform/src/test/java)
+bundled_test_java_files := $(filter-out \
+	%/org/conscrypt/NativeCryptoTest.java \
+	%/org/conscrypt/OpenSSLSocketImplTest.java \
+	, $(call all-java-files-under,openjdk/src/test/java))
+bundled_test_java_files += $(call all-java-files-under,platform/src/test/java)
 bundled_test_java_files += $(call all-java-files-under,testing/src/main/java)
 bundled_test_java_files := $(foreach j,$(bundled_test_java_files),\
 	$(if $(findstring testing/src/main/java/libcore/,$(j)),,$(j)))
@@ -211,7 +218,8 @@
 
 # Make the conscrypt-hostdex library
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(bundled_main_java_files)
+LOCAL_SRC_FILES := $(common_java_files)
+LOCAL_SRC_FILES += $(call all-java-files-under,platform/src/main/java)
 LOCAL_GENERATED_SOURCES := $(conscrypt_gen_java_files)
 LOCAL_JAVACFLAGS := $(local_javac_flags)
 LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
@@ -223,7 +231,8 @@
 
 # Make the conscrypt-hostdex-nojarjar for tests
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(bundled_main_java_files)
+LOCAL_SRC_FILES := $(common_java_files)
+LOCAL_SRC_FILES += $(call all-java-files-under,platform/src/main/java)
 LOCAL_GENERATED_SOURCES := $(conscrypt_gen_java_files)
 LOCAL_JAVACFLAGS := $(local_javac_flags)
 LOCAL_BUILD_HOST_DEX := true
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index afaf1d4..6326c29 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -32,6 +32,6 @@
 ## Proposing changes
 
 Make sure that `./gradlew build` (`gradlew build` on Windows) completes
-successfully without any new warnings (see [Building](BUILDING.md)). Then create a Pull Request
+successfully without any new warnings (see [Building](Building.md)). Then create a Pull Request
 with your changes. When the changes are accepted, they will be merged or cherry-picked by
-a Conscrypt developer.
+a Conscrypt developer.
\ No newline at end of file
diff --git a/RELEASING.md b/RELEASING.md
index 67ede26..2058387 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -170,8 +170,8 @@
 | OS | x86_32 | x86_64 |
 | --- | --- | --- |
 | Linux |  | X |
-| Mac |  | X |
 | Windows | X | X |
+| Mac |  | X |
 
 Deployment to Maven Central (or the snapshot repo) is a two-step process. The only
 artifact that is platform-specific is codegen, so we only need to deploy the other
@@ -196,7 +196,7 @@
 go to the snapshot repository. Otherwise it's a release deployment and the
 artifacts will go to a freshly created staging repository.
 
-### Deploy OpenJDK for Additional Platforms (Release Deployment Only)
+### Deploy Additional Platforms (Release Deployment Only)
 The previous step will only deploy the artifacts for the OS you run on
 it and the architecture of your JVM. For a fully fledged deployment, you will
 need to deploy for each supported OS/architecture.
@@ -211,8 +211,8 @@
 ensure that the artifacts are pushed to the same staging repository.
 
 ```bash
-conscrypt$ ./gradlew build conscrypt-openjdk:uploadArchives \
-    -Dorg.gradle.parallel=false -PrepositoryId=<repository-id>
+conscrypt$ ./gradlew build uploadArchives -PtargetArch=<arch> \
+    -PrepositoryId=<repository-id> -Dorg.gradle.parallel=false
 ```
 
 Now finish [Releasing on Maven Central](#releasing-on-maven-central).
diff --git a/android-stub/build.gradle b/android-stub/build.gradle
index e57cd88..d0af41a 100644
--- a/android-stub/build.gradle
+++ b/android-stub/build.gradle
@@ -4,10 +4,6 @@
 sourceCompatibility = androidMinJavaVersion
 targetCompatibility = androidMinJavaVersion
 
-dependencies {
-    compile project(':conscrypt-libcore-stub')
-}
-
 // Don't include this artifact in the distribution.
 tasks.install.enabled = false
 tasks.uploadArchives.enabled = false;
diff --git a/android-stub/src/main/java/com/android/org/conscrypt/NativeCrypto.java b/android-stub/src/main/java/com/android/org/conscrypt/NativeCrypto.java
index 831dd17..5bb0a5d 100644
--- a/android-stub/src/main/java/com/android/org/conscrypt/NativeCrypto.java
+++ b/android-stub/src/main/java/com/android/org/conscrypt/NativeCrypto.java
@@ -20,7 +20,7 @@
 import java.security.cert.CertificateException;
 import javax.net.ssl.SSLException;
 
-final class NativeCrypto {
+class NativeCrypto {
     public interface SSLHandshakeCallbacks {
         /**
          * Verify that we trust the certificate chain is trusted.
@@ -30,7 +30,7 @@
          *
          * @throws CertificateException if the certificate is untrusted
          */
-        void verifyCertificateChain(byte[][] asn1DerEncodedCertificateChain,
+        public void verifyCertificateChain(byte[][] asn1DerEncodedCertificateChain,
                 String authMethod) throws CertificateException;
         /**
          * Called on an SSL client when the server requests (or
@@ -43,7 +43,7 @@
          * convertible to strings with #keyType
          * @param asn1DerEncodedX500Principals CAs known to the server
          */
-        void clientCertificateRequested(
+        public void clientCertificateRequested(
                 byte[] keyTypes, byte[][] asn1DerEncodedX500Principals)
                 throws CertificateEncodingException, SSLException;
         /**
@@ -51,6 +51,6 @@
          * be after SSL_do_handshake returns when handshake cutthrough
          * is enabled.
          */
-        void handshakeCompleted();
+        public void handshakeCompleted();
     }
 }
diff --git a/android-stub/src/main/java/com/android/org/conscrypt/OpenSSLSocketImpl.java b/android-stub/src/main/java/com/android/org/conscrypt/OpenSSLSocketImpl.java
index 8beee1c..e97f06f 100644
--- a/android-stub/src/main/java/com/android/org/conscrypt/OpenSSLSocketImpl.java
+++ b/android-stub/src/main/java/com/android/org/conscrypt/OpenSSLSocketImpl.java
@@ -81,18 +81,15 @@
     }
 
     @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / client_cert_cb
-    @Override
     public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
             throws CertificateEncodingException, SSLException {
         throw new RuntimeException("Stub!");
     }
 
     @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / info_callback
-    @Override
     public void handshakeCompleted() {
         throw new RuntimeException("Stub!");
     }
-
     @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks
     @Override
     public void verifyCertificateChain(byte[][] bytes, String authMethod)
@@ -109,7 +106,6 @@
     public OutputStream getOutputStream() throws IOException {
         throw new RuntimeException("Stub!");
     }
-
     @Override
     public SSLSession getSession() {
         throw new RuntimeException("Stub!");
@@ -179,7 +175,6 @@
     public byte[] getChannelId() throws SSLException {
         throw new RuntimeException("Stub!");
     }
-
     public void setChannelIdPrivateKey(PrivateKey privateKey) {
         throw new RuntimeException("Stub!");
     }
@@ -224,13 +219,11 @@
         throw new RuntimeException("Stub!");
     }
 
-    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
     @Override
     public void setSoTimeout(int readTimeoutMilliseconds) throws SocketException {
         throw new RuntimeException("Stub!");
     }
 
-    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
     @Override
     public int getSoTimeout() throws SocketException {
         throw new RuntimeException("Stub!");
@@ -258,7 +251,6 @@
         throw new RuntimeException("Stub!");
     }
 
-    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
     @Override
     public void close() throws IOException {
         throw new RuntimeException("Stub!");
diff --git a/android-stub/src/main/java/dalvik/system/BlockGuard.java b/android-stub/src/main/java/dalvik/system/BlockGuard.java
index aada669..c2e1fa9 100644
--- a/android-stub/src/main/java/dalvik/system/BlockGuard.java
+++ b/android-stub/src/main/java/dalvik/system/BlockGuard.java
@@ -28,7 +28,6 @@
     public static class PolicyWrapper implements Policy {
         private PolicyWrapper() {}
 
-        @Override
         public void onNetwork() {
             throw new UnsupportedOperationException("Stub!");
         }
diff --git a/android-stub/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/android-stub/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index 7ace28d..f53bc2e 100644
--- a/android-stub/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/android-stub/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -20,7 +20,7 @@
 import java.security.cert.CertificateException;
 import javax.net.ssl.SSLException;
 
-final class NativeCrypto {
+class NativeCrypto {
     public interface SSLHandshakeCallbacks {
         /**
          * Verify that we trust the certificate chain is trusted.
@@ -30,7 +30,7 @@
          *
          * @throws CertificateException if the certificate is untrusted
          */
-        void verifyCertificateChain(byte[][] asn1DerEncodedCertificateChain,
+        public void verifyCertificateChain(byte[][] asn1DerEncodedCertificateChain,
                 String authMethod) throws CertificateException;
         /**
          * Called on an SSL client when the server requests (or
@@ -43,7 +43,7 @@
          * convertible to strings with #keyType
          * @param asn1DerEncodedX500Principals CAs known to the server
          */
-        void clientCertificateRequested(
+        public void clientCertificateRequested(
                 byte[] keyTypes, byte[][] asn1DerEncodedX500Principals)
                 throws CertificateEncodingException, SSLException;
         /**
@@ -51,6 +51,6 @@
          * be after SSL_do_handshake returns when handshake cutthrough
          * is enabled.
          */
-        void handshakeCompleted();
+        public void handshakeCompleted();
     }
 }
diff --git a/android-stub/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/android-stub/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
index 77c510e..3b9be18 100644
--- a/android-stub/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
+++ b/android-stub/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
@@ -81,18 +81,15 @@
     }
 
     @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / client_cert_cb
-    @Override
     public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
             throws CertificateEncodingException, SSLException {
         throw new RuntimeException("Stub!");
     }
 
     @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / info_callback
-    @Override
     public void handshakeCompleted() {
         throw new RuntimeException("Stub!");
     }
-
     @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks
     @Override
     public void verifyCertificateChain(byte[][] bytes, String authMethod)
@@ -109,7 +106,6 @@
     public OutputStream getOutputStream() throws IOException {
         throw new RuntimeException("Stub!");
     }
-
     @Override
     public SSLSession getSession() {
         throw new RuntimeException("Stub!");
@@ -164,7 +160,6 @@
     public void setEnabledProtocols(String[] protocols) {
         throw new RuntimeException("Stub!");
     }
-
     public void setUseSessionTickets(boolean useSessionTickets) {
         throw new RuntimeException("Stub!");
     }
@@ -180,7 +175,6 @@
     public byte[] getChannelId() throws SSLException {
         throw new RuntimeException("Stub!");
     }
-
     public void setChannelIdPrivateKey(PrivateKey privateKey) {
         throw new RuntimeException("Stub!");
     }
@@ -225,13 +219,11 @@
         throw new RuntimeException("Stub!");
     }
 
-    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
     @Override
     public void setSoTimeout(int readTimeoutMilliseconds) throws SocketException {
         throw new RuntimeException("Stub!");
     }
 
-    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
     @Override
     public int getSoTimeout() throws SocketException {
         throw new RuntimeException("Stub!");
@@ -259,7 +251,6 @@
         throw new RuntimeException("Stub!");
     }
 
-    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
     @Override
     public void close() throws IOException {
         throw new RuntimeException("Stub!");
diff --git a/benchmark-graphs/build.gradle b/benchmark-graphs/build.gradle
deleted file mode 100644
index 9f5ee15..0000000
--- a/benchmark-graphs/build.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-apply plugin: 'application'
-
-dependencies {
-    compile 'com.bazaarvoice.jolt:jolt-core:0.1.0'
-    compile 'com.bazaarvoice.jolt:json-utils:0.1.0'
-}
-
-mainClassName = 'org.conscrypt.graphgen.Main'
diff --git a/benchmark-graphs/src/main/java/org/conscrypt/graphgen/Main.java b/benchmark-graphs/src/main/java/org/conscrypt/graphgen/Main.java
deleted file mode 100644
index db8e21a..0000000
--- a/benchmark-graphs/src/main/java/org/conscrypt/graphgen/Main.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2017 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 org.conscrypt.graphgen;
-
-import static java.nio.file.FileVisitResult.CONTINUE;
-
-import com.bazaarvoice.jolt.Chainr;
-import com.bazaarvoice.jolt.JsonUtils;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Utility to convert from the JMH JSON output to an HTML file.
- */
-public class Main {
-
-  public static final String JSON_TEMPLATES = "/json/templates/";
-  public static final String HTML_TEMPLATES = "/html/";
-
-  public static void main(String[] args) throws IOException, URISyntaxException {
-    if (args.length != 3) {
-      System.err.println("Usage: graphgen [template] [input.json] [output.html]");
-      listAllResources(System.err);
-      System.exit(1);
-    }
-
-    try (InputStream spec = Main.class.getResourceAsStream(JSON_TEMPLATES + args[0]);
-        InputStream jmhIn = new BufferedInputStream(new FileInputStream(args[1]));
-        OutputStream output = new BufferedOutputStream(new FileOutputStream(args[2]))) {
-      writeHtml(output, "header.html");
-      convertJmhJsonData(spec, jmhIn, output);
-      writeHtml(output, "footer.html");
-    }
-  }
-
-  private static void writeHtml(OutputStream out, String name) throws IOException {
-    InputStream header = Main.class.getResourceAsStream(HTML_TEMPLATES + name);
-    byte[] buffer = new byte[4096];
-    int numRead;
-    while ((numRead = header.read(buffer)) != -1) {
-      out.write(buffer, 0, numRead);
-    }
-  }
-
-  /**
-   * Load the JSON template data and convert it.
-   */
-  private static void convertJmhJsonData(InputStream specIn, InputStream jmhIn, OutputStream out) throws IOException {
-    List<?> chainrConfig = JsonUtils.jsonToList(specIn);
-    Chainr chainr = Chainr.fromSpec(chainrConfig);
-    List<Object> input = JsonUtils.jsonToList(jmhIn);
-    Object jsonOutput = chainr.transform(input);
-    out.write(JsonUtils.toJsonString(jsonOutput).getBytes(StandardCharsets.UTF_8));
-  }
-
-  /**
-   * Lists all the JSON templates in the Classpath.
-   */
-  private static void listAllResources(PrintStream err) throws IOException, URISyntaxException {
-    URI uri = Main.class.getResource(JSON_TEMPLATES).toURI();
-
-    final Path templatesPath;
-    if (uri.getScheme().equals("jar")) {
-      FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap());
-      templatesPath = fs.getPath(JSON_TEMPLATES);
-    } else {
-      templatesPath = Paths.get(uri);
-    }
-
-    err.println("Possible templates:");
-    PrintFileNames pfn = new PrintFileNames("  ", err);
-    Files.walkFileTree(templatesPath, pfn);
-  }
-
-  private static class PrintFileNames extends SimpleFileVisitor<Path> {
-    private final String prefix;
-    private final PrintStream out;
-
-    public PrintFileNames(String prefix, PrintStream out) {
-      this.prefix = prefix;
-      this.out = out;
-    }
-
-    @Override
-    public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes)
-        throws IOException {
-      out.println(prefix + path.getFileName());
-      return CONTINUE;
-    }
-  }
-}
diff --git a/benchmark-graphs/src/main/resources/html/footer.html b/benchmark-graphs/src/main/resources/html/footer.html
deleted file mode 100644
index 4f96fd3..0000000
--- a/benchmark-graphs/src/main/resources/html/footer.html
+++ /dev/null
@@ -1,33 +0,0 @@
-;
-
-var d3 = Plotly.d3;
-
-var WIDTH_IN_PERCENT_OF_PARENT = 60,
-    HEIGHT_IN_PERCENT_OF_PARENT = 80;
-
-var gd3 = d3.select('body')
-    .append('div')
-    .style({
-        width: WIDTH_IN_PERCENT_OF_PARENT + '%',
-        'margin-left': (100 - WIDTH_IN_PERCENT_OF_PARENT) / 2 + '%',
-
-        height: HEIGHT_IN_PERCENT_OF_PARENT + 'vh',
-        'margin-top': (100 - HEIGHT_IN_PERCENT_OF_PARENT) / 2 + 'vh'
-    });
-
-var gd = gd3.node();
-
-Plotly.plot(gd, plotlydata['data'], plotlydata['layout']);
-
-window.onresize = function() {
-  Plotly.Plots.resize(gd);
-};
-
-};
-
-window.onload = onload;
-</script>
-</head>
-<body>
-</body>
-</html>
\ No newline at end of file
diff --git a/benchmark-graphs/src/main/resources/html/header.html b/benchmark-graphs/src/main/resources/html/header.html
deleted file mode 100644
index fa1b573..0000000
--- a/benchmark-graphs/src/main/resources/html/header.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<html>
-<head>
-  <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
-  <script>
-var onload = function() {
-
-var plotlydata =
\ No newline at end of file
diff --git a/benchmark-graphs/src/main/resources/json/templates/SslEngineBenchmark.json b/benchmark-graphs/src/main/resources/json/templates/SslEngineBenchmark.json
deleted file mode 100644
index 5888356..0000000
--- a/benchmark-graphs/src/main/resources/json/templates/SslEngineBenchmark.json
+++ /dev/null
@@ -1,80 +0,0 @@
-[
-  {
-    "operation": "modify-overwrite-beta",
-    "spec": {
-      "*": {
-        "key-name": "=concat(@(1,params.sslProvider),' ',@(1,params.bufferType))"
-      }
-    }
-  },
-  {
-    "operation": "shift",
-    "spec": {
-      // pivot the data by benchmark name
-      "*": {
-        "key-name": {
-          "*": { // match any value of name
-            // go up the tree 3 levels
-            // grab the whole array element and
-            // write it to the output in an array
-            // aka benchmark1[] or benchmark2[]
-            "@(3,[&2])": "&.[]"
-          }
-        }
-      }
-    }
-  },
-  {
-    "operation": "shift",
-    "spec": {
-      // now group the relevant data
-      "*": { // benchmark 1 or 2
-        "$": "&.name",
-        "*": { // array of benchmark1 or 2
-          "params": {
-            "messageSize": "&3.x[]"
-          },
-          "primaryMetric": {
-            "score": "&3.y[]",
-            "scoreError": "&3.error_y.array[]"
-          }
-        }
-      }
-    }
-  },
-  {
-    // now convert from a map to a top level list
-    "operation": "shift",
-    "spec": {
-      // benchmark1 or benchmark2
-      "*": "data.[#1]"
-    }
-  },
-  {
-    // add graph default stuff
-    "operation": "modify-default-beta",
-    "spec": {
-      "data": {
-        "*": {
-          "mode": "lines+markers",
-          "type": "scatter"
-        }
-      },
-      "layout": {
-        "autosize": true,
-        "yaxis": {
-          "type": "linear",
-          "autorange": true,
-          "title": "messages/sec"
-        },
-        "title": "SslEngineBenchmark.sendMessage",
-        "showlegend": true,
-        "xaxis": {
-          "title": "size of message",
-          "type": "category",
-          "autorange": true
-        }
-      }
-    }
-  }
-]
diff --git a/build.gradle b/build.gradle
index 18e8b48..45e4240 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,7 +17,7 @@
 }
 
 subprojects {
-    def androidProject = (project.name == 'conscrypt-android') || (project.name == 'conscrypt-android-platform')
+    def androidProject = project.name == 'conscrypt-android'
     if (!androidProject) {
         apply plugin: 'java'
         apply plugin: 'cpp'
@@ -82,14 +82,15 @@
 
         jmhVersion = '1.17.4'
         libraries = [
-                roboelectric: 'org.robolectric:android-all:7.1.0_r7-robolectric-0',
+                roboelectric: 'org.robolectric:android-all:5.0.0_r2-robolectric-1',
 
                 // Test dependencies.
+                guava : 'com.google.guava:guava:19.0',
                 junit  : 'junit:junit:4.12',
                 mockito: 'org.mockito:mockito-core:1.9.5',
                 truth  : 'com.google.truth:truth:0.28',
-                bouncycastle_provider: 'org.bouncycastle:bcprov-jdk15on:1.56',
-                bouncycastle_apis: 'org.bouncycastle:bcpkix-jdk15on:1.56',
+                bouncycastle_provider: 'org.bouncycastle:bcprov-jdk15on:1.55',
+                bouncycastle_apis: 'org.bouncycastle:bcpkix-jdk15on:1.55',
 
                 // Benchmark dependencies
                 jmh_core: "org.openjdk.jmh:jmh-core:${jmhVersion}",
diff --git a/common/src/jni/main/cpp/NativeCrypto.cpp b/common/src/jni/main/cpp/NativeCrypto.cpp
index e021b93..cbb0f23 100644
--- a/common/src/jni/main/cpp/NativeCrypto.cpp
+++ b/common/src/jni/main/cpp/NativeCrypto.cpp
@@ -510,8 +510,6 @@
 ENGINE *g_engine;
 int g_rsa_exdata_index;
 int g_ecdsa_exdata_index;
-RSA_METHOD g_rsa_method;
-ECDSA_METHOD g_ecdsa_method;
 std::once_flag g_engine_once;
 
 void init_engine_globals();
@@ -690,6 +688,41 @@
   return 1;
 }
 
+int RsaMethodVerifyRaw(RSA* /* rsa */,
+                       size_t* /* out_len */,
+                       uint8_t* /* out */,
+                       size_t /* max_out */,
+                       const uint8_t* /* in */,
+                       size_t /* in_len */,
+                       int /* padding */) {
+  OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE);
+  return 0;
+}
+
+const RSA_METHOD android_rsa_method = {
+        {
+                0 /* references */, 1 /* is_static */
+        } /* common */,
+        nullptr /* app_data */,
+
+        nullptr /* init */,
+        nullptr /* finish */,
+        RsaMethodSize,
+        nullptr /* sign */,
+        nullptr /* verify */,
+        RsaMethodEncrypt,
+        RsaMethodSignRaw,
+        RsaMethodDecrypt,
+        RsaMethodVerifyRaw,
+        nullptr /* mod_exp */,
+        nullptr /* bn_mod_exp */,
+        nullptr /* private_transform */,
+        RSA_FLAG_OPAQUE,
+        nullptr /* keygen */,
+        nullptr /* multi_prime_keygen */,
+        nullptr /* supports_digest */,
+};
+
 // Custom ECDSA_METHOD that uses the platform APIs.
 // Note that for now, only signing through ECDSA_sign() is really supported.
 // all other method pointers are either stubs returning errors, or no-ops.
@@ -742,27 +775,38 @@
     return 1;
 }
 
+int EcdsaMethodVerify(const uint8_t* /* digest */,
+                      size_t /* digest_len */,
+                      const uint8_t* /* sig */,
+                      size_t /* sig_len */,
+                      EC_KEY* /* ec_key */) {
+  OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NOT_IMPLEMENTED);
+  return 0;
+}
+
+const ECDSA_METHOD android_ecdsa_method = {
+        {
+                0 /* references */, 1 /* is_static */
+        } /* common */,
+        nullptr /* app_data */,
+
+        nullptr /* init */,
+        nullptr /* finish */,
+        nullptr /* group order size */,
+        EcdsaMethodSign,
+        EcdsaMethodVerify,
+        ECDSA_FLAG_OPAQUE,
+};
+
 void init_engine_globals() {
     g_rsa_exdata_index = RSA_get_ex_new_index(0 /* argl */, nullptr /* argp */,
                                               nullptr /* new_func */, ExDataDup, ExDataFree);
     g_ecdsa_exdata_index = EC_KEY_get_ex_new_index(0 /* argl */, nullptr /* argp */,
                                                    nullptr /* new_func */, ExDataDup, ExDataFree);
 
-    g_rsa_method.common.is_static = 1;
-    g_rsa_method.size = RsaMethodSize;
-    // TODO(davidben): Update BoringSSL to ignore this hook and remove this.
-    g_rsa_method.encrypt = RsaMethodEncrypt;
-    g_rsa_method.sign_raw = RsaMethodSignRaw;
-    g_rsa_method.decrypt = RsaMethodDecrypt;
-    g_rsa_method.flags = RSA_FLAG_OPAQUE;
-
-    g_ecdsa_method.common.is_static = 1;
-    g_ecdsa_method.sign = EcdsaMethodSign;
-    g_ecdsa_method.flags = ECDSA_FLAG_OPAQUE;
-
     g_engine = ENGINE_new();
-    ENGINE_set_RSA_method(g_engine, &g_rsa_method, sizeof(g_rsa_method));
-    ENGINE_set_ECDSA_method(g_engine, &g_ecdsa_method, sizeof(g_ecdsa_method));
+    ENGINE_set_RSA_method(g_engine, &android_rsa_method, sizeof(android_rsa_method));
+    ENGINE_set_ECDSA_method(g_engine, &android_ecdsa_method, sizeof(android_ecdsa_method));
 }
 
 }  // anonymous namespace
diff --git a/common/src/main/java/org/conscrypt/AbstractOpenSSLSession.java b/common/src/main/java/org/conscrypt/AbstractOpenSSLSession.java
index e5de29a..815150d 100644
--- a/common/src/main/java/org/conscrypt/AbstractOpenSSLSession.java
+++ b/common/src/main/java/org/conscrypt/AbstractOpenSSLSession.java
@@ -32,8 +32,11 @@
 
 /**
  * Extends the base SSLSession with some methods used exclusively in Conscrypt.
+ *
+ * @hide
  */
-abstract class AbstractOpenSSLSession implements SSLSession {
+@Internal
+public abstract class AbstractOpenSSLSession implements SSLSession {
     private final Map<String, Object> values = new HashMap<String, Object>();
 
     private volatile javax.security.cert.X509Certificate[] peerCertificateChain;
@@ -46,7 +49,7 @@
      * Class constructor creates an SSL session context given the appropriate
      * session context.
      */
-    AbstractOpenSSLSession(AbstractSessionContext sessionContext) {
+    protected AbstractOpenSSLSession(AbstractSessionContext sessionContext) {
         this.sessionContext = sessionContext;
     }
 
diff --git a/common/src/main/java/org/conscrypt/AbstractSessionContext.java b/common/src/main/java/org/conscrypt/AbstractSessionContext.java
index 61d595d..c034511 100644
--- a/common/src/main/java/org/conscrypt/AbstractSessionContext.java
+++ b/common/src/main/java/org/conscrypt/AbstractSessionContext.java
@@ -45,19 +45,19 @@
      */
     private static final int DEFAULT_SESSION_TIMEOUT_SECONDS = 8 * 60 * 60;
 
-    private volatile int maximumSize;
-    private volatile int timeout = DEFAULT_SESSION_TIMEOUT_SECONDS;
+    volatile int maximumSize;
+    volatile int timeout = DEFAULT_SESSION_TIMEOUT_SECONDS;
 
     final long sslCtxNativePointer = NativeCrypto.SSL_CTX_new();
 
     /** Identifies OpenSSL sessions. */
-    private static final int OPEN_SSL = 1;
+    static final int OPEN_SSL = 1;
 
     /** Identifies OpenSSL sessions with OCSP stapled data. */
-    private static final int OPEN_SSL_WITH_OCSP = 2;
+    static final int OPEN_SSL_WITH_OCSP = 2;
 
     /** Identifies OpenSSL sessions with TLS SCT data. */
-    private static final int OPEN_SSL_WITH_TLS_SCT = 3;
+    static final int OPEN_SSL_WITH_TLS_SCT = 3;
 
     @SuppressWarnings("serial")
     private final Map<ByteArray, SSLSession> sessions = new LinkedHashMap<ByteArray, SSLSession>() {
@@ -140,7 +140,7 @@
     /**
      * Makes sure cache size is < maximumSize.
      */
-    private void trimToSize() {
+    protected void trimToSize() {
         synchronized (sessions) {
             int size = sessions.size();
             if (size > maximumSize) {
@@ -204,7 +204,7 @@
      *
      * @return session data as bytes or null if the session can't be converted
      */
-    byte[] toBytes(SSLSession session) {
+    public byte[] toBytes(SSLSession session) {
         // TODO: Support SSLSessionImpl, too.
         if (!(session instanceof OpenSSLSessionImpl)) {
             return null;
@@ -274,7 +274,7 @@
      *
      * @return a session or null if the session can't be converted
      */
-    OpenSSLSessionImpl toSession(byte[] data, String host, int port) {
+    public OpenSSLSessionImpl toSession(byte[] data, String host, int port) {
         ByteBuffer buf = ByteBuffer.wrap(data);
         try {
             int type = buf.getInt();
@@ -355,7 +355,7 @@
         }
     }
 
-    SSLSession wrapSSLSessionIfNeeded(SSLSession session) {
+    protected SSLSession wrapSSLSessionIfNeeded(SSLSession session) {
         if (session instanceof AbstractOpenSSLSession) {
             return Platform.wrapSSLSession((AbstractOpenSSLSession) session);
         } else {
@@ -390,7 +390,7 @@
         }
     }
 
-    private static void log(Throwable t) {
+    static void log(Throwable t) {
         System.out.println("Error inflating SSL session: "
                 + (t.getMessage() != null ? t.getMessage() : t.getClass().getName()));
     }
diff --git a/common/src/main/java/org/conscrypt/AddressUtils.java b/common/src/main/java/org/conscrypt/AddressUtils.java
index a363411..0ea524d 100644
--- a/common/src/main/java/org/conscrypt/AddressUtils.java
+++ b/common/src/main/java/org/conscrypt/AddressUtils.java
@@ -20,8 +20,11 @@
 
 /**
  * Utilities to check whether IP addresses meet some criteria.
+ *
+ * Visible for testing only.
+ * @hide
  */
-final class AddressUtils {
+public final class AddressUtils {
     /*
      * Regex that matches valid IPv4 and IPv6 addresses.
      */
@@ -30,25 +33,33 @@
 
     private static Pattern ipPattern;
 
-    private AddressUtils() {}
+    private AddressUtils() {
+    }
 
     /**
      * Returns true when the supplied hostname is valid for SNI purposes.
      */
-    static boolean isValidSniHostname(String sniHostname) {
+    public static boolean isValidSniHostname(String sniHostname) {
         if (sniHostname == null) {
             return false;
         }
 
         // Must be a FQDN.
-        return sniHostname.indexOf('.') != -1 && !Platform.isLiteralIpAddress(sniHostname);
+        if (sniHostname.indexOf('.') == -1) {
+            return false;
+        }
 
+        if (Platform.isLiteralIpAddress(sniHostname)) {
+            return false;
+        }
+
+        return true;
     }
 
     /**
      * Returns true if the supplied hostname is an literal IP address.
      */
-    static boolean isLiteralIpAddress(String hostname) {
+    public static boolean isLiteralIpAddress(String hostname) {
         /* This is here for backwards compatibility for pre-Honeycomb devices. */
         Pattern ipPattern = AddressUtils.ipPattern;
         if (ipPattern == null) {
diff --git a/common/src/main/java/org/conscrypt/ArrayUtils.java b/common/src/main/java/org/conscrypt/ArrayUtils.java
index d254e2e..34246bd 100644
--- a/common/src/main/java/org/conscrypt/ArrayUtils.java
+++ b/common/src/main/java/org/conscrypt/ArrayUtils.java
@@ -20,7 +20,8 @@
  * Compatibility utility for Arrays.
  */
 final class ArrayUtils {
-    private ArrayUtils() {}
+    private ArrayUtils() {
+    }
 
     /**
      * Checks that the range described by {@code offset} and {@code count}
diff --git a/platform/src/main/java/org/conscrypt/ChainStrengthAnalyzer.java b/common/src/main/java/org/conscrypt/ChainStrengthAnalyzer.java
similarity index 96%
rename from platform/src/main/java/org/conscrypt/ChainStrengthAnalyzer.java
rename to common/src/main/java/org/conscrypt/ChainStrengthAnalyzer.java
index 0807178..9c4d5cc 100644
--- a/platform/src/main/java/org/conscrypt/ChainStrengthAnalyzer.java
+++ b/common/src/main/java/org/conscrypt/ChainStrengthAnalyzer.java
@@ -71,7 +71,7 @@
         checkSignatureAlgorithm(cert);
     }
 
-    private static void checkKeyLength(X509Certificate cert) throws CertificateException {
+    private static final void checkKeyLength(X509Certificate cert) throws CertificateException {
         Object pubkey = cert.getPublicKey();
         if (pubkey instanceof RSAPublicKey) {
             int modulusLength = ((RSAPublicKey) pubkey).getModulus().bitLength();
@@ -100,7 +100,7 @@
         }
     }
 
-    private static void checkSignatureAlgorithm(
+    private static final void checkSignatureAlgorithm(
             X509Certificate cert) throws CertificateException {
         String oid = cert.getSigAlgOID();
         for (String blacklisted : SIGNATURE_ALGORITHM_OID_BLACKLIST) {
diff --git a/common/src/main/java/org/conscrypt/Conscrypt.java b/common/src/main/java/org/conscrypt/Conscrypt.java
index f2ede81..1862cb2 100644
--- a/common/src/main/java/org/conscrypt/Conscrypt.java
+++ b/common/src/main/java/org/conscrypt/Conscrypt.java
@@ -19,17 +19,14 @@
 import java.io.UnsupportedEncodingException;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
-import java.security.KeyManagementException;
 import java.security.PrivateKey;
 import java.security.Provider;
-import javax.net.ssl.SSLContextSpi;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLEngineResult;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.X509TrustManager;
 
 /**
  * Core API for creating and configuring all Conscrypt types.
@@ -52,36 +49,6 @@
     }
 
     /**
-     * Constructs a new instance of the preferred {@link SSLContextSpi}.
-     */
-    public static SSLContextSpi newPreferredSSLContextSpi() {
-        return OpenSSLContextImpl.getPreferred();
-    }
-
-    /**
-     * Gets the default X.509 trust manager.
-     */
-    @ExperimentalApi
-    public static X509TrustManager getDefaultX509TrustManager() throws KeyManagementException {
-        return SSLParametersImpl.getDefaultX509TrustManager();
-    }
-
-    /**
-     * Utility that exposes common TLS constants.
-     */
-    @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;
-        }
-    }
-
-    /**
      * Utility methods for configuring Conscrypt socket factories.
      */
     public static final class SocketFactories {
diff --git a/common/src/main/java/org/conscrypt/CryptoUpcalls.java b/common/src/main/java/org/conscrypt/CryptoUpcalls.java
index af1164d..c40dac4 100644
--- a/common/src/main/java/org/conscrypt/CryptoUpcalls.java
+++ b/common/src/main/java/org/conscrypt/CryptoUpcalls.java
@@ -32,8 +32,11 @@
  * usually backed by hardware so we don't have access directly to the private
  * key material. If it were a key where we can get to the private key, we
  * would not ever call into this class.
+ *
+ * @hide
  */
-final class CryptoUpcalls {
+@Internal
+public final class CryptoUpcalls {
 
     private CryptoUpcalls() {
     }
@@ -58,7 +61,7 @@
         return providers;
     }
 
-    static byte[] rawSignDigestWithPrivateKey(PrivateKey javaKey, byte[] message) {
+    public static byte[] rawSignDigestWithPrivateKey(PrivateKey javaKey, byte[] message) {
         // Get the raw signature algorithm for this key type.
         String algorithm;
         // Hint: Algorithm names come from:
@@ -129,7 +132,7 @@
         }
     }
 
-    static byte[] rsaDecryptWithPrivateKey(PrivateKey javaKey, int openSSLPadding,
+    public static byte[] rsaDecryptWithPrivateKey(PrivateKey javaKey, int openSSLPadding,
             byte[] input) {
         String keyAlgorithm = javaKey.getAlgorithm();
         if (!"RSA".equals(keyAlgorithm)) {
diff --git a/common/src/main/java/org/conscrypt/DuckTypedPSKKeyManager.java b/common/src/main/java/org/conscrypt/DuckTypedPSKKeyManager.java
index c6e1ae9..d67db77 100644
--- a/common/src/main/java/org/conscrypt/DuckTypedPSKKeyManager.java
+++ b/common/src/main/java/org/conscrypt/DuckTypedPSKKeyManager.java
@@ -31,9 +31,10 @@
  * Visible for testing only.
  *
  * @deprecated This abstraction is deprecated because it does not work with TLS 1.3.
+ * @hide
  */
 @Deprecated
-final class DuckTypedPSKKeyManager implements PSKKeyManager {
+public class DuckTypedPSKKeyManager implements PSKKeyManager {
 
     private final Object mDelegate;
 
@@ -48,7 +49,7 @@
      * @throws NoSuchMethodException if {@code obj} does not implement a method of the
      *         {@code PSKKeyManager} interface.
      */
-    static DuckTypedPSKKeyManager getInstance(Object obj) throws NoSuchMethodException {
+    public static DuckTypedPSKKeyManager getInstance(Object obj) throws NoSuchMethodException {
         Class<?> sourceClass = obj.getClass();
         for (Method targetMethod : PSKKeyManager.class.getMethods()) {
             if (targetMethod.isSynthetic()) {
diff --git a/common/src/main/java/org/conscrypt/EvpMdRef.java b/common/src/main/java/org/conscrypt/EvpMdRef.java
index 633b088..24021bc 100644
--- a/common/src/main/java/org/conscrypt/EvpMdRef.java
+++ b/common/src/main/java/org/conscrypt/EvpMdRef.java
@@ -21,16 +21,19 @@
 
 /**
  * Utility class to convert between BoringSSL- and JCE-style message digest identifiers.
+ *
+ * @hide
  */
-final class EvpMdRef {
-    static final String MGF1_ALGORITHM_NAME = "MGF1";
-    static final String MGF1_OID = "1.2.840.113549.1.1.8";
+@Internal
+public final class EvpMdRef {
+    public static final String MGF1_ALGORITHM_NAME = "MGF1";
+    public static final String MGF1_OID = "1.2.840.113549.1.1.8";
 
     /**
      * Returns the canonical JCA digest algorithm name for the provided digest
      * algorithm name or {@code null} if the digest algorithm is not known.
      */
-    static String getJcaDigestAlgorithmStandardName(String algorithm) {
+    public static String getJcaDigestAlgorithmStandardName(String algorithm) {
         String algorithmUpper = algorithm.toUpperCase(Locale.US);
         if ((SHA256.JCA_NAME.equals(algorithmUpper)) || (SHA256.OID.equals(algorithmUpper))) {
             return SHA256.JCA_NAME;
@@ -50,7 +53,7 @@
         }
     }
 
-    static long getEVP_MDByJcaDigestAlgorithmStandardName(String algorithm)
+    public static long getEVP_MDByJcaDigestAlgorithmStandardName(String algorithm)
             throws NoSuchAlgorithmException {
         String algorithmUpper = algorithm.toUpperCase(Locale.US);
         if (SHA256.JCA_NAME.equals(algorithmUpper)) {
@@ -68,7 +71,7 @@
         }
     }
 
-    static int getDigestSizeBytesByJcaDigestAlgorithmStandardName(String algorithm)
+    public static int getDigestSizeBytesByJcaDigestAlgorithmStandardName(String algorithm)
             throws NoSuchAlgorithmException {
         String algorithmUpper = algorithm.toUpperCase(Locale.US);
         if (SHA256.JCA_NAME.equals(algorithmUpper)) {
@@ -86,7 +89,7 @@
         }
     }
 
-    static String getJcaDigestAlgorithmStandardNameFromEVP_MD(long evpMdRef) {
+    public static String getJcaDigestAlgorithmStandardNameFromEVP_MD(long evpMdRef) {
         if (evpMdRef == MD5.EVP_MD) {
             return MD5.JCA_NAME;
         } else if (evpMdRef == SHA1.EVP_MD) {
@@ -104,55 +107,55 @@
         }
     }
 
-    static final class MD5 {
-        static final String JCA_NAME = "MD5";
-        static final String OID = "1.2.840.113549.2.5";
-        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("md5");
-        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+    public static final class MD5 {
+        public static final String JCA_NAME = "MD5";
+        public static final String OID = "1.2.840.113549.2.5";
+        public static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("md5");
+        public static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
 
         private MD5() {}
     }
 
-    static final class SHA1 {
-        static final String JCA_NAME = "SHA-1";
-        static final String OID = "1.3.14.3.2.26";
-        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha1");
-        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+    public static final class SHA1 {
+        public static final String JCA_NAME = "SHA-1";
+        public static final String OID = "1.3.14.3.2.26";
+        public static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha1");
+        public static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
         private SHA1() {}
     }
 
-    static final class SHA224 {
-        static final String JCA_NAME = "SHA-224";
-        static final String OID = "2.16.840.1.101.3.4.2.4";
-        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha224");
-        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+    public static final class SHA224 {
+        public static final String JCA_NAME = "SHA-224";
+        public static final String OID = "2.16.840.1.101.3.4.2.4";
+        public static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha224");
+        public static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
 
         private SHA224() {}
     }
 
-    static final class SHA256 {
-        static final String JCA_NAME = "SHA-256";
-        static final String OID = "2.16.840.1.101.3.4.2.1";
-        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha256");
-        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+    public static final class SHA256 {
+        public static final String JCA_NAME = "SHA-256";
+        public static final String OID = "2.16.840.1.101.3.4.2.1";
+        public static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha256");
+        public static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
 
         private SHA256() {}
     }
 
-    static final class SHA384 {
-        static final String JCA_NAME = "SHA-384";
-        static final String OID = "2.16.840.1.101.3.4.2.2";
-        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha384");
-        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+    public static final class SHA384 {
+        public static final String JCA_NAME = "SHA-384";
+        public static final String OID = "2.16.840.1.101.3.4.2.2";
+        public static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha384");
+        public static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
 
         private SHA384() {}
     }
 
-    static final class SHA512 {
-        static final String JCA_NAME = "SHA-512";
-        static final String OID = "2.16.840.1.101.3.4.2.3";
-        static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha512");
-        static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
+    public static final class SHA512 {
+        public static final String JCA_NAME = "SHA-512";
+        public static final String OID = "2.16.840.1.101.3.4.2.3";
+        public static final long EVP_MD = NativeCrypto.EVP_get_digestbyname("sha512");
+        public static final int SIZE_BYTES = NativeCrypto.EVP_MD_size(EVP_MD);
 
         private SHA512() {}
     }
diff --git a/common/src/main/java/org/conscrypt/FileClientSessionCache.java b/common/src/main/java/org/conscrypt/FileClientSessionCache.java
index 9334f0a..1bbebd7 100644
--- a/common/src/main/java/org/conscrypt/FileClientSessionCache.java
+++ b/common/src/main/java/org/conscrypt/FileClientSessionCache.java
@@ -38,7 +38,7 @@
  * @hide
  */
 @Internal
-public final class FileClientSessionCache {
+public class FileClientSessionCache {
     public static final int MAX_SIZE = 12; // ~72k
 
     private FileClientSessionCache() {}
diff --git a/common/src/main/java/org/conscrypt/GCMParameters.java b/common/src/main/java/org/conscrypt/GCMParameters.java
index d3c95bd..a111420 100644
--- a/common/src/main/java/org/conscrypt/GCMParameters.java
+++ b/common/src/main/java/org/conscrypt/GCMParameters.java
@@ -20,15 +20,18 @@
  * GCM parameters used during an ciphering operation with {@link OpenSSLCipher}.
  * This class exists solely for backward compatibility with Android versions
  * that did not have the {@code GCMParameterSpec} class.
+ *
+ * @hide
  */
-final class GCMParameters {
+@Internal
+public class GCMParameters {
     /** The tag length in bits. */
-    private final int tLen;
+    public final int tLen;
 
     /** Actually the nonce value for the GCM operation. */
-    private final byte[] iv;
+    public final byte[] iv;
 
-    GCMParameters(int tLen, byte[] iv) {
+    public GCMParameters(int tLen, byte[] iv) {
         this.tLen = tLen;
         this.iv = iv;
     }
@@ -36,14 +39,14 @@
     /**
      * Returns the tag length in bits.
      */
-    int getTLen() {
+    public int getTLen() {
         return tLen;
     }
 
     /**
      * Returns a non-cloned version of the IV.
      */
-    byte[] getIV() {
+    public byte[] getIV() {
         return iv;
     }
 }
diff --git a/common/src/main/java/org/conscrypt/HandshakeListener.java b/common/src/main/java/org/conscrypt/HandshakeListener.java
index 82bfa5b..2ac1a21 100644
--- a/common/src/main/java/org/conscrypt/HandshakeListener.java
+++ b/common/src/main/java/org/conscrypt/HandshakeListener.java
@@ -22,10 +22,10 @@
  * Similar in concept to {@link javax.net.ssl.HandshakeCompletedListener}, but used for listening directly
  * to the engine. Allows the caller to be notified immediately upon completion of the TLS handshake.
  */
-public abstract class HandshakeListener {
+public interface HandshakeListener {
 
     /**
      * Called by the engine when the TLS handshake has completed.
      */
-    public abstract void onHandshakeFinished() throws SSLException;
+    void onHandshakeFinished() throws SSLException;
 }
diff --git a/common/src/main/java/org/conscrypt/KeyManagerFactoryImpl.java b/common/src/main/java/org/conscrypt/KeyManagerFactoryImpl.java
index 575316a..36a7e6f 100644
--- a/common/src/main/java/org/conscrypt/KeyManagerFactoryImpl.java
+++ b/common/src/main/java/org/conscrypt/KeyManagerFactoryImpl.java
@@ -113,4 +113,5 @@
         }
         return new KeyManager[] { new KeyManagerImpl(keyStore, pwd) };
     }
+
 }
diff --git a/common/src/main/java/org/conscrypt/KeyManagerImpl.java b/common/src/main/java/org/conscrypt/KeyManagerImpl.java
index 27c0f60..01c618d 100644
--- a/common/src/main/java/org/conscrypt/KeyManagerImpl.java
+++ b/common/src/main/java/org/conscrypt/KeyManagerImpl.java
@@ -45,8 +45,10 @@
  * from socket or engine.
  *
  * @see javax.net.ssl.KeyManager
+ * @hide
  */
-class KeyManagerImpl extends X509ExtendedKeyManager {
+@Internal
+public class KeyManagerImpl extends X509ExtendedKeyManager {
 
     // hashed key store information
     private final Hashtable<String, PrivateKeyEntry> hash;
@@ -57,7 +59,7 @@
      * @param keyStore
      * @param pwd
      */
-    KeyManagerImpl(KeyStore keyStore, char[] pwd) {
+    public KeyManagerImpl(KeyStore keyStore, char[] pwd) {
         this.hash = new Hashtable<String, PrivateKeyEntry>();
         final Enumeration<String> aliases;
         try {
diff --git a/common/src/main/java/org/conscrypt/NativeCrypto.java b/common/src/main/java/org/conscrypt/NativeCrypto.java
index 509b2ef..d7956de 100644
--- a/common/src/main/java/org/conscrypt/NativeCrypto.java
+++ b/common/src/main/java/org/conscrypt/NativeCrypto.java
@@ -51,6 +51,7 @@
  */
 @Internal
 public final class NativeCrypto {
+
     // --- OpenSSL library initialization --------------------------------------
     static {
         NativeCryptoJni.init();
@@ -61,275 +62,281 @@
 
     // --- DSA/RSA public/private key handling functions -----------------------
 
-    static native long EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q,
+    public static native long EVP_PKEY_new_RSA(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q,
             byte[] dmp1, byte[] dmq1, byte[] iqmp);
 
-    static native int EVP_PKEY_size(NativeRef.EVP_PKEY pkey);
+    public static native int EVP_PKEY_size(NativeRef.EVP_PKEY pkey);
 
-    static native int EVP_PKEY_type(NativeRef.EVP_PKEY pkey);
+    public static native int EVP_PKEY_type(NativeRef.EVP_PKEY pkey);
 
-    static native String EVP_PKEY_print_public(NativeRef.EVP_PKEY pkeyRef);
+    public static native String EVP_PKEY_print_public(NativeRef.EVP_PKEY pkeyRef);
 
-    static native String EVP_PKEY_print_params(NativeRef.EVP_PKEY pkeyRef);
+    public static native String EVP_PKEY_print_params(NativeRef.EVP_PKEY pkeyRef);
 
-    static native void EVP_PKEY_free(long pkey);
+    public static native void EVP_PKEY_free(long pkey);
 
-    static native int EVP_PKEY_cmp(NativeRef.EVP_PKEY pkey1, NativeRef.EVP_PKEY pkey2);
+    public static native int EVP_PKEY_cmp(NativeRef.EVP_PKEY pkey1, NativeRef.EVP_PKEY pkey2);
 
-    static native byte[] i2d_PKCS8_PRIV_KEY_INFO(NativeRef.EVP_PKEY pkey);
+    public static native byte[] i2d_PKCS8_PRIV_KEY_INFO(NativeRef.EVP_PKEY pkey);
 
-    static native long d2i_PKCS8_PRIV_KEY_INFO(byte[] data);
+    public static native long d2i_PKCS8_PRIV_KEY_INFO(byte[] data);
 
-    static native byte[] i2d_PUBKEY(NativeRef.EVP_PKEY pkey);
+    public static native byte[] i2d_PUBKEY(NativeRef.EVP_PKEY pkey);
 
-    static native long d2i_PUBKEY(byte[] data);
+    public static native long d2i_PUBKEY(byte[] data);
 
-    static native long PEM_read_bio_PUBKEY(long bioCtx);
+    public static native long PEM_read_bio_PUBKEY(long bioCtx);
 
-    static native long PEM_read_bio_PrivateKey(long bioCtx);
+    public static native long PEM_read_bio_PrivateKey(long bioCtx);
 
-    static native long getRSAPrivateKeyWrapper(PrivateKey key, byte[] modulus);
+    public static native long getRSAPrivateKeyWrapper(PrivateKey key, byte[] modulus);
 
-    static native long getECPrivateKeyWrapper(PrivateKey key, NativeRef.EC_GROUP ecGroupRef);
+    public static native long getECPrivateKeyWrapper(PrivateKey key,
+            NativeRef.EC_GROUP ecGroupRef);
 
-    static native long RSA_generate_key_ex(int modulusBits, byte[] publicExponent);
+    public static native long RSA_generate_key_ex(int modulusBits, byte[] publicExponent);
 
-    static native int RSA_size(NativeRef.EVP_PKEY pkey);
+    public static native int RSA_size(NativeRef.EVP_PKEY pkey);
 
-    static native int RSA_private_encrypt(
-            int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey, int padding);
+    public static native int RSA_private_encrypt(int flen, byte[] from, byte[] to,
+            NativeRef.EVP_PKEY pkey, int padding);
 
-    static native int RSA_public_decrypt(int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey,
-            int padding) throws BadPaddingException, SignatureException;
+    public static native int RSA_public_decrypt(int flen, byte[] from, byte[] to,
+            NativeRef.EVP_PKEY pkey, int padding) throws BadPaddingException, SignatureException;
 
-    static native int RSA_public_encrypt(
-            int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey, int padding);
+    public static native int RSA_public_encrypt(int flen, byte[] from, byte[] to,
+            NativeRef.EVP_PKEY pkey, int padding);
 
-    static native int RSA_private_decrypt(int flen, byte[] from, byte[] to, NativeRef.EVP_PKEY pkey,
-            int padding) throws BadPaddingException, SignatureException;
+    public static native int RSA_private_decrypt(int flen, byte[] from, byte[] to,
+            NativeRef.EVP_PKEY pkey, int padding) throws BadPaddingException, SignatureException;
 
     /**
      * @return array of {n, e}
      */
-    static native byte[][] get_RSA_public_params(NativeRef.EVP_PKEY rsa);
+    public static native byte[][] get_RSA_public_params(NativeRef.EVP_PKEY rsa);
 
     /**
      * @return array of {n, e, d, p, q, dmp1, dmq1, iqmp}
      */
-    static native byte[][] get_RSA_private_params(NativeRef.EVP_PKEY rsa);
+    public static native byte[][] get_RSA_private_params(NativeRef.EVP_PKEY rsa);
 
-    static native byte[] i2d_RSAPublicKey(NativeRef.EVP_PKEY rsa);
+    public static native byte[] i2d_RSAPublicKey(NativeRef.EVP_PKEY rsa);
 
-    static native byte[] i2d_RSAPrivateKey(NativeRef.EVP_PKEY rsa);
+    public static native byte[] i2d_RSAPrivateKey(NativeRef.EVP_PKEY rsa);
 
     // --- EC functions --------------------------
 
-    static native long EVP_PKEY_new_EC_KEY(
-            NativeRef.EC_GROUP groupRef, NativeRef.EC_POINT pubkeyRef, byte[] privkey);
+    public static native long EVP_PKEY_new_EC_KEY(NativeRef.EC_GROUP groupRef,
+            NativeRef.EC_POINT pubkeyRef, byte[] privkey);
 
-    static native long EC_GROUP_new_by_curve_name(String curveName);
+    public static native long EC_GROUP_new_by_curve_name(String curveName);
 
-    static native long EC_GROUP_new_arbitrary(
-            byte[] p, byte[] a, byte[] b, byte[] x, byte[] y, byte[] order, int cofactor);
+    public static native long EC_GROUP_new_arbitrary(byte[] p, byte[] a, byte[] b, byte[] x,
+                                                     byte[] y, byte[] order, int cofactor);
 
-    static native String EC_GROUP_get_curve_name(NativeRef.EC_GROUP groupRef);
+    public static native String EC_GROUP_get_curve_name(NativeRef.EC_GROUP groupRef);
 
-    static native byte[][] EC_GROUP_get_curve(NativeRef.EC_GROUP groupRef);
+    public static native byte[][] EC_GROUP_get_curve(NativeRef.EC_GROUP groupRef);
 
-    static native void EC_GROUP_clear_free(long groupRef);
+    public static native void EC_GROUP_clear_free(long groupRef);
 
-    static native long EC_GROUP_get_generator(NativeRef.EC_GROUP groupRef);
+    public static native long EC_GROUP_get_generator(NativeRef.EC_GROUP groupRef);
 
-    static native byte[] EC_GROUP_get_order(NativeRef.EC_GROUP groupRef);
+    public static native byte[] EC_GROUP_get_order(NativeRef.EC_GROUP groupRef);
 
-    static native int EC_GROUP_get_degree(NativeRef.EC_GROUP groupRef);
+    public static native int EC_GROUP_get_degree(NativeRef.EC_GROUP groupRef);
 
-    static native byte[] EC_GROUP_get_cofactor(NativeRef.EC_GROUP groupRef);
+    public static native byte[] EC_GROUP_get_cofactor(NativeRef.EC_GROUP groupRef);
 
-    static native long EC_POINT_new(NativeRef.EC_GROUP groupRef);
+    public static native long EC_POINT_new(NativeRef.EC_GROUP groupRef);
 
-    static native void EC_POINT_clear_free(long pointRef);
+    public static native void EC_POINT_clear_free(long pointRef);
 
-    static native byte[][] EC_POINT_get_affine_coordinates(
-            NativeRef.EC_GROUP groupRef, NativeRef.EC_POINT pointRef);
+    public static native byte[][] EC_POINT_get_affine_coordinates(NativeRef.EC_GROUP groupRef,
+            NativeRef.EC_POINT pointRef);
 
-    static native void EC_POINT_set_affine_coordinates(
-            NativeRef.EC_GROUP groupRef, NativeRef.EC_POINT pointRef, byte[] x, byte[] y);
+    public static native void EC_POINT_set_affine_coordinates(NativeRef.EC_GROUP groupRef,
+            NativeRef.EC_POINT pointRef, byte[] x, byte[] y);
 
-    static native long EC_KEY_generate_key(NativeRef.EC_GROUP groupRef);
+    public static native long EC_KEY_generate_key(NativeRef.EC_GROUP groupRef);
 
-    static native long EC_KEY_get1_group(NativeRef.EVP_PKEY pkeyRef);
+    public static native long EC_KEY_get1_group(NativeRef.EVP_PKEY pkeyRef);
 
-    static native byte[] EC_KEY_get_private_key(NativeRef.EVP_PKEY keyRef);
+    public static native byte[] EC_KEY_get_private_key(NativeRef.EVP_PKEY keyRef);
 
-    static native long EC_KEY_get_public_key(NativeRef.EVP_PKEY keyRef);
+    public static native long EC_KEY_get_public_key(NativeRef.EVP_PKEY keyRef);
 
-    static native int ECDH_compute_key(byte[] out, int outOffset, NativeRef.EVP_PKEY publicKeyRef,
-            NativeRef.EVP_PKEY privateKeyRef) throws InvalidKeyException;
+    public static native int ECDH_compute_key(byte[] out, int outOffset,
+            NativeRef.EVP_PKEY publicKeyRef, NativeRef.EVP_PKEY privateKeyRef) throws
+            InvalidKeyException;
 
     // --- Message digest functions --------------
 
     // These return const references
-    static native long EVP_get_digestbyname(String name);
+    public static native long EVP_get_digestbyname(String name);
 
-    static native int EVP_MD_size(long evp_md_const);
+    public static native int EVP_MD_size(long evp_md_const);
 
-    static native int EVP_MD_block_size(long evp_md_const);
+    public static native int EVP_MD_block_size(long evp_md_const);
 
     // --- Message digest context functions --------------
 
-    static native long EVP_MD_CTX_create();
+    public static native long EVP_MD_CTX_create();
 
-    static native void EVP_MD_CTX_cleanup(NativeRef.EVP_MD_CTX ctx);
+    public static native void EVP_MD_CTX_cleanup(NativeRef.EVP_MD_CTX ctx);
 
-    static native void EVP_MD_CTX_destroy(long ctx);
+    public static native void EVP_MD_CTX_destroy(long ctx);
 
-    static native int EVP_MD_CTX_copy_ex(
-            NativeRef.EVP_MD_CTX dst_ctx, NativeRef.EVP_MD_CTX src_ctx);
+    public static native int EVP_MD_CTX_copy_ex(NativeRef.EVP_MD_CTX dst_ctx,
+            NativeRef.EVP_MD_CTX src_ctx);
 
     // --- Digest handling functions -------------------------------------------
 
-    static native int EVP_DigestInit_ex(NativeRef.EVP_MD_CTX ctx, long evp_md);
+    public static native int EVP_DigestInit_ex(NativeRef.EVP_MD_CTX ctx, long evp_md);
 
-    static native void EVP_DigestUpdate(
-            NativeRef.EVP_MD_CTX ctx, byte[] buffer, int offset, int length);
+    public static native void EVP_DigestUpdate(NativeRef.EVP_MD_CTX ctx,
+            byte[] buffer, int offset, int length);
 
-    static native void EVP_DigestUpdateDirect(NativeRef.EVP_MD_CTX ctx, long ptr, int length);
+    public static native void EVP_DigestUpdateDirect(NativeRef.EVP_MD_CTX ctx,
+            long ptr, int length);
 
-    static native int EVP_DigestFinal_ex(NativeRef.EVP_MD_CTX ctx, byte[] hash, int offset);
+    public static native int EVP_DigestFinal_ex(NativeRef.EVP_MD_CTX ctx, byte[] hash,
+            int offset);
 
     // --- Signature handling functions ----------------------------------------
 
-    static native long EVP_DigestSignInit(
-            NativeRef.EVP_MD_CTX ctx, long evpMdRef, NativeRef.EVP_PKEY key);
+    public static native long EVP_DigestSignInit(NativeRef.EVP_MD_CTX ctx,
+            long evpMdRef, NativeRef.EVP_PKEY key);
 
-    static native long EVP_DigestVerifyInit(
-            NativeRef.EVP_MD_CTX ctx, long evpMdRef, NativeRef.EVP_PKEY key);
+    public static native long EVP_DigestVerifyInit(NativeRef.EVP_MD_CTX ctx,
+            long evpMdRef, NativeRef.EVP_PKEY key);
 
-    static native void EVP_DigestSignUpdate(
-            NativeRef.EVP_MD_CTX ctx, byte[] buffer, int offset, int length);
+    public static native void EVP_DigestSignUpdate(NativeRef.EVP_MD_CTX ctx,
+            byte[] buffer, int offset, int length);
 
-    static native void EVP_DigestSignUpdateDirect(NativeRef.EVP_MD_CTX ctx, long ptr, int length);
+    public static native void EVP_DigestSignUpdateDirect(NativeRef.EVP_MD_CTX ctx,
+            long ptr, int length);
 
-    static native void EVP_DigestVerifyUpdate(
-            NativeRef.EVP_MD_CTX ctx, byte[] buffer, int offset, int length);
+    public static native void EVP_DigestVerifyUpdate(NativeRef.EVP_MD_CTX ctx,
+            byte[] buffer, int offset, int length);
 
-    static native void EVP_DigestVerifyUpdateDirect(NativeRef.EVP_MD_CTX ctx, long ptr, int length);
+    public static native void EVP_DigestVerifyUpdateDirect(NativeRef.EVP_MD_CTX ctx,
+            long ptr, int length);
 
-    static native byte[] EVP_DigestSignFinal(NativeRef.EVP_MD_CTX ctx);
+    public static native byte[] EVP_DigestSignFinal(NativeRef.EVP_MD_CTX ctx);
 
-    static native boolean EVP_DigestVerifyFinal(
-            NativeRef.EVP_MD_CTX ctx, byte[] signature, int offset, int length);
+    public static native boolean EVP_DigestVerifyFinal(NativeRef.EVP_MD_CTX ctx,
+            byte[] signature, int offset, int length);
 
-    static native long EVP_PKEY_encrypt_init(NativeRef.EVP_PKEY pkey);
+    public static native long EVP_PKEY_encrypt_init(NativeRef.EVP_PKEY pkey);
 
-    static native int EVP_PKEY_encrypt(NativeRef.EVP_PKEY_CTX ctx, byte[] out, int outOffset,
+    public static native int EVP_PKEY_encrypt(NativeRef.EVP_PKEY_CTX ctx, byte[] out, int outOffset,
             byte[] input, int inOffset, int inLength);
 
-    static native long EVP_PKEY_decrypt_init(NativeRef.EVP_PKEY pkey);
+    public static native long EVP_PKEY_decrypt_init(NativeRef.EVP_PKEY pkey);
 
-    static native int EVP_PKEY_decrypt(NativeRef.EVP_PKEY_CTX ctx, byte[] out, int outOffset,
+    public static native int EVP_PKEY_decrypt(NativeRef.EVP_PKEY_CTX ctx, byte[] out, int outOffset,
             byte[] input, int inOffset, int inLength);
 
-    static native void EVP_PKEY_CTX_free(long pkeyCtx);
+    public static native void EVP_PKEY_CTX_free(long pkeyCtx);
 
-    static native void EVP_PKEY_CTX_set_rsa_padding(long ctx, int pad)
+    public static native void EVP_PKEY_CTX_set_rsa_padding(long ctx, int pad)
             throws InvalidAlgorithmParameterException;
 
-    static native void EVP_PKEY_CTX_set_rsa_pss_saltlen(long ctx, int len)
+    public static native void EVP_PKEY_CTX_set_rsa_pss_saltlen(long ctx, int len)
             throws InvalidAlgorithmParameterException;
 
-    static native void EVP_PKEY_CTX_set_rsa_mgf1_md(long ctx, long evpMdRef)
+    public static native void EVP_PKEY_CTX_set_rsa_mgf1_md(long ctx, long evpMdRef)
             throws InvalidAlgorithmParameterException;
 
-    static native void EVP_PKEY_CTX_set_rsa_oaep_md(long ctx, long evpMdRef)
+    public static native void EVP_PKEY_CTX_set_rsa_oaep_md(long ctx, long evpMdRef)
             throws InvalidAlgorithmParameterException;
 
-    static native void EVP_PKEY_CTX_set_rsa_oaep_label(long ctx, byte[] label)
+    public static native void EVP_PKEY_CTX_set_rsa_oaep_label(long ctx, byte[] label)
             throws InvalidAlgorithmParameterException;
 
     // --- Block ciphers -------------------------------------------------------
 
     // These return const references
-    static native long EVP_get_cipherbyname(String string);
+    public static native long EVP_get_cipherbyname(String string);
 
-    static native void EVP_CipherInit_ex(NativeRef.EVP_CIPHER_CTX ctx, long evpCipher, byte[] key,
-            byte[] iv, boolean encrypting);
+    public static native void EVP_CipherInit_ex(NativeRef.EVP_CIPHER_CTX ctx, long evpCipher,
+            byte[] key, byte[] iv, boolean encrypting);
 
-    static native int EVP_CipherUpdate(NativeRef.EVP_CIPHER_CTX ctx, byte[] out, int outOffset,
-            byte[] in, int inOffset, int inLength);
+    public static native int EVP_CipherUpdate(NativeRef.EVP_CIPHER_CTX ctx, byte[] out,
+            int outOffset, byte[] in, int inOffset, int inLength);
 
-    static native int EVP_CipherFinal_ex(NativeRef.EVP_CIPHER_CTX ctx, byte[] out, int outOffset)
-            throws BadPaddingException, IllegalBlockSizeException;
+    public static native int EVP_CipherFinal_ex(NativeRef.EVP_CIPHER_CTX ctx, byte[] out,
+            int outOffset) throws BadPaddingException, IllegalBlockSizeException;
 
-    static native int EVP_CIPHER_iv_length(long evpCipher);
+    public static native int EVP_CIPHER_iv_length(long evpCipher);
 
-    static native long EVP_CIPHER_CTX_new();
+    public static native long EVP_CIPHER_CTX_new();
 
-    static native int EVP_CIPHER_CTX_block_size(NativeRef.EVP_CIPHER_CTX ctx);
+    public static native int EVP_CIPHER_CTX_block_size(NativeRef.EVP_CIPHER_CTX ctx);
 
-    static native int get_EVP_CIPHER_CTX_buf_len(NativeRef.EVP_CIPHER_CTX ctx);
+    public static native int get_EVP_CIPHER_CTX_buf_len(NativeRef.EVP_CIPHER_CTX ctx);
 
-    static native boolean get_EVP_CIPHER_CTX_final_used(NativeRef.EVP_CIPHER_CTX ctx);
+    public static native boolean get_EVP_CIPHER_CTX_final_used(NativeRef.EVP_CIPHER_CTX ctx);
 
-    static native void EVP_CIPHER_CTX_set_padding(
-            NativeRef.EVP_CIPHER_CTX ctx, boolean enablePadding);
+    public static native void EVP_CIPHER_CTX_set_padding(NativeRef.EVP_CIPHER_CTX ctx,
+            boolean enablePadding);
 
-    static native void EVP_CIPHER_CTX_set_key_length(NativeRef.EVP_CIPHER_CTX ctx, int keyBitSize);
+    public static native void EVP_CIPHER_CTX_set_key_length(NativeRef.EVP_CIPHER_CTX ctx,
+            int keyBitSize);
 
-    static native void EVP_CIPHER_CTX_free(long ctx);
+    public static native void EVP_CIPHER_CTX_free(long ctx);
 
     // --- AEAD ----------------------------------------------------------------
-    static native long EVP_aead_aes_128_gcm();
+    public static native long EVP_aead_aes_128_gcm();
 
-    static native long EVP_aead_aes_256_gcm();
+    public static native long EVP_aead_aes_256_gcm();
 
-    static native int EVP_AEAD_max_overhead(long evpAead);
+    public static native int EVP_AEAD_max_overhead(long evpAead);
 
-    static native int EVP_AEAD_nonce_length(long evpAead);
+    public static native int EVP_AEAD_nonce_length(long evpAead);
 
-    static native int EVP_AEAD_max_tag_len(long evpAead);
+    public static native int EVP_AEAD_max_tag_len(long evpAead);
 
-    static native int EVP_AEAD_CTX_seal(long evpAead, byte[] key, int tagLengthInBytes, byte[] out,
-            int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength, byte[] ad)
-            throws BadPaddingException;
+    public static native int EVP_AEAD_CTX_seal(long evpAead, byte[] key, int tagLengthInBytes,
+            byte[] out, int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength,
+            byte[] ad) throws BadPaddingException;
 
-    static native int EVP_AEAD_CTX_open(long evpAead, byte[] key, int tagLengthInBytes, byte[] out,
-            int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength, byte[] ad)
-            throws BadPaddingException;
+    public static native int EVP_AEAD_CTX_open(long evpAead, byte[] key, int tagLengthInBytes,
+            byte[] out, int outOffset, byte[] nonce, byte[] in, int inOffset, int inLength,
+            byte[] ad) throws BadPaddingException;
 
     // --- HMAC functions ------------------------------------------------------
 
-    static native long HMAC_CTX_new();
+    public static native long HMAC_CTX_new();
 
-    static native void HMAC_CTX_free(long ctx);
+    public static native void HMAC_CTX_free(long ctx);
 
-    static native void HMAC_Init_ex(NativeRef.HMAC_CTX ctx, byte[] key, long evp_md);
+    public static native void HMAC_Init_ex(NativeRef.HMAC_CTX ctx, byte[] key, long evp_md);
 
-    static native void HMAC_Update(NativeRef.HMAC_CTX ctx, byte[] in, int inOffset, int inLength);
+    public static native void HMAC_Update(NativeRef.HMAC_CTX ctx, byte[] in, int inOffset, int inLength);
 
-    static native void HMAC_UpdateDirect(NativeRef.HMAC_CTX ctx, long inPtr, int inLength);
+    public static native void HMAC_UpdateDirect(NativeRef.HMAC_CTX ctx, long inPtr, int inLength);
 
-    static native byte[] HMAC_Final(NativeRef.HMAC_CTX ctx);
+    public static native byte[] HMAC_Final(NativeRef.HMAC_CTX ctx);
 
     // --- RAND ----------------------------------------------------------------
 
-    static native void RAND_bytes(byte[] output);
+    public static native void RAND_bytes(byte[] output);
 
     // --- ASN.1 objects -------------------------------------------------------
 
-    static native int OBJ_txt2nid(String oid);
+    public static native int OBJ_txt2nid(String oid);
 
-    static native String OBJ_txt2nid_longName(String oid);
+    public static native String OBJ_txt2nid_longName(String oid);
 
-    static native String OBJ_txt2nid_oid(String oid);
+    public static native String OBJ_txt2nid_oid(String oid);
 
     // --- X509_NAME -----------------------------------------------------------
 
-    static int X509_NAME_hash(X500Principal principal) {
+    public static int X509_NAME_hash(X500Principal principal) {
         return X509_NAME_hash(principal, "SHA1");
     }
-
     public static int X509_NAME_hash_old(X500Principal principal) {
         return X509_NAME_hash(principal, "MD5");
     }
@@ -337,211 +344,213 @@
         try {
             byte[] digest = MessageDigest.getInstance(algorithm).digest(principal.getEncoded());
             int offset = 0;
-            return (((digest[offset++] & 0xff) << 0) | ((digest[offset++] & 0xff) << 8)
-                    | ((digest[offset++] & 0xff) << 16) | ((digest[offset] & 0xff) << 24));
+            return (((digest[offset++] & 0xff) <<  0) |
+                    ((digest[offset++] & 0xff) <<  8) |
+                    ((digest[offset++] & 0xff) << 16) |
+                    ((digest[offset  ] & 0xff) << 24));
         } catch (NoSuchAlgorithmException e) {
             throw new AssertionError(e);
         }
     }
 
-    static native String X509_NAME_print_ex(long x509nameCtx, long flags);
+    public static native String X509_NAME_print_ex(long x509nameCtx, long flags);
 
     // --- X509 ----------------------------------------------------------------
 
     /** Used to request get_X509_GENERAL_NAME_stack get the "altname" field. */
-    static final int GN_STACK_SUBJECT_ALT_NAME = 1;
+    public static final int GN_STACK_SUBJECT_ALT_NAME = 1;
 
     /**
      * Used to request get_X509_GENERAL_NAME_stack get the issuerAlternativeName
      * extension.
      */
-    static final int GN_STACK_ISSUER_ALT_NAME = 2;
+    public static final int GN_STACK_ISSUER_ALT_NAME = 2;
 
     /**
      * Used to request only non-critical types in get_X509*_ext_oids.
      */
-    static final int EXTENSION_TYPE_NON_CRITICAL = 0;
+    public static final int EXTENSION_TYPE_NON_CRITICAL = 0;
 
     /**
      * Used to request only critical types in get_X509*_ext_oids.
      */
-    static final int EXTENSION_TYPE_CRITICAL = 1;
+    public static final int EXTENSION_TYPE_CRITICAL = 1;
 
-    static native long d2i_X509_bio(long bioCtx);
+    public static native long d2i_X509_bio(long bioCtx);
 
-    static native long d2i_X509(byte[] encoded) throws ParsingException;
+    public static native long d2i_X509(byte[] encoded) throws ParsingException;
 
-    static native long PEM_read_bio_X509(long bioCtx);
+    public static native long PEM_read_bio_X509(long bioCtx);
 
-    static native byte[] i2d_X509(long x509ctx);
+    public static native byte[] i2d_X509(long x509ctx);
 
     /** Takes an X509 context not an X509_PUBKEY context. */
-    static native byte[] i2d_X509_PUBKEY(long x509ctx);
+    public static native byte[] i2d_X509_PUBKEY(long x509ctx);
 
-    static native byte[] ASN1_seq_pack_X509(long[] x509CertRefs);
+    public static native byte[] ASN1_seq_pack_X509(long[] x509CertRefs);
 
-    static native long[] ASN1_seq_unpack_X509_bio(long bioRef);
+    public static native long[] ASN1_seq_unpack_X509_bio(long bioRef);
 
-    static native void X509_free(long x509ctx);
+    public static native void X509_free(long x509ctx);
 
-    static native long X509_dup(long x509ctx);
+    public static native long X509_dup(long x509ctx);
 
-    static native int X509_cmp(long x509ctx1, long x509ctx2);
+    public static native int X509_cmp(long x509ctx1, long x509ctx2);
 
-    static native void X509_print_ex(long bioCtx, long x509ctx, long nmflag, long certflag);
+    public static native void X509_print_ex(long bioCtx, long x509ctx, long nmflag, long certflag);
 
-    static native byte[] X509_get_issuer_name(long x509ctx);
+    public static native byte[] X509_get_issuer_name(long x509ctx);
 
-    static native byte[] X509_get_subject_name(long x509ctx);
+    public static native byte[] X509_get_subject_name(long x509ctx);
 
-    static native String get_X509_sig_alg_oid(long x509ctx);
+    public static native String get_X509_sig_alg_oid(long x509ctx);
 
-    static native byte[] get_X509_sig_alg_parameter(long x509ctx);
+    public static native byte[] get_X509_sig_alg_parameter(long x509ctx);
 
-    static native boolean[] get_X509_issuerUID(long x509ctx);
+    public static native boolean[] get_X509_issuerUID(long x509ctx);
 
-    static native boolean[] get_X509_subjectUID(long x509ctx);
+    public static native boolean[] get_X509_subjectUID(long x509ctx);
 
-    static native long X509_get_pubkey(long x509ctx)
-            throws NoSuchAlgorithmException, InvalidKeyException;
+    public static native long X509_get_pubkey(long x509ctx) throws NoSuchAlgorithmException,
+           InvalidKeyException;
 
-    static native String get_X509_pubkey_oid(long x509ctx);
+    public static native String get_X509_pubkey_oid(long x509ctx);
 
-    static native byte[] X509_get_ext_oid(long x509ctx, String oid);
+    public static native byte[] X509_get_ext_oid(long x509ctx, String oid);
 
-    static native String[] get_X509_ext_oids(long x509ctx, int critical);
+    public static native String[] get_X509_ext_oids(long x509ctx, int critical);
 
-    static native Object[][] get_X509_GENERAL_NAME_stack(long x509ctx, int type)
+    public static native Object[][] get_X509_GENERAL_NAME_stack(long x509ctx, int type)
             throws CertificateParsingException;
 
-    static native boolean[] get_X509_ex_kusage(long x509ctx);
+    public static native boolean[] get_X509_ex_kusage(long x509ctx);
 
-    static native String[] get_X509_ex_xkusage(long x509ctx);
+    public static native String[] get_X509_ex_xkusage(long x509ctx);
 
-    static native int get_X509_ex_pathlen(long x509ctx);
+    public static native int get_X509_ex_pathlen(long x509ctx);
 
-    static native long X509_get_notBefore(long x509ctx);
+    public static native long X509_get_notBefore(long x509ctx);
 
-    static native long X509_get_notAfter(long x509ctx);
+    public static native long X509_get_notAfter(long x509ctx);
 
-    static native long X509_get_version(long x509ctx);
+    public static native long X509_get_version(long x509ctx);
 
-    static native byte[] X509_get_serialNumber(long x509ctx);
+    public static native byte[] X509_get_serialNumber(long x509ctx);
 
-    static native void X509_verify(long x509ctx, NativeRef.EVP_PKEY pkeyCtx)
+    public static native void X509_verify(long x509ctx, NativeRef.EVP_PKEY pkeyCtx)
             throws BadPaddingException;
 
-    static native byte[] get_X509_cert_info_enc(long x509ctx);
+    public static native byte[] get_X509_cert_info_enc(long x509ctx);
 
-    static native byte[] get_X509_signature(long x509ctx);
+    public static native byte[] get_X509_signature(long x509ctx);
 
-    static native int get_X509_ex_flags(long x509ctx);
+    public static native int get_X509_ex_flags(long x509ctx);
 
-    static native int X509_check_issued(long ctx, long ctx2);
+    public static native int X509_check_issued(long ctx, long ctx2);
 
     // --- PKCS7 ---------------------------------------------------------------
 
     /** Used as the "which" field in d2i_PKCS7_bio and PEM_read_bio_PKCS7. */
-    static final int PKCS7_CERTS = 1;
+    public static final int PKCS7_CERTS = 1;
 
     /** Used as the "which" field in d2i_PKCS7_bio and PEM_read_bio_PKCS7. */
-    static final int PKCS7_CRLS = 2;
+    public static final int PKCS7_CRLS = 2;
 
     /** Returns an array of X509 or X509_CRL pointers. */
-    static native long[] d2i_PKCS7_bio(long bioCtx, int which);
+    public static native long[] d2i_PKCS7_bio(long bioCtx, int which);
 
     /** Returns an array of X509 or X509_CRL pointers. */
-    static native byte[] i2d_PKCS7(long[] certs);
+    public static native byte[] i2d_PKCS7(long[] certs);
 
     /** Returns an array of X509 or X509_CRL pointers. */
-    static native long[] PEM_read_bio_PKCS7(long bioCtx, int which);
+    public static native long[] PEM_read_bio_PKCS7(long bioCtx, int which);
 
     // --- X509_CRL ------------------------------------------------------------
 
-    static native long d2i_X509_CRL_bio(long bioCtx);
+    public static native long d2i_X509_CRL_bio(long bioCtx);
 
-    static native long PEM_read_bio_X509_CRL(long bioCtx);
+    public static native long PEM_read_bio_X509_CRL(long bioCtx);
 
-    static native byte[] i2d_X509_CRL(long x509CrlCtx);
+    public static native byte[] i2d_X509_CRL(long x509CrlCtx);
 
-    static native void X509_CRL_free(long x509CrlCtx);
+    public static native void X509_CRL_free(long x509CrlCtx);
 
-    static native void X509_CRL_print(long bioCtx, long x509CrlCtx);
+    public static native void X509_CRL_print(long bioCtx, long x509CrlCtx);
 
-    static native String get_X509_CRL_sig_alg_oid(long x509CrlCtx);
+    public static native String get_X509_CRL_sig_alg_oid(long x509CrlCtx);
 
-    static native byte[] get_X509_CRL_sig_alg_parameter(long x509CrlCtx);
+    public static native byte[] get_X509_CRL_sig_alg_parameter(long x509CrlCtx);
 
-    static native byte[] X509_CRL_get_issuer_name(long x509CrlCtx);
+    public static native byte[] X509_CRL_get_issuer_name(long x509CrlCtx);
 
     /** Returns X509_REVOKED reference that is not duplicated! */
-    static native long X509_CRL_get0_by_cert(long x509CrlCtx, long x509Ctx);
+    public static native long X509_CRL_get0_by_cert(long x509CrlCtx, long x509Ctx);
 
     /** Returns X509_REVOKED reference that is not duplicated! */
-    static native long X509_CRL_get0_by_serial(long x509CrlCtx, byte[] serial);
+    public static native long X509_CRL_get0_by_serial(long x509CrlCtx, byte[] serial);
 
     /** Returns an array of X509_REVOKED that are owned by the caller. */
-    static native long[] X509_CRL_get_REVOKED(long x509CrlCtx);
+    public static native long[] X509_CRL_get_REVOKED(long x509CrlCtx);
 
-    static native String[] get_X509_CRL_ext_oids(long x509ctx, int critical);
+    public static native String[] get_X509_CRL_ext_oids(long x509ctx, int critical);
 
-    static native byte[] X509_CRL_get_ext_oid(long x509CrlCtx, String oid);
+    public static native byte[] X509_CRL_get_ext_oid(long x509CrlCtx, String oid);
 
-    static native void X509_delete_ext(long x509, String oid);
+    public static native void X509_delete_ext(long x509, String oid);
 
-    static native long X509_CRL_get_version(long x509CrlCtx);
+    public static native long X509_CRL_get_version(long x509CrlCtx);
 
-    static native long X509_CRL_get_ext(long x509CrlCtx, String oid);
+    public static native long X509_CRL_get_ext(long x509CrlCtx, String oid);
 
-    static native byte[] get_X509_CRL_signature(long x509ctx);
+    public static native byte[] get_X509_CRL_signature(long x509ctx);
 
-    static native void X509_CRL_verify(long x509CrlCtx, NativeRef.EVP_PKEY pkeyCtx);
+    public static native void X509_CRL_verify(long x509CrlCtx, NativeRef.EVP_PKEY pkeyCtx);
 
-    static native byte[] get_X509_CRL_crl_enc(long x509CrlCtx);
+    public static native byte[] get_X509_CRL_crl_enc(long x509CrlCtx);
 
-    static native long X509_CRL_get_lastUpdate(long x509CrlCtx);
+    public static native long X509_CRL_get_lastUpdate(long x509CrlCtx);
 
-    static native long X509_CRL_get_nextUpdate(long x509CrlCtx);
+    public static native long X509_CRL_get_nextUpdate(long x509CrlCtx);
 
     // --- X509_REVOKED --------------------------------------------------------
 
-    static native long X509_REVOKED_dup(long x509RevokedCtx);
+    public static native long X509_REVOKED_dup(long x509RevokedCtx);
 
-    static native byte[] i2d_X509_REVOKED(long x509RevokedCtx);
+    public static native byte[] i2d_X509_REVOKED(long x509RevokedCtx);
 
-    static native String[] get_X509_REVOKED_ext_oids(long x509ctx, int critical);
+    public static native String[] get_X509_REVOKED_ext_oids(long x509ctx, int critical);
 
-    static native byte[] X509_REVOKED_get_ext_oid(long x509RevokedCtx, String oid);
+    public static native byte[] X509_REVOKED_get_ext_oid(long x509RevokedCtx, String oid);
 
-    static native byte[] X509_REVOKED_get_serialNumber(long x509RevokedCtx);
+    public static native byte[] X509_REVOKED_get_serialNumber(long x509RevokedCtx);
 
-    static native long X509_REVOKED_get_ext(long x509RevokedCtx, String oid);
+    public static native long X509_REVOKED_get_ext(long x509RevokedCtx, String oid);
 
     /** Returns ASN1_TIME reference. */
-    static native long get_X509_REVOKED_revocationDate(long x509RevokedCtx);
+    public static native long get_X509_REVOKED_revocationDate(long x509RevokedCtx);
 
-    static native void X509_REVOKED_print(long bioRef, long x509RevokedCtx);
+    public static native void X509_REVOKED_print(long bioRef, long x509RevokedCtx);
 
     // --- X509_EXTENSION ------------------------------------------------------
 
-    static native int X509_supported_extension(long x509ExtensionRef);
+    public static native int X509_supported_extension(long x509ExtensionRef);
 
     // --- ASN1_TIME -----------------------------------------------------------
 
-    static native void ASN1_TIME_to_Calendar(long asn1TimeCtx, Calendar cal);
+    public static native void ASN1_TIME_to_Calendar(long asn1TimeCtx, Calendar cal);
 
     // --- BIO stream creation -------------------------------------------------
 
-    static native long create_BIO_InputStream(OpenSSLBIOInputStream is, boolean isFinite);
+    public static native long create_BIO_InputStream(OpenSSLBIOInputStream is, boolean isFinite);
 
-    static native long create_BIO_OutputStream(OutputStream os);
+    public static native long create_BIO_OutputStream(OutputStream os);
 
-    static native int BIO_read(long bioRef, byte[] buffer);
+    public static native int BIO_read(long bioRef, byte[] buffer);
 
-    static native void BIO_write(long bioRef, byte[] buffer, int offset, int length)
+    public static native void BIO_write(long bioRef, byte[] buffer, int offset, int length)
             throws IOException;
 
-    static native void BIO_free_all(long bioRef);
+    public static native void BIO_free_all(long bioRef);
 
     // --- SSL handling --------------------------------------------------------
 
@@ -553,18 +562,18 @@
     // STANDARD_TO_OPENSSL_CIPHER_SUITES is a map from OpenSSL-style
     // cipher-suite names to the standard name for the same (i.e. the name that
     // is registered with IANA).
-    static final Map<String, String> OPENSSL_TO_STANDARD_CIPHER_SUITES =
-            new HashMap<String, String>();
+    public static final Map<String, String> OPENSSL_TO_STANDARD_CIPHER_SUITES
+            = new HashMap<String, String>();
 
     // STANDARD_TO_OPENSSL_CIPHER_SUITES is a map from "standard" cipher suite
     // names (i.e. the names that are registered with IANA) to the
     // OpenSSL-style name for the same.
-    static final Map<String, String> STANDARD_TO_OPENSSL_CIPHER_SUITES =
-            new LinkedHashMap<String, String>();
+    public static final Map<String, String> STANDARD_TO_OPENSSL_CIPHER_SUITES
+            = new LinkedHashMap<String, String>();
 
     // SUPPORTED_CIPHER_SUITES_SET contains all the cipher suites supported by
     // OpenSSL, named using "standard" (as opposed to OpenSSL-style) names.
-    static final Set<String> SUPPORTED_CIPHER_SUITES_SET = new HashSet<String>();
+    public static final Set<String> SUPPORTED_CIPHER_SUITES_SET = new HashSet<String>();
 
     private static void add(String openssl, String standard) {
         OPENSSL_TO_STANDARD_CIPHER_SUITES.put(openssl, standard);
@@ -590,7 +599,8 @@
      * be passed for compatibility as to provide the hint that we
      * support secure renegotiation.
      */
-    static final String TLS_EMPTY_RENEGOTIATION_INFO_SCSV = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV";
+    public static final String TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+            = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV";
 
     /**
      * TLS_FALLBACK_SCSV is from
@@ -598,78 +608,78 @@
      * to indicate to the server that this is a fallback protocol
      * request.
      */
-    static final String TLS_FALLBACK_SCSV = "TLS_FALLBACK_SCSV";
+    public static final String TLS_FALLBACK_SCSV = "TLS_FALLBACK_SCSV";
 
     static {
-        add("ADH-AES128-GCM-SHA256", "TLS_DH_anon_WITH_AES_128_GCM_SHA256");
-        add("ADH-AES128-SHA256", "TLS_DH_anon_WITH_AES_128_CBC_SHA256");
-        add("ADH-AES128-SHA", "TLS_DH_anon_WITH_AES_128_CBC_SHA");
-        add("ADH-AES256-GCM-SHA384", "TLS_DH_anon_WITH_AES_256_GCM_SHA384");
-        add("ADH-AES256-SHA256", "TLS_DH_anon_WITH_AES_256_CBC_SHA256");
-        add("ADH-AES256-SHA", "TLS_DH_anon_WITH_AES_256_CBC_SHA");
-        add("ADH-DES-CBC3-SHA", "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA");
-        add("ADH-DES-CBC-SHA", "SSL_DH_anon_WITH_DES_CBC_SHA");
-        add("AECDH-AES128-SHA", "TLS_ECDH_anon_WITH_AES_128_CBC_SHA");
-        add("AECDH-AES256-SHA", "TLS_ECDH_anon_WITH_AES_256_CBC_SHA");
-        add("AECDH-DES-CBC3-SHA", "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA");
-        add("AECDH-NULL-SHA", "TLS_ECDH_anon_WITH_NULL_SHA");
-        add("AES128-GCM-SHA256", "TLS_RSA_WITH_AES_128_GCM_SHA256");
-        add("AES128-SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA256");
-        add("AES128-SHA", "TLS_RSA_WITH_AES_128_CBC_SHA");
-        add("AES256-GCM-SHA384", "TLS_RSA_WITH_AES_256_GCM_SHA384");
-        add("AES256-SHA256", "TLS_RSA_WITH_AES_256_CBC_SHA256");
-        add("AES256-SHA", "TLS_RSA_WITH_AES_256_CBC_SHA");
-        add("DES-CBC3-SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
-        add("DES-CBC-SHA", "SSL_RSA_WITH_DES_CBC_SHA");
-        add("ECDH-ECDSA-AES128-GCM-SHA256", "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256");
-        add("ECDH-ECDSA-AES128-SHA256", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256");
-        add("ECDH-ECDSA-AES128-SHA", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA");
-        add("ECDH-ECDSA-AES256-GCM-SHA384", "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384");
-        add("ECDH-ECDSA-AES256-SHA384", "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384");
-        add("ECDH-ECDSA-AES256-SHA", "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA");
-        add("ECDH-ECDSA-DES-CBC3-SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA");
-        add("ECDH-ECDSA-NULL-SHA", "TLS_ECDH_ECDSA_WITH_NULL_SHA");
-        add("ECDHE-ECDSA-AES128-GCM-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
-        add("ECDHE-ECDSA-AES128-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256");
-        add("ECDHE-ECDSA-AES128-SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA");
-        add("ECDHE-ECDSA-AES256-GCM-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
-        add("ECDHE-ECDSA-AES256-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384");
-        add("ECDHE-ECDSA-AES256-SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA");
-        add("ECDHE-ECDSA-CHACHA20-POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305");
-        add("ECDHE-ECDSA-CHACHA20-POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256");
-        add("ECDHE-ECDSA-DES-CBC3-SHA", "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA");
-        add("ECDHE-ECDSA-NULL-SHA", "TLS_ECDHE_ECDSA_WITH_NULL_SHA");
-        add("ECDHE-PSK-AES128-CBC-SHA", "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA");
-        add("ECDHE-PSK-AES128-GCM-SHA256", "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256");
-        add("ECDHE-PSK-AES256-CBC-SHA", "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA");
-        add("ECDHE-PSK-AES256-GCM-SHA384", "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384");
-        add("ECDHE-PSK-CHACHA20-POLY1305", "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256");
-        add("ECDHE-RSA-AES128-GCM-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
-        add("ECDHE-RSA-AES128-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256");
-        add("ECDHE-RSA-AES128-SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
-        add("ECDHE-RSA-AES256-GCM-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");
-        add("ECDHE-RSA-AES256-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384");
-        add("ECDHE-RSA-AES256-SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA");
-        add("ECDHE-RSA-CHACHA20-POLY1305", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305");
-        add("ECDHE-RSA-CHACHA20-POLY1305", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256");
-        add("ECDHE-RSA-DES-CBC3-SHA", "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA");
-        add("ECDHE-RSA-NULL-SHA", "TLS_ECDHE_RSA_WITH_NULL_SHA");
-        add("ECDH-RSA-AES128-GCM-SHA256", "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256");
-        add("ECDH-RSA-AES128-SHA256", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256");
-        add("ECDH-RSA-AES128-SHA", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA");
-        add("ECDH-RSA-AES256-GCM-SHA384", "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384");
-        add("ECDH-RSA-AES256-SHA384", "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384");
-        add("ECDH-RSA-AES256-SHA", "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA");
-        add("ECDH-RSA-DES-CBC3-SHA", "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA");
-        add("ECDH-RSA-NULL-SHA", "TLS_ECDH_RSA_WITH_NULL_SHA");
-        add("EXP-ADH-DES-CBC-SHA", "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA");
-        add("EXP-DES-CBC-SHA", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA");
-        add("NULL-MD5", "SSL_RSA_WITH_NULL_MD5");
-        add("NULL-SHA256", "TLS_RSA_WITH_NULL_SHA256");
-        add("NULL-SHA", "SSL_RSA_WITH_NULL_SHA");
-        add("PSK-3DES-EDE-CBC-SHA", "TLS_PSK_WITH_3DES_EDE_CBC_SHA");
-        add("PSK-AES128-CBC-SHA", "TLS_PSK_WITH_AES_128_CBC_SHA");
-        add("PSK-AES256-CBC-SHA", "TLS_PSK_WITH_AES_256_CBC_SHA");
+        add("ADH-AES128-GCM-SHA256",		"TLS_DH_anon_WITH_AES_128_GCM_SHA256");
+        add("ADH-AES128-SHA256",		"TLS_DH_anon_WITH_AES_128_CBC_SHA256");
+        add("ADH-AES128-SHA",			"TLS_DH_anon_WITH_AES_128_CBC_SHA");
+        add("ADH-AES256-GCM-SHA384",		"TLS_DH_anon_WITH_AES_256_GCM_SHA384");
+        add("ADH-AES256-SHA256",		"TLS_DH_anon_WITH_AES_256_CBC_SHA256");
+        add("ADH-AES256-SHA",			"TLS_DH_anon_WITH_AES_256_CBC_SHA");
+        add("ADH-DES-CBC3-SHA",			"SSL_DH_anon_WITH_3DES_EDE_CBC_SHA");
+        add("ADH-DES-CBC-SHA",			"SSL_DH_anon_WITH_DES_CBC_SHA");
+        add("AECDH-AES128-SHA",			"TLS_ECDH_anon_WITH_AES_128_CBC_SHA");
+        add("AECDH-AES256-SHA",			"TLS_ECDH_anon_WITH_AES_256_CBC_SHA");
+        add("AECDH-DES-CBC3-SHA",		"TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA");
+        add("AECDH-NULL-SHA",			"TLS_ECDH_anon_WITH_NULL_SHA");
+        add("AES128-GCM-SHA256",		"TLS_RSA_WITH_AES_128_GCM_SHA256");
+        add("AES128-SHA256",			"TLS_RSA_WITH_AES_128_CBC_SHA256");
+        add("AES128-SHA",			"TLS_RSA_WITH_AES_128_CBC_SHA");
+        add("AES256-GCM-SHA384",		"TLS_RSA_WITH_AES_256_GCM_SHA384");
+        add("AES256-SHA256",			"TLS_RSA_WITH_AES_256_CBC_SHA256");
+        add("AES256-SHA",			"TLS_RSA_WITH_AES_256_CBC_SHA");
+        add("DES-CBC3-SHA",			"SSL_RSA_WITH_3DES_EDE_CBC_SHA");
+        add("DES-CBC-SHA",			"SSL_RSA_WITH_DES_CBC_SHA");
+        add("ECDH-ECDSA-AES128-GCM-SHA256",    	"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256");
+        add("ECDH-ECDSA-AES128-SHA256",		"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256");
+        add("ECDH-ECDSA-AES128-SHA",		"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA");
+        add("ECDH-ECDSA-AES256-GCM-SHA384",    	"TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384");
+        add("ECDH-ECDSA-AES256-SHA384",		"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384");
+        add("ECDH-ECDSA-AES256-SHA",		"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA");
+        add("ECDH-ECDSA-DES-CBC3-SHA",		"TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA");
+        add("ECDH-ECDSA-NULL-SHA",		"TLS_ECDH_ECDSA_WITH_NULL_SHA");
+        add("ECDHE-ECDSA-AES128-GCM-SHA256",   	"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
+        add("ECDHE-ECDSA-AES128-SHA256",	"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256");
+        add("ECDHE-ECDSA-AES128-SHA",		"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA");
+        add("ECDHE-ECDSA-AES256-GCM-SHA384",   	"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
+        add("ECDHE-ECDSA-AES256-SHA384",	"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384");
+        add("ECDHE-ECDSA-AES256-SHA",		"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA");
+        add("ECDHE-ECDSA-CHACHA20-POLY1305",   	"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305");
+        add("ECDHE-ECDSA-CHACHA20-POLY1305",	"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256");
+        add("ECDHE-ECDSA-DES-CBC3-SHA",		"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA");
+        add("ECDHE-ECDSA-NULL-SHA",		"TLS_ECDHE_ECDSA_WITH_NULL_SHA");
+        add("ECDHE-PSK-AES128-CBC-SHA",		"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA");
+        add("ECDHE-PSK-AES128-GCM-SHA256",     	"TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256");
+        add("ECDHE-PSK-AES256-CBC-SHA",		"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA");
+        add("ECDHE-PSK-AES256-GCM-SHA384",     	"TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384");
+        add("ECDHE-PSK-CHACHA20-POLY1305",	"TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256");
+        add("ECDHE-RSA-AES128-GCM-SHA256",     	"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
+        add("ECDHE-RSA-AES128-SHA256",		"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256");
+        add("ECDHE-RSA-AES128-SHA",		"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
+        add("ECDHE-RSA-AES256-GCM-SHA384",     	"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384");
+        add("ECDHE-RSA-AES256-SHA384",		"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384");
+        add("ECDHE-RSA-AES256-SHA",		"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA");
+        add("ECDHE-RSA-CHACHA20-POLY1305",     	"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305");
+        add("ECDHE-RSA-CHACHA20-POLY1305",	"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256");
+        add("ECDHE-RSA-DES-CBC3-SHA",		"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA");
+        add("ECDHE-RSA-NULL-SHA",		"TLS_ECDHE_RSA_WITH_NULL_SHA");
+        add("ECDH-RSA-AES128-GCM-SHA256",      	"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256");
+        add("ECDH-RSA-AES128-SHA256",		"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256");
+        add("ECDH-RSA-AES128-SHA",		"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA");
+        add("ECDH-RSA-AES256-GCM-SHA384",      	"TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384");
+        add("ECDH-RSA-AES256-SHA384",		"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384");
+        add("ECDH-RSA-AES256-SHA",		"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA");
+        add("ECDH-RSA-DES-CBC3-SHA",		"TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA");
+        add("ECDH-RSA-NULL-SHA",		"TLS_ECDH_RSA_WITH_NULL_SHA");
+        add("EXP-ADH-DES-CBC-SHA",		"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA");
+        add("EXP-DES-CBC-SHA",			"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA");
+        add("NULL-MD5",				"SSL_RSA_WITH_NULL_MD5");
+        add("NULL-SHA256",			"TLS_RSA_WITH_NULL_SHA256");
+        add("NULL-SHA",				"SSL_RSA_WITH_NULL_SHA");
+        add("PSK-3DES-EDE-CBC-SHA",		"TLS_PSK_WITH_3DES_EDE_CBC_SHA");
+        add("PSK-AES128-CBC-SHA",		"TLS_PSK_WITH_AES_128_CBC_SHA");
+        add("PSK-AES256-CBC-SHA",		"TLS_PSK_WITH_AES_256_CBC_SHA");
 
         // Signaling Cipher Suite Value for secure renegotiation handled as special case.
         // add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", null);
@@ -687,8 +697,8 @@
         for (int i = 0; i < size; i++) {
             String standardName = OPENSSL_TO_STANDARD_CIPHER_SUITES.get(allOpenSSLCipherSuites[i]);
             if (standardName == null) {
-                throw new IllegalArgumentException("Unknown cipher suite supported by native code: "
-                        + allOpenSSLCipherSuites[i]);
+                throw new IllegalArgumentException("Unknown cipher suite supported by native code: " +
+                                                   allOpenSSLCipherSuites[i]);
             }
             SUPPORTED_CIPHER_SUITES[i] = standardName;
             SUPPORTED_CIPHER_SUITES_SET.add(standardName);
@@ -701,9 +711,9 @@
      * Returns 1 if the BoringSSL believes the CPU has AES accelerated hardware
      * instructions. Used to determine cipher suite ordering.
      */
-    static native int EVP_has_aes_hardware();
+    public static native int EVP_has_aes_hardware();
 
-    static native long SSL_CTX_new();
+    public static native long SSL_CTX_new();
 
     // IMPLEMENTATION NOTE: The default list of cipher suites is a trade-off between what we'd like
     // to use and what servers currently support. We strive to be secure enough by default. We thus
@@ -760,89 +770,99 @@
 
     /** TLS-PSK cipher suites enabled by default (if requested), in preference order. */
     static final String[] DEFAULT_PSK_CIPHER_SUITES = new String[] {
-            "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
-            "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", "TLS_PSK_WITH_AES_128_CBC_SHA",
-            "TLS_PSK_WITH_AES_256_CBC_SHA",
+        "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
+        "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
+        "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
+        "TLS_PSK_WITH_AES_128_CBC_SHA",
+        "TLS_PSK_WITH_AES_256_CBC_SHA",
     };
 
-    static String[] getSupportedCipherSuites() {
+    public static String[] getSupportedCipherSuites() {
         return SUPPORTED_CIPHER_SUITES.clone();
     }
 
-    static native void SSL_CTX_free(long ssl_ctx);
+    public static native void SSL_CTX_free(long ssl_ctx);
 
-    static native void SSL_CTX_set_session_id_context(long ssl_ctx, byte[] sid_ctx);
+    public static native void SSL_CTX_set_session_id_context(long ssl_ctx, byte[] sid_ctx);
 
-    static native long SSL_new(long ssl_ctx) throws SSLException;
+    public static native long SSL_new(long ssl_ctx) throws SSLException;
 
-    static native void SSL_enable_tls_channel_id(long ssl) throws SSLException;
+    public static native void SSL_enable_tls_channel_id(long ssl) throws SSLException;
 
-    static native byte[] SSL_get_tls_channel_id(long ssl) throws SSLException;
+    public static native byte[] SSL_get_tls_channel_id(long ssl) throws SSLException;
 
-    static native void SSL_set1_tls_channel_id(long ssl, NativeRef.EVP_PKEY pkey);
+    public static native void SSL_set1_tls_channel_id(long ssl, NativeRef.EVP_PKEY pkey);
 
-    static native void SSL_use_certificate(long ssl, long[] x509refs);
+    public static native void SSL_use_certificate(long ssl, long[] x509refs);
 
-    static native void SSL_use_PrivateKey(long ssl, NativeRef.EVP_PKEY pkey);
+    public static native void SSL_use_PrivateKey(long ssl, NativeRef.EVP_PKEY pkey);
 
-    static native void SSL_check_private_key(long ssl) throws SSLException;
+    public static native void SSL_check_private_key(long ssl) throws SSLException;
 
-    static native void SSL_set_client_CA_list(long ssl, byte[][] asn1DerEncodedX500Principals);
+    public static native void SSL_set_client_CA_list(long ssl, byte[][] asn1DerEncodedX500Principals);
 
-    static native long SSL_get_mode(long ssl);
+    public static native long SSL_get_mode(long ssl);
 
-    static native long SSL_set_mode(long ssl, long mode);
+    public static native long SSL_set_mode(long ssl, long mode);
 
-    static native long SSL_clear_mode(long ssl, long mode);
+    public static native long SSL_clear_mode(long ssl, long mode);
 
-    static native long SSL_get_options(long ssl);
+    public static native long SSL_get_options(long ssl);
 
-    static native long SSL_set_options(long ssl, long options);
+    public static native long SSL_set_options(long ssl, long options);
 
-    static native long SSL_clear_options(long ssl, long options);
+    public static native long SSL_clear_options(long ssl, long options);
 
-    static native void SSL_enable_signed_cert_timestamps(long ssl);
+    public static native void SSL_enable_signed_cert_timestamps(long ssl);
 
-    static native byte[] SSL_get_signed_cert_timestamp_list(long ssl);
+    public static native byte[] SSL_get_signed_cert_timestamp_list(long ssl);
 
-    static native void SSL_set_signed_cert_timestamp_list(long ssl, byte[] list);
+    public static native void SSL_set_signed_cert_timestamp_list(long ssl, byte[] list);
 
-    static native void SSL_enable_ocsp_stapling(long ssl);
+    public static native void SSL_enable_ocsp_stapling(long ssl);
 
-    static native byte[] SSL_get_ocsp_response(long ssl);
+    public static native byte[] SSL_get_ocsp_response(long ssl);
 
-    static native void SSL_set_ocsp_response(long ssl, byte[] response);
+    public static native void SSL_set_ocsp_response(long ssl, byte[] response);
 
-    static native void SSL_use_psk_identity_hint(long ssl, String identityHint) throws SSLException;
+    public static native void SSL_use_psk_identity_hint(long ssl, String identityHint)
+            throws SSLException;
 
-    static native void set_SSL_psk_client_callback_enabled(long ssl, boolean enabled);
+    public static native void set_SSL_psk_client_callback_enabled(long ssl, boolean enabled);
 
-    static native void set_SSL_psk_server_callback_enabled(long ssl, boolean enabled);
+    public static native void set_SSL_psk_server_callback_enabled(long ssl, boolean enabled);
 
     /** Protocols to enable by default when "TLSv1.2" is requested. */
-    static final String[] TLSV12_PROTOCOLS = new String[] {
-            SUPPORTED_PROTOCOL_TLSV1, SUPPORTED_PROTOCOL_TLSV1_1, SUPPORTED_PROTOCOL_TLSV1_2,
+    public static final String[] TLSV12_PROTOCOLS = new String[] {
+        SUPPORTED_PROTOCOL_TLSV1,
+        SUPPORTED_PROTOCOL_TLSV1_1,
+        SUPPORTED_PROTOCOL_TLSV1_2,
     };
 
     /** Protocols to enable by default when "TLSv1.1" is requested. */
-    static final String[] TLSV11_PROTOCOLS = new String[] {
-            SUPPORTED_PROTOCOL_TLSV1, SUPPORTED_PROTOCOL_TLSV1_1, SUPPORTED_PROTOCOL_TLSV1_2,
+    public static final String[] TLSV11_PROTOCOLS = new String[] {
+        SUPPORTED_PROTOCOL_TLSV1,
+        SUPPORTED_PROTOCOL_TLSV1_1,
+        SUPPORTED_PROTOCOL_TLSV1_2,
     };
 
     /** Protocols to enable by default when "TLSv1" is requested. */
-    static final String[] TLSV1_PROTOCOLS = new String[] {
-            SUPPORTED_PROTOCOL_TLSV1, SUPPORTED_PROTOCOL_TLSV1_1, SUPPORTED_PROTOCOL_TLSV1_2,
+    public static final String[] TLSV1_PROTOCOLS  = new String[] {
+        SUPPORTED_PROTOCOL_TLSV1,
+        SUPPORTED_PROTOCOL_TLSV1_1,
+        SUPPORTED_PROTOCOL_TLSV1_2,
     };
 
-    static final String[] DEFAULT_PROTOCOLS = TLSV12_PROTOCOLS;
+    public static final String[] DEFAULT_PROTOCOLS = TLSV12_PROTOCOLS;
 
-    static String[] getSupportedProtocols() {
-        return new String[] {
-                SUPPORTED_PROTOCOL_TLSV1, SUPPORTED_PROTOCOL_TLSV1_1, SUPPORTED_PROTOCOL_TLSV1_2,
+    public static String[] getSupportedProtocols() {
+        return new String[] { SUPPORTED_PROTOCOL_TLSV1,
+                              SUPPORTED_PROTOCOL_TLSV1_1,
+                              SUPPORTED_PROTOCOL_TLSV1_2,
         };
     }
 
-    static void setEnabledProtocols(long ssl, String[] protocols) {
+    public static void setEnabledProtocols(long ssl, String[] protocols) {
         checkEnabledProtocols(protocols);
         // openssl uses negative logic letting you disable protocols.
         // so first, assume we need to set all (disable all) and clear none (enable none).
@@ -874,7 +894,7 @@
         SSL_clear_options(ssl, optionsToClear);
     }
 
-    static String[] checkEnabledProtocols(String[] protocols) {
+    public static String[] checkEnabledProtocols(String[] protocols) {
         if (protocols == null) {
             throw new IllegalArgumentException("protocols == null");
         }
@@ -892,16 +912,16 @@
         return protocols;
     }
 
-    static native void SSL_set_cipher_lists(long ssl, String[] ciphers);
+    public static native void SSL_set_cipher_lists(long ssl, String[] ciphers);
 
     /**
      * Gets the list of cipher suites enabled for the provided {@code SSL} instance.
      *
      * @return array of {@code SSL_CIPHER} references.
      */
-    static native long[] SSL_get_ciphers(long ssl);
+    public static native long[] SSL_get_ciphers(long ssl);
 
-    static void setEnabledCipherSuites(long ssl, String[] cipherSuites) {
+    public static void setEnabledCipherSuites(long ssl, String[] cipherSuites) {
         checkEnabledCipherSuites(cipherSuites);
         List<String> opensslSuites = new ArrayList<String>();
         for (int i = 0; i < cipherSuites.length; i++) {
@@ -920,7 +940,7 @@
         SSL_set_cipher_lists(ssl, opensslSuites.toArray(new String[opensslSuites.size()]));
     }
 
-    static String[] checkEnabledCipherSuites(String[] cipherSuites) {
+    public static String[] checkEnabledCipherSuites(String[] cipherSuites) {
         if (cipherSuites == null) {
             throw new IllegalArgumentException("cipherSuites == null");
         }
@@ -930,8 +950,8 @@
             if (cipherSuite == null) {
                 throw new IllegalArgumentException("cipherSuites[" + i + "] == null");
             }
-            if (cipherSuite.equals(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
-                    || cipherSuite.equals(TLS_FALLBACK_SCSV)) {
+            if (cipherSuite.equals(TLS_EMPTY_RENEGOTIATION_INFO_SCSV) ||
+                    cipherSuite.equals(TLS_FALLBACK_SCSV)) {
                 continue;
             }
             if (SUPPORTED_CIPHER_SUITES_SET.contains(cipherSuite)) {
@@ -954,36 +974,36 @@
      * See the OpenSSL ssl.h header file for more information.
      */
     // TODO(nathanmittler): Should these move to NativeConstants.java?
-    static final int SSL_VERIFY_NONE = 0x00;
-    static final int SSL_VERIFY_PEER = 0x01;
-    static final int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02;
+    public static final int SSL_VERIFY_NONE =                 0x00;
+    public static final int SSL_VERIFY_PEER =                 0x01;
+    public static final int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02;
 
-    static native void SSL_set_accept_state(long sslNativePointer);
+    public static native void SSL_set_accept_state(long sslNativePointer);
 
-    static native void SSL_set_connect_state(long sslNativePointer);
+    public static native void SSL_set_connect_state(long sslNativePointer);
 
-    static native void SSL_set_verify(long sslNativePointer, int mode);
+    public static native void SSL_set_verify(long sslNativePointer, int mode);
 
-    static native void SSL_set_session(long sslNativePointer, long sslSessionNativePointer)
-            throws SSLException;
+    public static native void SSL_set_session(long sslNativePointer, long sslSessionNativePointer)
+        throws SSLException;
 
-    static native void SSL_set_session_creation_enabled(
+    public static native void SSL_set_session_creation_enabled(
             long sslNativePointer, boolean creationEnabled) throws SSLException;
 
-    static native boolean SSL_session_reused(long sslNativePointer);
+    public static native boolean SSL_session_reused(long sslNativePointer);
 
-    static native void SSL_accept_renegotiations(long sslNativePointer) throws SSLException;
+    public static native void SSL_accept_renegotiations(long sslNativePointer) throws SSLException;
 
-    static native void SSL_set_tlsext_host_name(long sslNativePointer, String hostname)
+    public static native void SSL_set_tlsext_host_name(long sslNativePointer, String hostname)
             throws SSLException;
-    static native String SSL_get_servername(long sslNativePointer);
+    public static native String SSL_get_servername(long sslNativePointer);
 
     /**
      * Returns the selected ALPN protocol. If the server did not select a
      * protocol, {@code null} will be returned.
      */
-    static native byte[] SSL_get0_alpn_selected(long sslPointer);
-    static native void SSL_do_handshake(
+    public static native byte[] SSL_get0_alpn_selected(long sslPointer);
+    public static native void SSL_do_handshake(
             long sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc, int timeoutMillis)
             throws SSLException, SocketTimeoutException, CertificateException;
 
@@ -991,64 +1011,71 @@
      * Currently only intended for forcing renegotiation for testing.
      * Not used within OpenSSLSocketImpl.
      */
-    static native void SSL_renegotiate(long sslNativePointer) throws SSLException;
+    public static native void SSL_renegotiate(long sslNativePointer) throws SSLException;
 
     /**
      * Returns the local X509 certificate references. Must X509_free when done.
      */
-    static native long[] SSL_get_certificate(long sslNativePointer);
+    public static native long[] SSL_get_certificate(long sslNativePointer);
 
     /**
      * Returns the peer X509 certificate references. Must X509_free when done.
      */
-    static native long[] SSL_get_peer_cert_chain(long sslNativePointer);
+    public static native long[] SSL_get_peer_cert_chain(long sslNativePointer);
 
     /**
      * Reads with the native SSL_read function from the encrypted data stream
      * @return -1 if error or the end of the stream is reached.
      */
-    static native int SSL_read(long sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc,
-            byte[] b, int off, int len, int readTimeoutMillis) throws IOException;
+    public static native int SSL_read(long sslNativePointer,
+                                      FileDescriptor fd,
+                                      SSLHandshakeCallbacks shc,
+                                      byte[] b, int off, int len, int readTimeoutMillis)
+        throws IOException;
 
     /**
      * Writes with the native SSL_write function to the encrypted data stream.
      */
-    static native void SSL_write(long sslNativePointer, FileDescriptor fd,
-            SSLHandshakeCallbacks shc, byte[] b, int off, int len, int writeTimeoutMillis)
-            throws IOException;
+    public static native void SSL_write(long sslNativePointer,
+                                        FileDescriptor fd,
+                                        SSLHandshakeCallbacks shc,
+                                        byte[] b, int off, int len, int writeTimeoutMillis)
+        throws IOException;
 
-    static native void SSL_interrupt(long sslNativePointer);
-    static native void SSL_shutdown(
-            long sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc) throws IOException;
+    public static native void SSL_interrupt(long sslNativePointer);
+    public static native void SSL_shutdown(long sslNativePointer,
+                                           FileDescriptor fd,
+                                           SSLHandshakeCallbacks shc) throws IOException;
 
-    static native void SSL_shutdown_BIO(long sslNativePointer, long sourceBioRef, long sinkBioRef,
-            SSLHandshakeCallbacks shc) throws IOException;
+    public static native void SSL_shutdown_BIO(long sslNativePointer,
+                                               long sourceBioRef, long sinkBioRef,
+                                               SSLHandshakeCallbacks shc) throws IOException;
 
-    static native int SSL_get_shutdown(long sslNativePointer);
+    public static native int SSL_get_shutdown(long sslNativePointer);
 
-    static native void SSL_free(long sslNativePointer);
+    public static native void SSL_free(long sslNativePointer);
 
-    static native byte[] SSL_SESSION_session_id(long sslSessionNativePointer);
+    public static native byte[] SSL_SESSION_session_id(long sslSessionNativePointer);
 
-    static native long SSL_SESSION_get_time(long sslSessionNativePointer);
+    public static native long SSL_SESSION_get_time(long sslSessionNativePointer);
 
-    static native String SSL_SESSION_get_version(long sslSessionNativePointer);
+    public static native String SSL_SESSION_get_version(long sslSessionNativePointer);
 
-    static native String SSL_SESSION_cipher(long sslSessionNativePointer);
+    public static native String SSL_SESSION_cipher(long sslSessionNativePointer);
 
-    static native String get_SSL_SESSION_tlsext_hostname(long sslSessionNativePointer);
+    public static native String get_SSL_SESSION_tlsext_hostname(long sslSessionNativePointer);
 
-    static native void SSL_SESSION_free(long sslSessionNativePointer);
+    public static native void SSL_SESSION_free(long sslSessionNativePointer);
 
-    static native byte[] i2d_SSL_SESSION(long sslSessionNativePointer);
+    public static native byte[] i2d_SSL_SESSION(long sslSessionNativePointer);
 
-    static native long d2i_SSL_SESSION(byte[] data) throws IOException;
+    public static native long d2i_SSL_SESSION(byte[] data) throws IOException;
 
     /**
      * A collection of callbacks from the native OpenSSL code that are
      * related to the SSL handshake initiated by SSL_do_handshake.
      */
-    interface SSLHandshakeCallbacks {
+    public interface SSLHandshakeCallbacks {
         /**
          * Verify that we trust the certificate chain is trusted.
          *
@@ -1109,14 +1136,14 @@
         void onSSLStateChange(int type, int val);
     }
 
-    static native long ERR_peek_last_error();
+    public static native long ERR_peek_last_error();
 
-    static native String SSL_CIPHER_get_kx_name(long cipherAddress);
+    public static native String SSL_CIPHER_get_kx_name(long cipherAddress);
 
-    static native String[] get_cipher_names(String selection);
+    public static native String[] get_cipher_names(String selection);
 
-    static native byte[] get_ocsp_single_extension(
-            byte[] ocspResponse, String oid, long x509Ref, long issuerX509Ref);
+    public static native byte[] get_ocsp_single_extension(byte[] ocspResponse, String oid,
+                                                          long x509Ref, long issuerX509Ref);
 
     /**
      * Returns the starting address of the memory region referenced by the provided direct
@@ -1125,99 +1152,99 @@
      *
      * <p>NOTE: This method ignores the buffer's current {@code position}.
      */
-    static native long getDirectBufferAddress(Buffer buf);
+    public static native long getDirectBufferAddress(Buffer buf);
 
-    static native long SSL_BIO_new(long ssl) throws SSLException;
+    public static native long SSL_BIO_new(long ssl) throws SSLException;
 
-    static native int SSL_get_last_error_number();
+    public static native int SSL_get_last_error_number();
 
-    static native int SSL_get_error(long ssl, int ret);
+    public static native int SSL_get_error(long ssl, int ret);
 
-    static native String SSL_get_error_string(long errorNumber);
+    public static native String SSL_get_error_string(long errorNumber);
 
-    static native void SSL_clear_error();
+    public static native void SSL_clear_error();
 
-    static native int SSL_pending_readable_bytes(long ssl);
+    public static native int SSL_pending_readable_bytes(long ssl);
 
-    static native int SSL_pending_written_bytes_in_BIO(long bio);
+    public static native int SSL_pending_written_bytes_in_BIO(long bio);
 
-    static native long SSL_get0_session(long ssl);
+    public static native long SSL_get0_session(long ssl);
 
-    static native long SSL_get1_session(long ssl);
+    public static native long SSL_get1_session(long ssl);
 
     /**
      * Returns the maximum overhead, in bytes, of sealing a record with SSL.
      */
-    static native int SSL_max_seal_overhead(long ssl);
+    public static native int SSL_max_seal_overhead(long ssl);
 
     /**
      * Sets the list of supported ALPN protocols in wire-format (length-prefixed 8-bit strings).
      */
-    static native void SSL_configure_alpn(
+    public static native void SSL_configure_alpn(
             long sslNativePointer, boolean clientMode, byte[] alpnProtocols) throws IOException;
 
     /**
      * Variant of the {@link #SSL_do_handshake} used by {@link OpenSSLEngineImpl}. This version
      * does not lock and does no error preprocessing.
      */
-    static native int ENGINE_SSL_do_handshake(long ssl, SSLHandshakeCallbacks shc);
+    public static native int ENGINE_SSL_do_handshake(long ssl, SSLHandshakeCallbacks shc);
 
     /**
      * Variant of the {@link #SSL_read} for a direct {@link java.nio.ByteBuffer} used by {@link
      * OpenSSLEngineImpl}. This version does not lock or and does no error pre-processing.
      */
-    static native int ENGINE_SSL_read_direct(long sslNativePointer, long address, int length,
+    public static native int ENGINE_SSL_read_direct(long sslNativePointer, long address, int length,
             SSLHandshakeCallbacks shc) throws IOException;
 
     /**
      * Variant of the {@link #SSL_read} for a heap {@link java.nio.ByteBuffer} used by {@link
      * OpenSSLEngineImpl}. This version does not lock or and does no error pre-processing.
      */
-    static native int ENGINE_SSL_read_heap(long sslNativePointer, byte[] destJava, int destOffset,
-            int destLength, SSLHandshakeCallbacks shc) throws IOException;
+    public static native int ENGINE_SSL_read_heap(long sslNativePointer, byte[] destJava,
+            int destOffset, int destLength, SSLHandshakeCallbacks shc) throws IOException;
 
     /**
      * Variant of the {@link #SSL_write} for a direct {@link java.nio.ByteBuffer} used by {@link
      * OpenSSLEngineImpl}. This version does not lock or and does no error pre-processing.
      */
-    static native int ENGINE_SSL_write_direct(long sslNativePointer, long address, int length,
-            SSLHandshakeCallbacks shc) throws IOException;
+    public static native int ENGINE_SSL_write_direct(long sslNativePointer, long address,
+            int length, SSLHandshakeCallbacks shc) throws IOException;
 
     /**
      * Variant of the {@link #SSL_write} for a heap {@link java.nio.ByteBuffer} used by {@link
      * OpenSSLEngineImpl}. This version does not lock or and does no error pre-processing.
      */
-    static native int ENGINE_SSL_write_heap(long sslNativePointer, byte[] sourceJava,
+    public static native int ENGINE_SSL_write_heap(long sslNativePointer, byte[] sourceJava,
             int sourceOffset, int sourceLength, SSLHandshakeCallbacks shc) throws IOException;
 
     /**
      * Writes data from the given direct {@link java.nio.ByteBuffer} to the BIO.
      */
-    static native int ENGINE_SSL_write_BIO_direct(long sslRef, long bioRef, long pos, int length,
-            SSLHandshakeCallbacks shc) throws IOException;
+    public static native int ENGINE_SSL_write_BIO_direct(long sslRef, long bioRef, long pos,
+            int length, SSLHandshakeCallbacks shc) throws IOException;
 
     /**
      * Writes data from the given array to the BIO.
      */
-    static native int ENGINE_SSL_write_BIO_heap(long sslRef, long bioRef, byte[] sourceJava,
+    public static native int ENGINE_SSL_write_BIO_heap(long sslRef, long bioRef, byte[] sourceJava,
             int sourceOffset, int sourceLength, SSLHandshakeCallbacks shc) throws IOException;
 
     /**
      * Reads data from the given BIO into a direct {@link java.nio.ByteBuffer}.
      */
-    static native int ENGINE_SSL_read_BIO_direct(long sslRef, long bioRef, long address, int len,
-            SSLHandshakeCallbacks shc) throws IOException;
+    public static native int ENGINE_SSL_read_BIO_direct(long sslRef, long bioRef, long address,
+            int len, SSLHandshakeCallbacks shc) throws IOException;
 
     /**
      * Reads data from the given BIO into an array.
      */
-    static native int ENGINE_SSL_read_BIO_heap(long sslRef, long bioRef, byte[] destJava,
+    public static native int ENGINE_SSL_read_BIO_heap(long sslRef, long bioRef, byte[] destJava,
             int destOffset, int destLength, SSLHandshakeCallbacks shc) throws IOException;
 
     /**
      * Variant of the {@link #SSL_shutdown} used by {@link OpenSSLEngineImpl}. This version does not
      * lock.
      */
-    static native void ENGINE_SSL_shutdown(long sslNativePointer, SSLHandshakeCallbacks shc)
+    public static native void ENGINE_SSL_shutdown(long sslNativePointer, SSLHandshakeCallbacks shc)
             throws IOException;
 }
diff --git a/common/src/main/java/org/conscrypt/NativeRef.java b/common/src/main/java/org/conscrypt/NativeRef.java
index ccc7cc0..85507ac 100644
--- a/common/src/main/java/org/conscrypt/NativeRef.java
+++ b/common/src/main/java/org/conscrypt/NativeRef.java
@@ -19,11 +19,14 @@
 /**
  * Used to hold onto native OpenSSL references and run finalization on those
  * objects. Individual types must subclass this and implement finalizer.
+ *
+ * @hide
  */
-abstract class NativeRef {
+@Internal
+public abstract class NativeRef {
     final long context;
 
-    NativeRef(long ctx) {
+    public NativeRef(long ctx) {
         if (ctx == 0) {
             throw new NullPointerException("ctx == 0");
         }
@@ -45,8 +48,8 @@
         return (int) context;
     }
 
-    static class EC_GROUP extends NativeRef {
-        EC_GROUP(long ctx) {
+    public static class EC_GROUP extends NativeRef {
+        public EC_GROUP(long ctx) {
             super(ctx);
         }
 
@@ -60,8 +63,8 @@
         }
     }
 
-    static class EC_POINT extends NativeRef {
-        EC_POINT(long ctx) {
+    public static class EC_POINT extends NativeRef {
+        public EC_POINT(long ctx) {
             super(ctx);
         }
 
@@ -75,8 +78,8 @@
         }
     }
 
-    static class EVP_CIPHER_CTX extends NativeRef {
-        EVP_CIPHER_CTX(long ctx) {
+    public static class EVP_CIPHER_CTX extends NativeRef {
+        public EVP_CIPHER_CTX(long ctx) {
             super(ctx);
         }
 
@@ -90,8 +93,8 @@
         }
     }
 
-    static class EVP_MD_CTX extends NativeRef {
-        EVP_MD_CTX(long ctx) {
+    public static class EVP_MD_CTX extends NativeRef {
+        public EVP_MD_CTX(long ctx) {
             super(ctx);
         }
 
@@ -105,8 +108,8 @@
         }
     }
 
-    static class EVP_PKEY extends NativeRef {
-        EVP_PKEY(long ctx) {
+    public static class EVP_PKEY extends NativeRef {
+        public EVP_PKEY(long ctx) {
             super(ctx);
         }
 
@@ -120,8 +123,8 @@
         }
     }
 
-    static class EVP_PKEY_CTX extends NativeRef {
-        EVP_PKEY_CTX(long ctx) {
+    public static class EVP_PKEY_CTX extends NativeRef {
+        public EVP_PKEY_CTX(long ctx) {
             super(ctx);
         }
 
@@ -135,8 +138,8 @@
         }
     }
 
-    static class HMAC_CTX extends NativeRef {
-        HMAC_CTX(long ctx) {
+    public static class HMAC_CTX extends NativeRef {
+        public HMAC_CTX(long ctx) {
             super(ctx);
         }
 
diff --git a/common/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java b/common/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java
index f1a0811..b40b8dd 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java
@@ -24,21 +24,24 @@
  * Provides an interface to OpenSSL's BIO system directly from a Java
  * InputStream. It allows an OpenSSL API to read directly from something more
  * flexible interface than a byte array.
+ *
+ * @hide
  */
-class OpenSSLBIOInputStream extends FilterInputStream {
+@Internal
+public class OpenSSLBIOInputStream extends FilterInputStream {
     private long ctx;
 
-    OpenSSLBIOInputStream(InputStream is, boolean isFinite) {
+    public OpenSSLBIOInputStream(InputStream is, boolean isFinite) {
         super(is);
 
         ctx = NativeCrypto.create_BIO_InputStream(this, isFinite);
     }
 
-    long getBioContext() {
+    public long getBioContext() {
         return ctx;
     }
 
-    void release() {
+    public void release() {
         NativeCrypto.BIO_free_all(ctx);
     }
 
@@ -46,7 +49,7 @@
      * Similar to a {@code readLine} method, but matches what OpenSSL expects
      * from a {@code BIO_gets} method.
      */
-    int gets(byte[] buffer) throws IOException {
+    public int gets(byte[] buffer) throws IOException {
         if (buffer == null || buffer.length == 0) {
             return 0;
         }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLBIOSink.java b/common/src/main/java/org/conscrypt/OpenSSLBIOSink.java
index b385eb9..40f2227 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLBIOSink.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLBIOSink.java
@@ -20,13 +20,16 @@
 
 /**
  * Wraps a BoringSSL BIO to act as a place to write out data.
+ *
+ * @hide
  */
-final class OpenSSLBIOSink {
+@Internal
+public final class OpenSSLBIOSink {
     private final long ctx;
     private final ByteArrayOutputStream buffer;
     private int position;
 
-    static OpenSSLBIOSink create() {
+    public static OpenSSLBIOSink create() {
         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
         return new OpenSSLBIOSink(buffer);
     }
@@ -36,16 +39,16 @@
         this.buffer = buffer;
     }
 
-    int available() {
+    public int available() {
         return buffer.size() - position;
     }
 
-    void reset() {
+    public void reset() {
         buffer.reset();
         position = 0;
     }
 
-    long skip(long byteCount) {
+    public long skip(long byteCount) {
         int maxLength = Math.min(available(), (int) byteCount);
         position += maxLength;
         if (position == buffer.size()) {
@@ -54,15 +57,15 @@
         return maxLength;
     }
 
-    long getContext() {
+    public long getContext() {
         return ctx;
     }
 
-    byte[] toByteArray() {
+    public byte[] toByteArray() {
         return buffer.toByteArray();
     }
 
-    int position() {
+    public int position() {
         return position;
     }
 
diff --git a/common/src/main/java/org/conscrypt/OpenSSLBIOSource.java b/common/src/main/java/org/conscrypt/OpenSSLBIOSource.java
index 314d494..e9b72e5 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLBIOSource.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLBIOSource.java
@@ -22,24 +22,27 @@
 
 /**
  * Wrapped by a BoringSSL BIO to act as a source of bytes.
+ *
+ * @hide
  */
-final class OpenSSLBIOSource {
+@Internal
+public final class OpenSSLBIOSource {
     private OpenSSLBIOInputStream source;
 
-    static OpenSSLBIOSource wrap(ByteBuffer buffer) {
+    public static OpenSSLBIOSource wrap(ByteBuffer buffer) {
         return new OpenSSLBIOSource(
             new OpenSSLBIOInputStream(new ByteBufferInputStream(buffer), false));
     }
 
-    private OpenSSLBIOSource(OpenSSLBIOInputStream source) {
+    public OpenSSLBIOSource(OpenSSLBIOInputStream source) {
         this.source = source;
     }
 
-    long getContext() {
+    public long getContext() {
         return source.getBioContext();
     }
 
-    private synchronized void release() {
+    public synchronized void release() {
         if (source != null) {
             NativeCrypto.BIO_free_all(source.getBioContext());
             source = null;
@@ -58,7 +61,7 @@
     private static class ByteBufferInputStream extends InputStream {
         private final ByteBuffer source;
 
-        ByteBufferInputStream(ByteBuffer source) {
+        public ByteBufferInputStream(ByteBuffer source) {
             this.source = source;
         }
 
diff --git a/common/src/main/java/org/conscrypt/OpenSSLCipher.java b/common/src/main/java/org/conscrypt/OpenSSLCipher.java
index f814906..2735a67 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLCipher.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLCipher.java
@@ -56,7 +56,7 @@
     /**
      * Modes that a block cipher may support.
      */
-    enum Mode {
+    protected static enum Mode {
         CBC,
         CTR,
         ECB,
@@ -66,7 +66,7 @@
     /**
      * Paddings that a block cipher may support.
      */
-    enum Padding {
+    protected static enum Padding {
         NOPADDING,
         PKCS5PADDING,
         ISO10126PADDING,
@@ -75,7 +75,7 @@
     /**
      * The current cipher mode.
      */
-    Mode mode = Mode.ECB;
+    protected Mode mode = Mode.ECB;
 
     /**
      * The current cipher padding.
@@ -86,12 +86,12 @@
      * May be used when reseting the cipher instance after calling
      * {@code doFinal}.
      */
-    byte[] encodedKey;
+    protected byte[] encodedKey;
 
     /**
      * The Initial Vector (IV) used for the current cipher.
      */
-    byte[] iv;
+    protected byte[] iv;
 
     /**
      * Current cipher mode: encrypting or decrypting.
@@ -103,10 +103,10 @@
      */
     private int blockSize;
 
-    OpenSSLCipher() {
+    protected OpenSSLCipher() {
     }
 
-    OpenSSLCipher(Mode mode, Padding padding) {
+    protected OpenSSLCipher(Mode mode, Padding padding) {
         this.mode = mode;
         this.padding = padding;
         blockSize = getCipherBlockSize();
@@ -118,7 +118,7 @@
      * initialized for encryption or decryption. The {@code encodedKey} will be
      * the bytes of a supported key size.
      */
-    abstract void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
+    protected abstract void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
             SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException;
 
     /**
@@ -128,7 +128,7 @@
      * number of bytes processed and placed into {@code output}. On error, an
      * exception must be thrown.
      */
-    abstract int updateInternal(byte[] input, int inputOffset, int inputLen,
+    protected abstract int updateInternal(byte[] input, int inputOffset, int inputLen,
             byte[] output, int outputOffset, int maximumLen) throws ShortBufferException;
 
     /**
@@ -138,39 +138,39 @@
      * of bytes processed and placed into {@code output}. On error, an exception
      * must be thrown.
      */
-    abstract int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
+    protected abstract int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
             throws IllegalBlockSizeException, BadPaddingException, ShortBufferException;
 
     /**
      * Returns the standard name for the particular algorithm.
      */
-    abstract String getBaseCipherName();
+    protected abstract String getBaseCipherName();
 
     /**
      * Checks whether the cipher supports this particular {@code keySize} (in
      * bytes) and throws {@code InvalidKeyException} if it doesn't.
      */
-    abstract void checkSupportedKeySize(int keySize) throws InvalidKeyException;
+    protected abstract void checkSupportedKeySize(int keySize) throws InvalidKeyException;
 
     /**
      * Checks whether the cipher supports this particular cipher {@code mode}
      * and throws {@code NoSuchAlgorithmException} if it doesn't.
      */
-    abstract void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException;
+    protected abstract void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException;
 
     /**
      * Checks whether the cipher supports this particular cipher {@code padding}
      * and throws {@code NoSuchPaddingException} if it doesn't.
      */
-    abstract void checkSupportedPadding(Padding padding) throws NoSuchPaddingException;
+    protected abstract void checkSupportedPadding(Padding padding) throws NoSuchPaddingException;
 
-    abstract int getCipherBlockSize();
+    protected abstract int getCipherBlockSize();
 
-    boolean supportsVariableSizeKey() {
+    protected boolean supportsVariableSizeKey() {
         return false;
     }
 
-    boolean supportsVariableSizeIv() {
+    protected boolean supportsVariableSizeIv() {
         return false;
     }
 
@@ -207,7 +207,7 @@
     /**
      * Returns the padding type for which this cipher is initialized.
      */
-    Padding getPadding() {
+    protected Padding getPadding() {
         return padding;
     }
 
@@ -221,14 +221,14 @@
      * {@code inputLen}. If padding is enabled and the size of the input puts it
      * right at the block size, it will add another block for the padding.
      */
-    abstract int getOutputSizeForFinal(int inputLen);
+    protected abstract int getOutputSizeForFinal(int inputLen);
 
     /**
      * The size of output if {@code update()} is called with this
      * {@code inputLen}. If padding is enabled and the size of the input puts it
      * right at the block size, it will add another block for the padding.
      */
-    abstract int getOutputSizeForUpdate(int inputLen);
+    protected abstract int getOutputSizeForUpdate(int inputLen);
 
     @Override
     protected int engineGetOutputSize(int inputLen) {
@@ -445,7 +445,7 @@
         return encodedKey;
     }
 
-    boolean isEncrypting() {
+    protected boolean isEncrypting() {
         return encrypting;
     }
 
@@ -461,7 +461,7 @@
          * like calling "doFinal()" in decryption mode without processing any
          * updates.
          */
-        boolean calledUpdate;
+        protected boolean calledUpdate;
 
         /**
          * The block size of the current mode.
@@ -473,7 +473,7 @@
         }
 
         @Override
-        void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
+        protected void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
                 SecureRandom random) throws InvalidKeyException,
                 InvalidAlgorithmParameterException {
             byte[] iv;
@@ -531,7 +531,7 @@
         }
 
         @Override
-        int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
+        protected int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
                 int outputOffset, int maximumLen) throws ShortBufferException {
             final int intialOutputOffset = outputOffset;
 
@@ -550,7 +550,7 @@
         }
 
         @Override
-        int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
+        protected int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
                 throws IllegalBlockSizeException, BadPaddingException, ShortBufferException {
             /* Remember this so we can tell how many characters were written. */
             final int initialOutputOffset = outputOffset;
@@ -586,7 +586,7 @@
         }
 
         @Override
-        int getOutputSizeForFinal(int inputLen) {
+        protected int getOutputSizeForFinal(int inputLen) {
             if (modeBlockSize == 1) {
                 return inputLen;
             } else {
@@ -610,7 +610,7 @@
         }
 
         @Override
-        int getOutputSizeForUpdate(int inputLen) {
+        protected int getOutputSizeForUpdate(int inputLen) {
             return getOutputSizeForFinal(inputLen);
         }
 
@@ -618,7 +618,7 @@
          * Returns the OpenSSL cipher name for the particular {@code keySize}
          * and cipher {@code mode}.
          */
-        abstract String getCipherName(int keySize, Mode mode);
+        protected abstract String getCipherName(int keySize, Mode mode);
 
         /**
          * Reset this Cipher instance state to process a new chunk of data.
@@ -628,15 +628,15 @@
             calledUpdate = false;
         }
 
-        abstract static class AES_BASE extends EVP_CIPHER {
+        protected abstract static class AES_BASE extends EVP_CIPHER {
             private static final int AES_BLOCK_SIZE = 16;
 
-            AES_BASE(Mode mode, Padding padding) {
+            protected AES_BASE(Mode mode, Padding padding) {
                 super(mode, padding);
             }
 
             @Override
-            void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+            protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
                 switch (mode) {
                     case CBC:
                     case CTR:
@@ -648,7 +648,7 @@
             }
 
             @Override
-            void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+            protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
                 switch (padding) {
                     case NOPADDING:
                     case PKCS5PADDING:
@@ -660,23 +660,23 @@
             }
 
             @Override
-            String getBaseCipherName() {
+            protected String getBaseCipherName() {
                 return "AES";
             }
 
             @Override
-            String getCipherName(int keyLength, Mode mode) {
+            protected String getCipherName(int keyLength, Mode mode) {
                 return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US);
             }
 
             @Override
-            int getCipherBlockSize() {
+            protected int getCipherBlockSize() {
                 return AES_BLOCK_SIZE;
             }
         }
 
         public static class AES extends AES_BASE {
-            AES(Mode mode, Padding padding) {
+            protected AES(Mode mode, Padding padding) {
                 super(mode, padding);
             }
 
@@ -723,7 +723,7 @@
             }
 
             @Override
-            void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+            protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
                 switch (keyLength) {
                     case 16: // AES 128
                     case 24: // AES 192
@@ -737,7 +737,7 @@
         }
 
         public static class AES_128 extends AES_BASE {
-            AES_128(Mode mode, Padding padding) {
+            protected AES_128(Mode mode, Padding padding) {
                 super(mode, padding);
             }
 
@@ -784,7 +784,7 @@
             }
 
             @Override
-            void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+            protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
                 if (keyLength != 16) { // 128 bits
                     throw new InvalidKeyException("Unsupported key size: " + keyLength + " bytes");
                 }
@@ -792,7 +792,7 @@
         }
 
         public static class AES_256 extends AES_BASE {
-            AES_256(Mode mode, Padding padding) {
+            protected AES_256(Mode mode, Padding padding) {
                 super(mode, padding);
             }
 
@@ -839,7 +839,7 @@
             }
 
             @Override
-            void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+            protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
                 if (keyLength != 32) { // 256 bits
                     throw new InvalidKeyException("Unsupported key size: " + keyLength + " bytes");
                 }
@@ -872,12 +872,12 @@
             }
 
             @Override
-            String getBaseCipherName() {
+            protected String getBaseCipherName() {
                 return "DESede";
             }
 
             @Override
-            String getCipherName(int keySize, Mode mode) {
+            protected String getCipherName(int keySize, Mode mode) {
                 final String baseCipherName;
                 if (keySize == 16) {
                     baseCipherName = "des-ede";
@@ -889,21 +889,21 @@
             }
 
             @Override
-            void checkSupportedKeySize(int keySize) throws InvalidKeyException {
+            protected void checkSupportedKeySize(int keySize) throws InvalidKeyException {
                 if (keySize != 16 && keySize != 24) {
                     throw new InvalidKeyException("key size must be 128 or 192 bits");
                 }
             }
 
             @Override
-            void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+            protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
                 if (mode != Mode.CBC) {
                     throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString());
                 }
             }
 
             @Override
-            void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+            protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
                 switch (padding) {
                     case NOPADDING:
                     case PKCS5PADDING:
@@ -915,7 +915,7 @@
             }
 
             @Override
-            int getCipherBlockSize() {
+            protected int getCipherBlockSize() {
                 return DES_BLOCK_SIZE;
             }
         }
@@ -927,36 +927,36 @@
             }
 
             @Override
-            String getBaseCipherName() {
+            protected String getBaseCipherName() {
                 return "ARCFOUR";
             }
 
             @Override
-            String getCipherName(int keySize, Mode mode) {
+            protected String getCipherName(int keySize, Mode mode) {
                 return "rc4";
             }
 
             @Override
-            void checkSupportedKeySize(int keySize) throws InvalidKeyException {
+            protected void checkSupportedKeySize(int keySize) throws InvalidKeyException {
             }
 
             @Override
-            void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+            protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
                 throw new NoSuchAlgorithmException("ARC4 does not support modes");
             }
 
             @Override
-            void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+            protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
                 throw new NoSuchPaddingException("ARC4 does not support padding");
             }
 
             @Override
-            int getCipherBlockSize() {
+            protected int getCipherBlockSize() {
                 return 0;
             }
 
             @Override
-            boolean supportsVariableSizeKey() {
+            protected boolean supportsVariableSizeKey() {
                 return true;
             }
         }
@@ -977,17 +977,17 @@
         /**
          * The byte array containing the bytes written.
          */
-        byte[] buf;
+        protected byte[] buf;
 
         /**
          * The number of bytes written.
          */
-        int bufCount;
+        protected int bufCount;
 
         /**
          * AEAD cipher reference.
          */
-        long evpAead;
+        protected long evpAead;
 
         /**
          * Additional authenticated data.
@@ -1029,7 +1029,7 @@
         }
 
         @Override
-        void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
+        protected void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
                 SecureRandom random) throws InvalidKeyException,
                 InvalidAlgorithmParameterException {
             byte[] iv;
@@ -1088,7 +1088,7 @@
         }
 
         @Override
-        int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
+        protected int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
                 int outputOffset, int maximumLen) throws ShortBufferException {
             if (buf == null) {
                 throw new IllegalStateException("Cipher not initialized");
@@ -1132,7 +1132,7 @@
         }
 
         @Override
-        int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
+        protected int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
                 throws IllegalBlockSizeException, BadPaddingException {
             final int bytesWritten;
             try {
@@ -1152,14 +1152,14 @@
         }
 
         @Override
-        void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+        protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
             if (padding != Padding.NOPADDING) {
                 throw new NoSuchPaddingException("Must be NoPadding for AEAD ciphers");
             }
         }
 
         @Override
-        int getOutputSizeForFinal(int inputLen) {
+        protected int getOutputSizeForFinal(int inputLen) {
             return bufCount + inputLen
                     + (isEncrypting() ? NativeCrypto.EVP_AEAD_max_overhead(evpAead) : 0);
         }
@@ -1203,17 +1203,17 @@
             }
         }
 
-        abstract long getEVP_AEAD(int keyLength) throws InvalidKeyException;
+        protected abstract long getEVP_AEAD(int keyLength) throws InvalidKeyException;
 
         public abstract static class AES extends EVP_AEAD {
             private static final int AES_BLOCK_SIZE = 16;
 
-            AES(Mode mode) {
+            protected AES(Mode mode) {
                 super(mode);
             }
 
             @Override
-            void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+            protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
                 switch (keyLength) {
                     case 16: // AES 128
                     case 32: // AES 256
@@ -1225,12 +1225,12 @@
             }
 
             @Override
-            String getBaseCipherName() {
+            protected String getBaseCipherName() {
                 return "AES";
             }
 
             @Override
-            int getCipherBlockSize() {
+            protected int getCipherBlockSize() {
                 return AES_BLOCK_SIZE;
             }
 
@@ -1238,7 +1238,7 @@
              * AEAD buffers everything until a final output.
              */
             @Override
-            int getOutputSizeForUpdate(int inputLen) {
+            protected int getOutputSizeForUpdate(int inputLen) {
                 return 0;
             }
 
@@ -1284,7 +1284,7 @@
                 }
 
                 @Override
-                void engineInitInternal(
+                protected void engineInitInternal(
                         byte[] encodedKey, AlgorithmParameterSpec params, SecureRandom random)
                         throws InvalidKeyException, InvalidAlgorithmParameterException {
                     super.engineInitInternal(encodedKey, params, random);
@@ -1305,7 +1305,7 @@
                 }
 
                 @Override
-                int updateInternal(byte[] input, int inputOffset, int inputLen,
+                protected int updateInternal(byte[] input, int inputOffset, int inputLen,
                         byte[] output, int outputOffset, int maximumLen)
                         throws ShortBufferException {
                     checkInitialization();
@@ -1314,7 +1314,7 @@
                 }
 
                 @Override
-                int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
+                protected int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
                         throws IllegalBlockSizeException, BadPaddingException {
                     checkInitialization();
                     int retVal = super.doFinalInternal(output, outputOffset, maximumLen);
@@ -1331,14 +1331,14 @@
                 }
 
                 @Override
-                void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+                protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
                     if (mode != Mode.GCM) {
                         throw new NoSuchAlgorithmException("Mode must be GCM");
                     }
                 }
 
                 @Override
-                long getEVP_AEAD(int keyLength) throws InvalidKeyException {
+                protected long getEVP_AEAD(int keyLength) throws InvalidKeyException {
                     if (keyLength == 16) {
                         return NativeCrypto.EVP_aead_aes_128_gcm();
                     } else if (keyLength == 32) {
@@ -1350,7 +1350,7 @@
 
                 public static class AES_128 extends GCM {
                     @Override
-                    void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+                    protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
                         if (keyLength != 16) { // 128 bits
                             throw new InvalidKeyException(
                                     "Unsupported key size: " + keyLength + " bytes (must be 16)");
@@ -1360,7 +1360,7 @@
 
                 public static class AES_256 extends GCM {
                     @Override
-                    void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+                    protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
                         if (keyLength != 32) { // 256 bits
                             throw new InvalidKeyException(
                                     "Unsupported key size: " + keyLength + " bytes (must be 32)");
diff --git a/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java b/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java
index dac03b7..a89cb5b 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLCipherRSA.java
@@ -46,25 +46,22 @@
 import javax.crypto.spec.PSource;
 import javax.crypto.spec.SecretKeySpec;
 
-/**
- * @hide
- */
 @Internal
 abstract class OpenSSLCipherRSA extends CipherSpi {
     /**
      * The current OpenSSL key we're operating on.
      */
-    OpenSSLKey key;
+    protected OpenSSLKey key;
 
     /**
      * Current key type: private or public.
      */
-    boolean usingPrivateKey;
+    protected boolean usingPrivateKey;
 
     /**
      * Current cipher mode: encrypting or decrypting.
      */
-    boolean encrypting;
+    protected boolean encrypting;
 
     /**
      * Buffer for operations
@@ -85,9 +82,9 @@
     /**
      * Current padding mode
      */
-    int padding = NativeConstants.RSA_PKCS1_PADDING;
+    protected int padding = NativeConstants.RSA_PKCS1_PADDING;
 
-    OpenSSLCipherRSA(int padding) {
+    protected OpenSSLCipherRSA(int padding) {
         this.padding = padding;
     }
 
@@ -132,7 +129,7 @@
         return paddedBlockSizeBytes();
     }
 
-    int paddedBlockSizeBytes() {
+    protected int paddedBlockSizeBytes() {
         int paddedBlockSizeBytes = keySizeBytes();
         if (padding == NativeConstants.RSA_PKCS1_PADDING) {
             paddedBlockSizeBytes--;  // for 0 prefix
@@ -141,7 +138,7 @@
         return paddedBlockSizeBytes;
     }
 
-    int keySizeBytes() {
+    protected int keySizeBytes() {
         if (!isInitialized()) {
             throw new IllegalStateException("cipher is not initialized");
         }
@@ -151,7 +148,7 @@
     /**
      * Returns {@code true} if the cipher has been initialized.
      */
-    boolean isInitialized() {
+    protected boolean isInitialized() {
         return key != null;
     }
 
@@ -165,10 +162,10 @@
         return null;
     }
 
-    void doCryptoInit(AlgorithmParameterSpec spec)
+    protected void doCryptoInit(AlgorithmParameterSpec spec)
             throws InvalidAlgorithmParameterException {}
 
-    void engineInitInternal(int opmode, Key key, AlgorithmParameterSpec spec)
+    protected void engineInitInternal(int opmode, Key key, AlgorithmParameterSpec spec)
             throws InvalidKeyException, InvalidAlgorithmParameterException {
         if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
             encrypting = true;
@@ -292,7 +289,7 @@
         return output;
     }
 
-    abstract int doCryptoOperation(final byte[] tmpBuf, byte[] output)
+    protected abstract int doCryptoOperation(final byte[] tmpBuf, byte[] output)
             throws BadPaddingException, IllegalBlockSizeException;
 
     @Override
@@ -354,7 +351,7 @@
         }
 
         @Override
-        int doCryptoOperation(final byte[] tmpBuf, byte[] output)
+        protected int doCryptoOperation(final byte[] tmpBuf, byte[] output)
                 throws BadPaddingException, IllegalBlockSizeException {
             int resultSize;
             if (encrypting) {
@@ -396,7 +393,7 @@
         }
     }
 
-    static class OAEP extends OpenSSLCipherRSA {
+    protected static class OAEP extends OpenSSLCipherRSA {
         private long oaepMd;
         private int oaepMdSizeBytes;
 
@@ -481,7 +478,7 @@
         }
 
         @Override
-        void doCryptoInit(AlgorithmParameterSpec spec)
+        protected void doCryptoInit(AlgorithmParameterSpec spec)
                 throws InvalidAlgorithmParameterException {
             pkeyCtx = new NativeRef.EVP_PKEY_CTX(encrypting
                             ? NativeCrypto.EVP_PKEY_encrypt_init(key.getNativeRef())
@@ -501,7 +498,7 @@
         }
 
         @Override
-        int paddedBlockSizeBytes() {
+        protected int paddedBlockSizeBytes() {
             int paddedBlockSizeBytes = keySizeBytes();
             // Size described in step 2 of decoding algorithm, but extra byte
             // needed to make sure it's smaller than the RSA key modulus size.
@@ -542,7 +539,7 @@
         }
 
         @Override
-        int doCryptoOperation(byte[] tmpBuf, byte[] output)
+        protected int doCryptoOperation(byte[] tmpBuf, byte[] output)
                 throws BadPaddingException, IllegalBlockSizeException {
             if (encrypting) {
                 return NativeCrypto.EVP_PKEY_encrypt(pkeyCtx, output, 0, tmpBuf, 0, tmpBuf.length);
diff --git a/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java b/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java
index 74050e3..88e050f 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLContextImpl.java
@@ -33,7 +33,7 @@
  * @hide
  */
 @Internal
-public abstract class OpenSSLContextImpl extends SSLContextSpi {
+public class OpenSSLContextImpl extends SSLContextSpi {
 
     /**
      * The default SSLContextImpl for use with
@@ -51,14 +51,14 @@
     /** Server session cache. */
     private final ServerSessionContext serverSessionContext;
 
-    SSLParametersImpl sslParameters;
+    protected SSLParametersImpl sslParameters;
 
     /** Allows outside callers to get the preferred SSLContext. */
-    static OpenSSLContextImpl getPreferred() {
+    public static OpenSSLContextImpl getPreferred() {
         return new TLSv12();
     }
 
-    OpenSSLContextImpl(String[] algorithms) {
+    protected OpenSSLContextImpl(String[] algorithms) {
         this.algorithms = algorithms;
         clientSessionContext = new ClientSessionContext();
         serverSessionContext = new ServerSessionContext();
@@ -67,7 +67,7 @@
     /**
      * Constuctor for the DefaultSSLContextImpl.
      */
-    OpenSSLContextImpl() throws GeneralSecurityException, IOException {
+    protected OpenSSLContextImpl() throws GeneralSecurityException, IOException {
         synchronized (DefaultSSLContextImpl.class) {
             this.algorithms = null;
             if (DEFAULT_SSL_CONTEXT_IMPL == null) {
@@ -147,19 +147,19 @@
         return clientSessionContext;
     }
 
-    public static final class TLSv12 extends OpenSSLContextImpl {
+    public static class TLSv12 extends OpenSSLContextImpl {
         public TLSv12() {
             super(NativeCrypto.TLSV12_PROTOCOLS);
         }
     }
 
-    public static final class TLSv11 extends OpenSSLContextImpl {
+    public static class TLSv11 extends OpenSSLContextImpl {
         public TLSv11() {
             super(NativeCrypto.TLSV11_PROTOCOLS);
         }
     }
 
-    public static final class TLSv1 extends OpenSSLContextImpl {
+    public static class TLSv1 extends OpenSSLContextImpl {
         public TLSv1() {
             super(NativeCrypto.TLSV1_PROTOCOLS);
         }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java b/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java
index 7ac70dd..dfecf30 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLECGroupContext.java
@@ -27,15 +27,18 @@
 
 /**
  * Represents a BoringSSL EC_GROUP object.
+ *
+ * @hide
  */
-final class OpenSSLECGroupContext {
+@Internal
+public final class OpenSSLECGroupContext {
     private final NativeRef.EC_GROUP groupCtx;
 
-    OpenSSLECGroupContext(NativeRef.EC_GROUP groupCtx) {
+    public OpenSSLECGroupContext(NativeRef.EC_GROUP groupCtx) {
         this.groupCtx = groupCtx;
     }
 
-    static OpenSSLECGroupContext getCurveByName(String curveName) {
+    public static OpenSSLECGroupContext getCurveByName(String curveName) {
         // Workaround for OpenSSL not supporting SECG names for NIST P-192 and P-256
         // (aka ANSI X9.62 prime192v1 and prime256v1) curve names.
         if ("secp256r1".equals(curveName)) {
@@ -64,11 +67,11 @@
         return super.hashCode();
     }
 
-    NativeRef.EC_GROUP getNativeRef() {
+    public NativeRef.EC_GROUP getNativeRef() {
         return groupCtx;
     }
 
-    static OpenSSLECGroupContext getInstance(ECParameterSpec params)
+    public static OpenSSLECGroupContext getInstance(ECParameterSpec params)
             throws InvalidAlgorithmParameterException {
         String curveName = Platform.getCurveName(params);
         if (curveName != null) {
@@ -156,7 +159,7 @@
         return new OpenSSLECGroupContext(groupRef);
     }
 
-    ECParameterSpec getECParameterSpec() {
+    public ECParameterSpec getECParameterSpec() {
         final String curveName = NativeCrypto.EC_GROUP_get_curve_name(groupCtx);
 
         final byte[][] curveParams = NativeCrypto.EC_GROUP_get_curve(groupCtx);
diff --git a/common/src/main/java/org/conscrypt/OpenSSLECKeyFactory.java b/common/src/main/java/org/conscrypt/OpenSSLECKeyFactory.java
index abacde9..bd810dc 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLECKeyFactory.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLECKeyFactory.java
@@ -34,12 +34,12 @@
 import java.security.spec.X509EncodedKeySpec;
 
 /**
- * An implementation of a {@link KeyFactorySpi} for EC keys based on BoringSSL.
+ * An implementation of a {@link KeyFactory} for EC keys based on BoringSSL.
  *
  * @hide
  */
 @Internal
-public final class OpenSSLECKeyFactory extends KeyFactorySpi {
+public class OpenSSLECKeyFactory extends KeyFactorySpi {
 
     @Override
     protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
@@ -200,4 +200,5 @@
                     + key.getClass().getName());
         }
     }
+
 }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLECPointContext.java b/common/src/main/java/org/conscrypt/OpenSSLECPointContext.java
index 95be03b..3ab1896 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLECPointContext.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLECPointContext.java
@@ -33,7 +33,7 @@
         throw new IllegalArgumentException("OpenSSLECPointContext.equals is not defined.");
     }
 
-    ECPoint getECPoint() {
+    public ECPoint getECPoint() {
         final byte[][] generatorCoords = NativeCrypto.EC_POINT_get_affine_coordinates(
                 group.getNativeRef(), pointCtx);
         final BigInteger x = new BigInteger(generatorCoords[0]);
@@ -47,11 +47,11 @@
         return super.hashCode();
     }
 
-    NativeRef.EC_POINT getNativeRef() {
+    public NativeRef.EC_POINT getNativeRef() {
         return pointCtx;
     }
 
-    static OpenSSLECPointContext getInstance(OpenSSLECGroupContext group,
+    public static OpenSSLECPointContext getInstance(OpenSSLECGroupContext group,
             ECPoint javaPoint) {
         OpenSSLECPointContext point = new OpenSSLECPointContext(group, new NativeRef.EC_POINT(
                 NativeCrypto.EC_POINT_new(group.getNativeRef())));
diff --git a/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java b/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java
index c0e1654..790918f 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLECPrivateKey.java
@@ -33,8 +33,11 @@
 
 /**
  * An implementation of a {@link PrivateKey} for EC keys based on BoringSSL.
+ *
+ * @hide
  */
-final class OpenSSLECPrivateKey implements ECPrivateKey, OpenSSLKeyHolder {
+@Internal
+public final class OpenSSLECPrivateKey implements ECPrivateKey, OpenSSLKeyHolder {
     private static final long serialVersionUID = -4036633595001083922L;
 
     private static final String ALGORITHM = "EC";
@@ -43,18 +46,18 @@
 
     protected transient OpenSSLECGroupContext group;
 
-    OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key) {
+    public OpenSSLECPrivateKey(OpenSSLECGroupContext group, OpenSSLKey key) {
         this.group = group;
         this.key = key;
     }
 
-    OpenSSLECPrivateKey(OpenSSLKey key) {
+    public OpenSSLECPrivateKey(OpenSSLKey key) {
         this.group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
                 NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
         this.key = key;
     }
 
-    OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec) throws InvalidKeySpecException {
+    public OpenSSLECPrivateKey(ECPrivateKeySpec ecKeySpec) throws InvalidKeySpecException {
         try {
             group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams());
             final BigInteger privKey = ecKeySpec.getS();
@@ -65,7 +68,7 @@
         }
     }
 
-    static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
+    public static OpenSSLKey wrapPlatformKey(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
         OpenSSLECGroupContext group;
         try {
             group = OpenSSLECGroupContext.getInstance(ecPrivateKey.getParams());
@@ -128,12 +131,12 @@
                 group.getNativeRef()), true);
     }
 
-    static OpenSSLKey getInstance(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
+    public static OpenSSLKey getInstance(ECPrivateKey ecPrivateKey) throws InvalidKeyException {
         try {
             OpenSSLECGroupContext group = OpenSSLECGroupContext.getInstance(ecPrivateKey
                     .getParams());
 
-            /*
+            /**
              * If the key is not encodable (PKCS11-like key), then wrap it and
              * use JNI upcalls to satisfy requests.
              */
diff --git a/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java b/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java
index 6e32810..9d45644 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLECPublicKey.java
@@ -29,8 +29,11 @@
 
 /**
  * An implementation of a {@link java.security.PublicKey} for EC keys based on BoringSSL.
+ *
+ * @hide
  */
-final class OpenSSLECPublicKey implements ECPublicKey, OpenSSLKeyHolder {
+@Internal
+public final class OpenSSLECPublicKey implements ECPublicKey, OpenSSLKeyHolder {
     private static final long serialVersionUID = 3215842926808298020L;
 
     private static final String ALGORITHM = "EC";
@@ -39,18 +42,18 @@
 
     protected transient OpenSSLECGroupContext group;
 
-    OpenSSLECPublicKey(OpenSSLECGroupContext group, OpenSSLKey key) {
+    public OpenSSLECPublicKey(OpenSSLECGroupContext group, OpenSSLKey key) {
         this.group = group;
         this.key = key;
     }
 
-    OpenSSLECPublicKey(OpenSSLKey key) {
+    public OpenSSLECPublicKey(OpenSSLKey key) {
         this.group = new OpenSSLECGroupContext(new NativeRef.EC_GROUP(
                 NativeCrypto.EC_KEY_get1_group(key.getNativeRef())));
         this.key = key;
     }
 
-    OpenSSLECPublicKey(ECPublicKeySpec ecKeySpec) throws InvalidKeySpecException {
+    public OpenSSLECPublicKey(ECPublicKeySpec ecKeySpec) throws InvalidKeySpecException {
         try {
             group = OpenSSLECGroupContext.getInstance(ecKeySpec.getParams());
             OpenSSLECPointContext pubKey = OpenSSLECPointContext.getInstance(group,
@@ -62,7 +65,7 @@
         }
     }
 
-    static OpenSSLKey getInstance(ECPublicKey ecPublicKey) throws InvalidKeyException {
+    public static OpenSSLKey getInstance(ECPublicKey ecPublicKey) throws InvalidKeyException {
         try {
             OpenSSLECGroupContext group = OpenSSLECGroupContext
                     .getInstance(ecPublicKey.getParams());
diff --git a/common/src/main/java/org/conscrypt/OpenSSLEngineSocketImpl.java b/common/src/main/java/org/conscrypt/OpenSSLEngineSocketImpl.java
index e87e9a0..bcf7d5d 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLEngineSocketImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLEngineSocketImpl.java
@@ -39,6 +39,8 @@
 /**
  * Implements crypto handling by delegating to OpenSSLEngine. Used for socket implementations
  * that are not backed by a real OS socket.
+ *
+ * @hide
  */
 final class OpenSSLEngineSocketImpl extends OpenSSLSocketImplWrapper {
     private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
diff --git a/common/src/main/java/org/conscrypt/OpenSSLExtendedSessionImpl.java b/common/src/main/java/org/conscrypt/OpenSSLExtendedSessionImpl.java
index 56411aa..a209109 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLExtendedSessionImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLExtendedSessionImpl.java
@@ -29,15 +29,18 @@
 /**
  * Implementation of the ExtendedSSLSession class for OpenSSL. Uses a delegate to maintain backward
  * compatibility with previous versions of Android which don't have ExtendedSSLSession.
+ *
+ * @hide
  */
-final class OpenSSLExtendedSessionImpl extends ExtendedSSLSession {
+@Internal
+public class OpenSSLExtendedSessionImpl extends ExtendedSSLSession {
     private final AbstractOpenSSLSession delegate;
 
-    OpenSSLExtendedSessionImpl(AbstractOpenSSLSession delegate) {
+    public OpenSSLExtendedSessionImpl(AbstractOpenSSLSession delegate) {
         this.delegate = delegate;
     }
 
-    AbstractOpenSSLSession getDelegate() {
+    public AbstractOpenSSLSession getDelegate() {
         return delegate;
     }
 
diff --git a/common/src/main/java/org/conscrypt/OpenSSLKey.java b/common/src/main/java/org/conscrypt/OpenSSLKey.java
index 1ca0b2c..9a7fe3c 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLKey.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLKey.java
@@ -30,17 +30,20 @@
 
 /**
  * Represents a BoringSSL {@code EVP_PKEY}.
+ *
+ * @hide
  */
-final class OpenSSLKey {
+@Internal
+public class OpenSSLKey {
     private final NativeRef.EVP_PKEY ctx;
 
     private final boolean wrapped;
 
-    OpenSSLKey(long ctx) {
+    public OpenSSLKey(long ctx) {
         this(ctx, false);
     }
 
-    OpenSSLKey(long ctx, boolean wrapped) {
+    public OpenSSLKey(long ctx, boolean wrapped) {
         this.ctx = new NativeRef.EVP_PKEY(ctx);
         this.wrapped = wrapped;
     }
@@ -48,15 +51,15 @@
     /**
      * Returns the EVP_PKEY context for use in JNI calls.
      */
-    NativeRef.EVP_PKEY getNativeRef() {
+    public NativeRef.EVP_PKEY getNativeRef() {
         return ctx;
     }
 
-    boolean isWrapped() {
+    public boolean isWrapped() {
         return wrapped;
     }
 
-    static OpenSSLKey fromPrivateKey(PrivateKey key) throws InvalidKeyException {
+    public static OpenSSLKey fromPrivateKey(PrivateKey key) throws InvalidKeyException {
         if (key instanceof OpenSSLKeyHolder) {
             return ((OpenSSLKeyHolder) key).getOpenSSLKey();
         }
@@ -81,7 +84,7 @@
      *
      * @throws InvalidKeyException if parsing fails
      */
-    static OpenSSLKey fromPrivateKeyPemInputStream(InputStream is)
+    public static OpenSSLKey fromPrivateKeyPemInputStream(InputStream is)
             throws InvalidKeyException {
         OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
         try {
@@ -106,7 +109,7 @@
      * @param publicKey corresponding public key or {@code null} if not available. Some opaque
      *        private keys cannot be used by the TLS/SSL stack without the public key.
      */
-    static OpenSSLKey fromPrivateKeyForTLSStackOnly(
+    public static OpenSSLKey fromPrivateKeyForTLSStackOnly(
             PrivateKey privateKey, PublicKey publicKey) throws InvalidKeyException {
         OpenSSLKey result = getOpenSSLKey(privateKey);
         if (result != null) {
@@ -130,7 +133,7 @@
      *        be used by the TLS/SSL stack without the parameters because the private key itself
      *        might not expose the parameters.
      */
-    static OpenSSLKey fromECPrivateKeyForTLSStackOnly(
+    public static OpenSSLKey fromECPrivateKeyForTLSStackOnly(
             PrivateKey key, ECParameterSpec ecParams) throws InvalidKeyException {
         OpenSSLKey result = getOpenSSLKey(key);
         if (result != null) {
@@ -207,7 +210,7 @@
         }
     }
 
-    static OpenSSLKey fromPublicKey(PublicKey key) throws InvalidKeyException {
+    public static OpenSSLKey fromPublicKey(PublicKey key) throws InvalidKeyException {
         if (key instanceof OpenSSLKeyHolder) {
             return ((OpenSSLKeyHolder) key).getOpenSSLKey();
         }
@@ -233,7 +236,7 @@
      *
      * @throws InvalidKeyException if parsing fails
      */
-    static OpenSSLKey fromPublicKeyPemInputStream(InputStream is)
+    public static OpenSSLKey fromPublicKeyPemInputStream(InputStream is)
             throws InvalidKeyException {
         OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
         try {
@@ -250,7 +253,7 @@
         }
     }
 
-    PublicKey getPublicKey() throws NoSuchAlgorithmException {
+    public PublicKey getPublicKey() throws NoSuchAlgorithmException {
         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
             case NativeConstants.EVP_PKEY_RSA:
                 return new OpenSSLRSAPublicKey(this);
@@ -283,7 +286,7 @@
         }
     }
 
-    PrivateKey getPrivateKey() throws NoSuchAlgorithmException {
+    public PrivateKey getPrivateKey() throws NoSuchAlgorithmException {
         switch (NativeCrypto.EVP_PKEY_type(ctx)) {
             case NativeConstants.EVP_PKEY_RSA:
                 return new OpenSSLRSAPrivateKey(this);
diff --git a/common/src/main/java/org/conscrypt/OpenSSLMac.java b/common/src/main/java/org/conscrypt/OpenSSLMac.java
index 0c979a1..83d19cc 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLMac.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLMac.java
@@ -157,37 +157,37 @@
         resetContext();
     }
 
-    public static final class HmacMD5 extends OpenSSLMac {
+    public static class HmacMD5 extends OpenSSLMac {
         public HmacMD5() {
             super(EvpMdRef.MD5.EVP_MD, EvpMdRef.MD5.SIZE_BYTES);
         }
     }
 
-    public static final class HmacSHA1 extends OpenSSLMac {
+    public static class HmacSHA1 extends OpenSSLMac {
         public HmacSHA1() {
             super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.SIZE_BYTES);
         }
     }
 
-    public static final class HmacSHA224 extends OpenSSLMac {
+    public static class HmacSHA224 extends OpenSSLMac {
         public HmacSHA224() throws NoSuchAlgorithmException {
             super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.SIZE_BYTES);
         }
     }
 
-    public static final class HmacSHA256 extends OpenSSLMac {
+    public static class HmacSHA256 extends OpenSSLMac {
         public HmacSHA256() throws NoSuchAlgorithmException {
             super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.SIZE_BYTES);
         }
     }
 
-    public static final class HmacSHA384 extends OpenSSLMac {
+    public static class HmacSHA384 extends OpenSSLMac {
         public HmacSHA384() throws NoSuchAlgorithmException {
             super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.SIZE_BYTES);
         }
     }
 
-    public static final class HmacSHA512 extends OpenSSLMac {
+    public static class HmacSHA512 extends OpenSSLMac {
         public HmacSHA512() {
             super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.SIZE_BYTES);
         }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLMessageDigestJDK.java b/common/src/main/java/org/conscrypt/OpenSSLMessageDigestJDK.java
index 0c58ff2..4d7f2a5 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLMessageDigestJDK.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLMessageDigestJDK.java
@@ -157,37 +157,37 @@
         return result;
     }
 
-    public static final class MD5 extends OpenSSLMessageDigestJDK {
+    public static class MD5 extends OpenSSLMessageDigestJDK {
         public MD5() throws NoSuchAlgorithmException {
             super(EvpMdRef.MD5.EVP_MD, EvpMdRef.MD5.SIZE_BYTES);
         }
     }
 
-    public static final class SHA1 extends OpenSSLMessageDigestJDK {
+    public static class SHA1 extends OpenSSLMessageDigestJDK {
         public SHA1() throws NoSuchAlgorithmException {
             super(EvpMdRef.SHA1.EVP_MD, EvpMdRef.SHA1.SIZE_BYTES);
         }
     }
 
-    public static final class SHA224 extends OpenSSLMessageDigestJDK {
+    public static class SHA224 extends OpenSSLMessageDigestJDK {
         public SHA224() throws NoSuchAlgorithmException {
             super(EvpMdRef.SHA224.EVP_MD, EvpMdRef.SHA224.SIZE_BYTES);
         }
     }
 
-    public static final class SHA256 extends OpenSSLMessageDigestJDK {
+    public static class SHA256 extends OpenSSLMessageDigestJDK {
         public SHA256() throws NoSuchAlgorithmException {
             super(EvpMdRef.SHA256.EVP_MD, EvpMdRef.SHA256.SIZE_BYTES);
         }
     }
 
-    public static final class SHA384 extends OpenSSLMessageDigestJDK {
+    public static class SHA384 extends OpenSSLMessageDigestJDK {
         public SHA384() throws NoSuchAlgorithmException {
             super(EvpMdRef.SHA384.EVP_MD, EvpMdRef.SHA384.SIZE_BYTES);
         }
     }
 
-    public static final class SHA512 extends OpenSSLMessageDigestJDK {
+    public static class SHA512 extends OpenSSLMessageDigestJDK {
         public SHA512() throws NoSuchAlgorithmException {
             super(EvpMdRef.SHA512.EVP_MD, EvpMdRef.SHA512.SIZE_BYTES);
         }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLProvider.java b/common/src/main/java/org/conscrypt/OpenSSLProvider.java
index 15fea2c..829c169 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLProvider.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLProvider.java
@@ -29,8 +29,6 @@
  * href="http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html">NIST cryptographic
  * algorithms</a></li>
  * </ul>
- *
- * @hide
  */
 @Internal
 public final class OpenSSLProvider extends Provider {
@@ -40,7 +38,7 @@
      * 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";
+    public static final String PROVIDER_NAME = "AndroidOpenSSL";
 
     private static final String PREFIX = OpenSSLProvider.class.getPackage().getName() + ".";
 
diff --git a/common/src/main/java/org/conscrypt/OpenSSLRSAKeyFactory.java b/common/src/main/java/org/conscrypt/OpenSSLRSAKeyFactory.java
index 18d9d5b..edec077 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLRSAKeyFactory.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLRSAKeyFactory.java
@@ -40,7 +40,7 @@
  * @hide
  */
 @Internal
-public final class OpenSSLRSAKeyFactory extends KeyFactorySpi {
+public class OpenSSLRSAKeyFactory extends KeyFactorySpi {
 
     @Override
     protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException {
diff --git a/common/src/main/java/org/conscrypt/OpenSSLRSAKeyPairGenerator.java b/common/src/main/java/org/conscrypt/OpenSSLRSAKeyPairGenerator.java
index 5259638..48203c1 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLRSAKeyPairGenerator.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLRSAKeyPairGenerator.java
@@ -33,7 +33,7 @@
  * @hide
  */
 @Internal
-public final class OpenSSLRSAKeyPairGenerator extends KeyPairGeneratorSpi {
+public class OpenSSLRSAKeyPairGenerator extends KeyPairGeneratorSpi {
     /**
      * Default modulus size is 0x10001 (65537)
      */
diff --git a/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateCrtKey.java b/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateCrtKey.java
index 41cbacb..58b003a 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateCrtKey.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateCrtKey.java
@@ -29,8 +29,11 @@
 /**
  * An implementation of {@link java.security.PrivateKey} for RSA keys which uses BoringSSL to
  * perform all the operations.
+ *
+ * @hide
  */
-final class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSAPrivateCrtKey {
+@Internal
+public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSAPrivateCrtKey {
     private static final long serialVersionUID = 3785291944868707197L;
 
     private BigInteger publicExponent;
@@ -53,7 +56,7 @@
         super(key, params);
     }
 
-    OpenSSLRSAPrivateCrtKey(RSAPrivateCrtKeySpec rsaKeySpec) throws InvalidKeySpecException {
+    public OpenSSLRSAPrivateCrtKey(RSAPrivateCrtKeySpec rsaKeySpec) throws InvalidKeySpecException {
         super(init(rsaKeySpec));
     }
 
diff --git a/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java b/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java
index 259e38e..33efe68 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLRSAPrivateKey.java
@@ -34,16 +34,17 @@
  *
  * @hide
  */
-class OpenSSLRSAPrivateKey implements RSAPrivateKey, OpenSSLKeyHolder {
+@Internal
+public class OpenSSLRSAPrivateKey implements RSAPrivateKey, OpenSSLKeyHolder {
     private static final long serialVersionUID = 4872170254439578735L;
 
-    transient OpenSSLKey key;
+    protected transient OpenSSLKey key;
 
-    transient boolean fetchedParams;
+    protected transient boolean fetchedParams;
 
-    BigInteger modulus;
+    protected BigInteger modulus;
 
-    BigInteger privateExponent;
+    protected BigInteger privateExponent;
 
     OpenSSLRSAPrivateKey(OpenSSLKey key) {
         this.key = key;
@@ -97,7 +98,7 @@
         return new OpenSSLRSAPrivateKey(key, params);
     }
 
-    static OpenSSLKey wrapPlatformKey(RSAPrivateKey rsaPrivateKey)
+    protected static OpenSSLKey wrapPlatformKey(RSAPrivateKey rsaPrivateKey)
             throws InvalidKeyException {
         OpenSSLKey wrapper = Platform.wrapRsaKey(rsaPrivateKey);
         if (wrapper != null) {
diff --git a/common/src/main/java/org/conscrypt/OpenSSLRandom.java b/common/src/main/java/org/conscrypt/OpenSSLRandom.java
index bbe46a5..e9bd8d6 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLRandom.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLRandom.java
@@ -25,7 +25,7 @@
  * @hide
  */
 @Internal
-public final class OpenSSLRandom extends SecureRandomSpi implements Serializable {
+public class OpenSSLRandom extends SecureRandomSpi implements Serializable {
     private static final long serialVersionUID = 8506210602917522861L;
 
     @Override
diff --git a/common/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java b/common/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java
index 008e92f..347351c 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLServerSocketFactoryImpl.java
@@ -20,19 +20,21 @@
 import java.net.InetAddress;
 import java.net.ServerSocket;
 import java.security.KeyManagementException;
-import javax.net.ssl.SSLServerSocketFactory;
 
 /**
- * An implementation of {@link SSLServerSocketFactory} using BoringSSL.
+ * An implementation of {@link javax.net.ssl.SSLServerSocketFactory} using BoringSSL.
+ *
+ * @hide
  */
-final class OpenSSLServerSocketFactoryImpl extends SSLServerSocketFactory {
+@Internal
+public class OpenSSLServerSocketFactoryImpl extends javax.net.ssl.SSLServerSocketFactory {
     private static boolean useEngineSocketByDefault = SSLUtils.USE_ENGINE_SOCKET_BY_DEFAULT;
 
     private SSLParametersImpl sslParameters;
     private IOException instantiationException;
     private boolean useEngineSocket = useEngineSocketByDefault;
 
-    OpenSSLServerSocketFactoryImpl() {
+    public OpenSSLServerSocketFactoryImpl() {
         try {
             this.sslParameters = SSLParametersImpl.getDefault();
             this.sslParameters.setUseClientMode(false);
@@ -43,7 +45,7 @@
         }
     }
 
-    OpenSSLServerSocketFactoryImpl(SSLParametersImpl sslParameters) {
+    public OpenSSLServerSocketFactoryImpl(SSLParametersImpl sslParameters) {
         this.sslParameters = (SSLParametersImpl) sslParameters.clone();
         this.sslParameters.setUseClientMode(false);
     }
@@ -51,7 +53,7 @@
     /**
      * Configures the default socket to be created for all instances.
      */
-    static void setUseEngineSocketByDefault(boolean useEngineSocket) {
+    public static void setUseEngineSocketByDefault(boolean useEngineSocket) {
         useEngineSocketByDefault = useEngineSocket;
     }
 
@@ -59,7 +61,7 @@
      * Configures the socket to be created for this instance. If not called,
      * {@link #useEngineSocketByDefault} will be used.
      */
-    void setUseEngineSocket(boolean useEngineSocket) {
+    public void setUseEngineSocket(boolean useEngineSocket) {
         this.useEngineSocket = useEngineSocket;
     }
 
diff --git a/common/src/main/java/org/conscrypt/OpenSSLServerSocketImpl.java b/common/src/main/java/org/conscrypt/OpenSSLServerSocketImpl.java
index a7e3654..fc320f1 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLServerSocketImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLServerSocketImpl.java
@@ -22,29 +22,32 @@
 
 /**
  * BoringSSL-based implementation of server sockets.
+ *
+ * @hide
  */
-final class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
+@Internal
+public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket {
     private final SSLParametersImpl sslParameters;
     private boolean channelIdEnabled;
     private boolean useEngineSocket;
 
-    OpenSSLServerSocketImpl(SSLParametersImpl sslParameters) throws IOException {
+    protected OpenSSLServerSocketImpl(SSLParametersImpl sslParameters) throws IOException {
         this.sslParameters = sslParameters;
     }
 
-    OpenSSLServerSocketImpl(int port, SSLParametersImpl sslParameters)
+    protected OpenSSLServerSocketImpl(int port, SSLParametersImpl sslParameters)
         throws IOException {
         super(port);
         this.sslParameters = sslParameters;
     }
 
-    OpenSSLServerSocketImpl(int port, int backlog, SSLParametersImpl sslParameters)
+    protected OpenSSLServerSocketImpl(int port, int backlog, SSLParametersImpl sslParameters)
         throws IOException {
         super(port, backlog);
         this.sslParameters = sslParameters;
     }
 
-    OpenSSLServerSocketImpl(int port,
+    protected OpenSSLServerSocketImpl(int port,
                                       int backlog,
                                       InetAddress iAddress,
                                       SSLParametersImpl sslParameters)
@@ -56,7 +59,7 @@
     /**
      * Configures the socket to be created for this instance.
      */
-    OpenSSLServerSocketImpl setUseEngineSocket(boolean useEngineSocket) {
+    public OpenSSLServerSocketImpl setUseEngineSocket(boolean useEngineSocket) {
         this.useEngineSocket = useEngineSocket;
         return this;
     }
@@ -118,14 +121,14 @@
     /**
      * Enables/disables the TLS Channel ID extension for this server socket.
      */
-    void setChannelIdEnabled(boolean enabled) {
+    public void setChannelIdEnabled(boolean enabled) {
       channelIdEnabled = enabled;
     }
 
     /**
      * Checks whether the TLS Channel ID extension is enabled for this server socket.
      */
-    boolean isChannelIdEnabled() {
+    public boolean isChannelIdEnabled() {
       return channelIdEnabled;
     }
 
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSessionImpl.java b/common/src/main/java/org/conscrypt/OpenSSLSessionImpl.java
index d94252b..76143c0 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLSessionImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLSessionImpl.java
@@ -29,8 +29,11 @@
 /**
  * Implementation of the class OpenSSLSessionImpl
  * based on BoringSSL.
+ *
+ * @hide
  */
-class OpenSSLSessionImpl extends AbstractOpenSSLSession {
+@Internal
+public class OpenSSLSessionImpl extends AbstractOpenSSLSession {
     private long creationTime = 0;
     long lastAccessedTime = 0;
     final X509Certificate[] localCertificates;
@@ -39,7 +42,7 @@
     private final Map<String, Object> values = new HashMap<String, Object>();
     private byte[] peerCertificateOcspData;
     private byte[] peerTlsSctData;
-    long sslSessionNativePointer;
+    protected long sslSessionNativePointer;
     private String peerHost;
     private int peerPort = -1;
     private String cipherSuite;
@@ -50,7 +53,7 @@
      * Class constructor creates an SSL session context given the appropriate
      * SSL parameters.
      */
-    OpenSSLSessionImpl(long sslSessionNativePointer, X509Certificate[] localCertificates,
+    protected OpenSSLSessionImpl(long sslSessionNativePointer, X509Certificate[] localCertificates,
             X509Certificate[] peerCertificates, byte[] peerCertificateOcspData,
             byte[] peerTlsSctData, String peerHost, int peerPort,
             AbstractSessionContext sessionContext) {
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSignature.java b/common/src/main/java/org/conscrypt/OpenSSLSignature.java
index c26322c..470ea48 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLSignature.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLSignature.java
@@ -40,7 +40,7 @@
  */
 @Internal
 public class OpenSSLSignature extends SignatureSpi {
-    private enum EngineType {
+    private static enum EngineType {
         RSA, EC,
     }
 
@@ -86,7 +86,7 @@
         this.evpMdRef = evpMdRef;
     }
 
-    private void resetContext() throws InvalidAlgorithmParameterException {
+    private final void resetContext() throws InvalidAlgorithmParameterException {
         NativeRef.EVP_MD_CTX ctxLocal = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
         if (signing) {
             evpPkeyCtx = NativeCrypto.EVP_DigestSignInit(ctxLocal, evpMdRef, key.getNativeRef());
@@ -354,7 +354,7 @@
         private long mgf1EvpMdRef;
         private int saltSizeBytes;
 
-        RSAPSSPadding(
+        public RSAPSSPadding(
                 long contentDigestEvpMdRef, String contentDigestAlgorithm, int saltSizeBytes) {
             super(contentDigestEvpMdRef, EngineType.RSA);
             this.contentDigestAlgorithm = contentDigestAlgorithm;
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSignatureRawRSA.java b/common/src/main/java/org/conscrypt/OpenSSLSignatureRawRSA.java
index 011c819..895d12d 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLSignatureRawRSA.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLSignatureRawRSA.java
@@ -33,7 +33,7 @@
  * @hide
  */
 @Internal
-public final class OpenSSLSignatureRawRSA extends SignatureSpi {
+public class OpenSSLSignatureRawRSA extends SignatureSpi {
     /**
      * The current OpenSSL key we're operating on.
      */
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java b/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
index 8e38e53..847b559 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
@@ -21,31 +21,34 @@
 import java.net.Socket;
 import java.net.UnknownHostException;
 import java.security.KeyManagementException;
-import javax.net.ssl.SSLSocketFactory;
 
 /**
- * An implementation of {@link SSLSocketFactory} based on BoringSSL.
+ * An implementation of {@link javax.net.ssl.SSLSocketFactory} based on BoringSSL.
+ *
+ * @hide
  */
-final class OpenSSLSocketFactoryImpl extends SSLSocketFactory {
+@Internal
+public class OpenSSLSocketFactoryImpl extends javax.net.ssl.SSLSocketFactory {
     private static boolean useEngineSocketByDefault = SSLUtils.USE_ENGINE_SOCKET_BY_DEFAULT;
 
     private final SSLParametersImpl sslParameters;
     private final IOException instantiationException;
     private boolean useEngineSocket = useEngineSocketByDefault;
 
-    OpenSSLSocketFactoryImpl() {
+    public OpenSSLSocketFactoryImpl() {
         SSLParametersImpl sslParametersLocal = null;
         IOException instantiationExceptionLocal = null;
         try {
             sslParametersLocal = SSLParametersImpl.getDefault();
         } catch (KeyManagementException e) {
-            instantiationExceptionLocal = new IOException("Delayed instantiation exception:", e);
+            instantiationExceptionLocal = new IOException("Delayed instantiation exception:");
+            instantiationExceptionLocal.initCause(e);
         }
         this.sslParameters = sslParametersLocal;
         this.instantiationException = instantiationExceptionLocal;
     }
 
-    OpenSSLSocketFactoryImpl(SSLParametersImpl sslParameters) {
+    public OpenSSLSocketFactoryImpl(SSLParametersImpl sslParameters) {
         this.sslParameters = sslParameters;
         this.instantiationException = null;
     }
@@ -61,7 +64,7 @@
      * Configures the socket to be created for this instance. If not called,
      * {@link #useEngineSocketByDefault} will be used.
      */
-    void setUseEngineSocket(boolean useEngineSocket) {
+    public void setUseEngineSocket(boolean useEngineSocket) {
         this.useEngineSocket = useEngineSocket;
     }
 
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java b/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
index a50f121..b1e5977 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLSocketImpl.java
@@ -177,7 +177,7 @@
 
     private int handshakeTimeoutMilliseconds = -1;  // -1 = same as timeout; 0 = infinite
 
-    OpenSSLSocketImpl(SSLParametersImpl sslParameters) throws IOException {
+    protected OpenSSLSocketImpl(SSLParametersImpl sslParameters) throws IOException {
         this.socket = this;
         this.peerHostname = null;
         this.peerPort = -1;
@@ -185,7 +185,7 @@
         this.sslParameters = sslParameters;
     }
 
-    OpenSSLSocketImpl(String hostname, int port, SSLParametersImpl sslParameters)
+    protected OpenSSLSocketImpl(String hostname, int port, SSLParametersImpl sslParameters)
             throws IOException {
         super(hostname, port);
         this.socket = this;
@@ -195,7 +195,7 @@
         this.sslParameters = sslParameters;
     }
 
-    OpenSSLSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters)
+    protected OpenSSLSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters)
             throws IOException {
         super(address, port);
         this.socket = this;
@@ -206,7 +206,7 @@
     }
 
 
-    OpenSSLSocketImpl(String hostname, int port,
+    protected OpenSSLSocketImpl(String hostname, int port,
                                 InetAddress clientAddress, int clientPort,
                                 SSLParametersImpl sslParameters) throws IOException {
         super(hostname, port, clientAddress, clientPort);
@@ -217,7 +217,7 @@
         this.sslParameters = sslParameters;
     }
 
-    OpenSSLSocketImpl(InetAddress address, int port,
+    protected OpenSSLSocketImpl(InetAddress address, int port,
                                 InetAddress clientAddress, int clientPort,
                                 SSLParametersImpl sslParameters) throws IOException {
         super(address, port, clientAddress, clientPort);
@@ -232,7 +232,7 @@
      * Create an SSL socket that wraps another socket. Invoked by
      * OpenSSLSocketImplWrapper constructor.
      */
-    OpenSSLSocketImpl(Socket socket, String hostname, int port,
+    protected OpenSSLSocketImpl(Socket socket, String hostname, int port,
             boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
         this.socket = socket;
         this.peerHostname = hostname;
@@ -672,7 +672,10 @@
                     stateLock.wait();
                 } catch (InterruptedException e) {
                     Thread.currentThread().interrupt();
-                    throw new IOException("Interrupted waiting for handshake", e);
+                    IOException ioe = new IOException("Interrupted waiting for handshake");
+                    ioe.initCause(e);
+
+                    throw ioe;
                 }
             }
 
@@ -740,7 +743,7 @@
             }
         }
 
-        void awaitPendingOps() {
+        public void awaitPendingOps() {
             if (DBG_STATE) {
                 synchronized (stateLock) {
                     if (state != STATE_CLOSED) throw new AssertionError("State is: " + state);
@@ -807,7 +810,7 @@
         }
 
 
-        void awaitPendingOps() {
+        public void awaitPendingOps() {
             if (DBG_STATE) {
                 synchronized (stateLock) {
                     if (state != STATE_CLOSED) throw new AssertionError("State is: " + state);
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSocketImplWrapper.java b/common/src/main/java/org/conscrypt/OpenSSLSocketImplWrapper.java
index 126dbb2..204acf8 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLSocketImplWrapper.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLSocketImplWrapper.java
@@ -25,12 +25,15 @@
 
 /**
  * This class wraps the SSL functionality over an existing connected socket.
+ *
+ * @hide
  */
-class OpenSSLSocketImplWrapper extends OpenSSLSocketImpl {
+@Internal
+public class OpenSSLSocketImplWrapper extends OpenSSLSocketImpl {
 
     private Socket socket;
 
-    OpenSSLSocketImplWrapper(Socket socket, String hostname, int port,
+    protected OpenSSLSocketImplWrapper(Socket socket, String hostname, int port,
             boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
         super(socket, hostname, port, autoClose, sslParameters);
         if (!socket.isConnected()) {
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX509CRL.java b/common/src/main/java/org/conscrypt/OpenSSLX509CRL.java
index 57e1cdc..1c479a8 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLX509CRL.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLX509CRL.java
@@ -45,15 +45,18 @@
 
 /**
  * An implementation of {@link X509CRL} based on BoringSSL.
+ *
+ * @hide
  */
-final class OpenSSLX509CRL extends X509CRL {
+@Internal
+public class OpenSSLX509CRL extends X509CRL {
     private final long mContext;
 
     private OpenSSLX509CRL(long ctx) {
         mContext = ctx;
     }
 
-    static OpenSSLX509CRL fromX509DerInputStream(InputStream is) throws ParsingException {
+    public static OpenSSLX509CRL fromX509DerInputStream(InputStream is) throws ParsingException {
         final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
         try {
@@ -69,7 +72,7 @@
         }
     }
 
-    static List<OpenSSLX509CRL> fromPkcs7DerInputStream(InputStream is)
+    public static List<OpenSSLX509CRL> fromPkcs7DerInputStream(InputStream is)
             throws ParsingException {
         OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
@@ -92,7 +95,7 @@
         return certs;
     }
 
-    static OpenSSLX509CRL fromX509PemInputStream(InputStream is) throws ParsingException {
+    public static OpenSSLX509CRL fromX509PemInputStream(InputStream is) throws ParsingException {
         final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
         try {
@@ -108,7 +111,7 @@
         }
     }
 
-    static List<OpenSSLX509CRL> fromPkcs7PemInputStream(InputStream is)
+    public static List<OpenSSLX509CRL> fromPkcs7PemInputStream(InputStream is)
             throws ParsingException {
         OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java b/common/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java
index f233421..20dc54a 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLX509CRLEntry.java
@@ -29,8 +29,11 @@
 
 /**
  * An implementation of {@link X509CRLEntry} based on BoringSSL.
+ *
+ * @hide
  */
-final class OpenSSLX509CRLEntry extends X509CRLEntry {
+@Internal
+public class OpenSSLX509CRLEntry extends X509CRLEntry {
     private final long mContext;
 
     OpenSSLX509CRLEntry(long ctx) {
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX509CertPath.java b/common/src/main/java/org/conscrypt/OpenSSLX509CertPath.java
index c82674f..31cfd55 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLX509CertPath.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLX509CertPath.java
@@ -33,8 +33,11 @@
 
 /**
  * An implementation of {@link CertPath} based on BoringSSL.
+ *
+ * @hide
  */
-final class OpenSSLX509CertPath extends CertPath {
+@Internal
+public class OpenSSLX509CertPath extends CertPath {
     private static final long serialVersionUID = -3249106005255170761L;
 
     private static final byte[] PKCS7_MARKER = new byte[] {
@@ -83,7 +86,7 @@
         return ALL_ENCODINGS.iterator();
     }
 
-    OpenSSLX509CertPath(List<? extends X509Certificate> certificates) {
+    protected OpenSSLX509CertPath(List<? extends X509Certificate> certificates) {
         super("X.509");
 
         mCertificates = certificates;
@@ -233,7 +236,7 @@
         }
     }
 
-    static CertPath fromEncoding(InputStream inStream, String encoding)
+    public static CertPath fromEncoding(InputStream inStream, String encoding)
             throws CertificateException {
         if (inStream == null) {
             throw new CertificateException("inStream == null");
@@ -247,7 +250,7 @@
         return fromEncoding(inStream, enc);
     }
 
-    static CertPath fromEncoding(InputStream inStream) throws CertificateException {
+    public static CertPath fromEncoding(InputStream inStream) throws CertificateException {
         if (inStream == null) {
             throw new CertificateException("inStream == null");
         }
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX509Certificate.java b/common/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
index 751fd89..1627fa4 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
@@ -57,7 +57,7 @@
  * @hide
  */
 @Internal
-public final class OpenSSLX509Certificate extends X509Certificate {
+public class OpenSSLX509Certificate extends X509Certificate {
     private static final long serialVersionUID = 1992239142393372128L;
 
     private transient final long mContext;
diff --git a/common/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java b/common/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java
index cdec9e1..e1d23f8 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLX509CertificateFactory.java
@@ -49,15 +49,15 @@
     static class ParsingException extends Exception {
         private static final long serialVersionUID = 8390802697728301325L;
 
-        ParsingException(String message) {
+        public ParsingException(String message) {
             super(message);
         }
 
-        ParsingException(Exception cause) {
+        public ParsingException(Exception cause) {
             super(cause);
         }
 
-        ParsingException(String message, Exception cause) {
+        public ParsingException(String message, Exception cause) {
             super(message, cause);
         }
     }
@@ -68,7 +68,7 @@
      * but it's already written in this language anyway.
      */
     private static abstract class Parser<T> {
-        T generateItem(InputStream inStream) throws ParsingException {
+        public T generateItem(InputStream inStream) throws ParsingException {
             if (inStream == null) {
                 throw new ParsingException("inStream == null");
             }
@@ -122,7 +122,7 @@
             }
         }
 
-        Collection<? extends T> generateItems(InputStream inStream)
+        public Collection<? extends T> generateItems(InputStream inStream)
                 throws ParsingException {
             if (inStream == null) {
                 throw new ParsingException("inStream == null");
@@ -175,7 +175,7 @@
              * can't anymore.
              */
             final List<T> coll = new ArrayList<T>();
-            T c;
+            T c = null;
             do {
                 /*
                  * If this stream supports marking, try to mark here in case
diff --git a/common/src/main/java/org/conscrypt/PSKKeyManager.java b/common/src/main/java/org/conscrypt/PSKKeyManager.java
index bd68f1f..5f6222d 100644
--- a/common/src/main/java/org/conscrypt/PSKKeyManager.java
+++ b/common/src/main/java/org/conscrypt/PSKKeyManager.java
@@ -95,7 +95,6 @@
  * }</pre>
  *
  * @deprecated This abstraction is deprecated because it does not work with TLS 1.3.
- * @hide
  */
 @Deprecated
 @Internal
diff --git a/common/src/main/java/org/conscrypt/SSLNullSession.java b/common/src/main/java/org/conscrypt/SSLNullSession.java
index 88a0a9c..4195b0a 100644
--- a/common/src/main/java/org/conscrypt/SSLNullSession.java
+++ b/common/src/main/java/org/conscrypt/SSLNullSession.java
@@ -26,27 +26,30 @@
  * This is returned in the place of a {@link SSLSession} when no TLS connection could be negotiated,
  * but one was requested from a method that can't throw an exception such as {@link
  * SSLSocket#getSession()} before {@link SSLSocket#startHandshake()} is called.
+ *
+ * @hide
  */
-final class SSLNullSession implements SSLSession, Cloneable {
+@Internal
+public final class SSLNullSession implements SSLSession, Cloneable {
 
     /*
      * Holds default instances so class preloading doesn't create an instance of
      * it.
      */
     private static class DefaultHolder {
-        static final SSLNullSession NULL_SESSION = new SSLNullSession();
+        public static final SSLNullSession NULL_SESSION = new SSLNullSession();
     }
 
     private final HashMap<String, Object> values = new HashMap<String, Object>();
 
-    private long creationTime;
-    private long lastAccessedTime;
+    long creationTime;
+    long lastAccessedTime;
 
-    static SSLSession getNullSession() {
+    public static SSLSession getNullSession() {
         return DefaultHolder.NULL_SESSION;
     }
 
-    SSLNullSession() {
+    public SSLNullSession() {
         creationTime = System.currentTimeMillis();
         lastAccessedTime = creationTime;
     }
diff --git a/common/src/main/java/org/conscrypt/SSLParametersImpl.java b/common/src/main/java/org/conscrypt/SSLParametersImpl.java
index 1b1e567..4fdcfe3 100644
--- a/common/src/main/java/org/conscrypt/SSLParametersImpl.java
+++ b/common/src/main/java/org/conscrypt/SSLParametersImpl.java
@@ -54,8 +54,11 @@
  * ssl socket, whether it require/want client authentication or not,
  * and controls whether new SSL sessions may be established by this
  * socket or not.
+ *
+ * @hide
  */
-final class SSLParametersImpl implements Cloneable {
+@Internal
+public class SSLParametersImpl implements Cloneable {
 
     // default source of X.509 certificate based authentication keys
     private static volatile X509KeyManager defaultX509KeyManager;
@@ -127,7 +130,7 @@
      * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],
      * SecureRandom)} for more information
      */
-    SSLParametersImpl(KeyManager[] kms, TrustManager[] tms,
+    protected SSLParametersImpl(KeyManager[] kms, TrustManager[] tms,
             SecureRandom sr, ClientSessionContext clientSessionContext,
             ServerSessionContext serverSessionContext, String[] protocols)
             throws KeyManagementException {
@@ -167,7 +170,7 @@
                 x509CipherSuitesNeeded, pskCipherSuitesNeeded);
     }
 
-    static SSLParametersImpl getDefault() throws KeyManagementException {
+    protected static SSLParametersImpl getDefault() throws KeyManagementException {
         SSLParametersImpl result = defaultParameters;
         if (result == null) {
             // single-check idiom
@@ -184,28 +187,28 @@
     /**
      * Returns the appropriate session context.
      */
-    AbstractSessionContext getSessionContext() {
+    public AbstractSessionContext getSessionContext() {
         return client_mode ? clientSessionContext : serverSessionContext;
     }
 
     /**
      * @return server session context
      */
-    ServerSessionContext getServerSessionContext() {
+    protected ServerSessionContext getServerSessionContext() {
         return serverSessionContext;
     }
 
     /**
      * @return client session context
      */
-    ClientSessionContext getClientSessionContext() {
+    protected ClientSessionContext getClientSessionContext() {
         return clientSessionContext;
     }
 
     /**
      * @return X.509 key manager or {@code null} for none.
      */
-    X509KeyManager getX509KeyManager() {
+    protected X509KeyManager getX509KeyManager() {
         return x509KeyManager;
     }
 
@@ -213,21 +216,21 @@
      * @return Pre-Shared Key (PSK) key manager or {@code null} for none.
      */
     @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
-    PSKKeyManager getPSKKeyManager() {
+    protected PSKKeyManager getPSKKeyManager() {
         return pskKeyManager;
     }
 
     /**
      * @return X.509 trust manager or {@code null} for none.
      */
-    X509TrustManager getX509TrustManager() {
+    protected X509TrustManager getX509TrustManager() {
         return x509TrustManager;
     }
 
     /**
      * @return secure random
      */
-    SecureRandom getSecureRandom() {
+    protected SecureRandom getSecureRandom() {
         if (secureRandom != null) {
             return secureRandom;
         }
@@ -243,28 +246,28 @@
     /**
      * @return the secure random member reference, even it is null
      */
-    SecureRandom getSecureRandomMember() {
+    protected SecureRandom getSecureRandomMember() {
         return secureRandom;
     }
 
     /**
      * @return the names of enabled cipher suites
      */
-    String[] getEnabledCipherSuites() {
+    protected String[] getEnabledCipherSuites() {
         return enabledCipherSuites.clone();
     }
 
     /**
      * Sets the enabled cipher suites after filtering through OpenSSL.
      */
-    void setEnabledCipherSuites(String[] cipherSuites) {
+    protected void setEnabledCipherSuites(String[] cipherSuites) {
         enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(cipherSuites).clone();
     }
 
     /**
      * @return the set of enabled protocols
      */
-    String[] getEnabledProtocols() {
+    protected String[] getEnabledProtocols() {
         return enabledProtocols.clone();
     }
 
@@ -272,7 +275,7 @@
      * Sets the list of available protocols for use in SSL connection.
      * @throws IllegalArgumentException if {@code protocols == null}
      */
-    void setEnabledProtocols(String[] protocols) {
+    protected void setEnabledProtocols(String[] protocols) {
         if (protocols == null) {
             throw new IllegalArgumentException("protocols == null");
         }
@@ -312,7 +315,7 @@
      * Tunes the peer holding this parameters to work in client mode.
      * @param   mode if the peer is configured to work in client mode
      */
-    void setUseClientMode(boolean mode) {
+    protected void setUseClientMode(boolean mode) {
         client_mode = mode;
     }
 
@@ -320,14 +323,14 @@
      * Returns the value indicating if the parameters configured to work
      * in client mode.
      */
-    boolean getUseClientMode() {
+    protected boolean getUseClientMode() {
         return client_mode;
     }
 
     /**
      * Tunes the peer holding this parameters to require client authentication
      */
-    void setNeedClientAuth(boolean need) {
+    protected void setNeedClientAuth(boolean need) {
         need_client_auth = need;
         // reset the want_client_auth setting
         want_client_auth = false;
@@ -337,14 +340,14 @@
      * Returns the value indicating if the peer with this parameters tuned
      * to require client authentication
      */
-    boolean getNeedClientAuth() {
+    protected boolean getNeedClientAuth() {
         return need_client_auth;
     }
 
     /**
      * Tunes the peer holding this parameters to request client authentication
      */
-    void setWantClientAuth(boolean want) {
+    protected void setWantClientAuth(boolean want) {
         want_client_auth = want;
         // reset the need_client_auth setting
         need_client_auth = false;
@@ -354,7 +357,7 @@
      * Returns the value indicating if the peer with this parameters
      * tuned to request client authentication
      */
-    boolean getWantClientAuth() {
+    protected boolean getWantClientAuth() {
         return want_client_auth;
     }
 
@@ -362,7 +365,7 @@
      * Allows/disallows the peer holding this parameters to
      * create new SSL session
      */
-    void setEnableSessionCreation(boolean flag) {
+    protected void setEnableSessionCreation(boolean flag) {
         enable_session_creation = flag;
     }
 
@@ -370,7 +373,7 @@
      * Returns the value indicating if the peer with this parameters
      * allowed to cteate new SSL session
      */
-    boolean getEnableSessionCreation() {
+    protected boolean getEnableSessionCreation() {
         return enable_session_creation;
     }
 
@@ -382,7 +385,7 @@
      * Whether connections using this SSL connection should use the TLS
      * extension Server Name Indication (SNI).
      */
-    void setUseSni(boolean flag) {
+    protected void setUseSni(boolean flag) {
         useSni = Boolean.valueOf(flag);
     }
 
@@ -390,23 +393,23 @@
      * Returns whether connections using this SSL connection should use the TLS
      * extension Server Name Indication (SNI).
      */
-    boolean getUseSni() {
+    protected boolean getUseSni() {
         return useSni != null ? useSni.booleanValue() : isSniEnabledByDefault();
     }
 
-    void setCTVerificationEnabled(boolean enabled) {
+    public void setCTVerificationEnabled(boolean enabled) {
         ctVerificationEnabled = enabled;
     }
 
-    void setSCTExtension(byte[] extension) {
+    public void setSCTExtension(byte[] extension) {
         sctExtension = extension;
     }
 
-    void setOCSPResponse(byte[] response) {
+    public void setOCSPResponse(byte[] response) {
         ocspResponse = response;
     }
 
-    byte[] getOCSPResponse() {
+    public byte[] getOCSPResponse() {
         return ocspResponse;
     }
 
@@ -842,7 +845,7 @@
      * and
      * {@link X509ExtendedKeyManager#chooseEngineClientAlias(String[], java.security.Principal[], javax.net.ssl.SSLEngine)}
      */
-    interface AliasChooser {
+    public interface AliasChooser {
         String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers,
                 String[] keyTypes);
 
@@ -854,7 +857,7 @@
      * those taking an {@code SSLEngine}.
      */
     @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
-    interface PSKCallbacks {
+    public interface PSKCallbacks {
         String chooseServerPSKIdentityHint(PSKKeyManager keyManager);
         String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint);
         SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity);
@@ -937,8 +940,10 @@
 
     /**
      * Gets the default X.509 trust manager.
+     * <p>
+     * TODO: Move this to a published API under dalvik.system.
      */
-    static X509TrustManager getDefaultX509TrustManager()
+    public static X509TrustManager getDefaultX509TrustManager()
             throws KeyManagementException {
         X509TrustManager result = defaultX509TrustManager;
         if (result == null) {
@@ -970,7 +975,8 @@
     }
 
     /**
-     * Finds the first {@link X509TrustManager} element in the provided array.
+     * Finds the first {@link X509ExtendedTrustManager} or
+     * {@link X509TrustManager} element in the provided array.
      *
      * @return the first {@code X509ExtendedTrustManager} or
      *         {@code X509TrustManager} or {@code null} if not found.
@@ -984,19 +990,19 @@
         return null;
     }
 
-    String getEndpointIdentificationAlgorithm() {
+    public String getEndpointIdentificationAlgorithm() {
         return endpointIdentificationAlgorithm;
     }
 
-    void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) {
+    public void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) {
         this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
     }
 
-    boolean getUseCipherSuitesOrder() {
+    public boolean getUseCipherSuitesOrder() {
         return useCipherSuitesOrder;
     }
 
-    void setUseCipherSuitesOrder(boolean useCipherSuitesOrder) {
+    public void setUseCipherSuitesOrder(boolean useCipherSuitesOrder) {
         this.useCipherSuitesOrder = useCipherSuitesOrder;
     }
 
@@ -1045,7 +1051,7 @@
      * <p>
      * Visible for testing.
      */
-    static String getClientKeyType(byte clientCertificateType) {
+    public static String getClientKeyType(byte clientCertificateType) {
         // See also http://www.ietf.org/assignments/tls-parameters/tls-parameters.xml
         switch (clientCertificateType) {
             case NativeConstants.TLS_CT_RSA_SIGN:
@@ -1074,7 +1080,7 @@
      *
      * Visible for testing.
      */
-    static Set<String> getSupportedClientKeyTypes(byte[] clientCertificateTypes) {
+    public static Set<String> getSupportedClientKeyTypes(byte[] clientCertificateTypes) {
         Set<String> result = new HashSet<String>(clientCertificateTypes.length);
         for (byte keyTypeCode : clientCertificateTypes) {
             String keyType = getClientKeyType(keyTypeCode);
@@ -1137,7 +1143,7 @@
     /**
      * Check if SCT verification is enforced for a given hostname.
      */
-    boolean isCTVerificationEnabled(String hostname) {
+    public boolean isCTVerificationEnabled(String hostname) {
         if (hostname == null) {
             return false;
         }
diff --git a/common/src/main/java/org/conscrypt/SSLServerSessionCache.java b/common/src/main/java/org/conscrypt/SSLServerSessionCache.java
index 962dcde..dd90f92 100644
--- a/common/src/main/java/org/conscrypt/SSLServerSessionCache.java
+++ b/common/src/main/java/org/conscrypt/SSLServerSessionCache.java
@@ -29,8 +29,11 @@
  * {@code SSLSession}s into raw bytes and vice versa. The exact makeup of the
  * session data is dependent upon the caller's implementation and is opaque to
  * the {@code SSLServerSessionCache} implementation.
+ *
+ * @hide
  */
-interface SSLServerSessionCache {
+@Internal
+public interface SSLServerSessionCache {
     /**
      * Gets the session data for given session ID.
      *
diff --git a/common/src/main/java/org/conscrypt/SSLUtils.java b/common/src/main/java/org/conscrypt/SSLUtils.java
index c770260..f228329 100644
--- a/common/src/main/java/org/conscrypt/SSLUtils.java
+++ b/common/src/main/java/org/conscrypt/SSLUtils.java
@@ -48,11 +48,13 @@
  * Utility methods for SSL packet processing. Copied from the Netty project.
  * <p>
  * This is a public class to allow testing to occur on Android via CTS.
+ *
+ * @hide
  */
-final class SSLUtils {
+public final class SSLUtils {
     static final boolean USE_ENGINE_SOCKET_BY_DEFAULT =
             Boolean.parseBoolean(System.getProperty("org.conscrypt.useEngineSocketByDefault"));
-    private static final int MAX_PROTOCOL_LENGTH = 255;
+    static final int MAX_PROTOCOL_LENGTH = 255;
 
     /**
      * This is the maximum overhead when encrypting plaintext as defined by
@@ -78,7 +80,7 @@
      * Calculates the minimum bytes required in the encrypted output buffer for the given number of
      * plaintext source bytes.
      */
-    static int calculateOutNetBufSize(int pendingBytes) {
+    public static int calculateOutNetBufSize(int pendingBytes) {
         return min(SSL3_RT_MAX_PACKET_SIZE,
                 MAX_ENCRYPTION_OVERHEAD_LENGTH + min(MAX_ENCRYPTION_OVERHEAD_DIFF, pendingBytes));
     }
@@ -117,7 +119,7 @@
      * @throws IllegalArgumentException Is thrown if the given {@link ByteBuffer} has not at least
      * {@link org.conscrypt.NativeConstants#SSL3_RT_HEADER_LENGTH} bytes to read.
      */
-    static int getEncryptedPacketLength(ByteBuffer[] buffers, int offset) {
+    public static int getEncryptedPacketLength(ByteBuffer[] buffers, int offset) {
         ByteBuffer buffer = buffers[offset];
 
         // Check if everything we need is in one ByteBuffer. If so we can make use of the fast-path.
@@ -156,7 +158,7 @@
      * @param protocols the list of protocols to be encoded
      * @return the encoded form of the protocol list.
      */
-    static byte[] toLengthPrefixedList(String... protocols) {
+    public static byte[] toLengthPrefixedList(String... protocols) {
         // Calculate the encoded length.
         int length = 0;
         for (int i = 0; i < protocols.length; ++i) {
diff --git a/common/src/main/java/org/conscrypt/ServerSessionContext.java b/common/src/main/java/org/conscrypt/ServerSessionContext.java
index 20fc3ae..f71f99c 100644
--- a/common/src/main/java/org/conscrypt/ServerSessionContext.java
+++ b/common/src/main/java/org/conscrypt/ServerSessionContext.java
@@ -25,7 +25,7 @@
  * @hide
  */
 @Internal
-public final class ServerSessionContext extends AbstractSessionContext {
+public class ServerSessionContext extends AbstractSessionContext {
 
     private SSLServerSessionCache persistentCache;
 
diff --git a/common/src/main/java/org/conscrypt/X509PublicKey.java b/common/src/main/java/org/conscrypt/X509PublicKey.java
index 37abb29..df4ad1d 100644
--- a/common/src/main/java/org/conscrypt/X509PublicKey.java
+++ b/common/src/main/java/org/conscrypt/X509PublicKey.java
@@ -22,15 +22,18 @@
 /**
  * A simple but useless key class that holds X.509 public key information when
  * the appropriate KeyFactory for the key algorithm is not available.
+ *
+ * @hide
  */
-final class X509PublicKey implements PublicKey {
+@Internal
+public class X509PublicKey implements PublicKey {
     private static final long serialVersionUID = -8610156854731664298L;
 
     private final String algorithm;
 
     private final byte[] encoded;
 
-    X509PublicKey(String algorithm, byte[] encoded) {
+    public X509PublicKey(String algorithm, byte[] encoded) {
         this.algorithm = algorithm;
         this.encoded = encoded;
     }
diff --git a/constants/build.gradle b/constants/build.gradle
index 140c209..69efd7e 100644
--- a/constants/build.gradle
+++ b/constants/build.gradle
@@ -70,6 +70,3 @@
 // Don't include this artifact in the distribution.
 tasks.install.enabled = false
 tasks.uploadArchives.enabled = false;
-
-// Disable the javadoc task.
-tasks.withType(Javadoc).all { enabled = false }
diff --git a/constants/src/gen/cpp/generate_constants.cpp b/constants/src/gen/cpp/generate_constants.cpp
index 92b5c7f..0de98f6 100644
--- a/constants/src/gen/cpp/generate_constants.cpp
+++ b/constants/src/gen/cpp/generate_constants.cpp
@@ -44,9 +44,10 @@
   printf("%s\n", kCopyright);
   printf("/* This file was generated by generate_constants.cc. */\n\n");
   printf("package org.conscrypt;\n\n");
-  printf("final class NativeConstants {\n");
+  printf("/** @hide */\n");
+  printf("public final class NativeConstants {\n");
 
-  printf("    static final boolean HAS_EVP_AEAD = %s;\n",
+  printf("    public static final boolean HAS_EVP_AEAD = %s;\n",
 #if defined(EVP_AEAD_DEFAULT_TAG_LENGTH)
          "true"
 #else
@@ -55,8 +56,8 @@
   );
 
 #define CONST(x) \
-  printf("    static final int %s = %ld;\n", #x, (long int)(x))
-#define CONST_MINUS_1(x) printf("    static final int %s = -1;\n", #x)
+  printf("    public static final int %s = %ld;\n", #x, (long int)(x))
+#define CONST_MINUS_1(x) printf("    public static final int %s = -1;\n", #x)
   CONST(OPENSSL_EC_NAMED_CURVE);
 
   CONST(POINT_CONVERSION_COMPRESSED);
diff --git a/libcore-stub/build.gradle b/libcore-stub/build.gradle
deleted file mode 100644
index 92e5344..0000000
--- a/libcore-stub/build.gradle
+++ /dev/null
@@ -1,14 +0,0 @@
-description = 'Conscrypt: libcore Stub'
-
-dependencies {
-    // Only compile against this. Other modules will embed the generated code directly.
-    compileOnly project(':conscrypt-constants')
-
-    compile libraries.bouncycastle_provider,
-            libraries.bouncycastle_apis,
-            libraries.junit
-}
-
-// Don't include this artifact in the distribution.
-tasks.install.enabled = false
-tasks.uploadArchives.enabled = false;
diff --git a/libcore-stub/src/main/java/android/system/StructTimeval.java b/libcore-stub/src/main/java/android/system/StructTimeval.java
deleted file mode 100644
index 91a6f2a..0000000
--- a/libcore-stub/src/main/java/android/system/StructTimeval.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.system;
-
-import libcore.util.Objects;
-
-/**
- * Corresponds to C's {@code struct timeval} from
- * <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html">&lt;sys/time.h&gt;</a>
- *
- * @hide
- */
-public final class StructTimeval {
-    /** Seconds. */
-    public final long tv_sec;
-
-    /** Microseconds. */
-    public final long tv_usec;
-
-    private StructTimeval(long tv_sec, long tv_usec) {
-        this.tv_sec = tv_sec;
-        this.tv_usec = tv_usec;
-    }
-
-    public static StructTimeval fromMillis(long millis) {
-        long tv_sec = millis / 1000;
-        long tv_usec = (millis - (tv_sec * 1000)) * 1000;
-        return new StructTimeval(tv_sec, tv_usec);
-    }
-
-    public long toMillis() {
-        return (tv_sec * 1000) + (tv_usec / 1000);
-    }
-
-    @Override public String toString() {
-        return Objects.toString(this);
-    }
-}
diff --git a/libcore-stub/src/main/java/libcore/net/NetworkSecurityPolicy.java b/libcore-stub/src/main/java/libcore/net/NetworkSecurityPolicy.java
deleted file mode 100644
index d9c87a4..0000000
--- a/libcore-stub/src/main/java/libcore/net/NetworkSecurityPolicy.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2015 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 libcore.net;
-
-/**
- * Network security policy for this process/application.
- *
- * <p>Network stacks/components are expected to honor this policy. Components which can use the
- * Android framework API should be accessing this policy via the framework's
- * {@code android.security.NetworkSecurityPolicy} instead of via this class.
- *
- * <p>The policy currently consists of a single flag: whether cleartext network traffic is
- * permitted. See {@link #isCleartextTrafficPermitted()}.
- */
-public abstract class NetworkSecurityPolicy {
-
-    private static volatile NetworkSecurityPolicy instance = new DefaultNetworkSecurityPolicy();
-
-    public static NetworkSecurityPolicy getInstance() {
-        return instance;
-    }
-
-    public static void setInstance(NetworkSecurityPolicy policy) {
-        if (policy == null) {
-            throw new NullPointerException("policy == null");
-        }
-        instance = policy;
-    }
-
-    /**
-     * Returns {@code true} if cleartext network traffic (e.g. HTTP, FTP, XMPP, IMAP, SMTP --
-     * without TLS or STARTTLS) is permitted for all network communications of this process.
-     *
-     * <p>{@link #isCleartextTrafficPermitted(String)} should be used to determine if cleartext
-     * traffic is permitted for a specific host.
-     *
-     * <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP
-     * stacks, {@code WebView}, {@code MediaPlayer}) will refuse this process's requests to use
-     * cleartext traffic. Third-party libraries are encouraged to do the same.
-     *
-     * <p>This flag is honored on a best effort basis because it's impossible to prevent all
-     * cleartext traffic from an application given the level of access provided to applications on
-     * Android. For example, there's no expectation that {@link java.net.Socket} API will honor this
-     * flag. Luckily, most network traffic from apps is handled by higher-level network stacks which
-     * can be made to honor this flag. Platform-provided network stacks (e.g. HTTP and FTP) honor
-     * this flag from day one, and well-established third-party network stacks will eventually
-     * honor it.
-     */
-    public abstract boolean isCleartextTrafficPermitted();
-
-    /**
-     * Returns {@code true} if cleartext network traffic (e.g. HTTP, FTP, XMPP, IMAP, SMTP --
-     * without TLS or STARTTLS) is permitted for communicating with {@code hostname} for this
-     * process.
-     *
-     * <p>See {@link #isCleartextTrafficPermitted} for more details.
-     */
-    public abstract boolean isCleartextTrafficPermitted(String hostname);
-
-    /**
-     * Returns {@code true} if Certificate Transparency information is required to be presented by
-     * the server and verified by the client in TLS connections to {@code hostname}.
-     *
-     * <p>See RFC6962 section 3.3 for more details.
-     */
-    public abstract boolean isCertificateTransparencyVerificationRequired(String hostname);
-
-    public static final class DefaultNetworkSecurityPolicy extends NetworkSecurityPolicy {
-        @Override
-        public boolean isCleartextTrafficPermitted() {
-            return true;
-        }
-
-        @Override
-        public boolean isCleartextTrafficPermitted(String hostname) {
-            return isCleartextTrafficPermitted();
-        }
-
-        @Override
-        public boolean isCertificateTransparencyVerificationRequired(String hostname) {
-            return false;
-        }
-    }
-}
diff --git a/libcore-stub/src/main/java/libcore/util/Objects.java b/libcore-stub/src/main/java/libcore/util/Objects.java
deleted file mode 100644
index 573b973..0000000
--- a/libcore-stub/src/main/java/libcore/util/Objects.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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 libcore.util;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-
-public final class Objects {
-    private Objects() {}
-
-    /**
-     * Returns true if two possibly-null objects are equal.
-     */
-    public static boolean equal(Object a, Object b) {
-        return a == b || (a != null && a.equals(b));
-    }
-
-    public static int hashCode(Object o) {
-        return (o == null) ? 0 : o.hashCode();
-    }
-
-    /**
-     * Returns a string reporting the value of each declared field, via reflection.
-     * Static and transient fields are automatically skipped. Produces output like
-     * "SimpleClassName[integer=1234,string="hello",character='c',intArray=[1,2,3]]".
-     */
-    public static String toString(Object o) {
-        Class<?> c = o.getClass();
-        StringBuilder sb = new StringBuilder();
-        sb.append(c.getSimpleName()).append('[');
-        int i = 0;
-        for (Field f : c.getDeclaredFields()) {
-            if ((f.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) {
-                continue;
-            }
-            f.setAccessible(true);
-            try {
-                Object value = f.get(o);
-
-                if (i++ > 0) {
-                    sb.append(',');
-                }
-
-                sb.append(f.getName());
-                sb.append('=');
-
-                if (value.getClass().isArray()) {
-                    if (value.getClass() == boolean[].class) {
-                        sb.append(Arrays.toString((boolean[]) value));
-                    } else if (value.getClass() == byte[].class) {
-                        sb.append(Arrays.toString((byte[]) value));
-                    } else if (value.getClass() == char[].class) {
-                        sb.append(Arrays.toString((char[]) value));
-                    } else if (value.getClass() == double[].class) {
-                        sb.append(Arrays.toString((double[]) value));
-                    } else if (value.getClass() == float[].class) {
-                        sb.append(Arrays.toString((float[]) value));
-                    } else if (value.getClass() == int[].class) {
-                        sb.append(Arrays.toString((int[]) value));
-                    } else if (value.getClass() == long[].class) {
-                        sb.append(Arrays.toString((long[]) value));
-                    } else if (value.getClass() == short[].class) {
-                        sb.append(Arrays.toString((short[]) value));
-                    } else {
-                        sb.append(Arrays.toString((Object[]) value));
-                    }
-                } else if (value.getClass() == Character.class) {
-                    sb.append('\'').append(value).append('\'');
-                } else if (value.getClass() == String.class) {
-                    sb.append('"').append(value).append('"');
-                } else {
-                    sb.append(value);
-                }
-            } catch (IllegalAccessException unexpected) {
-                throw new AssertionError(unexpected);
-            }
-        }
-        sb.append("]");
-        return sb.toString();
-    }
-}
diff --git a/openjdk-benchmarks/build.gradle b/openjdk-benchmarks/build.gradle
index f26196e..cfbf43d 100644
--- a/openjdk-benchmarks/build.gradle
+++ b/openjdk-benchmarks/build.gradle
@@ -21,6 +21,7 @@
 
 dependencies {
     compile project(':conscrypt-testing'),
+            libraries.guava,
             libraries.junit,
             libraries.netty_handler,
             libraries.netty_tcnative
diff --git a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ClientSocketThroughputBenchmark.java b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ClientSocketThroughputBenchmark.java
index d9db3f7..d98a278 100644
--- a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ClientSocketThroughputBenchmark.java
+++ b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ClientSocketThroughputBenchmark.java
@@ -16,14 +16,14 @@
 
 package org.conscrypt.benchmarks;
 
-import static org.conscrypt.TestUtils.LOCALHOST;
-import static org.conscrypt.TestUtils.getConscryptServerSocketFactory;
-import static org.conscrypt.TestUtils.getConscryptSocketFactory;
-import static org.conscrypt.TestUtils.getJdkServerSocketFactory;
-import static org.conscrypt.TestUtils.getJdkSocketFactory;
-import static org.conscrypt.TestUtils.getProtocols;
-import static org.conscrypt.TestUtils.newTextMessage;
-import static org.conscrypt.TestUtils.pickUnusedPort;
+import static org.conscrypt.testing.TestUtil.LOCALHOST;
+import static org.conscrypt.testing.TestUtil.getConscryptServerSocketFactory;
+import static org.conscrypt.testing.TestUtil.getConscryptSocketFactory;
+import static org.conscrypt.testing.TestUtil.getJdkServerSocketFactory;
+import static org.conscrypt.testing.TestUtil.getJdkSocketFactory;
+import static org.conscrypt.testing.TestUtil.getProtocols;
+import static org.conscrypt.testing.TestUtil.newTextMessage;
+import static org.conscrypt.testing.TestUtil.pickUnusedPort;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -38,8 +38,8 @@
 import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
-import org.conscrypt.TestClient;
-import org.conscrypt.TestServer;
+import org.conscrypt.testing.TestClient;
+import org.conscrypt.testing.TestServer;
 import org.openjdk.jmh.annotations.AuxCounters;
 import org.openjdk.jmh.annotations.Benchmark;
 import org.openjdk.jmh.annotations.Fork;
diff --git a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ServerSocketThroughputBenchmark.java b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ServerSocketThroughputBenchmark.java
index 8778da9..6b1ff21 100644
--- a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ServerSocketThroughputBenchmark.java
+++ b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ServerSocketThroughputBenchmark.java
@@ -16,13 +16,13 @@
 
 package org.conscrypt.benchmarks;
 
-import static org.conscrypt.TestUtils.LOCALHOST;
-import static org.conscrypt.TestUtils.getConscryptServerSocketFactory;
-import static org.conscrypt.TestUtils.getJdkServerSocketFactory;
-import static org.conscrypt.TestUtils.getJdkSocketFactory;
-import static org.conscrypt.TestUtils.getProtocols;
-import static org.conscrypt.TestUtils.newTextMessage;
-import static org.conscrypt.TestUtils.pickUnusedPort;
+import static org.conscrypt.testing.TestUtil.LOCALHOST;
+import static org.conscrypt.testing.TestUtil.getConscryptServerSocketFactory;
+import static org.conscrypt.testing.TestUtil.getJdkServerSocketFactory;
+import static org.conscrypt.testing.TestUtil.getJdkSocketFactory;
+import static org.conscrypt.testing.TestUtil.getProtocols;
+import static org.conscrypt.testing.TestUtil.newTextMessage;
+import static org.conscrypt.testing.TestUtil.pickUnusedPort;
 import static org.junit.Assert.assertEquals;
 
 import java.io.IOException;
@@ -37,9 +37,9 @@
 import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
-import org.conscrypt.TestClient;
-import org.conscrypt.TestServer;
-import org.conscrypt.TestServer.MessageProcessor;
+import org.conscrypt.testing.TestClient;
+import org.conscrypt.testing.TestServer;
+import org.conscrypt.testing.TestServer.MessageProcessor;
 import org.openjdk.jmh.annotations.AuxCounters;
 import org.openjdk.jmh.annotations.Benchmark;
 import org.openjdk.jmh.annotations.Level;
diff --git a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/SslEngineBenchmark.java b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/SslEngineBenchmark.java
index 679dec1..3a9deab 100644
--- a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/SslEngineBenchmark.java
+++ b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/SslEngineBenchmark.java
@@ -33,12 +33,12 @@
 package org.conscrypt.benchmarks;
 
 import static java.lang.Math.max;
-import static org.conscrypt.TestUtils.PROTOCOL_TLS_V1_2;
-import static org.conscrypt.TestUtils.doEngineHandshake;
-import static org.conscrypt.TestUtils.initClientSslContext;
-import static org.conscrypt.TestUtils.initEngine;
-import static org.conscrypt.TestUtils.initServerSslContext;
-import static org.conscrypt.TestUtils.newTextMessage;
+import static org.conscrypt.testing.TestUtil.PROTOCOL_TLS_V1_2;
+import static org.conscrypt.testing.TestUtil.doEngineHandshake;
+import static org.conscrypt.testing.TestUtil.initClientSslContext;
+import static org.conscrypt.testing.TestUtil.initEngine;
+import static org.conscrypt.testing.TestUtil.initServerSslContext;
+import static org.conscrypt.testing.TestUtil.newTextMessage;
 import static org.junit.Assert.assertEquals;
 
 import io.netty.buffer.UnpooledByteBufAllocator;
diff --git a/openjdk/build.gradle b/openjdk/build.gradle
index 898e5a6..9641b5b 100644
--- a/openjdk/build.gradle
+++ b/openjdk/build.gradle
@@ -30,26 +30,10 @@
             srcDirs += project(':conscrypt-constants').sourceSets.main.java.srcDirs
         }
     }
-
-    platform {
-        java {
-            srcDirs = [ "src/main/java" ]
-            includes = [ "org/conscrypt/Platform.java" ]
-        }
-    }
-}
-
-task platformJar(type: Jar) {
-    from sourceSets.platform.output
 }
 
 configurations {
     publicApiDocs
-    platform
-}
-
-artifacts {
-    platform platformJar
 }
 
 // Append the BoringSSL-Version to the manifest.
@@ -67,8 +51,6 @@
                 project(':conscrypt-testing'),
                 libraries.junit,
                 libraries.mockito
-
-    platformCompileOnly sourceSets.main.output
 }
 
 javadoc {
diff --git a/openjdk/src/main/java/org/conscrypt/Platform.java b/openjdk/src/main/java/org/conscrypt/Platform.java
index 6708cda..cf569f3 100644
--- a/openjdk/src/main/java/org/conscrypt/Platform.java
+++ b/openjdk/src/main/java/org/conscrypt/Platform.java
@@ -63,10 +63,10 @@
     private Platform() {
     }
 
-    static void setup() {
+    public static void setup() {
     }
 
-    static FileDescriptor getFileDescriptor(Socket s) {
+    public static FileDescriptor getFileDescriptor(Socket s) {
         try {
             SocketChannel channel = s.getChannel();
             if (channel != null) {
@@ -90,11 +90,11 @@
         }
     }
 
-    static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
+    public static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
         return getFileDescriptor(openSSLSocketImpl);
     }
 
-    static String getCurveName(ECParameterSpec spec) {
+    public static String getCurveName(ECParameterSpec spec) {
         if (m_getCurveName == null) {
             return null;
         }
@@ -105,18 +105,18 @@
         }
     }
 
-    static void setCurveName(ECParameterSpec spec, String curveName) {
+    public static void setCurveName(ECParameterSpec spec, String curveName) {
         // This doesn't appear to be needed.
     }
 
     /*
      * Call Os.setsockoptTimeval via reflection.
      */
-    static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException {
+    public static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException {
         // TODO: figure this out on the RI
     }
 
-    static void setSSLParameters(SSLParameters params, SSLParametersImpl impl,
+    public static void setSSLParameters(SSLParameters params, SSLParametersImpl impl,
             OpenSSLSocketImpl socket) {
         impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
         impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
@@ -131,7 +131,7 @@
         }
     }
 
-    static void getSSLParameters(SSLParameters params, SSLParametersImpl impl,
+    public static void getSSLParameters(SSLParameters params, SSLParametersImpl impl,
             OpenSSLSocketImpl socket) {
         params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
         params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
@@ -141,7 +141,7 @@
         }
     }
 
-    static void setSSLParameters(
+    public static void setSSLParameters(
             SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
         impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
         impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
@@ -156,7 +156,7 @@
         }
     }
 
-    static void getSSLParameters(
+    public static void getSSLParameters(
             SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
         params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
         params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
@@ -179,16 +179,16 @@
         return null;
     }
 
-    static void setEndpointIdentificationAlgorithm(SSLParameters params,
+    public static void setEndpointIdentificationAlgorithm(SSLParameters params,
             String endpointIdentificationAlgorithm) {
         params.setEndpointIdentificationAlgorithm(endpointIdentificationAlgorithm);
     }
 
-    static String getEndpointIdentificationAlgorithm(SSLParameters params) {
+    public static String getEndpointIdentificationAlgorithm(SSLParameters params) {
         return params.getEndpointIdentificationAlgorithm();
     }
 
-    static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
+    public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
             String authType, OpenSSLSocketImpl socket) throws CertificateException {
         if (tm instanceof X509ExtendedTrustManager) {
             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
@@ -198,7 +198,7 @@
         }
     }
 
-    static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
+    public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
             String authType, OpenSSLSocketImpl socket) throws CertificateException {
         if (tm instanceof X509ExtendedTrustManager) {
             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
@@ -208,7 +208,7 @@
         }
     }
 
-    static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
+    public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
             String authType, OpenSSLEngineImpl engine) throws CertificateException {
         if (tm instanceof X509ExtendedTrustManager) {
             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
@@ -218,7 +218,7 @@
         }
     }
 
-    static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
+    public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
             String authType, OpenSSLEngineImpl engine) throws CertificateException {
         if (tm instanceof X509ExtendedTrustManager) {
             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
@@ -231,20 +231,20 @@
     /**
      * Wraps an old AndroidOpenSSL key instance. This is not needed on RI.
      */
-    static OpenSSLKey wrapRsaKey(PrivateKey javaKey) {
+    public static OpenSSLKey wrapRsaKey(PrivateKey javaKey) {
         return null;
     }
 
     /**
      * Logs to the system EventLog system.
      */
-    static void logEvent(String message) {
+    public static void logEvent(String message) {
     }
 
     /**
      * Returns true if the supplied hostname is an literal IP address.
      */
-    static boolean isLiteralIpAddress(String hostname) {
+    public static boolean isLiteralIpAddress(String hostname) {
         // TODO: any RI API to make this better?
         return AddressUtils.isLiteralIpAddress(hostname);
     }
@@ -252,21 +252,21 @@
     /**
      * For unbundled versions, SNI is always enabled by default.
      */
-    static boolean isSniEnabledByDefault() {
+    public static boolean isSniEnabledByDefault() {
         return true;
     }
 
     /**
      * Currently we don't wrap anything from the RI.
      */
-    static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
+    public static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
         return factory;
     }
 
     /**
      * Convert from platform's GCMParameterSpec to our internal version.
      */
-    static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) {
+    public static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) {
         if (params instanceof GCMParameterSpec) {
             GCMParameterSpec gcmParams = (GCMParameterSpec) params;
             return new GCMParameters(gcmParams.getTLen(), gcmParams.getIV());
@@ -277,7 +277,7 @@
     /**
      * Creates a platform version of {@code GCMParameterSpec}.
      */
-    static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) {
+    public static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) {
         return new GCMParameterSpec(tagLenInBits, iv);
     }
 
@@ -285,30 +285,30 @@
      * CloseGuard functions.
      */
 
-    static Object closeGuardGet() {
+    public static Object closeGuardGet() {
         return null;
     }
 
-    static void closeGuardOpen(Object guardObj, String message) {
+    public static void closeGuardOpen(Object guardObj, String message) {
     }
 
-    static void closeGuardClose(Object guardObj) {
+    public static void closeGuardClose(Object guardObj) {
     }
 
-    static void closeGuardWarnIfOpen(Object guardObj) {
+    public static void closeGuardWarnIfOpen(Object guardObj) {
     }
 
     /*
      * BlockGuard functions.
      */
 
-    static void blockGuardOnNetwork() {
+    public static void blockGuardOnNetwork() {
     }
 
     /**
      * OID to Algorithm Name mapping.
      */
-    static String oidToAlgorithmName(String oid) {
+    public static String oidToAlgorithmName(String oid) {
         try {
             return AlgorithmId.get(oid).getName();
         } catch (NoSuchAlgorithmException e) {
@@ -320,11 +320,11 @@
      * Pre-Java-8 backward compatibility.
      */
 
-    static SSLSession wrapSSLSession(AbstractOpenSSLSession sslSession) {
+    public static SSLSession wrapSSLSession(AbstractOpenSSLSession sslSession) {
         return new OpenSSLExtendedSessionImpl(sslSession);
     }
 
-    static SSLSession unwrapSSLSession(SSLSession sslSession) {
+    public static SSLSession unwrapSSLSession(SSLSession sslSession) {
         if (sslSession instanceof OpenSSLExtendedSessionImpl) {
             return ((OpenSSLExtendedSessionImpl) sslSession).getDelegate();
         }
@@ -335,7 +335,7 @@
      * Pre-Java-7 backward compatibility.
      */
 
-    static String getHostStringFromInetSocketAddress(InetSocketAddress addr) {
+    public static String getHostStringFromInetSocketAddress(InetSocketAddress addr) {
         return addr.getHostString();
     }
 
@@ -355,7 +355,7 @@
      * - conscrypt.ct.enforce.com.*
      * - conscrypt.ct.enforce.*
      */
-    static boolean isCTVerificationRequired(String hostname) {
+    public static boolean isCTVerificationRequired(String hostname) {
         if (hostname == null) {
             return false;
         }
diff --git a/platform/src/test/java/org/conscrypt/ChainStrengthAnalyzerTest.java b/openjdk/src/test/java/org/conscrypt/ChainStrengthAnalyzerTest.java
similarity index 99%
rename from platform/src/test/java/org/conscrypt/ChainStrengthAnalyzerTest.java
rename to openjdk/src/test/java/org/conscrypt/ChainStrengthAnalyzerTest.java
index da25acc..6bea582 100644
--- a/platform/src/test/java/org/conscrypt/ChainStrengthAnalyzerTest.java
+++ b/openjdk/src/test/java/org/conscrypt/ChainStrengthAnalyzerTest.java
@@ -273,7 +273,7 @@
 
     private static X509Certificate createCert(String pem) throws Exception {
         CertificateFactory cf = CertificateFactory.getInstance("X509");
-        InputStream pemInput = new ByteArrayInputStream(pem.getBytes("UTF-8"));
+        InputStream pemInput = new ByteArrayInputStream(pem.getBytes());
         return (X509Certificate) cf.generateCertificate(pemInput);
     }
 }
diff --git a/openjdk/src/test/java/org/conscrypt/DuckTypedPSKKeyManagerTest.java b/openjdk/src/test/java/org/conscrypt/DuckTypedPSKKeyManagerTest.java
index 5c27f33..40fc7a0 100644
--- a/openjdk/src/test/java/org/conscrypt/DuckTypedPSKKeyManagerTest.java
+++ b/openjdk/src/test/java/org/conscrypt/DuckTypedPSKKeyManagerTest.java
@@ -139,7 +139,7 @@
         assertSame(identityHint, mockInvocationHandler.lastInvokedMethodArgs[0]);
         assertSame(mSSLEngine, mockInvocationHandler.lastInvokedMethodArgs[1]);
 
-        SecretKey key = new SecretKeySpec("arbitrary".getBytes("UTF-8"), "RAW");
+        SecretKey key = new SecretKeySpec("arbitrary".getBytes(), "RAW");
         mockInvocationHandler.returnValue = key;
         assertSame(key, pskKeyManager.getKey(identityHint, identity, mSSLSocket));
         assertEquals("getKey", mockInvocationHandler.lastInvokedMethod.getName());
diff --git a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
index 9668787..b3f67bb 100644
--- a/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
+++ b/openjdk/src/test/java/org/conscrypt/NativeCryptoTest.java
@@ -325,9 +325,7 @@
             NativeCrypto.SSL_CTX_set_session_id_context(c, new byte[32]);
             try {
                 NativeCrypto.SSL_CTX_set_session_id_context(c, new byte[33]);
-                fail("Expected IllegalArgumentException");
             } catch (IllegalArgumentException expected) {
-                // Expected.
             }
         } finally {
             NativeCrypto.SSL_CTX_free(c);
@@ -1869,7 +1867,7 @@
             public void afterHandshake(long session, long ssl, long context, Socket socket,
                     FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
                 byte[] negotiated = NativeCrypto.SSL_get0_alpn_selected(ssl);
-                assertEquals("spdy/2", new String(negotiated, "UTF-8"));
+                assertEquals("spdy/2", new String(negotiated));
                 super.afterHandshake(session, ssl, context, socket, fd, callback);
             }
         };
@@ -1878,7 +1876,7 @@
             public void afterHandshake(long session, long ssl, long c, Socket sock,
                     FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
                 byte[] negotiated = NativeCrypto.SSL_get0_alpn_selected(ssl);
-                assertEquals("spdy/2", new String(negotiated, "UTF-8"));
+                assertEquals("spdy/2", new String(negotiated));
                 super.afterHandshake(session, ssl, c, sock, fd, callback);
             }
         };
@@ -2813,7 +2811,7 @@
 
     @Test
     public void test_create_BIO_InputStream() throws Exception {
-        byte[] actual = "Test".getBytes("UTF-8");
+        byte[] actual = "Test".getBytes();
         ByteArrayInputStream is = new ByteArrayInputStream(actual);
 
         @SuppressWarnings("resource")
@@ -2831,7 +2829,7 @@
 
     @Test
     public void test_create_BIO_OutputStream() throws Exception {
-        byte[] actual = "Test".getBytes("UTF-8");
+        byte[] actual = "Test".getBytes();
         ByteArrayOutputStream os = new ByteArrayOutputStream();
 
         long ctx = NativeCrypto.create_BIO_OutputStream(os);
diff --git a/openjdk/src/test/java/org/conscrypt/OpenSSLEngineImplTest.java b/openjdk/src/test/java/org/conscrypt/OpenSSLEngineImplTest.java
index 02ee53e..39ec165 100644
--- a/openjdk/src/test/java/org/conscrypt/OpenSSLEngineImplTest.java
+++ b/openjdk/src/test/java/org/conscrypt/OpenSSLEngineImplTest.java
@@ -16,10 +16,10 @@
 
 package org.conscrypt;
 
-import static org.conscrypt.TestUtils.PROTOCOL_TLS_V1_2;
-import static org.conscrypt.TestUtils.initEngine;
-import static org.conscrypt.TestUtils.initSslContext;
-import static org.conscrypt.TestUtils.newTextMessage;
+import static org.conscrypt.testing.TestUtil.PROTOCOL_TLS_V1_2;
+import static org.conscrypt.testing.TestUtil.initEngine;
+import static org.conscrypt.testing.TestUtil.initSslContext;
+import static org.conscrypt.testing.TestUtil.newTextMessage;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
@@ -37,6 +37,7 @@
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLHandshakeException;
 import libcore.java.security.TestKeyStore;
+import org.conscrypt.testing.TestUtil;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -148,7 +149,7 @@
     @Test
     public void exchangeMessages() throws Exception {
         setupEngines(TestKeyStore.getClient(), TestKeyStore.getServer());
-        TestUtils.doEngineHandshake(clientEngine, serverEngine);
+        TestUtil.doEngineHandshake(clientEngine, serverEngine);
 
         ByteBuffer clientCleartextBuffer = bufferType.newBuffer(MESSAGE_SIZE);
         clientCleartextBuffer.put(newTextMessage(MESSAGE_SIZE));
@@ -252,7 +253,7 @@
     private void doMutualAuthHandshake(TestKeyStore clientKs, TestKeyStore serverKs, ClientAuth clientAuth) throws Exception {
         setupEngines(clientKs, serverKs);
         clientAuth.apply(serverEngine);
-        TestUtils.doEngineHandshake(clientEngine, serverEngine);
+        TestUtil.doEngineHandshake(clientEngine, serverEngine);
         assertEquals(HandshakeStatus.NOT_HANDSHAKING, clientEngine.getHandshakeStatus());
         assertEquals(HandshakeStatus.NOT_HANDSHAKING, serverEngine.getHandshakeStatus());
     }
diff --git a/openjdk/src/test/java/org/conscrypt/OpenSSLKeyTest.java b/openjdk/src/test/java/org/conscrypt/OpenSSLKeyTest.java
index d6dac20..177d287 100644
--- a/openjdk/src/test/java/org/conscrypt/OpenSSLKeyTest.java
+++ b/openjdk/src/test/java/org/conscrypt/OpenSSLKeyTest.java
@@ -48,7 +48,7 @@
         "e9e1326013a84467190dd94c5aabaf148ad5e3c452a2dd063e1d4c044d6994a1", 16);
 
     public void test_fromPublicKeyPemInputStream() throws Exception {
-        ByteArrayInputStream is = new ByteArrayInputStream(RSA_PUBLIC_KEY.getBytes("UTF-8"));
+        ByteArrayInputStream is = new ByteArrayInputStream(RSA_PUBLIC_KEY.getBytes());
         OpenSSLKey key = OpenSSLKey.fromPublicKeyPemInputStream(is);
         OpenSSLRSAPublicKey publicKey = (OpenSSLRSAPublicKey)key.getPublicKey();
         assertEquals(RSA_MODULUS, publicKey.getModulus());
@@ -56,7 +56,7 @@
     }
 
     public void test_fromPrivateKeyPemInputStream() throws Exception {
-        ByteArrayInputStream is = new ByteArrayInputStream(RSA_PRIVATE_KEY.getBytes("UTF-8"));
+        ByteArrayInputStream is = new ByteArrayInputStream(RSA_PRIVATE_KEY.getBytes());
         OpenSSLKey key = OpenSSLKey.fromPrivateKeyPemInputStream(is);
         OpenSSLRSAPrivateKey privateKey = (OpenSSLRSAPrivateKey)key.getPrivateKey();
         assertEquals(RSA_MODULUS, privateKey.getModulus());
diff --git a/openjdk/src/test/java/org/conscrypt/OpenSSLServerSocketImplTest.java b/openjdk/src/test/java/org/conscrypt/OpenSSLServerSocketImplTest.java
index 1618e8a..841c5fc 100644
--- a/openjdk/src/test/java/org/conscrypt/OpenSSLServerSocketImplTest.java
+++ b/openjdk/src/test/java/org/conscrypt/OpenSSLServerSocketImplTest.java
@@ -16,12 +16,12 @@
 
 package org.conscrypt;
 
-import static org.conscrypt.TestUtils.LOCALHOST;
-import static org.conscrypt.TestUtils.getConscryptServerSocketFactory;
-import static org.conscrypt.TestUtils.getJdkSocketFactory;
-import static org.conscrypt.TestUtils.getProtocols;
-import static org.conscrypt.TestUtils.newTextMessage;
-import static org.conscrypt.TestUtils.pickUnusedPort;
+import static org.conscrypt.testing.TestUtil.LOCALHOST;
+import static org.conscrypt.testing.TestUtil.getConscryptServerSocketFactory;
+import static org.conscrypt.testing.TestUtil.getJdkSocketFactory;
+import static org.conscrypt.testing.TestUtil.getProtocols;
+import static org.conscrypt.testing.TestUtil.newTextMessage;
+import static org.conscrypt.testing.TestUtil.pickUnusedPort;
 import static org.junit.Assert.assertArrayEquals;
 
 import java.io.IOException;
@@ -32,6 +32,8 @@
 import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
+import org.conscrypt.testing.TestClient;
+import org.conscrypt.testing.TestServer;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -52,7 +54,6 @@
         DEFAULT(getConscryptServerSocketFactory(false)),
         ENGINE(getConscryptServerSocketFactory(true));
 
-        @SuppressWarnings("ImmutableEnumChecker")
         private final SSLServerSocketFactory serverSocketFactory;
 
         SocketType(SSLServerSocketFactory serverSocketFactory) {
diff --git a/openjdk/src/test/java/org/conscrypt/SSLUtilsTest.java b/openjdk/src/test/java/org/conscrypt/SSLUtilsTest.java
index c7f4d9d..1ee0359 100644
--- a/openjdk/src/test/java/org/conscrypt/SSLUtilsTest.java
+++ b/openjdk/src/test/java/org/conscrypt/SSLUtilsTest.java
@@ -16,7 +16,6 @@
 
 package org.conscrypt;
 
-import static org.conscrypt.TestUtils.UTF_8;
 import static org.junit.Assert.assertArrayEquals;
 
 import org.junit.Test;
@@ -26,7 +25,7 @@
 @RunWith(JUnit4.class)
 public class SSLUtilsTest {
     private static final byte[] VALID_CHARACTERS =
-            "0123456789abcdefghijklmnopqrstuvwxyz".getBytes(UTF_8);
+            "0123456789abcdefghijklmnopqrstuvwxyz".getBytes();
 
     @Test
     public void noProtocolsShouldSucceed() {
@@ -42,7 +41,7 @@
 
     @Test(expected = IllegalArgumentException.class)
     public void longProtocolShouldThrow() {
-        SSLUtils.toLengthPrefixedList(new String(newValidProtocol(256), UTF_8));
+        SSLUtils.toLengthPrefixedList(new String(newValidProtocol(256)));
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -53,9 +52,9 @@
     @Test
     public void validProtocolsShouldSucceed() {
         byte[][] protocols = new byte[][]{
-                "protocol-1".getBytes(UTF_8),
-                "protocol-2".getBytes(UTF_8),
-                "protocol-3".getBytes(UTF_8),
+                "protocol-1".getBytes(),
+                "protocol-2".getBytes(),
+                "protocol-3".getBytes(),
         };
         byte[] expected = getExpectedEncodedBytes(protocols);
         byte[] actual = SSLUtils.toLengthPrefixedList(toStrings(protocols));
@@ -66,7 +65,7 @@
         int numProtocols = protocols.length;
         String[] out = new String[numProtocols];
         for(int i = 0; i < numProtocols; ++i) {
-            out[i] = new String(protocols[i], UTF_8);
+            out[i] = new String(protocols[i]);
         }
         return out;
     }
diff --git a/openjdk/src/test/java/org/conscrypt/TestUtils.java b/openjdk/src/test/java/org/conscrypt/TestUtils.java
new file mode 100644
index 0000000..5e640fc
--- /dev/null
+++ b/openjdk/src/test/java/org/conscrypt/TestUtils.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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 org.conscrypt;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import libcore.io.Streams;
+
+public class TestUtils {
+    private TestUtils() {}
+
+    public static InputStream openTestFile(String name) throws FileNotFoundException {
+        InputStream is = TestUtils.class.getResourceAsStream("/" + name);
+        if (is == null) {
+            throw new FileNotFoundException(name);
+        }
+        return is;
+    }
+
+    public static byte[] readTestFile(String name) throws IOException {
+        return Streams.readFully(openTestFile(name));
+    }
+}
diff --git a/platform/build.gradle b/platform/build.gradle
deleted file mode 100644
index 78c8f1b..0000000
--- a/platform/build.gradle
+++ /dev/null
@@ -1,118 +0,0 @@
-buildscript {
-    repositories {
-        mavenCentral()
-        mavenLocal()
-        jcenter()
-    }
-    dependencies {
-        classpath 'com.android.tools.build:gradle:2.2.3' // jcenter has the latest
-        classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
-    }
-}
-
-description = 'Conscrypt: Android Platform'
-
-ext {
-    androidHome = "$System.env.ANDROID_HOME"
-    androidSdkInstalled = file("$androidHome").exists()
-    androidVersionCode = 1
-    androidVersionName = "$version"
-    androidMinSdkVersion = 24
-    androidTargetSdkVersion = 25
-    androidBuildToolsVersion = "25.0.0"
-}
-
-if (androidSdkInstalled) {
-    apply plugin: 'com.android.library'
-
-    android {
-        compileSdkVersion androidTargetSdkVersion
-        buildToolsVersion androidBuildToolsVersion
-
-        compileOptions {
-            sourceCompatibility androidMinJavaVersion;
-            targetCompatibility androidMinJavaVersion
-        }
-
-        defaultConfig {
-            minSdkVersion androidMinSdkVersion
-            targetSdkVersion androidTargetSdkVersion
-            versionCode androidVersionCode
-            versionName androidVersionName
-
-            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
-
-            consumerProguardFiles 'proguard-rules.pro'
-
-            externalNativeBuild {
-                cmake {
-                    arguments '-DANDROID=True',
-                            '-DANDROID_STL=c++_static',
-                            "-DBORINGSSL_HOME=$boringsslHome"
-                    cFlags '-fvisibility=hidden',
-                            '-DBORINGSSL_SHARED_LIBRARY',
-                            '-DBORINGSSL_IMPLEMENTATION',
-                            '-DOPENSSL_SMALL',
-                            '-D_XOPEN_SOURCE=700',
-                            '-Wno-unused-parameter'
-                }
-            }
-            ndk {
-                abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
-            }
-        }
-        buildTypes {
-            release {
-                minifyEnabled false
-                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
-            }
-        }
-        sourceSets {
-            main {
-                java {
-                    srcDirs = [
-                            "${rootDir}/common/src/main/java",
-                            "src/main/java",
-                    ]
-                    excludes = [ 'org/conscrypt/Platform.java' ]
-                }
-            }
-        }
-        lintOptions {
-            lintConfig file('lint.xml')
-        }
-    }
-
-    configurations {
-        publicApiDocs
-    }
-
-    dependencies {
-        compile fileTree(dir: 'libs', include: ['*.jar'])
-        compile project(path: ':conscrypt-openjdk', configuration: 'platform')
-        publicApiDocs project(':conscrypt-api-doclet')
-        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
-            exclude group: 'com.android.support', module: 'support-annotations'
-        })
-        testCompile project(':conscrypt-testing'),
-                    libraries.junit
-        provided project(':conscrypt-android-stub'),
-                 project(':conscrypt-libcore-stub')
-
-        // Adds the constants module as a dependency so that we can include its generated source
-        provided project(':conscrypt-constants')
-    }
-
-    // Disable running the tests.
-    tasks.withType(Test){
-        enabled = false
-    }
-
-} else {
-    logger.warn('Android SDK has not been detected. The Android Platform module will not be built.')
-
-    // Disable all tasks
-    tasks.collect {
-        it.enabled = false
-    }
-}
diff --git a/platform/proguard-rules.pro b/platform/proguard-rules.pro
deleted file mode 100644
index c3bdd2c..0000000
--- a/platform/proguard-rules.pro
+++ /dev/null
@@ -1,24 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can edit the include path and order by changing the proguardFiles
-# directive in build.gradle.
-#
-# For more details, see
-#   http://developer.android.com/guide/developing/tools/proguard.html
-
-# Many of the Conscrypt classes are referenced indirectly via JNI or
-# reflection.
-# This could probably be tightened up, but this will get it building for now.
-# TODO(kroot): Need anything special to prevent obfuscation?
--keep class org.conscrypt.** { *; }
-
-# Backward compatibility code.
--dontnote libcore.io.Libcore
--dontnote org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey
--dontnote org.apache.harmony.security.utils.AlgNameMapper
--dontnote sun.security.x509.AlgorithmId
-
--dontwarn dalvik.system.BlockGuard
--dontwarn dalvik.system.BlockGuard$Policy
--dontwarn dalvik.system.CloseGuard
--dontwarn com.android.org.conscrypt.OpenSSLSocketImpl
--dontwarn org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl
diff --git a/platform/src/main/AndroidManifest.xml b/platform/src/main/AndroidManifest.xml
deleted file mode 100644
index ae4f1d7..0000000
--- a/platform/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="org.conscrypt">
-
-    <application/>
-
-</manifest>
diff --git a/platform/src/main/java/org/conscrypt/CertBlacklist.java b/platform/src/main/java/org/conscrypt/CertBlacklist.java
index da8dc0c..8db2690 100644
--- a/platform/src/main/java/org/conscrypt/CertBlacklist.java
+++ b/platform/src/main/java/org/conscrypt/CertBlacklist.java
@@ -16,8 +16,6 @@
 
 package org.conscrypt;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
-
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.FileNotFoundException;
@@ -34,11 +32,7 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-/**
- * @hide
- */
-@Internal
-public final class CertBlacklist {
+public class CertBlacklist {
     private static final Logger logger = Logger.getLogger(CertBlacklist.class.getName());
 
     private final Set<BigInteger> serialBlacklist;
@@ -127,7 +121,7 @@
         }
     }
 
-    private static Set<BigInteger> readSerialBlackList(String path) {
+    private static final Set<BigInteger> readSerialBlackList(String path) {
 
         /* Start out with a base set of known bad values.
          *
@@ -169,42 +163,42 @@
         return Collections.unmodifiableSet(bl);
     }
 
-    private static Set<byte[]> readPublicKeyBlackList(String path) {
+    private static final Set<byte[]> readPublicKeyBlackList(String path) {
 
         // start out with a base set of known bad values
         Set<byte[]> bl = new HashSet<byte[]>(Arrays.asList(
             // Blacklist test cert for CTS. The cert and key can be found in
             // src/test/resources/blacklist_test_ca.pem and
             // src/test/resources/blacklist_test_ca_key.pem.
-            "bae78e6bed65a2bf60ddedde7fd91e825865e93d".getBytes(UTF_8),
+            "bae78e6bed65a2bf60ddedde7fd91e825865e93d".getBytes(),
             // From http://src.chromium.org/viewvc/chrome/branches/782/src/net/base/x509_certificate.cc?r1=98750&r2=98749&pathrev=98750
             // C=NL, O=DigiNotar, CN=DigiNotar Root CA/emailAddress=info@diginotar.nl
-            "410f36363258f30b347d12ce4863e433437806a8".getBytes(UTF_8),
+            "410f36363258f30b347d12ce4863e433437806a8".getBytes(),
             // Subject: CN=DigiNotar Cyber CA
             // Issuer: CN=GTE CyberTrust Global Root
-            "ba3e7bd38cd7e1e6b9cd4c219962e59d7a2f4e37".getBytes(UTF_8),
+            "ba3e7bd38cd7e1e6b9cd4c219962e59d7a2f4e37".getBytes(),
             // Subject: CN=DigiNotar Services 1024 CA
             // Issuer: CN=Entrust.net
-            "e23b8d105f87710a68d9248050ebefc627be4ca6".getBytes(UTF_8),
+            "e23b8d105f87710a68d9248050ebefc627be4ca6".getBytes(),
             // Subject: CN=DigiNotar PKIoverheid CA Organisatie - G2
             // Issuer: CN=Staat der Nederlanden Organisatie CA - G2
-            "7b2e16bc39bcd72b456e9f055d1de615b74945db".getBytes(UTF_8),
+            "7b2e16bc39bcd72b456e9f055d1de615b74945db".getBytes(),
             // Subject: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven
             // Issuer: CN=Staat der Nederlanden Overheid CA
-            "e8f91200c65cee16e039b9f883841661635f81c5".getBytes(UTF_8),
+            "e8f91200c65cee16e039b9f883841661635f81c5".getBytes(),
             // From http://src.chromium.org/viewvc/chrome?view=rev&revision=108479
             // Subject: O=Digicert Sdn. Bhd.
             // Issuer: CN=GTE CyberTrust Global Root
-            "0129bcd5b448ae8d2496d1c3e19723919088e152".getBytes(UTF_8),
+            "0129bcd5b448ae8d2496d1c3e19723919088e152".getBytes(),
             // Subject: CN=e-islem.kktcmerkezbankasi.org/emailAddress=ileti@kktcmerkezbankasi.org
             // Issuer: CN=T\xC3\x9CRKTRUST Elektronik Sunucu Sertifikas\xC4\xB1 Hizmetleri
-            "5f3ab33d55007054bc5e3e5553cd8d8465d77c61".getBytes(UTF_8),
+            "5f3ab33d55007054bc5e3e5553cd8d8465d77c61".getBytes(),
             // Subject: CN=*.EGO.GOV.TR 93
             // Issuer: CN=T\xC3\x9CRKTRUST Elektronik Sunucu Sertifikas\xC4\xB1 Hizmetleri
-            "783333c9687df63377efceddd82efa9101913e8e".getBytes(UTF_8),
+            "783333c9687df63377efceddd82efa9101913e8e".getBytes(),
             // Subject: Subject: C=FR, O=DG Tr\xC3\xA9sor, CN=AC DG Tr\xC3\xA9sor SSL
             // Issuer: C=FR, O=DGTPE, CN=AC DGTPE Signature Authentification
-            "3ecf4bbbe46096d514bb539bb913d77aa4ef31bf".getBytes(UTF_8)
+            "3ecf4bbbe46096d514bb539bb913d77aa4ef31bf".getBytes()
         ));
 
         // attempt to augment it with values taken from gservices
@@ -213,7 +207,7 @@
             for (String value : pubkeyBlacklist.split(",")) {
                 value = value.trim();
                 if (isPubkeyHash(value)) {
-                    bl.add(value.getBytes(UTF_8));
+                    bl.add(value.getBytes());
                 } else {
                     logger.log(Level.WARNING, "Tried to blacklist invalid pubkey " + value);
                 }
@@ -245,7 +239,7 @@
         (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a',
         (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f'};
 
-    private static byte[] toHex(byte[] in) {
+    private static final byte[] toHex(byte[] in) {
         byte[] out = new byte[in.length * 2];
         int outIndex = 0;
         for (int i = 0; i < in.length; i++) {
diff --git a/platform/src/main/java/org/conscrypt/CertPinManager.java b/platform/src/main/java/org/conscrypt/CertPinManager.java
index 8cac85d..e728fe7 100644
--- a/platform/src/main/java/org/conscrypt/CertPinManager.java
+++ b/platform/src/main/java/org/conscrypt/CertPinManager.java
@@ -22,10 +22,7 @@
 
 /**
  * Interface for classes that implement certificate pinning for use in {@link TrustManagerImpl}.
- *
- * @hide
  */
-@Internal
 public interface CertPinManager {
     /**
      * Given a {@code hostname} and a {@code chain} this verifies that the
diff --git a/platform/src/main/java/org/conscrypt/CertificatePriorityComparator.java b/platform/src/main/java/org/conscrypt/CertificatePriorityComparator.java
index b176236..95e3c5c 100644
--- a/platform/src/main/java/org/conscrypt/CertificatePriorityComparator.java
+++ b/platform/src/main/java/org/conscrypt/CertificatePriorityComparator.java
@@ -39,7 +39,6 @@
  * </ol>
  * </p>
  */
-@Internal
 public final class CertificatePriorityComparator implements Comparator<X509Certificate> {
 
     /**
@@ -59,7 +58,7 @@
     private static final Integer PRIORITY_SHA512 = 1;
     private static final Integer PRIORITY_UNKNOWN = -1;
     static {
-        ALGORITHM_OID_PRIORITY_MAP = new HashMap<>();
+        ALGORITHM_OID_PRIORITY_MAP = new HashMap<String, Integer>();
         // RSA oids
         ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.113549.1.1.13", PRIORITY_SHA512);
         ALGORITHM_OID_PRIORITY_MAP.put("1.2.840.113549.1.1.12", PRIORITY_SHA384);
diff --git a/platform/src/main/java/org/conscrypt/Hex.java b/platform/src/main/java/org/conscrypt/Hex.java
index aea947a..2bd17a9 100644
--- a/platform/src/main/java/org/conscrypt/Hex.java
+++ b/platform/src/main/java/org/conscrypt/Hex.java
@@ -20,16 +20,12 @@
  *
  * Helper class for dealing with hexadecimal strings.
  *
- * @hide
  */
-@Internal
 // public for testing by TrustedCertificateStoreTest
-// TODO(nathanmittler): Move to InternalUtil?
-public final class Hex {
+public class Hex {
     private Hex() {}
 
-    private final static char[] DIGITS = {
-            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+    private final static char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
 
     public static String bytesToHexString(byte[] bytes) {
         char[] buf = new char[bytes.length * 2];
@@ -52,4 +48,5 @@
 
         return new String(buf, cursor, bufLen - cursor);
     }
+
 }
diff --git a/platform/src/main/java/org/conscrypt/InternalUtil.java b/platform/src/main/java/org/conscrypt/InternalUtil.java
deleted file mode 100644
index 40937c8..0000000
--- a/platform/src/main/java/org/conscrypt/InternalUtil.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2017 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 org.conscrypt;
-
-import java.io.InputStream;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-
-/**
- * Helper to initialize the JNI libraries. This version runs when compiled
- * as part of the platform.
- *
- * @hide
- */
-@Internal
-public final class InternalUtil {
-    public static PublicKey logKeyToPublicKey(byte[] logKey) throws NoSuchAlgorithmException {
-        return new OpenSSLKey(NativeCrypto.d2i_PUBKEY(logKey)).getPublicKey();
-    }
-
-    public static PublicKey readPublicKeyPem(InputStream pem) throws InvalidKeyException, NoSuchAlgorithmException {
-        return OpenSSLKey.fromPublicKeyPemInputStream(pem).getPublicKey();
-    }
-
-    public static byte[] getOcspSingleExtension(
-            byte[] ocspResponse, String oid, long x509Ref, long issuerX509Ref) {
-        return NativeCrypto.get_ocsp_single_extension(ocspResponse, oid, x509Ref, issuerX509Ref);
-    }
-
-    private InternalUtil() {
-    }
-}
diff --git a/platform/src/main/java/org/conscrypt/JSSEProvider.java b/platform/src/main/java/org/conscrypt/JSSEProvider.java
index f97b797..66f1ba8 100644
--- a/platform/src/main/java/org/conscrypt/JSSEProvider.java
+++ b/platform/src/main/java/org/conscrypt/JSSEProvider.java
@@ -36,9 +36,7 @@
  *     CertPathValidator    PKIX
  *     CertificateFactory    X509
  *
- * @hide
  */
-@Internal
 public final class JSSEProvider extends Provider {
 
     private static final long serialVersionUID = 3075686092260669675L;
diff --git a/platform/src/main/java/org/conscrypt/Platform.java b/platform/src/main/java/org/conscrypt/Platform.java
index 1d2942e..da62bcd 100644
--- a/platform/src/main/java/org/conscrypt/Platform.java
+++ b/platform/src/main/java/org/conscrypt/Platform.java
@@ -54,7 +54,7 @@
 import libcore.net.NetworkSecurityPolicy;
 import sun.security.x509.AlgorithmId;
 
-final class Platform {
+class Platform {
     private static class NoPreloadHolder {
         public static final Platform MAPPER = new Platform();
     }
@@ -75,11 +75,11 @@
     private Platform() {
     }
 
-    static FileDescriptor getFileDescriptor(Socket s) {
+    public static FileDescriptor getFileDescriptor(Socket s) {
         return s.getFileDescriptor$();
     }
 
-    static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
+    public static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
         try {
             Field f_impl = Socket.class.getDeclaredField("impl");
             f_impl.setAccessible(true);
@@ -92,15 +92,15 @@
         }
     }
 
-    static String getCurveName(ECParameterSpec spec) {
+    public static String getCurveName(ECParameterSpec spec) {
         return spec.getCurveName();
     }
 
-    static void setCurveName(ECParameterSpec spec, String curveName) {
+    public static void setCurveName(ECParameterSpec spec, String curveName) {
         spec.setCurveName(curveName);
     }
 
-    static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException {
+    public static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException {
         StructTimeval tv = StructTimeval.fromMillis(timeoutMillis);
         try {
             Os.setsockoptTimeval(s.getFileDescriptor$(), SOL_SOCKET, SO_SNDTIMEO, tv);
@@ -109,7 +109,7 @@
         }
     }
 
-    static void setSSLParameters(SSLParameters params, SSLParametersImpl impl,
+    public static void setSSLParameters(SSLParameters params, SSLParametersImpl impl,
             OpenSSLSocketImpl socket) {
         impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
         impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
@@ -124,7 +124,7 @@
         }
     }
 
-    static void getSSLParameters(SSLParameters params, SSLParametersImpl impl,
+    public static void getSSLParameters(SSLParameters params, SSLParametersImpl impl,
             OpenSSLSocketImpl socket) {
         params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
         params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
@@ -134,7 +134,7 @@
         }
     }
 
-    static void setSSLParameters(
+    public static void setSSLParameters(
             SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
         impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
         impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
@@ -149,7 +149,7 @@
         }
     }
 
-    static void getSSLParameters(
+    public static void getSSLParameters(
             SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
         params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
         params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
@@ -184,7 +184,7 @@
         return false;
     }
 
-    static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
+    public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
             String authType, OpenSSLSocketImpl socket) throws CertificateException {
         if (tm instanceof X509ExtendedTrustManager) {
             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
@@ -196,7 +196,7 @@
         }
     }
 
-    static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
+    public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
             String authType, OpenSSLSocketImpl socket) throws CertificateException {
         if (tm instanceof X509ExtendedTrustManager) {
             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
@@ -208,7 +208,7 @@
         }
     }
 
-    static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
+    public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
             String authType, OpenSSLEngineImpl engine) throws CertificateException {
         if (tm instanceof X509ExtendedTrustManager) {
             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
@@ -220,7 +220,7 @@
         }
     }
 
-    static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
+    public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
             String authType, OpenSSLEngineImpl engine) throws CertificateException {
         if (tm instanceof X509ExtendedTrustManager) {
             X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
@@ -236,14 +236,14 @@
      * Wraps an old AndroidOpenSSL key instance. This is not needed on platform
      * builds since we didn't backport, so return null.
      */
-    static OpenSSLKey wrapRsaKey(PrivateKey key) {
+    public static OpenSSLKey wrapRsaKey(PrivateKey key) {
         return null;
     }
 
     /**
      * Logs to the system EventLog system.
      */
-    static void logEvent(String message) {
+    public static void logEvent(String message) {
         try {
             Class processClass = Class.forName("android.os.Process");
             Object processInstance = processClass.newInstance();
@@ -264,7 +264,7 @@
     /**
      * Returns true if the supplied hostname is an literal IP address.
      */
-    static boolean isLiteralIpAddress(String hostname) {
+    public static boolean isLiteralIpAddress(String hostname) {
         return InetAddress.isNumeric(hostname);
     }
 
@@ -272,14 +272,14 @@
      * Wrap the SocketFactory with the platform wrapper if needed for compatability.
      * For the platform-bundled library we never need to wrap.
      */
-    static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
+    public static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
         return factory;
     }
 
     /**
      * Convert from platform's GCMParameterSpec to our internal version.
      */
-    static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) {
+    public static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) {
         if (params instanceof GCMParameterSpec) {
             GCMParameterSpec gcmParams = (GCMParameterSpec) params;
             return new GCMParameters(gcmParams.getTLen(), gcmParams.getIV());
@@ -290,7 +290,7 @@
     /**
      * Creates a platform version of {@code GCMParameterSpec}.
      */
-    static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) {
+    public static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) {
         return new GCMParameterSpec(tagLenInBits, iv);
     }
 
@@ -298,21 +298,21 @@
      * CloseGuard functions.
      */
 
-    static CloseGuard closeGuardGet() {
+    public static CloseGuard closeGuardGet() {
         return CloseGuard.get();
     }
 
-    static void closeGuardOpen(Object guardObj, String message) {
+    public static void closeGuardOpen(Object guardObj, String message) {
         CloseGuard guard = (CloseGuard) guardObj;
         guard.open(message);
     }
 
-    static void closeGuardClose(Object guardObj) {
+    public static void closeGuardClose(Object guardObj) {
         CloseGuard guard = (CloseGuard) guardObj;
         guard.close();
     }
 
-    static void closeGuardWarnIfOpen(Object guardObj) {
+    public static void closeGuardWarnIfOpen(Object guardObj) {
         CloseGuard guard = (CloseGuard) guardObj;
         guard.warnIfOpen();
     }
@@ -321,14 +321,14 @@
      * BlockGuard functions.
      */
 
-    static void blockGuardOnNetwork() {
+    public static void blockGuardOnNetwork() {
         BlockGuard.getThreadPolicy().onNetwork();
     }
 
     /**
      * OID to Algorithm Name mapping.
      */
-    static String oidToAlgorithmName(String oid) {
+    public static String oidToAlgorithmName(String oid) {
         try {
             return AlgorithmId.get(oid).getName();
         } catch (NoSuchAlgorithmException e) {
@@ -340,11 +340,11 @@
      * Pre-Java 8 backward compatibility.
      */
 
-    static SSLSession wrapSSLSession(AbstractOpenSSLSession sslSession) {
+    public static SSLSession wrapSSLSession(AbstractOpenSSLSession sslSession) {
         return new OpenSSLExtendedSessionImpl(sslSession);
     }
 
-    static SSLSession unwrapSSLSession(SSLSession sslSession) {
+    public static SSLSession unwrapSSLSession(SSLSession sslSession) {
         if (sslSession instanceof OpenSSLExtendedSessionImpl) {
             return ((OpenSSLExtendedSessionImpl) sslSession).getDelegate();
         }
@@ -355,11 +355,11 @@
      * Pre-Java-7 backward compatibility.
      */
 
-    static String getHostStringFromInetSocketAddress(InetSocketAddress addr) {
+    public static String getHostStringFromInetSocketAddress(InetSocketAddress addr) {
         return addr.getHostString();
     }
 
-    static boolean isCTVerificationRequired(String hostname) {
+    public static boolean isCTVerificationRequired(String hostname) {
         return NetworkSecurityPolicy.getInstance()
                 .isCertificateTransparencyVerificationRequired(hostname);
     }
diff --git a/platform/src/main/java/org/conscrypt/TrustManagerFactoryImpl.java b/platform/src/main/java/org/conscrypt/TrustManagerFactoryImpl.java
index 790f88c..fd12d8c 100644
--- a/platform/src/main/java/org/conscrypt/TrustManagerFactoryImpl.java
+++ b/platform/src/main/java/org/conscrypt/TrustManagerFactoryImpl.java
@@ -49,9 +49,7 @@
  * TrustManagerFactory service provider interface implementation.
  *
  * @see javax.net.ssl.TrustManagerFactorySpi
- * @hide
  */
-@Internal
 public class TrustManagerFactoryImpl extends TrustManagerFactorySpi {
 
     private KeyStore keyStore;
diff --git a/platform/src/main/java/org/conscrypt/TrustManagerImpl.java b/platform/src/main/java/org/conscrypt/TrustManagerImpl.java
index 010c75a..590f739 100644
--- a/platform/src/main/java/org/conscrypt/TrustManagerImpl.java
+++ b/platform/src/main/java/org/conscrypt/TrustManagerImpl.java
@@ -84,9 +84,7 @@
  * be provided by some certification provider.
  *
  * @see javax.net.ssl.X509ExtendedTrustManager
- * @hide
  */
-@Internal
 public final class TrustManagerImpl extends X509ExtendedTrustManager {
 
     /**
@@ -844,7 +842,6 @@
             return SUPPORTED_EXTENSIONS;
         }
 
-        @SuppressWarnings("ReferenceEquality")
         @Override
         public void check(Certificate c, Collection<String> unresolvedCritExts)
                 throws CertPathValidatorException {
diff --git a/platform/src/main/java/org/conscrypt/TrustedCertificateIndex.java b/platform/src/main/java/org/conscrypt/TrustedCertificateIndex.java
index 323a545..012912c 100644
--- a/platform/src/main/java/org/conscrypt/TrustedCertificateIndex.java
+++ b/platform/src/main/java/org/conscrypt/TrustedCertificateIndex.java
@@ -33,10 +33,7 @@
 /**
  * Indexes {@code TrustAnchor} instances so they can be found in O(1)
  * time instead of O(N).
- *
- * @hide
  */
-@Internal
 public final class TrustedCertificateIndex {
 
     private final Map<X500Principal, List<TrustAnchor>> subjectToTrustAnchors
diff --git a/platform/src/main/java/org/conscrypt/TrustedCertificateKeyStoreSpi.java b/platform/src/main/java/org/conscrypt/TrustedCertificateKeyStoreSpi.java
index 31cf576..8ae1c6f 100644
--- a/platform/src/main/java/org/conscrypt/TrustedCertificateKeyStoreSpi.java
+++ b/platform/src/main/java/org/conscrypt/TrustedCertificateKeyStoreSpi.java
@@ -27,10 +27,7 @@
 
 /**
  * A KeyStoreSpi wrapper for the TrustedCertificateStore.
- *
- * @hide
  */
-@Internal
 public final class TrustedCertificateKeyStoreSpi extends KeyStoreSpi {
 
     private final TrustedCertificateStore store = new TrustedCertificateStore();
diff --git a/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java b/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java
index acce18e..3d815ad 100644
--- a/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java
+++ b/platform/src/main/java/org/conscrypt/TrustedCertificateStore.java
@@ -79,10 +79,7 @@
  * ensures that its owner and group are the system uid and system
  * gid and that it is world readable but only writable by the system
  * user.
- *
- * @hide
  */
-@Internal
 public class TrustedCertificateStore {
 
     private static final String PREFIX_SYSTEM = "system:";
diff --git a/platform/src/main/java/org/conscrypt/ct/CTConstants.java b/platform/src/main/java/org/conscrypt/ct/CTConstants.java
index 7c57605..1bf4abc 100644
--- a/platform/src/main/java/org/conscrypt/ct/CTConstants.java
+++ b/platform/src/main/java/org/conscrypt/ct/CTConstants.java
@@ -16,12 +16,6 @@
 
 package org.conscrypt.ct;
 
-import org.conscrypt.Internal;
-
-/**
- * @hide
- */
-@Internal
 public class CTConstants {
     public static final String X509_SCT_LIST_OID = "1.3.6.1.4.1.11129.2.4.2";
     public static final String OCSP_SCT_LIST_OID = "1.3.6.1.4.1.11129.2.4.5";
diff --git a/platform/src/main/java/org/conscrypt/ct/CTLogInfo.java b/platform/src/main/java/org/conscrypt/ct/CTLogInfo.java
index aed6f02..724b44d 100644
--- a/platform/src/main/java/org/conscrypt/ct/CTLogInfo.java
+++ b/platform/src/main/java/org/conscrypt/ct/CTLogInfo.java
@@ -23,16 +23,12 @@
 import java.security.Signature;
 import java.security.SignatureException;
 import java.util.Arrays;
-import org.conscrypt.Internal;
 
 /**
  * Properties about a Certificate Transparency Log.
  * This object stores information about a CT log, its public key, description and URL.
  * It allows verification of SCTs against the log's public key.
- *
- * @hide
  */
-@Internal
 public class CTLogInfo {
     private final byte[] logId;
     private final PublicKey publicKey;
diff --git a/platform/src/main/java/org/conscrypt/ct/CTLogStore.java b/platform/src/main/java/org/conscrypt/ct/CTLogStore.java
index 96751c5..24a0b43 100644
--- a/platform/src/main/java/org/conscrypt/ct/CTLogStore.java
+++ b/platform/src/main/java/org/conscrypt/ct/CTLogStore.java
@@ -16,12 +16,6 @@
 
 package org.conscrypt.ct;
 
-import org.conscrypt.Internal;
-
-/**
- * @hide
- */
-@Internal
 public interface CTLogStore {
     CTLogInfo getKnownLog(byte[] logId);
 }
diff --git a/platform/src/main/java/org/conscrypt/ct/CTLogStoreImpl.java b/platform/src/main/java/org/conscrypt/ct/CTLogStoreImpl.java
index a6aadf2..778b045 100644
--- a/platform/src/main/java/org/conscrypt/ct/CTLogStoreImpl.java
+++ b/platform/src/main/java/org/conscrypt/ct/CTLogStoreImpl.java
@@ -31,13 +31,9 @@
 import java.util.HashSet;
 import java.util.Scanner;
 import java.util.Set;
-import org.conscrypt.Internal;
-import org.conscrypt.InternalUtil;
+import org.conscrypt.NativeCrypto;
+import org.conscrypt.OpenSSLKey;
 
-/**
- * @hide
- */
-@Internal
 public class CTLogStoreImpl implements CTLogStore {
     /**
      * Thrown when parsing of a log file fails.
@@ -148,7 +144,8 @@
         CTLogInfo[] logs = new CTLogInfo[KnownLogs.LOG_COUNT];
         for (int i = 0; i < KnownLogs.LOG_COUNT; i++) {
             try {
-                PublicKey key = InternalUtil.logKeyToPublicKey(KnownLogs.LOG_KEYS[i]);
+                PublicKey key = new OpenSSLKey(NativeCrypto.d2i_PUBKEY(KnownLogs.LOG_KEYS[i]))
+                                .getPublicKey();
 
                 logs[i] = new CTLogInfo(key,
                                         KnownLogs.LOG_DESCRIPTIONS[i],
@@ -223,10 +220,10 @@
 
         PublicKey pubkey;
         try {
-            pubkey = InternalUtil.readPublicKeyPem(new StringBufferInputStream(
+            pubkey = OpenSSLKey.fromPublicKeyPemInputStream(new StringBufferInputStream(
                         "-----BEGIN PUBLIC KEY-----\n" +
                         key + "\n" +
-                        "-----END PUBLIC KEY-----"));
+                        "-----END PUBLIC KEY-----")).getPublicKey();
         } catch (InvalidKeyException e) {
             throw new InvalidLogFileException(e);
         } catch (NoSuchAlgorithmException e) {
diff --git a/platform/src/main/java/org/conscrypt/ct/CTPolicy.java b/platform/src/main/java/org/conscrypt/ct/CTPolicy.java
index 455cabd..7d8a0c7 100644
--- a/platform/src/main/java/org/conscrypt/ct/CTPolicy.java
+++ b/platform/src/main/java/org/conscrypt/ct/CTPolicy.java
@@ -17,9 +17,7 @@
 package org.conscrypt.ct;
 
 import java.security.cert.X509Certificate;
-import org.conscrypt.Internal;
 
-@Internal
 public interface CTPolicy {
     boolean doesResultConformToPolicy(CTVerificationResult result, String hostname,
             X509Certificate[] chain);
diff --git a/platform/src/main/java/org/conscrypt/ct/CTPolicyImpl.java b/platform/src/main/java/org/conscrypt/ct/CTPolicyImpl.java
index 6368b2c..b97bd34 100644
--- a/platform/src/main/java/org/conscrypt/ct/CTPolicyImpl.java
+++ b/platform/src/main/java/org/conscrypt/ct/CTPolicyImpl.java
@@ -19,12 +19,7 @@
 import java.security.cert.X509Certificate;
 import java.util.HashSet;
 import java.util.Set;
-import org.conscrypt.Internal;
 
-/**
- * @hide
- */
-@Internal
 public class CTPolicyImpl implements CTPolicy {
     private final CTLogStore logStore;
     private final int minimumLogCount;
diff --git a/platform/src/main/java/org/conscrypt/ct/CTVerificationResult.java b/platform/src/main/java/org/conscrypt/ct/CTVerificationResult.java
index d17b449..06aa88d 100644
--- a/platform/src/main/java/org/conscrypt/ct/CTVerificationResult.java
+++ b/platform/src/main/java/org/conscrypt/ct/CTVerificationResult.java
@@ -19,12 +19,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import org.conscrypt.Internal;
 
-/**
- * @hide
- */
-@Internal
 public class CTVerificationResult {
     private final ArrayList<VerifiedSCT> validSCTs = new ArrayList<>();
     private final ArrayList<VerifiedSCT> invalidSCTs = new ArrayList<>();
diff --git a/platform/src/main/java/org/conscrypt/ct/CTVerifier.java b/platform/src/main/java/org/conscrypt/ct/CTVerifier.java
index 24bc3db..d221ec1 100644
--- a/platform/src/main/java/org/conscrypt/ct/CTVerifier.java
+++ b/platform/src/main/java/org/conscrypt/ct/CTVerifier.java
@@ -22,14 +22,9 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import org.conscrypt.Internal;
-import org.conscrypt.InternalUtil;
+import org.conscrypt.NativeCrypto;
 import org.conscrypt.OpenSSLX509Certificate;
 
-/**
- * @hide
- */
-@Internal
 public class CTVerifier {
     private final CTLogStore store;
 
@@ -218,8 +213,9 @@
             return Collections.emptyList();
         }
 
-        byte[] extData = InternalUtil.getOcspSingleExtension(data, CTConstants.OCSP_SCT_LIST_OID,
-                                                             chain[0].getContext(), chain[1].getContext());
+        byte[] extData = NativeCrypto.get_ocsp_single_extension(data, CTConstants.OCSP_SCT_LIST_OID,
+                                                                chain[0].getContext(),
+                                                                chain[1].getContext());
         if (extData == null) {
             return Collections.emptyList();
         }
diff --git a/platform/src/main/java/org/conscrypt/ct/CertificateEntry.java b/platform/src/main/java/org/conscrypt/ct/CertificateEntry.java
index 232ef79..7361149 100644
--- a/platform/src/main/java/org/conscrypt/ct/CertificateEntry.java
+++ b/platform/src/main/java/org/conscrypt/ct/CertificateEntry.java
@@ -22,7 +22,6 @@
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
-import org.conscrypt.Internal;
 import org.conscrypt.OpenSSLX509Certificate;
 
 /**
@@ -39,9 +38,7 @@
  *     } signed_entry;
  * } CertificateEntry;
  *
- * @hide
  */
-@Internal
 public class CertificateEntry {
     public enum LogEntryType {
         X509_ENTRY,
diff --git a/platform/src/main/java/org/conscrypt/ct/DigitallySigned.java b/platform/src/main/java/org/conscrypt/ct/DigitallySigned.java
index 105924f..7d470de 100644
--- a/platform/src/main/java/org/conscrypt/ct/DigitallySigned.java
+++ b/platform/src/main/java/org/conscrypt/ct/DigitallySigned.java
@@ -18,14 +18,10 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
-import org.conscrypt.Internal;
 
 /**
  * DigitallySigned structure, as defined by RFC5246 Section 4.7.
- *
- * @hide
  */
-@Internal
 public class DigitallySigned {
     public enum HashAlgorithm {
         NONE,
diff --git a/platform/src/main/java/org/conscrypt/ct/KnownLogs.java b/platform/src/main/java/org/conscrypt/ct/KnownLogs.java
index 960c46c..dbb9eb3 100644
--- a/platform/src/main/java/org/conscrypt/ct/KnownLogs.java
+++ b/platform/src/main/java/org/conscrypt/ct/KnownLogs.java
@@ -19,12 +19,6 @@
 
 package org.conscrypt.ct;
 
-import org.conscrypt.Internal;
-
-/**
- * @hide
- */
-@Internal
 public final class KnownLogs {
     public static final int LOG_COUNT = 8;
     public static final String[] LOG_DESCRIPTIONS = new String[] {
diff --git a/platform/src/main/java/org/conscrypt/ct/Serialization.java b/platform/src/main/java/org/conscrypt/ct/Serialization.java
index 31657c8..aebca4d 100644
--- a/platform/src/main/java/org/conscrypt/ct/Serialization.java
+++ b/platform/src/main/java/org/conscrypt/ct/Serialization.java
@@ -21,12 +21,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
-import org.conscrypt.Internal;
 
-/**
- * @hide
- */
-@Internal
 public class Serialization {
     private Serialization() {}
 
diff --git a/platform/src/main/java/org/conscrypt/ct/SerializationException.java b/platform/src/main/java/org/conscrypt/ct/SerializationException.java
index 19e58a4..2beb6cd 100644
--- a/platform/src/main/java/org/conscrypt/ct/SerializationException.java
+++ b/platform/src/main/java/org/conscrypt/ct/SerializationException.java
@@ -16,12 +16,6 @@
 
 package org.conscrypt.ct;
 
-import org.conscrypt.Internal;
-
-/**
- * @hide
- */
-@Internal
 public class SerializationException extends Exception {
     public SerializationException() {
     }
diff --git a/platform/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java b/platform/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java
index e9b5c4b..5364e54 100644
--- a/platform/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java
+++ b/platform/src/main/java/org/conscrypt/ct/SignedCertificateTimestamp.java
@@ -20,14 +20,10 @@
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
-import org.conscrypt.Internal;
 
 /**
  * SignedCertificateTimestamp structure, as defined by RFC6962 Section 3.2.
- *
- * @hide
  */
-@Internal
 public class SignedCertificateTimestamp {
     public enum Version {
         V1
diff --git a/platform/src/main/java/org/conscrypt/ct/VerifiedSCT.java b/platform/src/main/java/org/conscrypt/ct/VerifiedSCT.java
index 839250a..91936b9 100644
--- a/platform/src/main/java/org/conscrypt/ct/VerifiedSCT.java
+++ b/platform/src/main/java/org/conscrypt/ct/VerifiedSCT.java
@@ -16,14 +16,9 @@
 
 package org.conscrypt.ct;
 
-import org.conscrypt.Internal;
-
 /**
  * Verification result for a single SCT.
- *
- * @hide
  */
-@Internal
 public final class VerifiedSCT {
     public enum Status {
         VALID,
diff --git a/platform/src/main/res/values/strings.xml b/platform/src/main/res/values/strings.xml
deleted file mode 100644
index 8542005..0000000
--- a/platform/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<resources>
-</resources>
diff --git a/platform/src/test/java/org/conscrypt/NativeCryptoTest.java b/platform/src/test/java/org/conscrypt/NativeCryptoTest.java
new file mode 100644
index 0000000..fcd5595
--- /dev/null
+++ b/platform/src/test/java/org/conscrypt/NativeCryptoTest.java
@@ -0,0 +1,2898 @@
+/*
+ * 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 org.conscrypt;
+
+import static org.conscrypt.NativeConstants.SSL_MODE_CBC_RECORD_SPLITTING;
+import static org.conscrypt.NativeConstants.SSL_MODE_ENABLE_FALSE_START;
+import static org.conscrypt.TestUtils.openTestFile;
+import static org.conscrypt.TestUtils.readTestFile;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Method;
+import java.math.BigInteger;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECPrivateKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLProtocolException;
+import javax.security.auth.x500.X500Principal;
+import libcore.io.IoUtils;
+import libcore.java.security.StandardNames;
+import libcore.java.security.TestKeyStore;
+import org.conscrypt.NativeCrypto.SSLHandshakeCallbacks;
+import org.conscrypt.OpenSSLX509CertificateFactory.ParsingException;
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class NativeCryptoTest {
+    private static final long NULL = 0;
+    private static final FileDescriptor INVALID_FD = new FileDescriptor();
+    private static final SSLHandshakeCallbacks DUMMY_CB =
+            new TestSSLHandshakeCallbacks(null, 0, null);
+
+    private static final long TIMEOUT_SECONDS = 5;
+
+    private static OpenSSLKey SERVER_PRIVATE_KEY;
+    private static OpenSSLX509Certificate[] SERVER_CERTIFICATES_HOLDER;
+    private static long[] SERVER_CERTIFICATES;
+    private static OpenSSLKey CLIENT_PRIVATE_KEY;
+    private static OpenSSLX509Certificate[] CLIENT_CERTIFICATES_HOLDER;
+    private static long[] CLIENT_CERTIFICATES;
+    private static byte[][] CA_PRINCIPALS;
+    private static OpenSSLKey CHANNEL_ID_PRIVATE_KEY;
+    private static byte[] CHANNEL_ID;
+    private static Method m_Platform_getFileDescriptor;
+
+    @BeforeClass
+    public static void getPlatformMethods() throws Exception {
+        Class<?> c_Platform =
+                Class.forName(NativeCryptoTest.class.getPackage().getName() + ".Platform");
+        m_Platform_getFileDescriptor =
+                c_Platform.getDeclaredMethod("getFileDescriptor", Socket.class);
+        m_Platform_getFileDescriptor.setAccessible(true);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        assertEquals(0, NativeCrypto.ERR_peek_last_error());
+    }
+
+    private static OpenSSLKey getServerPrivateKey() {
+        initCerts();
+        return SERVER_PRIVATE_KEY;
+    }
+
+    private static long[] getServerCertificates() {
+        initCerts();
+        return SERVER_CERTIFICATES;
+    }
+
+    private static OpenSSLKey getClientPrivateKey() {
+        initCerts();
+        return CLIENT_PRIVATE_KEY;
+    }
+
+    private static long[] getClientCertificates() {
+        initCerts();
+        return CLIENT_CERTIFICATES;
+    }
+
+    private static byte[][] getCaPrincipals() {
+        initCerts();
+        return CA_PRINCIPALS;
+    }
+
+    /**
+     * Lazily create shared test certificates.
+     */
+    private static synchronized void initCerts() {
+        if (SERVER_PRIVATE_KEY != null) {
+            return;
+        }
+
+        try {
+            PrivateKeyEntry serverPrivateKeyEntry =
+                    TestKeyStore.getServer().getPrivateKey("RSA", "RSA");
+            SERVER_PRIVATE_KEY = OpenSSLKey.fromPrivateKey(serverPrivateKeyEntry.getPrivateKey());
+            SERVER_CERTIFICATES_HOLDER =
+                    encodeCertificateList(serverPrivateKeyEntry.getCertificateChain());
+            SERVER_CERTIFICATES = getCertificateReferences(SERVER_CERTIFICATES_HOLDER);
+
+            PrivateKeyEntry clientPrivateKeyEntry =
+                    TestKeyStore.getClientCertificate().getPrivateKey("RSA", "RSA");
+            CLIENT_PRIVATE_KEY = OpenSSLKey.fromPrivateKey(clientPrivateKeyEntry.getPrivateKey());
+            CLIENT_CERTIFICATES_HOLDER =
+                    encodeCertificateList(clientPrivateKeyEntry.getCertificateChain());
+            CLIENT_CERTIFICATES = getCertificateReferences(CLIENT_CERTIFICATES_HOLDER);
+
+            KeyStore ks = TestKeyStore.getClient().keyStore;
+            String caCertAlias = ks.aliases().nextElement();
+            X509Certificate certificate = (X509Certificate) ks.getCertificate(caCertAlias);
+            X500Principal principal = certificate.getIssuerX500Principal();
+            CA_PRINCIPALS = new byte[][] {principal.getEncoded()};
+            initChannelIdKey();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static long[] getCertificateReferences(OpenSSLX509Certificate[] certs) {
+        final long[] certRefs = new long[certs.length];
+        for (int i = 0; i < certs.length; i++) {
+            certRefs[i] = certs[i].getContext();
+        }
+        return certRefs;
+    }
+
+    private static OpenSSLX509Certificate[] encodeCertificateList(Certificate[] chain)
+            throws CertificateEncodingException {
+        final OpenSSLX509Certificate[] openSslCerts = new OpenSSLX509Certificate[chain.length];
+        for (int i = 0; i < chain.length; i++) {
+            openSslCerts[i] = OpenSSLX509Certificate.fromCertificate(chain[i]);
+        }
+        return openSslCerts;
+    }
+
+    private static synchronized void initChannelIdKey() throws Exception {
+        if (CHANNEL_ID_PRIVATE_KEY != null) {
+            return;
+        }
+
+        // NIST P-256 aka SECG secp256r1 aka X9.62 prime256v1
+        OpenSSLECGroupContext openSslSpec = OpenSSLECGroupContext.getCurveByName("prime256v1");
+        BigInteger s = new BigInteger(
+                "229cdbbf489aea584828a261a23f9ff8b0f66f7ccac98bf2096ab3aee41497c5", 16);
+        CHANNEL_ID_PRIVATE_KEY =
+                new OpenSSLECPrivateKey(new ECPrivateKeySpec(s, openSslSpec.getECParameterSpec()))
+                        .getOpenSSLKey();
+
+        // Channel ID is the concatenation of the X and Y coordinates of the public key.
+        CHANNEL_ID = new BigInteger(
+                "702b07871fd7955c320b26f15e244e47eed60272124c92b9ebecf0b42f90069b"
+                        + "ab53592ebfeb4f167dbf3ce61513afb0e354c479b1c1b69874fa471293494f77",
+                16).toByteArray();
+    }
+
+    private static RSAPrivateCrtKey generateRsaKey() throws Exception {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        kpg.initialize(512);
+
+        KeyPair keyPair = kpg.generateKeyPair();
+        return (RSAPrivateCrtKey) keyPair.getPrivate();
+    }
+
+    private static NativeRef.EVP_PKEY getRsaPkey(RSAPrivateCrtKey privKey) throws Exception {
+        return new NativeRef.EVP_PKEY(NativeCrypto.EVP_PKEY_new_RSA(
+                privKey.getModulus().toByteArray(), privKey.getPublicExponent().toByteArray(),
+                privKey.getPrivateExponent().toByteArray(), privKey.getPrimeP().toByteArray(),
+                privKey.getPrimeQ().toByteArray(), privKey.getPrimeExponentP().toByteArray(),
+                privKey.getPrimeExponentQ().toByteArray(),
+                privKey.getCrtCoefficient().toByteArray()));
+    }
+
+    public static void assertEqualSessions(long expected, long actual) {
+        assertEqualByteArrays(NativeCrypto.SSL_SESSION_session_id(expected),
+                NativeCrypto.SSL_SESSION_session_id(actual));
+    }
+    public static void assertEqualByteArrays(byte[] expected, byte[] actual) {
+        assertEquals(Arrays.toString(expected), Arrays.toString(actual));
+    }
+
+    public static void assertEqualPrincipals(byte[][] expected, byte[][] actual) {
+        assertEqualByteArrays(expected, actual);
+    }
+
+    public static void assertEqualCertificateChains(long[] expected, long[] actual) {
+        assertEquals(expected.length, actual.length);
+        for (int i = 0; i < expected.length; i++) {
+            NativeCrypto.X509_cmp(expected[i], actual[i]);
+        }
+    }
+
+    public static void assertEqualByteArrays(byte[][] expected, byte[][] actual) {
+        assertEquals(Arrays.deepToString(expected), Arrays.deepToString(actual));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_cmp_BothNullParameters() throws Exception {
+        NativeCrypto.EVP_PKEY_cmp(null, null);
+    }
+
+    @Test
+    public void test_EVP_PKEY_cmp() throws Exception {
+        RSAPrivateCrtKey privKey1 = generateRsaKey();
+
+        NativeRef.EVP_PKEY pkey1 = getRsaPkey(privKey1);
+        assertNotSame(NULL, pkey1);
+
+        NativeRef.EVP_PKEY pkey1_copy = getRsaPkey(privKey1);
+        assertNotSame(NULL, pkey1_copy);
+
+        NativeRef.EVP_PKEY pkey2 = getRsaPkey(generateRsaKey());
+        assertNotSame(NULL, pkey2);
+
+        try {
+            NativeCrypto.EVP_PKEY_cmp(pkey1, null);
+            fail("Should throw NullPointerException when arguments are NULL");
+        } catch (NullPointerException expected) {
+        }
+
+        assertEquals("Same keys should be the equal", 1, NativeCrypto.EVP_PKEY_cmp(pkey1, pkey1));
+
+        assertEquals(
+                "Same keys should be the equal", 1, NativeCrypto.EVP_PKEY_cmp(pkey1, pkey1_copy));
+
+        assertEquals(
+                "Different keys should not be equal", 0, NativeCrypto.EVP_PKEY_cmp(pkey1, pkey2));
+    }
+
+    @Test
+    public void test_SSL_CTX_new() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        assertTrue(c != NULL);
+        long c2 = NativeCrypto.SSL_CTX_new();
+        assertTrue(c != c2);
+        NativeCrypto.SSL_CTX_free(c);
+        NativeCrypto.SSL_CTX_free(c2);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_SSL_CTX_free_NullArgument() throws Exception {
+        NativeCrypto.SSL_CTX_free(NULL);
+    }
+
+    @Test
+    public void test_SSL_CTX_free() throws Exception {
+        NativeCrypto.SSL_CTX_free(NativeCrypto.SSL_CTX_new());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_CTX_set_session_id_context_NullContextArgument() throws Exception {
+        NativeCrypto.SSL_CTX_set_session_id_context(NULL, new byte[0]);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_CTX_set_session_id_context_NullSessionIdArgument() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        try {
+            NativeCrypto.SSL_CTX_set_session_id_context(c, null);
+        } finally {
+            NativeCrypto.SSL_CTX_free(c);
+        }
+    }
+
+    @Test
+    public void test_SSL_CTX_set_session_id_context() throws Exception {
+        byte[] empty = new byte[0];
+
+        long c = NativeCrypto.SSL_CTX_new();
+        try {
+            NativeCrypto.SSL_CTX_set_session_id_context(c, empty);
+            NativeCrypto.SSL_CTX_set_session_id_context(c, new byte[32]);
+            try {
+                NativeCrypto.SSL_CTX_set_session_id_context(c, new byte[33]);
+            } catch (IllegalArgumentException expected) {
+            }
+        } finally {
+            NativeCrypto.SSL_CTX_free(c);
+        }
+    }
+
+    @Test
+    public void test_SSL_new() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+
+        assertTrue(s != NULL);
+        assertTrue((NativeCrypto.SSL_get_options(s) & NativeConstants.SSL_OP_NO_SSLv3) == 0);
+        assertTrue((NativeCrypto.SSL_get_options(s) & NativeConstants.SSL_OP_NO_TLSv1) == 0);
+        assertTrue((NativeCrypto.SSL_get_options(s) & NativeConstants.SSL_OP_NO_TLSv1_1) == 0);
+        assertTrue((NativeCrypto.SSL_get_options(s) & NativeConstants.SSL_OP_NO_TLSv1_2) == 0);
+
+        long s2 = NativeCrypto.SSL_new(c);
+        assertTrue(s != s2);
+        NativeCrypto.SSL_free(s2);
+
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_use_certificate_NullArguments() throws Exception {
+        NativeCrypto.SSL_use_certificate(NULL, null);
+    }
+
+    @Test
+    public void test_SSL_use_certificate() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+
+        try {
+            NativeCrypto.SSL_use_certificate(s, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        NativeCrypto.SSL_use_certificate(s, getServerCertificates());
+
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test
+    public void test_SSL_use_PrivateKey_for_tls_channel_id() throws Exception {
+        initChannelIdKey();
+
+        try {
+            NativeCrypto.SSL_set1_tls_channel_id(NULL, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+
+        try {
+            NativeCrypto.SSL_set1_tls_channel_id(s, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        // Use the key natively. This works because the initChannelIdKey method ensures that the
+        // key is backed by OpenSSL.
+        NativeCrypto.SSL_set1_tls_channel_id(s, CHANNEL_ID_PRIVATE_KEY.getNativeRef());
+
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test
+    public void test_SSL_use_PrivateKey() throws Exception {
+        try {
+            NativeCrypto.SSL_use_PrivateKey(NULL, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+
+        try {
+            NativeCrypto.SSL_use_PrivateKey(s, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey().getNativeRef());
+
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void SSL_check_private_key_NullArgument() throws Exception {
+        NativeCrypto.SSL_check_private_key(NULL);
+    }
+
+    @Test
+    public void test_SSL_check_private_key_no_key_no_cert() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+
+        // neither private or certificate set
+        try {
+            NativeCrypto.SSL_check_private_key(s);
+            fail();
+        } catch (SSLException expected) {
+        }
+
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test
+    public void test_SSL_check_private_key_cert_then_key() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+
+        // first certificate, then private
+        NativeCrypto.SSL_use_certificate(s, getServerCertificates());
+
+        try {
+            NativeCrypto.SSL_check_private_key(s);
+            fail();
+        } catch (SSLException expected) {
+        }
+
+        NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey().getNativeRef());
+        NativeCrypto.SSL_check_private_key(s);
+
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+    @Test
+    public void test_SSL_check_private_key_key_then_cert() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+
+        // first private, then certificate
+        NativeCrypto.SSL_use_PrivateKey(s, getServerPrivateKey().getNativeRef());
+
+        try {
+            NativeCrypto.SSL_check_private_key(s);
+            fail();
+        } catch (SSLException expected) {
+        }
+
+        NativeCrypto.SSL_use_certificate(s, getServerCertificates());
+        NativeCrypto.SSL_check_private_key(s);
+
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test
+    public void test_SSL_get_mode() throws Exception {
+        try {
+            NativeCrypto.SSL_get_mode(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+        assertTrue(NativeCrypto.SSL_get_mode(s) != 0);
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test
+    public void test_SSL_set_mode_and_clear_mode() throws Exception {
+        try {
+            NativeCrypto.SSL_set_mode(NULL, 0);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+        // check SSL_MODE_ENABLE_FALSE_START on by default for BoringSSL
+        assertEquals(SSL_MODE_ENABLE_FALSE_START,
+                NativeCrypto.SSL_get_mode(s) & SSL_MODE_ENABLE_FALSE_START);
+        // check SSL_MODE_CBC_RECORD_SPLITTING off by default
+        assertEquals(0, NativeCrypto.SSL_get_mode(s) & SSL_MODE_CBC_RECORD_SPLITTING);
+
+        // set SSL_MODE_ENABLE_FALSE_START on
+        NativeCrypto.SSL_set_mode(s, SSL_MODE_ENABLE_FALSE_START);
+        assertTrue((NativeCrypto.SSL_get_mode(s) & SSL_MODE_ENABLE_FALSE_START) != 0);
+        // clear SSL_MODE_ENABLE_FALSE_START off
+        NativeCrypto.SSL_clear_mode(s, SSL_MODE_ENABLE_FALSE_START);
+        assertTrue((NativeCrypto.SSL_get_mode(s) & SSL_MODE_ENABLE_FALSE_START) == 0);
+
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test
+    public void test_SSL_get_options() throws Exception {
+        try {
+            NativeCrypto.SSL_get_options(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+        assertTrue(NativeCrypto.SSL_get_options(s) != 0);
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test
+    public void test_SSL_set_options() throws Exception {
+        try {
+            NativeCrypto.SSL_set_options(NULL, 0);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+        assertTrue((NativeCrypto.SSL_get_options(s) & NativeConstants.SSL_OP_NO_SSLv3) == 0);
+        NativeCrypto.SSL_set_options(s, NativeConstants.SSL_OP_NO_SSLv3);
+        assertTrue((NativeCrypto.SSL_get_options(s) & NativeConstants.SSL_OP_NO_SSLv3) != 0);
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test
+    public void test_SSL_clear_options() throws Exception {
+        try {
+            NativeCrypto.SSL_clear_options(NULL, 0);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+        assertTrue((NativeCrypto.SSL_get_options(s) & NativeConstants.SSL_OP_NO_SSLv3) == 0);
+        NativeCrypto.SSL_set_options(s, NativeConstants.SSL_OP_NO_SSLv3);
+        assertTrue((NativeCrypto.SSL_get_options(s) & NativeConstants.SSL_OP_NO_SSLv3) != 0);
+        NativeCrypto.SSL_clear_options(s, NativeConstants.SSL_OP_NO_SSLv3);
+        assertTrue((NativeCrypto.SSL_get_options(s) & NativeConstants.SSL_OP_NO_SSLv3) == 0);
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test
+    public void test_SSL_set_cipher_lists() throws Exception {
+        try {
+            NativeCrypto.SSL_set_cipher_lists(NULL, null);
+            fail("Exception not thrown for null ssl and null list");
+        } catch (NullPointerException expected) {
+        }
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+
+        try {
+            NativeCrypto.SSL_set_cipher_lists(s, null);
+            fail("Exception not thrown for null list");
+        } catch (NullPointerException expected) {
+        }
+
+        // Explicitly checking that the empty list is allowed.
+        // b/21816861
+        NativeCrypto.SSL_set_cipher_lists(s, new String[] {});
+
+        try {
+            NativeCrypto.SSL_set_cipher_lists(s, new String[] {null});
+            fail("Exception not thrown for list with null element");
+        } catch (NullPointerException expected) {
+        }
+
+        // see OpenSSL ciphers man page
+        String[] illegals = new String[] {// empty
+                "",
+                // never standardized
+                "EXP1024-DES-CBC-SHA",
+                // IDEA
+                "IDEA-CBC-SHA", "IDEA-CBC-MD5"};
+
+        for (String illegal : illegals) {
+            try {
+                NativeCrypto.SSL_set_cipher_lists(s, new String[] {illegal});
+                fail("Exception now thrown for illegal cipher: " + illegal);
+            } catch (IllegalArgumentException expected) {
+            }
+        }
+
+        List<String> ciphers =
+                new ArrayList<String>(NativeCrypto.OPENSSL_TO_STANDARD_CIPHER_SUITES.keySet());
+        NativeCrypto.SSL_set_cipher_lists(s, ciphers.toArray(new String[ciphers.size()]));
+
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test
+    public void test_SSL_set_verify() throws Exception {
+        try {
+            NativeCrypto.SSL_set_verify(NULL, 0);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+        NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_NONE);
+        NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_PEER);
+        NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+        NativeCrypto.SSL_set_verify(
+                s, (NativeCrypto.SSL_VERIFY_PEER | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT));
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    private static final boolean DEBUG = false;
+
+    public static class Hooks {
+        protected String negotiatedCipherSuite;
+        private OpenSSLKey channelIdPrivateKey;
+        protected boolean pskEnabled;
+        protected byte[] pskKey;
+        protected List<String> enabledCipherSuites;
+
+        /**
+         * @throws SSLException
+         */
+        public long getContext() throws SSLException {
+            return NativeCrypto.SSL_CTX_new();
+        }
+        public long beforeHandshake(long context) throws SSLException {
+            long s = NativeCrypto.SSL_new(context);
+            // Limit cipher suites to a known set so authMethod is known.
+            List<String> cipherSuites = new ArrayList<String>();
+            if (enabledCipherSuites == null) {
+                cipherSuites.add("ECDHE-RSA-AES128-SHA");
+                if (pskEnabled) {
+                    // In TLS-PSK the client indicates that PSK key exchange is desired by offering
+                    // at least one PSK cipher suite.
+                    cipherSuites.add(0, "PSK-AES128-CBC-SHA");
+                }
+            } else {
+                cipherSuites.addAll(enabledCipherSuites);
+            }
+            NativeCrypto.SSL_set_cipher_lists(
+                    s, cipherSuites.toArray(new String[cipherSuites.size()]));
+
+            if (channelIdPrivateKey != null) {
+                NativeCrypto.SSL_set1_tls_channel_id(s, channelIdPrivateKey.getNativeRef());
+            }
+            return s;
+        }
+        public void configureCallbacks(
+                @SuppressWarnings("unused") TestSSLHandshakeCallbacks callbacks) {}
+        public void clientCertificateRequested(@SuppressWarnings("unused") long s) {}
+        public void afterHandshake(long session, long ssl, long context, Socket socket,
+                FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+            if (session != NULL) {
+                negotiatedCipherSuite = NativeCrypto.SSL_SESSION_cipher(session);
+                NativeCrypto.SSL_SESSION_free(session);
+            }
+            if (ssl != NULL) {
+                try {
+                    NativeCrypto.SSL_shutdown(ssl, fd, callback);
+                } catch (IOException e) {
+                }
+                NativeCrypto.SSL_free(ssl);
+            }
+            if (context != NULL) {
+                NativeCrypto.SSL_CTX_free(context);
+            }
+            if (socket != null) {
+                socket.close();
+            }
+        }
+    }
+
+    public static class TestSSLHandshakeCallbacks implements SSLHandshakeCallbacks {
+        private final Socket socket;
+        private final long sslNativePointer;
+        private final Hooks hooks;
+
+        public TestSSLHandshakeCallbacks(Socket socket, long sslNativePointer, Hooks hooks) {
+            this.socket = socket;
+            this.sslNativePointer = sslNativePointer;
+            this.hooks = hooks;
+        }
+
+        public long[] certificateChainRefs;
+        public String authMethod;
+        public boolean verifyCertificateChainCalled;
+
+        @Override
+        public void verifyCertificateChain(long[] certChainRefs, String authMethod)
+                throws CertificateException {
+            if (DEBUG) {
+                System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+                        + " verifyCertificateChain"
+                        + " asn1DerEncodedCertificateChain=" + Arrays.toString(certChainRefs)
+                        + " authMethod=" + authMethod);
+            }
+            this.certificateChainRefs = certChainRefs;
+            this.authMethod = authMethod;
+            this.verifyCertificateChainCalled = true;
+        }
+
+        public byte[] keyTypes;
+        public byte[][] asn1DerEncodedX500Principals;
+        public boolean clientCertificateRequestedCalled;
+
+        @Override
+        public void clientCertificateRequested(
+                byte[] keyTypes, byte[][] asn1DerEncodedX500Principals) {
+            if (DEBUG) {
+                System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+                        + " clientCertificateRequested"
+                        + " keyTypes=" + keyTypes + " asn1DerEncodedX500Principals="
+                        + asn1DerEncodedX500Principals);
+            }
+            this.keyTypes = keyTypes;
+            this.asn1DerEncodedX500Principals = asn1DerEncodedX500Principals;
+            this.clientCertificateRequestedCalled = true;
+            if (hooks != null) {
+                hooks.clientCertificateRequested(sslNativePointer);
+            }
+        }
+
+        public boolean handshakeCompletedCalled;
+
+        @Override
+        public void onSSLStateChange(int type, int val) {
+            if (DEBUG) {
+                System.out.println(
+                        "ssl=0x" + Long.toString(sslNativePointer, 16) + " onSSLStateChange");
+            }
+            this.handshakeCompletedCalled = true;
+        }
+
+        public Socket getSocket() {
+            return socket;
+        }
+
+        private boolean clientPSKKeyRequestedInvoked;
+        private String clientPSKKeyRequestedIdentityHint;
+        private int clientPSKKeyRequestedResult;
+        private byte[] clientPSKKeyRequestedResultKey;
+        private byte[] clientPSKKeyRequestedResultIdentity;
+
+        @Override
+        public int clientPSKKeyRequested(String identityHint, byte[] identity, byte[] key) {
+            if (DEBUG) {
+                System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+                        + " clientPSKKeyRequested"
+                        + " identityHint=" + identityHint + " identity capacity=" + identity.length
+                        + " key capacity=" + key.length);
+            }
+            clientPSKKeyRequestedInvoked = true;
+            clientPSKKeyRequestedIdentityHint = identityHint;
+            if (clientPSKKeyRequestedResultKey != null) {
+                System.arraycopy(clientPSKKeyRequestedResultKey, 0, key, 0,
+                        clientPSKKeyRequestedResultKey.length);
+            }
+            if (clientPSKKeyRequestedResultIdentity != null) {
+                System.arraycopy(clientPSKKeyRequestedResultIdentity, 0, identity, 0,
+                        Math.min(clientPSKKeyRequestedResultIdentity.length, identity.length));
+            }
+            return clientPSKKeyRequestedResult;
+        }
+
+        private boolean serverPSKKeyRequestedInvoked;
+        private int serverPSKKeyRequestedResult;
+        private byte[] serverPSKKeyRequestedResultKey;
+        private String serverPSKKeyRequestedIdentityHint;
+        private String serverPSKKeyRequestedIdentity;
+        @Override
+        public int serverPSKKeyRequested(String identityHint, String identity, byte[] key) {
+            if (DEBUG) {
+                System.out.println("ssl=0x" + Long.toString(sslNativePointer, 16)
+                        + " serverPSKKeyRequested"
+                        + " identityHint=" + identityHint + " identity=" + identity
+                        + " key capacity=" + key.length);
+            }
+            serverPSKKeyRequestedInvoked = true;
+            serverPSKKeyRequestedIdentityHint = identityHint;
+            serverPSKKeyRequestedIdentity = identity;
+            if (serverPSKKeyRequestedResultKey != null) {
+                System.arraycopy(serverPSKKeyRequestedResultKey, 0, key, 0,
+                        serverPSKKeyRequestedResultKey.length);
+            }
+            return serverPSKKeyRequestedResult;
+        }
+    }
+
+    public static class ClientHooks extends Hooks {
+        protected String pskIdentity;
+
+        @Override
+        public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+            super.configureCallbacks(callbacks);
+            if (pskEnabled) {
+                if (pskIdentity != null) {
+                    // Create a NULL-terminated modified UTF-8 representation of pskIdentity.
+                    byte[] b;
+                    try {
+                        b = pskIdentity.getBytes("UTF-8");
+                    } catch (UnsupportedEncodingException e) {
+                        throw new RuntimeException("UTF-8 encoding not supported", e);
+                    }
+                    callbacks.clientPSKKeyRequestedResultIdentity = Arrays.copyOf(b, b.length + 1);
+                }
+                callbacks.clientPSKKeyRequestedResultKey = pskKey;
+                callbacks.clientPSKKeyRequestedResult = (pskKey != null) ? pskKey.length : 0;
+            }
+        }
+
+        @Override
+        public long beforeHandshake(long c) throws SSLException {
+            long s = super.beforeHandshake(c);
+            if (pskEnabled) {
+                NativeCrypto.set_SSL_psk_client_callback_enabled(s, true);
+            }
+            return s;
+        }
+    }
+
+    public static class ServerHooks extends Hooks {
+        private final OpenSSLKey privateKey;
+        private final long[] certificates;
+        private boolean channelIdEnabled;
+        private byte[] channelIdAfterHandshake;
+        private Throwable channelIdAfterHandshakeException;
+
+        protected String pskIdentityHint;
+
+        public ServerHooks() {
+            this(null, null);
+        }
+
+        public ServerHooks(OpenSSLKey privateKey, long[] certificates) {
+            this.privateKey = privateKey;
+            this.certificates = certificates;
+        }
+
+        @Override
+        public long beforeHandshake(long c) throws SSLException {
+            long s = super.beforeHandshake(c);
+            if (privateKey != null) {
+                NativeCrypto.SSL_use_PrivateKey(s, privateKey.getNativeRef());
+            }
+            if (certificates != null) {
+                NativeCrypto.SSL_use_certificate(s, certificates);
+            }
+            if (channelIdEnabled) {
+                NativeCrypto.SSL_enable_tls_channel_id(s);
+            }
+            if (pskEnabled) {
+                NativeCrypto.set_SSL_psk_server_callback_enabled(s, true);
+                NativeCrypto.SSL_use_psk_identity_hint(s, pskIdentityHint);
+            }
+            NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_NONE);
+            return s;
+        }
+
+        @Override
+        public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+            super.configureCallbacks(callbacks);
+            if (pskEnabled) {
+                callbacks.serverPSKKeyRequestedResultKey = pskKey;
+                callbacks.serverPSKKeyRequestedResult = (pskKey != null) ? pskKey.length : 0;
+            }
+        }
+
+        @Override
+        public void afterHandshake(long session, long ssl, long context, Socket socket,
+                FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+            if (channelIdEnabled) {
+                try {
+                    channelIdAfterHandshake = NativeCrypto.SSL_get_tls_channel_id(ssl);
+                } catch (Exception e) {
+                    channelIdAfterHandshakeException = e;
+                }
+            }
+            super.afterHandshake(session, ssl, context, socket, fd, callback);
+        }
+
+        @Override
+        public void clientCertificateRequested(long s) {
+            fail("Server asked for client certificates");
+        }
+    }
+
+    public static Future<TestSSLHandshakeCallbacks> handshake(final ServerSocket listener,
+            final int timeout, final boolean client, final Hooks hooks,
+            final byte[] alpnProtocols) {
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        Future<TestSSLHandshakeCallbacks> future =
+                executor.submit(new Callable<TestSSLHandshakeCallbacks>() {
+                    @Override
+                    public TestSSLHandshakeCallbacks call() throws Exception {
+                        @SuppressWarnings("resource")
+                        // Socket needs to remain open after the handshake
+                        Socket socket = (client ? new Socket(listener.getInetAddress(),
+                                                          listener.getLocalPort())
+                                                : listener.accept());
+                        if (timeout == -1) {
+                            return new TestSSLHandshakeCallbacks(socket, 0, null);
+                        }
+                        FileDescriptor fd =
+                                (FileDescriptor) m_Platform_getFileDescriptor.invoke(null, socket);
+                        long c = hooks.getContext();
+                        long s = hooks.beforeHandshake(c);
+                        TestSSLHandshakeCallbacks callback =
+                                new TestSSLHandshakeCallbacks(socket, s, hooks);
+                        hooks.configureCallbacks(callback);
+                        if (DEBUG) {
+                            System.out.println("ssl=0x" + Long.toString(s, 16) + " handshake"
+                                    + " context=0x" + Long.toString(c, 16) + " socket=" + socket
+                                    + " fd=" + fd + " timeout=" + timeout + " client=" + client);
+                        }
+                        long session = NULL;
+                        try {
+                            if (client) {
+                                NativeCrypto.SSL_set_connect_state(s);
+                            } else {
+                                NativeCrypto.SSL_set_accept_state(s);
+                            }
+                            NativeCrypto.SSL_configure_alpn(s, client, alpnProtocols);
+                            NativeCrypto.SSL_do_handshake(s, fd, callback, timeout);
+                            session = NativeCrypto.SSL_get1_session(s);
+                            if (DEBUG) {
+                                System.out.println("ssl=0x" + Long.toString(s, 16) + " handshake"
+                                        + " session=0x" + Long.toString(session, 16));
+                            }
+                        } finally {
+                            // Ensure afterHandshake is called to free resources
+                            hooks.afterHandshake(session, s, c, socket, fd, callback);
+                        }
+                        return callback;
+                    }
+                });
+        executor.shutdown();
+        return future;
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_SSL_do_handshake_NULL_SSL() throws Exception {
+        NativeCrypto.SSL_do_handshake(NULL, null, null, 0);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_null_args() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+        NativeCrypto.SSL_set_connect_state(s);
+
+        try {
+            NativeCrypto.SSL_do_handshake(s, null, null, 0);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            NativeCrypto.SSL_do_handshake(s, INVALID_FD, null, 0);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_normal() throws Exception {
+        // normal client and server case
+        final ServerSocket listener = new ServerSocket(0);
+        Hooks cHooks = new Hooks();
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(getServerCertificates(), clientCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback.authMethod);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_optional_client_certificate() throws Exception {
+        // optional client certificate case
+        final ServerSocket listener = new ServerSocket(0);
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void clientCertificateRequested(long s) {
+                super.clientCertificateRequested(s);
+                NativeCrypto.SSL_use_PrivateKey(s, getClientPrivateKey().getNativeRef());
+                NativeCrypto.SSL_use_certificate(s, getClientCertificates());
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_set_client_CA_list(s, getCaPrincipals());
+                NativeCrypto.SSL_set_verify(s, NativeCrypto.SSL_VERIFY_PEER);
+                return s;
+            }
+        };
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(getServerCertificates(), clientCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback.authMethod);
+        assertTrue(serverCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(getClientCertificates(), serverCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", serverCallback.authMethod);
+
+        assertTrue(clientCallback.clientCertificateRequestedCalled);
+        assertNotNull(clientCallback.keyTypes);
+        assertEquals(new HashSet<String>(Arrays.asList("EC", "RSA")),
+                SSLParametersImpl.getSupportedClientKeyTypes(clientCallback.keyTypes));
+        assertEqualPrincipals(getCaPrincipals(), clientCallback.asn1DerEncodedX500Principals);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+
+        assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_missing_required_certificate() throws Exception {
+        // required client certificate negative case
+        final ServerSocket listener = new ServerSocket(0);
+        try {
+            Hooks cHooks = new Hooks();
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+                @Override
+                public long beforeHandshake(long c) throws SSLException {
+                    long s = super.beforeHandshake(c);
+                    NativeCrypto.SSL_set_client_CA_list(s, getCaPrincipals());
+                    NativeCrypto.SSL_set_verify(s,
+                            NativeCrypto.SSL_VERIFY_PEER
+                                    | NativeCrypto.SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+                    return s;
+                }
+            };
+            @SuppressWarnings("unused")
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_client_timeout() throws Exception {
+        // client timeout
+        final ServerSocket listener = new ServerSocket(0);
+        Socket serverSocket = null;
+        try {
+            Hooks cHooks = new Hooks();
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 1, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, -1, false, sHooks, null);
+            serverSocket = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).getSocket();
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            if (SocketTimeoutException.class != expected.getCause().getClass()) {
+                expected.printStackTrace();
+            }
+            assertEquals(SocketTimeoutException.class, expected.getCause().getClass());
+        } finally {
+            // Manually close peer socket when testing timeout
+            IoUtils.closeQuietly(serverSocket);
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_server_timeout() throws Exception {
+        // server timeout
+        final ServerSocket listener = new ServerSocket(0);
+        Socket clientSocket = null;
+        try {
+            Hooks cHooks = new Hooks();
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, -1, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 1, false, sHooks, null);
+            clientSocket = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).getSocket();
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SocketTimeoutException.class, expected.getCause().getClass());
+        } finally {
+            // Manually close peer socket when testing timeout
+            IoUtils.closeQuietly(clientSocket);
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_channel_id_normal() throws Exception {
+        initChannelIdKey();
+
+        // Normal handshake with TLS Channel ID.
+        final ServerSocket listener = new ServerSocket(0);
+        Hooks cHooks = new Hooks();
+        cHooks.channelIdPrivateKey = CHANNEL_ID_PRIVATE_KEY;
+        // TLS Channel ID currently requires ECDHE-based key exchanges.
+        cHooks.enabledCipherSuites = Arrays.asList(new String[] {"ECDHE-RSA-AES128-SHA"});
+        ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+        sHooks.channelIdEnabled = true;
+        sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(getServerCertificates(), clientCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback.authMethod);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertNull(sHooks.channelIdAfterHandshakeException);
+        assertEqualByteArrays(CHANNEL_ID, sHooks.channelIdAfterHandshake);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_channel_id_not_supported_by_server() throws Exception {
+        initChannelIdKey();
+
+        // Client tries to use TLS Channel ID but the server does not enable/offer the extension.
+        final ServerSocket listener = new ServerSocket(0);
+        Hooks cHooks = new Hooks();
+        cHooks.channelIdPrivateKey = CHANNEL_ID_PRIVATE_KEY;
+        // TLS Channel ID currently requires ECDHE-based key exchanges.
+        cHooks.enabledCipherSuites = Arrays.asList(new String[] {"ECDHE-RSA-AES128-SHA"});
+        ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+        sHooks.channelIdEnabled = false;
+        sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(getServerCertificates(), clientCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback.authMethod);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertNull(sHooks.channelIdAfterHandshakeException);
+        assertNull(sHooks.channelIdAfterHandshake);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_channel_id_not_enabled_by_client() throws Exception {
+        initChannelIdKey();
+
+        // Client does not use TLS Channel ID when the server has the extension enabled/offered.
+        final ServerSocket listener = new ServerSocket(0);
+        Hooks cHooks = new Hooks();
+        cHooks.channelIdPrivateKey = null;
+        // TLS Channel ID currently requires ECDHE-based key exchanges.
+        cHooks.enabledCipherSuites = Arrays.asList(new String[] {"ECDHE-RSA-AES128-SHA"});
+        ServerHooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+        sHooks.channelIdEnabled = true;
+        sHooks.enabledCipherSuites = cHooks.enabledCipherSuites;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue(clientCallback.verifyCertificateChainCalled);
+        assertEqualCertificateChains(getServerCertificates(), clientCallback.certificateChainRefs);
+        assertEquals("ECDHE_RSA", clientCallback.authMethod);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertFalse(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.serverPSKKeyRequestedInvoked);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertNull(sHooks.channelIdAfterHandshakeException);
+        assertNull(sHooks.channelIdAfterHandshake);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_normal() throws Exception {
+        // normal TLS-PSK client and server case
+        final ServerSocket listener = new ServerSocket(0);
+        Hooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = cHooks.pskKey;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertFalse(clientCallback.verifyCertificateChainCalled);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
+        assertContains(cHooks.negotiatedCipherSuite, "PSK");
+        assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
+        assertNull(clientCallback.clientPSKKeyRequestedIdentityHint);
+        assertNull(serverCallback.serverPSKKeyRequestedIdentityHint);
+        assertEquals("", serverCallback.serverPSKKeyRequestedIdentity);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_with_identity_and_hint() throws Exception {
+        // normal TLS-PSK client and server case where the server provides the client with a PSK
+        // identity hint, and the client provides the server with a PSK identity.
+        final ServerSocket listener = new ServerSocket(0);
+        ClientHooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = cHooks.pskKey;
+        sHooks.pskIdentityHint = "Some non-ASCII characters: \u00c4\u0332";
+        cHooks.pskIdentity = "More non-ASCII characters: \u00f5\u044b";
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertFalse(clientCallback.verifyCertificateChainCalled);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
+        assertContains(cHooks.negotiatedCipherSuite, "PSK");
+        assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
+        assertEquals(sHooks.pskIdentityHint, clientCallback.clientPSKKeyRequestedIdentityHint);
+        assertEquals(sHooks.pskIdentityHint, serverCallback.serverPSKKeyRequestedIdentityHint);
+        assertEquals(cHooks.pskIdentity, serverCallback.serverPSKKeyRequestedIdentity);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_with_identity_and_hint_of_max_length()
+            throws Exception {
+        // normal TLS-PSK client and server case where the server provides the client with a PSK
+        // identity hint, and the client provides the server with a PSK identity.
+        final ServerSocket listener = new ServerSocket(0);
+        ClientHooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = cHooks.pskKey;
+        sHooks.pskIdentityHint = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
+                + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx";
+        cHooks.pskIdentity = "123456789012345678901234567890123456789012345678901234567890"
+                + "12345678901234567890123456789012345678901234567890123456789012345678";
+        assertEquals(PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES, sHooks.pskIdentityHint.length());
+        assertEquals(PSKKeyManager.MAX_IDENTITY_LENGTH_BYTES, cHooks.pskIdentity.length());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertFalse(clientCallback.verifyCertificateChainCalled);
+        assertFalse(serverCallback.verifyCertificateChainCalled);
+        assertFalse(clientCallback.clientCertificateRequestedCalled);
+        assertFalse(serverCallback.clientCertificateRequestedCalled);
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+        assertTrue(clientCallback.clientPSKKeyRequestedInvoked);
+        assertFalse(clientCallback.serverPSKKeyRequestedInvoked);
+        assertFalse(serverCallback.clientPSKKeyRequestedInvoked);
+        assertTrue(serverCallback.serverPSKKeyRequestedInvoked);
+        assertContains(cHooks.negotiatedCipherSuite, "PSK");
+        assertEquals(cHooks.negotiatedCipherSuite, sHooks.negotiatedCipherSuite);
+        assertEquals(sHooks.pskIdentityHint, clientCallback.clientPSKKeyRequestedIdentityHint);
+        assertEquals(sHooks.pskIdentityHint, serverCallback.serverPSKKeyRequestedIdentityHint);
+        assertEquals(cHooks.pskIdentity, serverCallback.serverPSKKeyRequestedIdentity);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_key_mismatch() throws Exception {
+        final ServerSocket listener = new ServerSocket(0);
+        ClientHooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = "1, 2, 3, 3, Testing...".getBytes("UTF-8");
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        try {
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_with_no_client_key() throws Exception {
+        final ServerSocket listener = new ServerSocket(0);
+        ClientHooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = null;
+        sHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        try {
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_with_no_server_key() throws Exception {
+        final ServerSocket listener = new ServerSocket(0);
+        ClientHooks cHooks = new ClientHooks();
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = null;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        try {
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_psk_key_too_long() throws Exception {
+        final ServerSocket listener = new ServerSocket(0);
+        ClientHooks cHooks = new ClientHooks() {
+            @Override
+            public void configureCallbacks(TestSSLHandshakeCallbacks callbacks) {
+                super.configureCallbacks(callbacks);
+                callbacks.clientPSKKeyRequestedResult = PSKKeyManager.MAX_KEY_LENGTH_BYTES + 1;
+            }
+        };
+        ServerHooks sHooks = new ServerHooks();
+        cHooks.pskEnabled = true;
+        sHooks.pskEnabled = true;
+        cHooks.pskKey = "1, 2, 3, 4, Testing...".getBytes("UTF-8");
+        sHooks.pskKey = cHooks.pskKey;
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        try {
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_ocsp_response() throws Exception {
+        final byte[] OCSP_TEST_DATA = new byte[] {1, 2, 3, 4};
+
+        final ServerSocket listener = new ServerSocket(0);
+        Hooks cHooks = new Hooks() {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_enable_ocsp_stapling(s);
+                return s;
+            }
+
+            @Override
+            public void afterHandshake(long session, long ssl, long context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                assertEqualByteArrays(OCSP_TEST_DATA, NativeCrypto.SSL_get_ocsp_response(ssl));
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_set_ocsp_response(s, OCSP_TEST_DATA);
+                return s;
+            }
+        };
+
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+    }
+
+    @Test
+    public void test_SSL_do_handshake_with_sct_extension() throws Exception {
+        // Fake SCT extension has a length of overall extension (unsigned 16-bit).
+        // Each SCT entry has a length (unsigned 16-bit) and data.
+        final byte[] SCT_TEST_DATA = new byte[] {0, 6, 0, 4, 1, 2, 3, 4};
+
+        final ServerSocket listener = new ServerSocket(0);
+        Hooks cHooks = new Hooks() {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_enable_signed_cert_timestamps(s);
+                return s;
+            }
+
+            @Override
+            public void afterHandshake(long session, long ssl, long context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                assertEqualByteArrays(
+                        SCT_TEST_DATA, NativeCrypto.SSL_get_signed_cert_timestamp_list(ssl));
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_set_signed_cert_timestamp_list(s, SCT_TEST_DATA);
+                return s;
+            }
+        };
+
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        TestSSLHandshakeCallbacks clientCallback = client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        TestSSLHandshakeCallbacks serverCallback = server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+
+        assertTrue(clientCallback.handshakeCompletedCalled);
+        assertTrue(serverCallback.handshakeCompletedCalled);
+    }
+
+    @Test
+    public void test_SSL_use_psk_identity_hint() throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+        try {
+            NativeCrypto.SSL_use_psk_identity_hint(s, null);
+            NativeCrypto.SSL_use_psk_identity_hint(s, "test");
+
+            try {
+                // 800 characters is much longer than the permitted maximum.
+                StringBuilder pskIdentityHint = new StringBuilder();
+                for (int i = 0; i < 160; i++) {
+                    pskIdentityHint.append(" long");
+                }
+                assertTrue(pskIdentityHint.length() > PSKKeyManager.MAX_IDENTITY_HINT_LENGTH_BYTES);
+                NativeCrypto.SSL_use_psk_identity_hint(s, pskIdentityHint.toString());
+                fail();
+            } catch (SSLException expected) {
+            }
+        } finally {
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+    }
+
+    @Test
+    public void test_SSL_set_session() throws Exception {
+        try {
+            NativeCrypto.SSL_set_session(NULL, NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+            NativeCrypto.SSL_set_session(s, NULL);
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        {
+            final long clientContext = NativeCrypto.SSL_CTX_new();
+            final long serverContext = NativeCrypto.SSL_CTX_new();
+            final ServerSocket listener = new ServerSocket(0);
+            final long[] clientSession = new long[] {NULL};
+            final long[] serverSession = new long[] {NULL};
+            {
+                Hooks cHooks = new Hooks() {
+                    @Override
+                    public long getContext() throws SSLException {
+                        return clientContext;
+                    }
+                    @Override
+                    public void afterHandshake(long session, long s, long c, Socket sock,
+                            FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                        super.afterHandshake(NULL, s, NULL, sock, fd, callback);
+                        clientSession[0] = session;
+                    }
+                };
+                Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+                    @Override
+                    public long getContext() throws SSLException {
+                        return serverContext;
+                    }
+                    @Override
+                    public void afterHandshake(long session, long s, long c, Socket sock,
+                            FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                        super.afterHandshake(NULL, s, NULL, sock, fd, callback);
+                        serverSession[0] = session;
+                    }
+                };
+                Future<TestSSLHandshakeCallbacks> client =
+                        handshake(listener, 0, true, cHooks, null);
+                Future<TestSSLHandshakeCallbacks> server =
+                        handshake(listener, 0, false, sHooks, null);
+                client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            }
+            assertEqualSessions(clientSession[0], serverSession[0]);
+            {
+                Hooks cHooks = new Hooks() {
+                    @Override
+                    public long getContext() throws SSLException {
+                        return clientContext;
+                    }
+                    @Override
+                    public long beforeHandshake(long c) throws SSLException {
+                        long s = NativeCrypto.SSL_new(clientContext);
+                        NativeCrypto.SSL_set_session(s, clientSession[0]);
+                        return s;
+                    }
+                    @Override
+                    public void afterHandshake(long session, long s, long c, Socket sock,
+                            FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                        assertEqualSessions(clientSession[0], session);
+                        super.afterHandshake(NULL, s, NULL, sock, fd, callback);
+                    }
+                };
+                Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+                    @Override
+                    public long getContext() throws SSLException {
+                        return serverContext;
+                    }
+                    @Override
+                    public void afterHandshake(long session, long s, long c, Socket sock,
+                            FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                        assertEqualSessions(serverSession[0], session);
+                        super.afterHandshake(NULL, s, NULL, sock, fd, callback);
+                    }
+                };
+                Future<TestSSLHandshakeCallbacks> client =
+                        handshake(listener, 0, true, cHooks, null);
+                Future<TestSSLHandshakeCallbacks> server =
+                        handshake(listener, 0, false, sHooks, null);
+                client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            }
+            NativeCrypto.SSL_SESSION_free(clientSession[0]);
+            NativeCrypto.SSL_SESSION_free(serverSession[0]);
+            NativeCrypto.SSL_CTX_free(serverContext);
+            NativeCrypto.SSL_CTX_free(clientContext);
+        }
+    }
+
+    @Test
+    public void test_SSL_set_session_creation_enabled() throws Exception {
+        try {
+            NativeCrypto.SSL_set_session_creation_enabled(NULL, false);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+            NativeCrypto.SSL_set_session_creation_enabled(s, false);
+            NativeCrypto.SSL_set_session_creation_enabled(s, true);
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+
+        // negative test case for SSL_set_session_creation_enabled(false) on client
+        {
+            Hooks cHooks = new Hooks() {
+                @Override
+                public long beforeHandshake(long c) throws SSLException {
+                    long s = super.beforeHandshake(c);
+                    NativeCrypto.SSL_set_session_creation_enabled(s, false);
+                    return s;
+                }
+            };
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            @SuppressWarnings("unused")
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            try {
+                client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                fail();
+            } catch (ExecutionException expected) {
+                assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+            }
+            try {
+                server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                fail();
+            } catch (ExecutionException expected) {
+                assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+            }
+        }
+
+        // negative test case for SSL_set_session_creation_enabled(false) on server
+        {
+            Hooks cHooks = new Hooks();
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+                @Override
+                public long beforeHandshake(long c) throws SSLException {
+                    long s = super.beforeHandshake(c);
+                    NativeCrypto.SSL_set_session_creation_enabled(s, false);
+                    return s;
+                }
+            };
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            @SuppressWarnings("unused")
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            try {
+                client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                fail();
+            } catch (ExecutionException expected) {
+                assertEquals(SSLHandshakeException.class, expected.getCause().getClass());
+            }
+            try {
+                server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+                fail();
+            } catch (ExecutionException expected) {
+                assertEquals(SSLProtocolException.class, expected.getCause().getClass());
+            }
+        }
+    }
+
+    @Test
+    public void test_SSL_set_tlsext_host_name() throws Exception {
+        // NULL SSL
+        try {
+            NativeCrypto.SSL_set_tlsext_host_name(NULL, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final String hostname = "www.android.com";
+
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+
+            // null hostname
+            try {
+                NativeCrypto.SSL_set_tlsext_host_name(s, null);
+                fail();
+            } catch (NullPointerException expected) {
+            }
+
+            // too long hostname
+            try {
+                char[] longHostname = new char[256];
+                Arrays.fill(longHostname, 'w');
+                NativeCrypto.SSL_set_tlsext_host_name(s, new String(longHostname));
+                fail();
+            } catch (SSLException expected) {
+            }
+
+            assertNull(NativeCrypto.SSL_get_servername(s));
+            NativeCrypto.SSL_set_tlsext_host_name(s, new String(hostname));
+            assertEquals(hostname, NativeCrypto.SSL_get_servername(s));
+
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+
+        // normal
+        Hooks cHooks = new Hooks() {
+            @Override
+            public long beforeHandshake(long c) throws SSLException {
+                long s = super.beforeHandshake(c);
+                NativeCrypto.SSL_set_tlsext_host_name(s, hostname);
+                return s;
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                assertEquals(hostname, NativeCrypto.SSL_get_servername(s));
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void test_SSL_AlpnNegotiateSuccess() throws Exception {
+        final byte[] clientAlpnProtocols = new byte[] {
+                8, 'h', 't', 't', 'p', '/', '1', '.', '1', 3, 'f', 'o', 'o', 6, 's', 'p', 'd', 'y',
+                '/', '2',
+        };
+        final byte[] serverAlpnProtocols = new byte[] {
+                6, 's', 'p', 'd', 'y', '/', '2', 3, 'f', 'o', 'o', 3, 'b', 'a', 'r',
+        };
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long ssl, long context, Socket socket,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.SSL_get0_alpn_selected(ssl);
+                assertEquals("spdy/2", new String(negotiated));
+                super.afterHandshake(session, ssl, context, socket, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+            @Override
+            public void afterHandshake(long session, long ssl, long c, Socket sock,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                byte[] negotiated = NativeCrypto.SSL_get0_alpn_selected(ssl);
+                assertEquals("spdy/2", new String(negotiated));
+                super.afterHandshake(session, ssl, c, sock, fd, callback);
+            }
+        };
+
+        ServerSocket listener = new ServerSocket(0);
+        Future<TestSSLHandshakeCallbacks> client =
+                handshake(listener, 0, true, cHooks, clientAlpnProtocols);
+        Future<TestSSLHandshakeCallbacks> server =
+                handshake(listener, 0, false, sHooks, serverAlpnProtocols);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void test_SSL_get_servername_null() throws Exception {
+        // NULL SSL
+        try {
+            NativeCrypto.SSL_get_servername(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+        assertNull(NativeCrypto.SSL_get_servername(s));
+        NativeCrypto.SSL_free(s);
+        NativeCrypto.SSL_CTX_free(c);
+
+        // additional positive testing by test_SSL_set_tlsext_host_name
+    }
+
+    @Test
+    public void test_SSL_renegotiate_fails() throws Exception {
+        try {
+            NativeCrypto.SSL_renegotiate(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                byte[] buffer = new byte[1];
+                NativeCrypto.SSL_read(s, fd, callback, buffer, 0, 1, 0);
+                assertEquals(42, buffer[0]);
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                try {
+                    // Ensure that BoringSSL throws an exception if you try to manually trigger
+                    // SSL renegotiation, which is what we expect.  The fail() may not directly
+                    // cause the test to fail, because we may be in a separate thread from the main
+                    // test thread, but the SSL_write() call that follows is required for the
+                    // test to pass.
+                    NativeCrypto.SSL_renegotiate(s);
+                    fail();
+                } catch (SSLException expected) {
+                }
+                NativeCrypto.SSL_write(s, fd, callback, new byte[] {42}, 0, 1, 0);
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void test_SSL_get_certificate() throws Exception {
+        try {
+            NativeCrypto.SSL_get_certificate(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                assertNull(NativeCrypto.SSL_get_certificate(s));
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                assertEqualCertificateChains(
+                        getServerCertificates(), NativeCrypto.SSL_get_certificate(s));
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void test_SSL_get_peer_cert_chain() throws Exception {
+        try {
+            NativeCrypto.SSL_get_peer_cert_chain(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                long[] cc = NativeCrypto.SSL_get_peer_cert_chain(s);
+                assertEqualCertificateChains(getServerCertificates(), cc);
+                for (long ref : cc) {
+                    NativeCrypto.X509_free(ref);
+                }
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    final byte[] BYTES = new byte[] {2, -3, 5, 127, 0, -128};
+
+    @Test
+    public void test_SSL_read() throws Exception {
+        // NULL ssl
+        try {
+            NativeCrypto.SSL_read(NULL, null, null, null, 0, 0, 0);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        // null FileDescriptor
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+            try {
+                NativeCrypto.SSL_read(s, null, DUMMY_CB, null, 0, 0, 0);
+                fail();
+            } catch (NullPointerException expected) {
+            }
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        // null SSLHandshakeCallbacks
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+            try {
+                NativeCrypto.SSL_read(s, INVALID_FD, null, null, 0, 0, 0);
+                fail();
+            } catch (NullPointerException expected) {
+            }
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        // null byte array
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+            try {
+                NativeCrypto.SSL_read(s, INVALID_FD, DUMMY_CB, null, 0, 0, 0);
+                fail();
+            } catch (NullPointerException expected) {
+            }
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        // handshaking not yet performed
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+            try {
+                NativeCrypto.SSL_read(s, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0);
+                fail();
+            } catch (SSLException expected) {
+            }
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+
+        // normal case
+        {
+            Hooks cHooks = new Hooks() {
+                @Override
+                public void afterHandshake(long session, long s, long c, Socket sock,
+                        FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                    byte[] in = new byte[256];
+                    assertEquals(BYTES.length,
+                            NativeCrypto.SSL_read(s, fd, callback, in, 0, BYTES.length, 0));
+                    for (int i = 0; i < BYTES.length; i++) {
+                        assertEquals(BYTES[i], in[i]);
+                    }
+                    super.afterHandshake(session, s, c, sock, fd, callback);
+                }
+            };
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+                @Override
+                public void afterHandshake(long session, long s, long c, Socket sock,
+                        FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                    NativeCrypto.SSL_write(s, fd, callback, BYTES, 0, BYTES.length, 0);
+                    super.afterHandshake(session, s, c, sock, fd, callback);
+                }
+            };
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        }
+
+        // timeout case
+        try {
+            Hooks cHooks = new Hooks() {
+                @Override
+                public void afterHandshake(long session, long s, long c, Socket sock,
+                        FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                    NativeCrypto.SSL_read(s, fd, callback, new byte[1], 0, 1, 1);
+                    fail();
+                }
+            };
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+                @Override
+                public void afterHandshake(long session, long s, long c, Socket sock,
+                        FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                    NativeCrypto.SSL_read(s, fd, callback, new byte[1], 0, 1, 0);
+                    super.afterHandshake(session, s, c, sock, fd, callback);
+                }
+            };
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            @SuppressWarnings("unused")
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            fail();
+        } catch (ExecutionException expected) {
+            assertEquals(SocketTimeoutException.class, expected.getCause().getClass());
+        }
+    }
+
+    @Test
+    public void test_SSL_write() throws Exception {
+        try {
+            NativeCrypto.SSL_write(NULL, null, null, null, 0, 0, 0);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        // null FileDescriptor
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+            try {
+                NativeCrypto.SSL_write(s, null, DUMMY_CB, null, 0, 1, 0);
+                fail();
+            } catch (NullPointerException expected) {
+            }
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        // null SSLHandshakeCallbacks
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+            try {
+                NativeCrypto.SSL_write(s, INVALID_FD, null, null, 0, 1, 0);
+                fail();
+            } catch (NullPointerException expected) {
+            }
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        // null byte array
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+            try {
+                NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, null, 0, 1, 0);
+                fail();
+            } catch (NullPointerException expected) {
+            }
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        // handshaking not yet performed
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+            try {
+                NativeCrypto.SSL_write(s, INVALID_FD, DUMMY_CB, new byte[1], 0, 1, 0);
+                fail();
+            } catch (SSLException expected) {
+            }
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        // positively tested by test_SSL_read
+    }
+
+    @Test
+    public void test_SSL_interrupt() throws Exception {
+        // SSL_interrupt is a rare case that tolerates a null SSL argument
+        NativeCrypto.SSL_interrupt(NULL);
+
+        // also works without handshaking
+        {
+            long c = NativeCrypto.SSL_CTX_new();
+            long s = NativeCrypto.SSL_new(c);
+            NativeCrypto.SSL_interrupt(s);
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                NativeCrypto.SSL_read(s, fd, callback, new byte[1], 0, 1, 0);
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates()) {
+            @Override
+            public void afterHandshake(long session, final long s, long c, Socket sock,
+                    FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                new Thread() {
+                    @Override
+                    public void run() {
+                        try {
+                            Thread.sleep(1 * 1000);
+                            NativeCrypto.SSL_interrupt(s);
+                        } catch (Exception e) {
+                        }
+                    }
+                }.start();
+                assertEquals(-1, NativeCrypto.SSL_read(s, fd, callback, new byte[1], 0, 1, 0));
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    private static abstract class SSLSessionWrappedTask {
+        public abstract void run(long sslSession) throws Exception;
+    }
+
+    private void wrapWithSSLSession(SSLSessionWrappedTask task) throws Exception {
+        long c = NativeCrypto.SSL_CTX_new();
+        long s = NativeCrypto.SSL_new(c);
+        try {
+            task.run(s);
+        } finally {
+            NativeCrypto.SSL_free(s);
+            NativeCrypto.SSL_CTX_free(c);
+        }
+    }
+
+    @Test
+    public void test_SSL_shutdown() throws Exception {
+        // null FileDescriptor
+        wrapWithSSLSession(new SSLSessionWrappedTask() {
+            @Override
+            public void run(long sslSession) throws Exception {
+                try {
+                    NativeCrypto.SSL_shutdown(sslSession, null, DUMMY_CB);
+                    fail();
+                } catch (NullPointerException expected) {
+                }
+            }
+        });
+
+        // null SSLHandshakeCallbacks
+        wrapWithSSLSession(new SSLSessionWrappedTask() {
+            @Override
+            public void run(long sslSession) throws Exception {
+                try {
+                    NativeCrypto.SSL_shutdown(sslSession, INVALID_FD, null);
+                    fail();
+                } catch (NullPointerException expected) {
+                }
+            }
+        });
+
+        // SSL_shutdown is a rare case that tolerates a null SSL argument
+        NativeCrypto.SSL_shutdown(NULL, INVALID_FD, DUMMY_CB);
+
+        // handshaking not yet performed
+        wrapWithSSLSession(new SSLSessionWrappedTask() {
+            @Override
+            public void run(long sslSession) throws Exception {
+                try {
+                    NativeCrypto.SSL_shutdown(sslSession, INVALID_FD, DUMMY_CB);
+                    fail();
+                } catch (SocketException expected) {
+                }
+            }
+        });
+
+        // positively tested elsewhere because handshake uses use
+        // SSL_shutdown to ensure SSL_SESSIONs are reused.
+    }
+
+    @Test
+    public void test_SSL_free() throws Exception {
+        try {
+            NativeCrypto.SSL_free(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        long c = NativeCrypto.SSL_CTX_new();
+        NativeCrypto.SSL_free(NativeCrypto.SSL_new(c));
+        NativeCrypto.SSL_CTX_free(c);
+
+        // additional positive testing elsewhere because handshake
+        // uses use SSL_free to cleanup in afterHandshake.
+    }
+
+    @Test
+    public void test_SSL_SESSION_session_id() throws Exception {
+        try {
+            NativeCrypto.SSL_SESSION_session_id(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                byte[] id = NativeCrypto.SSL_SESSION_session_id(session);
+                assertNotNull(id);
+                assertEquals(32, id.length);
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void test_SSL_SESSION_get_time() throws Exception {
+        try {
+            NativeCrypto.SSL_SESSION_get_time(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+
+        {
+            Hooks cHooks = new Hooks() {
+                @Override
+                public void afterHandshake(long session, long s, long c, Socket sock,
+                        FileDescriptor fd, SSLHandshakeCallbacks callback) throws Exception {
+                    long time = NativeCrypto.SSL_SESSION_get_time(session);
+                    assertTrue(time != 0);
+                    assertTrue(time < System.currentTimeMillis());
+                    super.afterHandshake(session, s, c, sock, fd, callback);
+                }
+            };
+            Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+            Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+            Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+            client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        }
+    }
+
+    @Test
+    public void test_SSL_SESSION_get_version() throws Exception {
+        try {
+            NativeCrypto.SSL_SESSION_get_version(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                String v = NativeCrypto.SSL_SESSION_get_version(session);
+                assertTrue(StandardNames.SSL_SOCKET_PROTOCOLS.contains(v));
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void test_SSL_SESSION_cipher() throws Exception {
+        try {
+            NativeCrypto.SSL_SESSION_cipher(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                String a = NativeCrypto.SSL_SESSION_cipher(session);
+                assertTrue(NativeCrypto.OPENSSL_TO_STANDARD_CIPHER_SUITES.containsKey(a));
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    /*
+     * Additional positive testing elsewhere because handshake
+     * uses use SSL_SESSION_free to cleanup in afterHandshake.
+     */
+    @Test(expected = NullPointerException.class)
+    public void SSL_SESSION_free_NullArgument() throws Exception {
+        NativeCrypto.SSL_SESSION_free(NULL);
+    }
+
+    @Test
+    public void test_i2d_SSL_SESSION() throws Exception {
+        try {
+            NativeCrypto.i2d_SSL_SESSION(NULL);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        final ServerSocket listener = new ServerSocket(0);
+
+        Hooks cHooks = new Hooks() {
+            @Override
+            public void afterHandshake(long session, long s, long c, Socket sock, FileDescriptor fd,
+                    SSLHandshakeCallbacks callback) throws Exception {
+                byte[] b = NativeCrypto.i2d_SSL_SESSION(session);
+                assertNotNull(b);
+                long session2 = NativeCrypto.d2i_SSL_SESSION(b);
+                assertTrue(session2 != NULL);
+
+                // Make sure d2i_SSL_SESSION retores SSL_SESSION_cipher value http://b/7091840
+                assertTrue(NativeCrypto.SSL_SESSION_cipher(session2) != null);
+                assertEquals(NativeCrypto.SSL_SESSION_cipher(session),
+                        NativeCrypto.SSL_SESSION_cipher(session2));
+
+                NativeCrypto.SSL_SESSION_free(session2);
+                super.afterHandshake(session, s, c, sock, fd, callback);
+            }
+        };
+        Hooks sHooks = new ServerHooks(getServerPrivateKey(), getServerCertificates());
+        Future<TestSSLHandshakeCallbacks> client = handshake(listener, 0, true, cHooks, null);
+        Future<TestSSLHandshakeCallbacks> server = handshake(listener, 0, false, sHooks, null);
+        client.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        server.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void d2i_SSL_SESSION_NullArgument() throws Exception {
+        NativeCrypto.d2i_SSL_SESSION(null);
+    }
+
+    @Test(expected = IOException.class)
+    public void d2i_SSL_SESSION_EmptyArgument() throws Exception {
+        NativeCrypto.d2i_SSL_SESSION(new byte[0]);
+    }
+
+    @Test(expected = IOException.class)
+    public void d2i_SSL_SESSION_InvalidArgument() throws Exception {
+        NativeCrypto.d2i_SSL_SESSION(new byte[1]);
+    }
+
+    @Test
+    public void test_X509_NAME_hashes() {
+        // ensure these hash functions are stable over time since the
+        // /system/etc/security/cacerts CA filenames have to be
+        // consistent with the output.
+        X500Principal name = new X500Principal("CN=localhost");
+        assertEquals(-1372642656, NativeCrypto.X509_NAME_hash(name)); // SHA1
+        assertEquals(-1626170662, NativeCrypto.X509_NAME_hash_old(name)); // MD5
+    }
+
+    @Test
+    public void test_RAND_bytes_Success() throws Exception {
+        byte[] output = new byte[128];
+        NativeCrypto.RAND_bytes(output);
+
+        boolean isZero = true;
+        for (int i = 0; i < output.length; i++) {
+            isZero &= (output[i] == 0);
+        }
+
+        assertFalse("Random output was zero. This is a very low probability event (1 in 2^128) "
+                        + "and probably indicates an error.",
+                isZero);
+    }
+
+    @Test
+    public void test_RAND_bytes_Null_Failure() throws Exception {
+        byte[] output = null;
+        try {
+            NativeCrypto.RAND_bytes(output);
+            fail("Should be an error on null buffer input");
+        } catch (RuntimeException expected) {
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_EVP_get_digestbyname_NullArgument() throws Exception {
+        NativeCrypto.EVP_get_digestbyname(null);
+    }
+
+    @Test
+    public void test_EVP_get_digestbyname() throws Exception {
+        assertTrue(NativeCrypto.EVP_get_digestbyname("sha256") != NULL);
+
+        try {
+            NativeCrypto.EVP_get_digestbyname("");
+            NativeCrypto.EVP_get_digestbyname("foobar");
+            fail();
+        } catch (RuntimeException expected) {
+        }
+    }
+
+    @Test
+    public void test_EVP_DigestSignInit() throws Exception {
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        kpg.initialize(512);
+
+        KeyPair kp = kpg.generateKeyPair();
+        RSAPrivateCrtKey privKey = (RSAPrivateCrtKey) kp.getPrivate();
+
+        NativeRef.EVP_PKEY pkey;
+        pkey = new NativeRef.EVP_PKEY(NativeCrypto.EVP_PKEY_new_RSA(
+                privKey.getModulus().toByteArray(), privKey.getPublicExponent().toByteArray(),
+                privKey.getPrivateExponent().toByteArray(), privKey.getPrimeP().toByteArray(),
+                privKey.getPrimeQ().toByteArray(), privKey.getPrimeExponentP().toByteArray(),
+                privKey.getPrimeExponentQ().toByteArray(),
+                privKey.getCrtCoefficient().toByteArray()));
+        assertNotNull(pkey);
+
+        final NativeRef.EVP_MD_CTX ctx = new NativeRef.EVP_MD_CTX(NativeCrypto.EVP_MD_CTX_create());
+        long evpMd = NativeCrypto.EVP_get_digestbyname("sha256");
+        NativeCrypto.EVP_DigestSignInit(ctx, evpMd, pkey);
+
+        try {
+            NativeCrypto.EVP_DigestSignInit(ctx, 0, pkey);
+            fail();
+        } catch (RuntimeException expected) {
+        }
+
+        try {
+            NativeCrypto.EVP_DigestSignInit(ctx, evpMd, null);
+            fail();
+        } catch (RuntimeException expected) {
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void get_RSA_private_params_NullArgument() throws Exception {
+        NativeCrypto.get_RSA_private_params(null);
+    }
+
+    @Test
+    public void test_get_RSA_private_params() throws Exception {
+        // Test getting params for the wrong kind of key.
+        final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
+        assertFalse(groupCtx == NULL);
+        NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupCtx);
+        NativeRef.EVP_PKEY ctx = new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(group));
+        try {
+            NativeCrypto.get_RSA_private_params(ctx);
+            fail();
+        } catch (RuntimeException expected) {
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void get_RSA_public_params_NullArgument() throws Exception {
+        NativeCrypto.get_RSA_public_params(null);
+    }
+
+    @Test
+    public void test_get_RSA_public_params() throws Exception {
+        // Test getting params for the wrong kind of key.
+        final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
+        assertFalse(groupCtx == NULL);
+        NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupCtx);
+        NativeRef.EVP_PKEY ctx = new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(group));
+        try {
+            NativeCrypto.get_RSA_public_params(ctx);
+            fail();
+        } catch (RuntimeException expected) {
+        }
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void RSA_size_NullArgumentFailure() throws Exception {
+        NativeCrypto.RSA_size(null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void RSA_private_encrypt_NullArgumentFailure() throws Exception {
+        NativeCrypto.RSA_private_encrypt(0, new byte[0], new byte[0], null, 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void RSA_private_decrypt_NullArgumentFailure() throws Exception {
+        NativeCrypto.RSA_private_decrypt(0, new byte[0], new byte[0], null, 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_RSA_public_encrypt_NullArgumentFailure() throws Exception {
+        NativeCrypto.RSA_public_encrypt(0, new byte[0], new byte[0], null, 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_RSA_public_decrypt_NullArgumentFailure() throws Exception {
+        NativeCrypto.RSA_public_decrypt(0, new byte[0], new byte[0], null, 0);
+    }
+
+    /*
+     * Test vector generation:
+     * openssl rand -hex 16
+     */
+    private static final byte[] AES_128_KEY = new byte[] {
+            (byte) 0x3d, (byte) 0x4f, (byte) 0x89, (byte) 0x70, (byte) 0xb1, (byte) 0xf2,
+            (byte) 0x75, (byte) 0x37, (byte) 0xf4, (byte) 0x0a, (byte) 0x39, (byte) 0x29,
+            (byte) 0x8a, (byte) 0x41, (byte) 0x55, (byte) 0x5f,
+    };
+
+    @Test
+    public void testEC_GROUP() throws Exception {
+        /* Test using NIST's P-256 curve */
+        check_EC_GROUP("prime256v1",
+                "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
+                "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
+                "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
+                "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
+                "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
+                "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 1L);
+    }
+
+    private void check_EC_GROUP(String name, String pStr, String aStr, String bStr, String xStr,
+            String yStr, String nStr, long hLong) throws Exception {
+        long groupRef = NativeCrypto.EC_GROUP_new_by_curve_name(name);
+        assertFalse(groupRef == NULL);
+        NativeRef.EC_GROUP group = new NativeRef.EC_GROUP(groupRef);
+        assertEquals(NativeCrypto.OBJ_txt2nid_longName(name),
+                NativeCrypto.EC_GROUP_get_curve_name(group));
+
+        // prime
+        BigInteger p = new BigInteger(pStr, 16);
+        // first coefficient
+        BigInteger a = new BigInteger(aStr, 16);
+        // second coefficient
+        BigInteger b = new BigInteger(bStr, 16);
+        // x affine coordinate of generator
+        BigInteger x = new BigInteger(xStr, 16);
+        // y affine coordinate of generator
+        BigInteger y = new BigInteger(yStr, 16);
+        // order of the generator
+        BigInteger n = new BigInteger(nStr, 16);
+        // cofactor of generator
+        BigInteger h = BigInteger.valueOf(hLong);
+
+        byte[][] pab = NativeCrypto.EC_GROUP_get_curve(group);
+        assertEquals(3, pab.length);
+
+        BigInteger p2 = new BigInteger(pab[0]);
+        assertEquals(p, p2);
+
+        BigInteger a2 = new BigInteger(pab[1]);
+        assertEquals(a, a2);
+
+        BigInteger b2 = new BigInteger(pab[2]);
+        assertEquals(b, b2);
+
+        NativeRef.EC_POINT point =
+                new NativeRef.EC_POINT(NativeCrypto.EC_GROUP_get_generator(group));
+
+        byte[][] xy = NativeCrypto.EC_POINT_get_affine_coordinates(group, point);
+        assertEquals(2, xy.length);
+
+        BigInteger x2 = new BigInteger(xy[0]);
+        assertEquals(x, x2);
+
+        BigInteger y2 = new BigInteger(xy[1]);
+        assertEquals(y, y2);
+
+        BigInteger n2 = new BigInteger(NativeCrypto.EC_GROUP_get_order(group));
+        assertEquals(n, n2);
+
+        BigInteger h2 = new BigInteger(NativeCrypto.EC_GROUP_get_cofactor(group));
+        assertEquals(h, h2);
+
+        NativeRef.EVP_PKEY key1 = new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(group));
+        NativeRef.EC_GROUP groupTmp = new NativeRef.EC_GROUP(NativeCrypto.EC_KEY_get1_group(key1));
+        assertEquals(NativeCrypto.EC_GROUP_get_curve_name(group),
+                NativeCrypto.EC_GROUP_get_curve_name(groupTmp));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_EC_KEY_get_private_key_NullArgumentFailure() throws Exception {
+        NativeCrypto.EC_KEY_get_private_key(null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void test_EC_KEY_get_public_key_NullArgumentFailure() throws Exception {
+        NativeCrypto.EC_KEY_get_public_key(null);
+    }
+
+    @Test
+    public void test_ECKeyPairGenerator_CurvesAreValid() throws Exception {
+        OpenSSLECKeyPairGenerator.assertCurvesAreValid();
+    }
+
+    @Test
+    public void test_ECDH_compute_key_null_key_Failure() throws Exception {
+        final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
+        assertFalse(groupCtx == NULL);
+        NativeRef.EC_GROUP groupRef = new NativeRef.EC_GROUP(groupCtx);
+        NativeRef.EVP_PKEY pkey1Ref =
+                new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(groupRef));
+        NativeRef.EVP_PKEY pkey2Ref =
+                new NativeRef.EVP_PKEY(NativeCrypto.EC_KEY_generate_key(groupRef));
+
+        byte[] out = new byte[128];
+        int outOffset = 0;
+        // Assert that the method under test works fine with the two
+        // non-null keys
+        NativeCrypto.ECDH_compute_key(out, outOffset, pkey1Ref, pkey2Ref);
+
+        // Assert that it fails when only the first key is null
+        try {
+            NativeCrypto.ECDH_compute_key(out, outOffset, null, pkey2Ref);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+
+        // Assert that it fails when only the second key is null
+        try {
+            NativeCrypto.ECDH_compute_key(out, outOffset, pkey1Ref, null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    @Test
+    public void test_EVP_CipherInit_ex_Null_Failure() throws Exception {
+        final NativeRef.EVP_CIPHER_CTX ctx =
+                new NativeRef.EVP_CIPHER_CTX(NativeCrypto.EVP_CIPHER_CTX_new());
+        final long evpCipher = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
+
+        try {
+            NativeCrypto.EVP_CipherInit_ex(null, evpCipher, null, null, true);
+            fail("Null context should throw NullPointerException");
+        } catch (NullPointerException expected) {
+        }
+
+        /* Initialize encrypting. */
+        NativeCrypto.EVP_CipherInit_ex(ctx, evpCipher, null, null, true);
+        NativeCrypto.EVP_CipherInit_ex(ctx, NULL, null, null, true);
+
+        /* Initialize decrypting. */
+        NativeCrypto.EVP_CipherInit_ex(ctx, evpCipher, null, null, false);
+        NativeCrypto.EVP_CipherInit_ex(ctx, NULL, null, null, false);
+    }
+
+    @Test
+    public void test_EVP_CipherInit_ex_Success() throws Exception {
+        final NativeRef.EVP_CIPHER_CTX ctx =
+                new NativeRef.EVP_CIPHER_CTX(NativeCrypto.EVP_CIPHER_CTX_new());
+        final long evpCipher = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
+        NativeCrypto.EVP_CipherInit_ex(ctx, evpCipher, AES_128_KEY, null, true);
+    }
+
+    @Test
+    public void test_EVP_CIPHER_iv_length() throws Exception {
+        long aes128ecb = NativeCrypto.EVP_get_cipherbyname("aes-128-ecb");
+        assertEquals(0, NativeCrypto.EVP_CIPHER_iv_length(aes128ecb));
+
+        long aes128cbc = NativeCrypto.EVP_get_cipherbyname("aes-128-cbc");
+        assertEquals(16, NativeCrypto.EVP_CIPHER_iv_length(aes128cbc));
+    }
+
+    @Test
+    public void test_OpenSSLKey_toJava() throws Exception {
+        OpenSSLKey key1;
+
+        BigInteger e = BigInteger.valueOf(65537);
+        key1 = new OpenSSLKey(NativeCrypto.RSA_generate_key_ex(1024, e.toByteArray()));
+        assertTrue(key1.getPublicKey() instanceof RSAPublicKey);
+
+        final long groupCtx = NativeCrypto.EC_GROUP_new_by_curve_name("prime256v1");
+        assertFalse(groupCtx == NULL);
+        NativeRef.EC_GROUP group1 = new NativeRef.EC_GROUP(groupCtx);
+        key1 = new OpenSSLKey(NativeCrypto.EC_KEY_generate_key(group1));
+        assertTrue(key1.getPublicKey() instanceof ECPublicKey);
+    }
+
+    @Test
+    public void test_create_BIO_InputStream() throws Exception {
+        byte[] actual = "Test".getBytes();
+        ByteArrayInputStream is = new ByteArrayInputStream(actual);
+
+        @SuppressWarnings("resource")
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
+        try {
+            byte[] buffer = new byte[1024];
+            int numRead = NativeCrypto.BIO_read(bis.getBioContext(), buffer);
+            assertEquals(actual.length, numRead);
+            assertEquals(Arrays.toString(actual),
+                    Arrays.toString(Arrays.copyOfRange(buffer, 0, numRead)));
+        } finally {
+            bis.release();
+        }
+    }
+
+    @Test
+    public void test_create_BIO_OutputStream() throws Exception {
+        byte[] actual = "Test".getBytes();
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+        long ctx = NativeCrypto.create_BIO_OutputStream(os);
+        try {
+            NativeCrypto.BIO_write(ctx, actual, 0, actual.length);
+            assertEquals(actual.length, os.size());
+            assertEquals(Arrays.toString(actual), Arrays.toString(os.toByteArray()));
+        } finally {
+            NativeCrypto.BIO_free_all(ctx);
+        }
+    }
+
+    @Test
+    public void test_get_ocsp_single_extension() throws Exception {
+        final String OCSP_SCT_LIST_OID = "1.3.6.1.4.1.11129.2.4.5";
+
+        byte[] ocspResponse = readTestFile("ocsp-response.der");
+        byte[] expected = readTestFile("ocsp-response-sct-extension.der");
+        OpenSSLX509Certificate certificate =
+                OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("cert-ct-poisoned.pem"));
+        OpenSSLX509Certificate issuer =
+                OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("ca-cert.pem"));
+
+        byte[] extension = NativeCrypto.get_ocsp_single_extension(
+                ocspResponse, OCSP_SCT_LIST_OID, certificate.getContext(), issuer.getContext());
+
+        assertEqualByteArrays(expected, extension);
+    }
+
+    private static long getRawPkeyCtxForEncrypt() throws Exception {
+        return NativeCrypto.EVP_PKEY_encrypt_init(getRsaPkey(generateRsaKey()));
+    }
+
+    private static NativeRef.EVP_PKEY_CTX getPkeyCtxForEncrypt() throws Exception {
+        return new NativeRef.EVP_PKEY_CTX(getRawPkeyCtxForEncrypt());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_encrypt_NullKeyArgument() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(null, new byte[128], 0, new byte[128], 0, 128);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_encrypt_NullOutputArgument() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(getPkeyCtxForEncrypt(), null, 0, new byte[128], 0, 128);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_encrypt_NullInputArgument() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(getPkeyCtxForEncrypt(), new byte[128], 0, null, 0, 128);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_OutputIndexOOBUnder() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], -1, new byte[128], 0, 128);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_OutputIndexOOBOver() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], 129, new byte[128], 0, 128);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_InputIndexOOBUnder() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], -1, 128);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_InputIndexOOBOver() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], 128, 128);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_InputLengthNegative() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], 0, -1);
+    }
+
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void EVP_PKEY_encrypt_InputIndexLengthOOB() throws Exception {
+        NativeCrypto.EVP_PKEY_encrypt(
+                getPkeyCtxForEncrypt(), new byte[128], 0, new byte[128], 100, 29);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_CTX_set_rsa_mgf1_md_NullPkeyCtx() throws Exception {
+        NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(NULL, EvpMdRef.SHA256.EVP_MD);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_CTX_set_rsa_mgf1_md_NullMdCtx() throws Exception {
+        long pkeyCtx = getRawPkeyCtxForEncrypt();
+        NativeRef.EVP_PKEY_CTX holder = new NativeRef.EVP_PKEY_CTX(pkeyCtx);
+        NativeCrypto.EVP_PKEY_CTX_set_rsa_mgf1_md(pkeyCtx, NULL);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_CTX_set_rsa_oaep_md_NullPkeyCtx() throws Exception {
+        NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_md(NULL, EvpMdRef.SHA256.EVP_MD);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void EVP_PKEY_CTX_set_rsa_oaep_md_NullMdCtx() throws Exception {
+        long pkeyCtx = getRawPkeyCtxForEncrypt();
+        NativeRef.EVP_PKEY_CTX holder = new NativeRef.EVP_PKEY_CTX(pkeyCtx);
+        NativeCrypto.EVP_PKEY_CTX_set_rsa_oaep_md(pkeyCtx, NULL);
+    }
+
+    @Test(expected = ParsingException.class)
+    public void d2i_X509_InvalidFailure() throws Exception {
+        NativeCrypto.d2i_X509(new byte[1]);
+    }
+
+    private static void assertContains(String actualValue, String expectedSubstring) {
+        if (actualValue == null) {
+            return;
+        }
+        if (actualValue.contains(expectedSubstring)) {
+            return;
+        }
+        fail("\"" + actualValue + "\" does not contain \"" + expectedSubstring + "\"");
+    }
+}
diff --git a/platform/src/test/java/org/conscrypt/OpenSSLSocketImplTest.java b/platform/src/test/java/org/conscrypt/OpenSSLSocketImplTest.java
new file mode 100644
index 0000000..b7b7987
--- /dev/null
+++ b/platform/src/test/java/org/conscrypt/OpenSSLSocketImplTest.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2015 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 org.conscrypt;
+
+import static org.conscrypt.TestUtils.openTestFile;
+import static org.conscrypt.TestUtils.readTestFile;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import org.conscrypt.ct.CTLogInfo;
+import org.conscrypt.ct.CTLogStore;
+import org.conscrypt.ct.CTPolicy;
+import org.conscrypt.ct.CTPolicyImpl;
+import org.conscrypt.ct.CTVerifier;
+import org.junit.Before;
+import org.junit.Test;
+
+public class OpenSSLSocketImplTest {
+    private static final long TIMEOUT_SECONDS = 5;
+    private static final char[] EMPTY_PASSWORD = new char[0];
+
+    private X509Certificate ca;
+    private X509Certificate cert;
+    private X509Certificate certEmbedded;
+    private PrivateKey certKey;
+    private CTVerifier defaultCTVerifier;
+    private CTPolicy defaultCTPolicy;
+
+    private Field contextSSLParameters;
+    private Field sslParametersTrustManager;
+
+    @Before
+    public void setUp() throws Exception {
+        contextSSLParameters = OpenSSLContextImpl.class.getDeclaredField("sslParameters");
+        contextSSLParameters.setAccessible(true);
+
+        sslParametersTrustManager = SSLParametersImpl.class.getDeclaredField("x509TrustManager");
+        sslParametersTrustManager.setAccessible(true);
+
+        ca = OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("ca-cert.pem"));
+        cert = OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("cert.pem"));
+        certEmbedded = OpenSSLX509Certificate.fromX509PemInputStream(
+                openTestFile("cert-ct-embedded.pem"));
+        certKey = OpenSSLKey.fromPrivateKeyPemInputStream(
+                openTestFile("cert-key.pem")).getPrivateKey();
+
+        PublicKey key = OpenSSLKey.fromPublicKeyPemInputStream(
+                openTestFile("ct-server-key-public.pem")).getPublicKey();
+        final CTLogInfo log = new CTLogInfo(key, "Test Log", "foo");
+        CTLogStore store = new CTLogStore() {
+            public CTLogInfo getKnownLog(byte[] logId) {
+                if (Arrays.equals(logId, log.getID())) {
+                    return log;
+                } else {
+                    return null;
+                }
+            }
+        };
+        defaultCTVerifier = new CTVerifier(store);
+        defaultCTPolicy = new CTPolicyImpl(store, 1);
+    }
+
+    abstract class Hooks implements HandshakeCompletedListener {
+        KeyManager[] keyManagers;
+        TrustManager[] trustManagers;
+
+        abstract OpenSSLSocketImpl createSocket(SSLSocketFactory factory, ServerSocket listener)
+                throws IOException;
+
+        public OpenSSLContextImpl createContext() throws Exception {
+            OpenSSLContextImpl context = OpenSSLContextImpl.getPreferred();
+            context.engineInit(keyManagers, trustManagers, null);
+            return context;
+        }
+
+        boolean isHandshakeCompleted = false;
+        @Override
+        public void handshakeCompleted(HandshakeCompletedEvent event) {
+            isHandshakeCompleted = true;
+        }
+
+        protected SSLParametersImpl getContextSSLParameters(OpenSSLContextImpl context)
+                throws IllegalAccessException {
+            return (SSLParametersImpl) contextSSLParameters.get(context);
+        }
+
+        protected TrustManagerImpl getSSLParametersTrustManager(SSLParametersImpl params)
+                throws IllegalAccessException {
+            return (TrustManagerImpl) sslParametersTrustManager.get(params);
+        }
+    }
+
+    class ClientHooks extends Hooks {
+        CTVerifier ctVerifier = defaultCTVerifier;
+        CTPolicy ctPolicy = defaultCTPolicy;
+        boolean ctVerificationEnabled;
+        String hostname = "example.com";
+
+        @Override
+        public OpenSSLContextImpl createContext() throws Exception {
+            OpenSSLContextImpl context = super.createContext();
+            SSLParametersImpl sslParameters = getContextSSLParameters(context);
+            if (ctVerificationEnabled) {
+                TrustManagerImpl trustManager = getSSLParametersTrustManager(sslParameters);
+                if (ctVerifier != null) {
+                    trustManager.setCTVerifier(ctVerifier);
+                }
+                if (ctPolicy != null) {
+                    trustManager.setCTPolicy(ctPolicy);
+                }
+                sslParameters.setCTVerificationEnabled(ctVerificationEnabled);
+                trustManager.setCTEnabledOverride(ctVerificationEnabled);
+            }
+            return context;
+        }
+
+        @Override
+        public OpenSSLSocketImpl createSocket(SSLSocketFactory factory, ServerSocket listener)
+                throws IOException {
+            OpenSSLSocketImpl socket = (OpenSSLSocketImpl) factory.createSocket(
+                    listener.getInetAddress(), listener.getLocalPort());
+            socket.setUseClientMode(true);
+            socket.setHostname(hostname);
+
+            return socket;
+        }
+    }
+
+    class ServerHooks extends Hooks {
+        byte[] sctTLSExtension;
+        byte[] ocspResponse;
+
+        @Override
+        public OpenSSLContextImpl createContext() throws Exception {
+            OpenSSLContextImpl context = super.createContext();
+            SSLParametersImpl sslParameters = getContextSSLParameters(context);
+            sslParameters.setSCTExtension(sctTLSExtension);
+            sslParameters.setOCSPResponse(ocspResponse);
+            return context;
+        }
+
+        @Override
+        public OpenSSLSocketImpl createSocket(SSLSocketFactory factory, ServerSocket listener)
+                throws IOException {
+            OpenSSLSocketImpl socket = (OpenSSLSocketImpl) factory.createSocket(listener.accept(),
+                    null, -1, // hostname, port
+                    true); // autoclose
+            socket.setUseClientMode(false);
+            return socket;
+        }
+    }
+
+    class TestConnection {
+        ServerHooks serverHooks;
+        ClientHooks clientHooks;
+
+        OpenSSLSocketImpl client;
+        OpenSSLSocketImpl server;
+
+        Exception clientException;
+        Exception serverException;
+
+        public TestConnection(X509Certificate[] chain, PrivateKey key) throws Exception {
+            this(chain, key, false);
+        }
+
+        public TestConnection(X509Certificate[] chain, PrivateKey key, boolean useTrustManagerImpl)
+                throws Exception {
+            clientHooks = new ClientHooks();
+            serverHooks = new ServerHooks();
+            setCertificates(chain, key, useTrustManagerImpl);
+        }
+
+        private void setCertificates(X509Certificate[] chain, PrivateKey key,
+                boolean useTrustManagerImpl) throws Exception {
+            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+            ks.load(null, null);
+            ks.setKeyEntry("default", key, EMPTY_PASSWORD, chain);
+            ks.setCertificateEntry("CA", chain[chain.length - 1]);
+
+            TrustManager[] tms;
+            if (useTrustManagerImpl) {
+                TrustManagerImpl tm = new TrustManagerImpl(ks);
+                tms = new TrustManager[] {tm};
+            } else {
+                TrustManagerFactory tmf =
+                        TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+                tmf.init(ks);
+                tms = tmf.getTrustManagers();
+            }
+
+            KeyManagerFactory kmf =
+                    KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+            kmf.init(ks, EMPTY_PASSWORD);
+            KeyManager[] kms = kmf.getKeyManagers();
+
+            clientHooks.trustManagers = tms;
+            serverHooks.keyManagers = kms;
+            serverHooks.trustManagers = tms;
+        }
+
+        private <T> T getOrThrowCause(Future<T> future, long timeout, TimeUnit timeUnit)
+                throws Exception {
+            try {
+                return future.get(timeout, timeUnit);
+            } catch (ExecutionException e) {
+                if (e.getCause() instanceof Exception) {
+                    throw(Exception) e.getCause();
+                } else {
+                    throw e;
+                }
+            }
+        }
+
+        public void doHandshake() throws Exception {
+            ServerSocket listener = new ServerSocket(0);
+            Future<OpenSSLSocketImpl> clientFuture = handshake(listener, clientHooks);
+            Future<OpenSSLSocketImpl> serverFuture = handshake(listener, serverHooks);
+
+            Exception cause = null;
+            try {
+                client = getOrThrowCause(clientFuture, TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            } catch (Exception e) {
+                clientException = e;
+            }
+            try {
+                server = getOrThrowCause(serverFuture, TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            } catch (Exception e) {
+                serverException = e;
+            }
+        }
+
+        Future<OpenSSLSocketImpl> handshake(final ServerSocket listener, final Hooks hooks) {
+            ExecutorService executor = Executors.newSingleThreadExecutor();
+            Future<OpenSSLSocketImpl> future = executor.submit(new Callable<OpenSSLSocketImpl>() {
+                @Override
+                public OpenSSLSocketImpl call() throws Exception {
+                    OpenSSLContextImpl context = hooks.createContext();
+                    SSLSocketFactory factory = context.engineGetSocketFactory();
+                    OpenSSLSocketImpl socket = hooks.createSocket(factory, listener);
+                    socket.addHandshakeCompletedListener(hooks);
+
+                    socket.startHandshake();
+
+                    return socket;
+                }
+            });
+
+            executor.shutdown();
+
+            return future;
+        }
+    }
+
+    @Test
+    public void test_handshake() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey);
+        connection.doHandshake();
+
+        assertTrue(connection.clientHooks.isHandshakeCompleted);
+        assertTrue(connection.serverHooks.isHandshakeCompleted);
+    }
+
+    @Test
+    public void test_handshakeWithEmbeddedSCT() throws Exception {
+        TestConnection connection =
+                new TestConnection(new X509Certificate[] {certEmbedded, ca}, certKey, true);
+
+        connection.clientHooks.ctVerificationEnabled = true;
+
+        connection.doHandshake();
+
+        assertTrue(connection.clientHooks.isHandshakeCompleted);
+        assertTrue(connection.serverHooks.isHandshakeCompleted);
+    }
+
+    @Test
+    public void test_handshakeWithSCTFromOCSPResponse() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey, true);
+
+        connection.clientHooks.ctVerificationEnabled = true;
+        connection.serverHooks.ocspResponse = readTestFile("ocsp-response.der");
+
+        connection.doHandshake();
+
+        assertTrue(connection.clientHooks.isHandshakeCompleted);
+        assertTrue(connection.serverHooks.isHandshakeCompleted);
+    }
+
+    @Test
+    public void test_handshakeWithSCTFromTLSExtension() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey, true);
+
+        connection.clientHooks.ctVerificationEnabled = true;
+        connection.serverHooks.sctTLSExtension = readTestFile("ct-signed-timestamp-list");
+
+        connection.doHandshake();
+
+        assertTrue(connection.clientHooks.isHandshakeCompleted);
+        assertTrue(connection.serverHooks.isHandshakeCompleted);
+    }
+
+    @Test
+    public void test_handshake_failsWithMissingSCT() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey, true);
+
+        connection.clientHooks.ctVerificationEnabled = true;
+
+        connection.doHandshake();
+        assertThat(connection.clientException, instanceOf(SSLHandshakeException.class));
+        assertThat(connection.clientException.getCause(), instanceOf(CertificateException.class));
+    }
+
+    @Test
+    public void test_handshake_failsWithInvalidSCT() throws Exception {
+        TestConnection connection = new TestConnection(new X509Certificate[] {cert, ca}, certKey, true);
+
+        connection.clientHooks.ctVerificationEnabled = true;
+        connection.serverHooks.sctTLSExtension = readTestFile("ct-signed-timestamp-list-invalid");
+
+        connection.doHandshake();
+        assertThat(connection.clientException, instanceOf(SSLHandshakeException.class));
+        assertThat(connection.clientException.getCause(), instanceOf(CertificateException.class));
+    }
+
+    // http://b/27250522
+    @Test
+    public void test_setSoTimeout_doesNotCreateSocketImpl() throws Exception {
+        ServerSocket listening = new ServerSocket(0);
+        Socket underlying = new Socket(listening.getInetAddress(), listening.getLocalPort());
+
+        Constructor<OpenSSLSocketImpl> cons = OpenSSLSocketImpl.class.getDeclaredConstructor(
+                Socket.class, String.class, Integer.TYPE, Boolean.TYPE, SSLParametersImpl.class);
+        cons.setAccessible(true);
+        OpenSSLSocketImpl simpl =
+                cons.newInstance(underlying, null, listening.getLocalPort(), false, null);
+        simpl.setSoTimeout(1000);
+        simpl.close();
+
+        Field f = Socket.class.getDeclaredField("created");
+        f.setAccessible(true);
+        assertFalse(f.getBoolean(simpl));
+    }
+
+    @Test
+    public void test_setEnabledProtocols_FiltersSSLv3_HandshakeException() throws Exception {
+        TestConnection connection =
+                new TestConnection(new X509Certificate[] {cert, ca}, certKey, true);
+
+        connection.clientHooks = new ClientHooks() {
+            @Override
+            public OpenSSLSocketImpl createSocket(SSLSocketFactory factory, ServerSocket listener)
+                    throws IOException {
+                OpenSSLSocketImpl socket = super.createSocket(factory, listener);
+                socket.setEnabledProtocols(new String[] {"SSLv3"});
+                assertEquals(
+                        "SSLv3 should be filtered out", 0, socket.getEnabledProtocols().length);
+                return socket;
+            }
+        };
+
+        connection.doHandshake();
+        assertThat(connection.clientException, instanceOf(SSLHandshakeException.class));
+        assertTrue(
+                connection.clientException.getMessage().contains("SSLv3 is no longer supported"));
+        assertThat(connection.serverException, instanceOf(SSLHandshakeException.class));
+
+        assertFalse(connection.clientHooks.isHandshakeCompleted);
+        assertFalse(connection.serverHooks.isHandshakeCompleted);
+    }
+}
diff --git a/platform/src/test/java/org/conscrypt/TrustManagerImplTest.java b/platform/src/test/java/org/conscrypt/TrustManagerImplTest.java
index f5a91c2..52d35fd 100644
--- a/platform/src/test/java/org/conscrypt/TrustManagerImplTest.java
+++ b/platform/src/test/java/org/conscrypt/TrustManagerImplTest.java
@@ -126,20 +126,18 @@
             tm.checkClientTrusted(chain, "RSA");
             fail();
         } catch (CertificateException expected) {
-            // Expected.
         }
         try {
             tm.checkServerTrusted(chain, "RSA");
             fail();
         } catch (CertificateException expected) {
-            // Expected.
         }
     }
 
-    private static class MySSLSession implements SSLSession {
+    private class MySSLSession implements SSLSession {
         private final String hostname;
 
-        MySSLSession(String hostname) {
+        public MySSLSession(String hostname) {
             this.hostname = hostname;
         }
 
diff --git a/platform/src/test/java/org/conscrypt/ct/CTLogStoreImplTest.java b/platform/src/test/java/org/conscrypt/ct/CTLogStoreImplTest.java
index 328540c..5c860c8 100644
--- a/platform/src/test/java/org/conscrypt/ct/CTLogStoreImplTest.java
+++ b/platform/src/test/java/org/conscrypt/ct/CTLogStoreImplTest.java
@@ -16,19 +16,14 @@
 
 package org.conscrypt.ct;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.StringBufferInputStream;
 import java.security.PublicKey;
 import junit.framework.TestCase;
-import org.conscrypt.InternalUtil;
+import org.conscrypt.OpenSSLKey;
 
 public class CTLogStoreImplTest extends TestCase {
     private static final String[] LOG_KEYS = new String[] {
@@ -64,10 +59,10 @@
             LOGS = new CTLogInfo[logCount];
             LOGS_SERIALIZED = new String[logCount];
             for (int i = 0; i < logCount; i++) {
-                PublicKey key = InternalUtil.readPublicKeyPem(new StringBufferInputStream(
+                PublicKey key = OpenSSLKey.fromPublicKeyPemInputStream(new StringBufferInputStream(
                     "-----BEGIN PUBLIC KEY-----\n" +
                     LOG_KEYS[i] + "\n" +
-                    "-----END PUBLIC KEY-----\n"));
+                    "-----END PUBLIC KEY-----\n")).getPublicKey();
                 String description = String.format("Test Log %d", i);
                 String url = String.format("log%d.example.com", i);
                 LOGS[i] = new CTLogInfo(key, description, url);
@@ -177,9 +172,7 @@
     }
 
     private static void writeFile(File file, String contents) throws FileNotFoundException {
-        PrintWriter writer = new PrintWriter(
-                new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), UTF_8)),
-                false);
+        PrintWriter writer = new PrintWriter(file);
         try {
             writer.write(contents);
         } finally {
diff --git a/platform/src/test/java/org/conscrypt/ct/CTVerifierTest.java b/platform/src/test/java/org/conscrypt/ct/CTVerifierTest.java
index d2e0a0b..1053afb 100644
--- a/platform/src/test/java/org/conscrypt/ct/CTVerifierTest.java
+++ b/platform/src/test/java/org/conscrypt/ct/CTVerifierTest.java
@@ -22,7 +22,7 @@
 import java.security.PublicKey;
 import java.util.Arrays;
 import junit.framework.TestCase;
-import org.conscrypt.InternalUtil;
+import org.conscrypt.OpenSSLKey;
 import org.conscrypt.OpenSSLX509Certificate;
 
 public class CTVerifierTest extends TestCase {
@@ -33,17 +33,16 @@
 
     @Override
     public void setUp() throws Exception {
-        super.setUp();
         ca = OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("ca-cert.pem"));
         cert = OpenSSLX509Certificate.fromX509PemInputStream(openTestFile("cert.pem"));
         certEmbedded = OpenSSLX509Certificate.fromX509PemInputStream(
                 openTestFile("cert-ct-embedded.pem"));
 
-        PublicKey key = InternalUtil.readPublicKeyPem(openTestFile("ct-server-key-public.pem"));
+        PublicKey key = OpenSSLKey.fromPublicKeyPemInputStream(
+                openTestFile("ct-server-key-public.pem")).getPublicKey();
 
         final CTLogInfo log = new CTLogInfo(key, "Test Log", "foo");
         CTLogStore store = new CTLogStore() {
-            @Override
             public CTLogInfo getKnownLog(byte[] logId) {
                 if (Arrays.equals(logId, log.getID())) {
                     return log;
diff --git a/settings.gradle b/settings.gradle
index 894bf75..3478dbb 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -6,10 +6,7 @@
 include ":conscrypt-openjdk-uber"
 include ":conscrypt-android"
 include ":conscrypt-android-stub"
-include ":conscrypt-android-platform"
-include ":conscrypt-libcore-stub"
 include ":conscrypt-api-doclet"
-include ":conscrypt-benchmark-graphs"
 
 project(':conscrypt-constants').projectDir = "$rootDir/constants" as File
 project(':conscrypt-openjdk').projectDir = "$rootDir/openjdk" as File
@@ -18,7 +15,4 @@
 project(':conscrypt-openjdk-uber').projectDir = "$rootDir/openjdk-uber" as File
 project(':conscrypt-android').projectDir = "$rootDir/android" as File
 project(':conscrypt-android-stub').projectDir = "$rootDir/android-stub" as File
-project(':conscrypt-libcore-stub').projectDir = "$rootDir/libcore-stub" as File
-project(':conscrypt-android-platform').projectDir = "$rootDir/platform" as File
 project(':conscrypt-api-doclet').projectDir = "$rootDir/api-doclet" as File
-project(':conscrypt-benchmark-graphs').projectDir = "$rootDir/benchmark-graphs" as File
diff --git a/testing/build.gradle b/testing/build.gradle
index a4fd599..90a1700 100644
--- a/testing/build.gradle
+++ b/testing/build.gradle
@@ -12,10 +12,9 @@
     // Only compile against this. Other modules will embed the generated code directly.
     compileOnly project(':conscrypt-constants')
 
-    compile project(':conscrypt-libcore-stub'),
+    compile libraries.bouncycastle_provider,
+            libraries.bouncycastle_apis,
+            libraries.guava,
+            libraries.junit,
             libraries.netty_handler
 }
-
-// Don't include this artifact in the distribution.
-tasks.install.enabled = false
-tasks.uploadArchives.enabled = false;
\ No newline at end of file
diff --git a/libcore-stub/src/main/java/libcore/io/IoUtils.java b/testing/src/main/java/libcore/io/IoUtils.java
similarity index 100%
rename from libcore-stub/src/main/java/libcore/io/IoUtils.java
rename to testing/src/main/java/libcore/io/IoUtils.java
diff --git a/libcore-stub/src/main/java/libcore/io/Streams.java b/testing/src/main/java/libcore/io/Streams.java
similarity index 100%
rename from libcore-stub/src/main/java/libcore/io/Streams.java
rename to testing/src/main/java/libcore/io/Streams.java
diff --git a/libcore-stub/src/main/java/libcore/java/io/NullPrintStream.java b/testing/src/main/java/libcore/java/io/NullPrintStream.java
similarity index 84%
rename from libcore-stub/src/main/java/libcore/java/io/NullPrintStream.java
rename to testing/src/main/java/libcore/java/io/NullPrintStream.java
index 673286c..78df886 100644
--- a/libcore-stub/src/main/java/libcore/java/io/NullPrintStream.java
+++ b/testing/src/main/java/libcore/java/io/NullPrintStream.java
@@ -29,118 +29,52 @@
         // super class complains if argument is null
         super((OutputStream) new ByteArrayOutputStream());
     }
-
-    @Override
     public boolean checkError() {
         return false;
     }
-
-    @Override
     protected void clearError() {}
-
-    @Override
     public void close() {}
-
-    @Override
     public void flush() {}
-
-    @Override
     public PrintStream format(String format, Object... args) {
         return this;
     }
-
-    @Override
     public PrintStream format(Locale l, String format, Object... args) {
         return this;
     }
-
-    @Override
     public PrintStream printf(String format, Object... args) {
         return this;
     }
-
-    @Override
     public PrintStream printf(Locale l, String format, Object... args) {
         return this;
     }
-
-    @Override
     public void print(char[] charArray) {}
-
-    @Override
     public void print(char ch) {}
-
-    @Override
     public void print(double dnum) {}
-
-    @Override
     public void print(float fnum) {}
-
-    @Override
     public void print(int inum) {}
-
-    @Override
     public void print(long lnum) {}
-
-    @Override
     public void print(Object obj) {}
-
-    @Override
     public void print(String str) {}
-
-    @Override
     public void print(boolean bool) {}
-
-    @Override
     public void println() {}
-
-    @Override
     public void println(char[] charArray) {}
-
-    @Override
     public void println(char ch) {}
-
-    @Override
     public void println(double dnum) {}
-
-    @Override
     public void println(float fnum) {}
-
-    @Override
     public void println(int inum) {}
-
-    @Override
     public void println(long lnum) {}
-
-    @Override
     public void println(Object obj) {}
-
-    @Override
     public void println(String str) {}
-
-    @Override
     public void println(boolean bool) {}
-
-    @Override
     protected void setError() {}
-
-    @Override
     public void write(byte[] buffer, int offset, int length) {}
-
-    @Override
     public void write(int oneByte) {}
-
-    @Override
     public PrintStream append(char c) {
         return this;
     }
-
-    @Override
     public PrintStream append(CharSequence csq) {
         return this;
     }
-
-    @Override
     public PrintStream append(CharSequence csq, int start, int end) {
         return this;
     }
diff --git a/libcore-stub/src/main/java/libcore/java/security/CpuFeatures.java b/testing/src/main/java/libcore/java/security/CpuFeatures.java
similarity index 98%
rename from libcore-stub/src/main/java/libcore/java/security/CpuFeatures.java
rename to testing/src/main/java/libcore/java/security/CpuFeatures.java
index 3b7ce36..726df9e 100644
--- a/libcore-stub/src/main/java/libcore/java/security/CpuFeatures.java
+++ b/testing/src/main/java/libcore/java/security/CpuFeatures.java
@@ -58,7 +58,6 @@
 
     private static String getFieldFromCpuinfo(String field) {
         try {
-            @SuppressWarnings("DefaultCharset")
             BufferedReader br = new BufferedReader(new FileReader("/proc/cpuinfo"));
             Pattern p = Pattern.compile(field + "\\s*:\\s*(.*)");
 
diff --git a/libcore-stub/src/main/java/libcore/java/security/StandardNames.java b/testing/src/main/java/libcore/java/security/StandardNames.java
similarity index 100%
rename from libcore-stub/src/main/java/libcore/java/security/StandardNames.java
rename to testing/src/main/java/libcore/java/security/StandardNames.java
diff --git a/libcore-stub/src/main/java/libcore/java/security/TestKeyStore.java b/testing/src/main/java/libcore/java/security/TestKeyStore.java
similarity index 100%
rename from libcore-stub/src/main/java/libcore/java/security/TestKeyStore.java
rename to testing/src/main/java/libcore/java/security/TestKeyStore.java
diff --git a/libcore-stub/src/main/java/libcore/javax/net/ssl/FakeSSLSession.java b/testing/src/main/java/libcore/javax/net/ssl/FakeSSLSession.java
similarity index 86%
rename from libcore-stub/src/main/java/libcore/javax/net/ssl/FakeSSLSession.java
rename to testing/src/main/java/libcore/javax/net/ssl/FakeSSLSession.java
index 4f2ae72..1390c99 100644
--- a/libcore-stub/src/main/java/libcore/javax/net/ssl/FakeSSLSession.java
+++ b/testing/src/main/java/libcore/javax/net/ssl/FakeSSLSession.java
@@ -16,121 +16,98 @@
 
 package libcore.javax.net.ssl;
 
-import java.nio.charset.Charset;
 import java.security.Principal;
 import java.security.cert.Certificate;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSessionContext;
 
 public class FakeSSLSession implements SSLSession {
-    private static final Charset UTF_8 = Charset.forName("UTF-8");
     final String host;
 
     public FakeSSLSession(String host) {
         this.host = host;
     }
 
-    @Override
     public int getApplicationBufferSize() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public String getCipherSuite() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public long getCreationTime() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public byte[] getId() {
-        return host.getBytes(UTF_8);
+        return host.getBytes();
     }
 
-    @Override
     public long getLastAccessedTime() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public Certificate[] getLocalCertificates() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public Principal getLocalPrincipal() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public int getPacketBufferSize() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public javax.security.cert.X509Certificate[] getPeerCertificateChain() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public Certificate[] getPeerCertificates() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public String getPeerHost() {
         return host;
     }
 
-    @Override
     public int getPeerPort() {
         return 443;
     }
 
-    @Override
     public Principal getPeerPrincipal() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public String getProtocol() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public SSLSessionContext getSessionContext() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public Object getValue(String name) {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public String[] getValueNames() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public void invalidate() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public boolean isValid() {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public void putValue(String name, Object value) {
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public void removeValue(String name) {
         throw new UnsupportedOperationException();
     }
diff --git a/libcore-stub/src/main/java/libcore/javax/net/ssl/TestKeyManager.java b/testing/src/main/java/libcore/javax/net/ssl/TestKeyManager.java
similarity index 98%
rename from libcore-stub/src/main/java/libcore/javax/net/ssl/TestKeyManager.java
rename to testing/src/main/java/libcore/javax/net/ssl/TestKeyManager.java
index 7fbbc77..1131958 100644
--- a/libcore-stub/src/main/java/libcore/javax/net/ssl/TestKeyManager.java
+++ b/testing/src/main/java/libcore/javax/net/ssl/TestKeyManager.java
@@ -58,7 +58,6 @@
         this.keyManager = keyManager;
     }
 
-    @Override
     public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
         out.print("TestKeyManager.chooseClientAlias");
         out.print(" | keyTypes: ");
@@ -84,7 +83,6 @@
         }
     }
 
-    @Override
     public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
         out.print("TestKeyManager.chooseServerAlias");
         out.print(" | keyType: ");
@@ -119,7 +117,6 @@
         return alias;
     }
 
-    @Override
     public X509Certificate[] getCertificateChain(String alias) {
         out.print("TestKeyManager.getCertificateChain");
         out.print(" | alias: ");
@@ -137,7 +134,6 @@
         return certs;
     }
 
-    @Override
     public String[] getClientAliases(String keyType, Principal[] issuers) {
         out.print("TestKeyManager.getClientAliases");
         out.print(" | keyType: ");
@@ -147,7 +143,6 @@
         return dumpAliases(keyManager.getClientAliases(keyType, issuers));
     }
 
-    @Override
     public String[] getServerAliases(String keyType, Principal[] issuers) {
         out.print("TestKeyManager.getServerAliases");
         out.print(" | keyType: ");
@@ -167,7 +162,6 @@
         return aliases;
     }
 
-    @Override
     public PrivateKey getPrivateKey(String alias) {
         out.print("TestKeyManager.getPrivateKey");
         out.print(" | alias: ");
@@ -178,7 +172,6 @@
         return pk;
     }
 
-    @Override
     public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine e) {
         out.print("TestKeyManager.chooseEngineClientAlias");
         out.print(" | keyTypes: ");
@@ -192,7 +185,6 @@
         return dumpAlias(keyManager.chooseEngineClientAlias(keyTypes, issuers, e));
     }
 
-    @Override
     public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine e) {
         out.print("TestKeyManager.chooseEngineServerAlias");
         out.print(" | keyType: ");
diff --git a/libcore-stub/src/main/java/libcore/javax/net/ssl/TestTrustManager.java b/testing/src/main/java/libcore/javax/net/ssl/TestTrustManager.java
similarity index 99%
rename from libcore-stub/src/main/java/libcore/javax/net/ssl/TestTrustManager.java
rename to testing/src/main/java/libcore/javax/net/ssl/TestTrustManager.java
index ac6f0e6..2d00632 100644
--- a/libcore-stub/src/main/java/libcore/javax/net/ssl/TestTrustManager.java
+++ b/testing/src/main/java/libcore/javax/net/ssl/TestTrustManager.java
@@ -68,7 +68,6 @@
         this.trustManager = trustManager;
     }
 
-    @Override
     public void checkClientTrusted(X509Certificate[] chain, String authType)
             throws CertificateException {
         out.print("TestTrustManager.checkClientTrusted "
@@ -134,7 +133,6 @@
         }
     }
 
-    @Override
     public void checkServerTrusted(X509Certificate[] chain, String authType)
             throws CertificateException {
         out.print("TestTrustManager.checkServerTrusted "
@@ -207,7 +205,6 @@
      * @return the list of certificate issuer authorities which are trusted for
      *         authentication of peers.
      */
-    @Override
     public X509Certificate[] getAcceptedIssuers() {
         X509Certificate[] result = trustManager.getAcceptedIssuers();
         out.print("TestTrustManager.getAcceptedIssuers result=" + result.length);
diff --git a/testing/src/main/java/org/conscrypt/TestClient.java b/testing/src/main/java/org/conscrypt/testing/TestClient.java
similarity index 98%
rename from testing/src/main/java/org/conscrypt/TestClient.java
rename to testing/src/main/java/org/conscrypt/testing/TestClient.java
index 6ef37f9..15bd89b 100644
--- a/testing/src/main/java/org/conscrypt/TestClient.java
+++ b/testing/src/main/java/org/conscrypt/testing/TestClient.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.conscrypt;
+package org.conscrypt.testing;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/testing/src/main/java/org/conscrypt/TestServer.java b/testing/src/main/java/org/conscrypt/testing/TestServer.java
similarity index 99%
rename from testing/src/main/java/org/conscrypt/TestServer.java
rename to testing/src/main/java/org/conscrypt/testing/TestServer.java
index c706ba9..f4b61cf 100644
--- a/testing/src/main/java/org/conscrypt/TestServer.java
+++ b/testing/src/main/java/org/conscrypt/testing/TestServer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.conscrypt;
+package org.conscrypt.testing;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/testing/src/main/java/org/conscrypt/TestUtils.java b/testing/src/main/java/org/conscrypt/testing/TestUtil.java
similarity index 81%
rename from testing/src/main/java/org/conscrypt/TestUtils.java
rename to testing/src/main/java/org/conscrypt/testing/TestUtil.java
index 9f496ea..40ae85e 100644
--- a/testing/src/main/java/org/conscrypt/TestUtils.java
+++ b/testing/src/main/java/org/conscrypt/testing/TestUtil.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright 2017 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.
@@ -14,94 +14,81 @@
  * limitations under the License.
  */
 
-package org.conscrypt;
+package org.conscrypt.testing;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
 import java.lang.reflect.Method;
 import java.net.ServerSocket;
 import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
 import java.security.NoSuchAlgorithmException;
 import java.security.Provider;
 import java.security.Security;
+import java.util.regex.Pattern;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLEngineResult;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SSLSocketFactory;
-import libcore.io.Streams;
 import libcore.java.security.TestKeyStore;
 
 /**
  * Utility methods to support testing.
  */
-public final class TestUtils {
-    static final Charset UTF_8 = Charset.forName("UTF-8");
-
+public final class TestUtil {
     private static final Provider JDK_PROVIDER = getDefaultTlsProvider();
     private static final Provider CONSCRYPT_PROVIDER = getConscryptProvider();
     private static final byte[] CHARS =
-            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".getBytes(UTF_8);
+            "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".getBytes();
+    private static final Pattern KEY_PATTERN =
+            Pattern.compile("-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header
+                            "([a-z0-9+/=\\r\\n]+)" + // Base64 text
+                            "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer
+                    Pattern.CASE_INSENSITIVE);
 
     public static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
-    private static final String PROVIDER_PROPERTY = "SSLContext.TLSv1.2";
+    public static final String PROVIDER_PROPERTY = "SSLContext.TLSv1.2";
     public static final String LOCALHOST = "localhost";
 
-    private TestUtils() {}
-
-    public static InputStream openTestFile(String name) throws FileNotFoundException {
-        InputStream is = TestUtils.class.getResourceAsStream("/" + name);
-        if (is == null) {
-            throw new FileNotFoundException(name);
-        }
-        return is;
-    }
-
-    public static byte[] readTestFile(String name) throws IOException {
-        return Streams.readFully(openTestFile(name));
-    }
+    private TestUtil() {}
 
     /**
      * Returns an array containing only {@link #PROTOCOL_TLS_V1_2}.
      */
-    static String[] getProtocols() {
+    public static String[] getProtocols() {
         return new String[] {PROTOCOL_TLS_V1_2};
     }
 
-    static SSLSocketFactory getJdkSocketFactory() {
+    public static SSLSocketFactory getJdkSocketFactory() {
         return getSocketFactory(JDK_PROVIDER);
     }
 
-    static SSLServerSocketFactory getJdkServerSocketFactory() {
+    public static SSLServerSocketFactory getJdkServerSocketFactory() {
         return getServerSocketFactory(JDK_PROVIDER);
     }
 
-    static SSLSocketFactory getConscryptSocketFactory(boolean useEngineSocket) {
+    public static SSLSocketFactory getConscryptSocketFactory(boolean useEngineSocket) {
         try {
-            Class<?> clazz = Class.forName("org.conscrypt.Conscrypt$SocketFactories");
-            Method method = clazz.getMethod("setUseEngineSocket", SSLSocketFactory.class, boolean.class);
-
+            Class<?> clazz = Class.forName("org.conscrypt.OpenSSLSocketFactoryImpl");
+            Method method = clazz.getMethod("setUseEngineSocket", boolean.class);
             SSLSocketFactory socketFactory = getSocketFactory(CONSCRYPT_PROVIDER);
-            method.invoke(null, socketFactory, useEngineSocket);
+            method.invoke(socketFactory, useEngineSocket);
             return socketFactory;
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
 
-    static SSLServerSocketFactory getConscryptServerSocketFactory(boolean useEngineSocket) {
+    public static SSLServerSocketFactory getConscryptServerSocketFactory(boolean useEngineSocket) {
         try {
-            Class<?> clazz = Class.forName("org.conscrypt.Conscrypt$ServerSocketFactories");
-            Method method = clazz.getMethod("setUseEngineSocket", SSLServerSocketFactory.class, boolean.class);
+            Class<?> clazz = Class.forName("org.conscrypt.OpenSSLServerSocketFactoryImpl");
+            Method method = clazz.getMethod("setUseEngineSocket", boolean.class);
 
             SSLServerSocketFactory socketFactory = getServerSocketFactory(CONSCRYPT_PROVIDER);
-            method.invoke(null, socketFactory, useEngineSocket);
+            method.invoke(socketFactory, useEngineSocket);
             return socketFactory;
         } catch (Exception e) {
             throw new RuntimeException(e);
@@ -118,7 +105,7 @@
         return serverContext.getServerSocketFactory();
     }
 
-    private static SSLContext newContext(Provider provider) {
+    public static SSLContext newContext(Provider provider) {
         try {
             return SSLContext.getInstance("TLS", provider);
         } catch (NoSuchAlgorithmException e) {
@@ -132,7 +119,7 @@
      * returned port to create a new server socket when other threads/processes are concurrently
      * creating new sockets without a specific port.
      */
-    static int pickUnusedPort() {
+    public static int pickUnusedPort() {
         try {
             ServerSocket serverSocket = new ServerSocket(0);
             int port = serverSocket.getLocalPort();
@@ -146,7 +133,7 @@
     /**
      * Creates a text message of the given length.
      */
-    static byte[] newTextMessage(int length) {
+    public static byte[] newTextMessage(int length) {
         byte[] msg = new byte[length];
         for (int msgIndex = 0; msgIndex < length;) {
             int remaining = length - msgIndex;
@@ -160,7 +147,7 @@
     /**
      * Initializes the given engine with the cipher and client mode.
      */
-    static SSLEngine initEngine(SSLEngine engine, String cipher, boolean client) {
+    public static SSLEngine initEngine(SSLEngine engine, String cipher, boolean client) {
         engine.setEnabledProtocols(getProtocols());
         engine.setEnabledCipherSuites(new String[] {cipher});
         engine.setUseClientMode(client);
@@ -170,21 +157,21 @@
     /**
      * Initializes the given client-side {@code context} with a default cert.
      */
-    private static SSLContext initClientSslContext(SSLContext context) {
+    public static SSLContext initClientSslContext(SSLContext context) {
         return initSslContext(context, TestKeyStore.getClient());
     }
 
     /**
      * Initializes the given server-side {@code context} with the given cert chain and private key.
      */
-    private static SSLContext initServerSslContext(SSLContext context) {
+    public static SSLContext initServerSslContext(SSLContext context) {
         return initSslContext(context, TestKeyStore.getServer());
     }
 
     /**
      * Initializes the given {@code context} from the {@code keyStore}.
      */
-    static SSLContext initSslContext(SSLContext context, TestKeyStore keyStore) {
+    public static SSLContext initSslContext(SSLContext context, TestKeyStore keyStore) {
         try {
             context.init(keyStore.keyManagers, keyStore.trustManagers, null);
             return context;
@@ -196,7 +183,7 @@
     /**
      * Performs the intial TLS handshake between the two {@link SSLEngine} instances.
      */
-    static void doEngineHandshake(SSLEngine clientEngine, SSLEngine serverEngine)
+    public static void doEngineHandshake(SSLEngine clientEngine, SSLEngine serverEngine)
             throws SSLException {
         ByteBuffer cTOs = ByteBuffer.allocate(clientEngine.getSession().getPacketBufferSize());
         ByteBuffer sTOc = ByteBuffer.allocate(serverEngine.getSession().getPacketBufferSize());
@@ -304,7 +291,7 @@
         throw new RuntimeException("Unable to find a default provider for " + PROVIDER_PROPERTY);
     }
 
-    private static Provider getConscryptProvider() {
+    private static final Provider getConscryptProvider() {
         try {
             return (Provider) Class.forName("org.conscrypt.OpenSSLProvider")
                     .getConstructor()