Add Platform adapter around unbundled conscrypt

This wraps the conscrypt OpenSSLSocketImpl with an adapter that is a
subclass of the platform's OpenSSLSocketImpl in order to support old
code that does casts to the platform OpenSSLSocketImpl in order to set
things like SNI.

Until KK the platform OpenSSLSocketImpl was
org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl, in KK it became
com.android.org.conscrypt.OpenSSLSocketImpl. As of L MR1 the platform
HTTP stack no longer casts to the platform OpenSSLSocketImpl and this
work around is not needed on those devices.

Change-Id: I196ad957eabfc70246d9c01aa12855a8eab036f0
diff --git a/Android.mk b/Android.mk
index 124f151..8359c2e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -126,9 +126,16 @@
 LOCAL_JAVACFLAGS := $(local_javac_flags)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := conscrypt_unbundled
+LOCAL_JAVA_LIBRARIES := conscrypt-stubs
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
+# Stub library for unbundled builds
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under,src/stub/java)
+LOCAL_MODULE := conscrypt-stubs
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
 # Unbundled Conscrypt crypto JNI library
 include $(CLEAR_VARS)
 LOCAL_CFLAGS += $(core_cflags)
diff --git a/build.gradle b/build.gradle
index 12d7b99..a2db9fa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,19 @@
 apply plugin: 'java'
 
+sourceSets {
+    stub {
+        java.srcDirs = [
+            'src/stub/java'
+        ]
+    }
+}
 // this is the "Unbundled Conscrypt jar"
 sourceSets.main {
     java.srcDirs = [
         'src/main/java',
         'src/compat/java',
     ]
+    compileClasspath += sourceSets.stub.output
 }
 
 compileJava.options.encoding = 'UTF-8'
diff --git a/src/compat/java/org/conscrypt/BaseOpenSSLSocketAdapterFactory.java b/src/compat/java/org/conscrypt/BaseOpenSSLSocketAdapterFactory.java
new file mode 100644
index 0000000..99719c4
--- /dev/null
+++ b/src/compat/java/org/conscrypt/BaseOpenSSLSocketAdapterFactory.java
@@ -0,0 +1,86 @@
+/*
+ * 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.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.io.IOException;
+import javax.net.ssl.SSLSocketFactory;
+
+public abstract class BaseOpenSSLSocketAdapterFactory extends SSLSocketFactory {
+
+    private final OpenSSLSocketFactoryImpl delegate;
+
+    protected BaseOpenSSLSocketAdapterFactory(OpenSSLSocketFactoryImpl delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public String[] getDefaultCipherSuites() {
+        return delegate.getDefaultCipherSuites();
+    }
+
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return delegate.getSupportedCipherSuites();
+    }
+
+    @Override
+    public Socket createSocket() throws IOException {
+        return wrap((OpenSSLSocketImpl) delegate.createSocket());
+    }
+
+    @Override
+    public Socket createSocket(String hostname, int port)
+            throws IOException, UnknownHostException {
+        return wrap((OpenSSLSocketImpl) delegate.createSocket(hostname, port));
+    }
+
+    @Override
+    public Socket createSocket(String hostname, int port, InetAddress localHost, int localPort)
+            throws IOException, UnknownHostException {
+        return wrap(
+                (OpenSSLSocketImpl) delegate.createSocket(hostname, port, localHost, localPort));
+    }
+    @Override
+    public Socket createSocket(InetAddress address, int port) throws IOException {
+        return wrap((OpenSSLSocketImpl) delegate.createSocket(address, port));
+    }
+
+    @Override
+    public Socket createSocket(InetAddress address,
+                               int port,
+                               InetAddress localAddress,
+                               int localPort)
+            throws IOException {
+        return wrap(
+                (OpenSSLSocketImpl) delegate.createSocket(address, port, localAddress, localPort));
+    }
+
+    @Override
+    public Socket createSocket(Socket s, String hostname, int port, boolean autoClose)
+            throws IOException {
+        return wrap((OpenSSLSocketImpl) delegate.createSocket(s, hostname, port, autoClose));
+    }
+
+    /**
+     * Wraps the provided unbundled conscrypt SSLSocket into a platform bundled conscrypt
+     * SSLSocket.
+     */
+    protected abstract Socket wrap(OpenSSLSocketImpl sock) throws IOException;
+}
diff --git a/src/compat/java/org/conscrypt/KitKatPlatformOpenSSLSocketAdapterFactory.java b/src/compat/java/org/conscrypt/KitKatPlatformOpenSSLSocketAdapterFactory.java
new file mode 100644
index 0000000..c3c6d88
--- /dev/null
+++ b/src/compat/java/org/conscrypt/KitKatPlatformOpenSSLSocketAdapterFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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.IOException;
+import java.net.Socket;
+
+/**
+ * {@link SSLSocketFactory} which creates unbundled conscrypt SSLSockets and wraps them into
+ * KitKat (and newer) platform SSLSockets.
+ */
+public class KitKatPlatformOpenSSLSocketAdapterFactory extends BaseOpenSSLSocketAdapterFactory {
+
+    public KitKatPlatformOpenSSLSocketAdapterFactory(OpenSSLSocketFactoryImpl delegate) {
+        super(delegate);
+    }
+
+    @Override
+    protected Socket wrap(OpenSSLSocketImpl socket) throws IOException {
+        return new KitKatPlatformOpenSSLSocketImplAdapter(socket);
+    }
+}
diff --git a/src/compat/java/org/conscrypt/KitKatPlatformOpenSSLSocketImplAdapter.java b/src/compat/java/org/conscrypt/KitKatPlatformOpenSSLSocketImplAdapter.java
new file mode 100644
index 0000000..1f41110
--- /dev/null
+++ b/src/compat/java/org/conscrypt/KitKatPlatformOpenSSLSocketImplAdapter.java
@@ -0,0 +1,448 @@
+/*
+ * 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.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.channels.SocketChannel;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+
+/**
+ * This class delegates all calls to an {@code org.conscrypt.OpenSSLSocketImpl}.
+ * This is to work around code that checks that the socket is an
+ * {@code org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl} before
+ * calling methods, such as setting SNI. This is only for KitKat.
+ *
+ * It delegates all public methods in Socket, SSLSocket, and OpenSSLSocket from
+ * KK.
+ */
+public class KitKatPlatformOpenSSLSocketImplAdapter
+        extends com.android.org.conscrypt.OpenSSLSocketImpl {
+
+
+    private final org.conscrypt.OpenSSLSocketImpl delegate;
+
+    public KitKatPlatformOpenSSLSocketImplAdapter(org.conscrypt.OpenSSLSocketImpl delegate)
+            throws IOException {
+        super(null);
+        this.delegate = delegate;
+    }
+
+    // Socket methods.
+
+    @Override
+    public void close() throws IOException {
+        delegate.close();
+    }
+
+    @Override
+    public InputStream getInputStream() throws IOException {
+        return delegate.getInputStream();
+    }
+
+    @Override
+    public int getLocalPort() {
+        return delegate.getLocalPort();
+    }
+
+    @Override
+    public OutputStream getOutputStream() throws IOException {
+        return delegate.getOutputStream();
+    }
+
+    @Override
+    public int getPort() {
+        return delegate.getPort();
+    }
+
+    @Override
+    public void connect(SocketAddress sockaddr, int timeout) throws IOException {
+        delegate.connect(sockaddr, timeout);
+    }
+
+    @Override
+    public void connect(SocketAddress sockaddr) throws IOException {
+        delegate.connect(sockaddr);
+    }
+
+    @Override
+    public void bind(SocketAddress sockaddr) throws IOException {
+        delegate.bind(sockaddr);
+    }
+
+    @Override
+    public SocketAddress getRemoteSocketAddress() {
+        return delegate.getRemoteSocketAddress();
+    }
+
+    @Override
+    public SocketAddress getLocalSocketAddress() {
+        return delegate.getLocalSocketAddress();
+    }
+
+    @Override
+    public InetAddress getLocalAddress() {
+        return delegate.getLocalAddress();
+    }
+
+    @Override
+    public InetAddress getInetAddress() {
+        return delegate.getInetAddress();
+    }
+
+    @Override
+    public String toString() {
+        return delegate.toString();
+    }
+
+    @Override
+    public void setSoLinger(boolean on, int linger) throws SocketException {
+        delegate.setSoLinger(on, linger);
+    }
+
+    @Override
+    public void setTcpNoDelay(boolean on) throws SocketException {
+        delegate.setTcpNoDelay(on);
+    }
+
+    @Override
+    public void setReuseAddress(boolean on) throws SocketException {
+        delegate.setReuseAddress(on);
+    }
+
+    @Override
+    public void setKeepAlive(boolean on) throws SocketException {
+        delegate.setKeepAlive(on);
+    }
+
+    @Override
+    public void setTrafficClass(int tos) throws SocketException {
+        delegate.setTrafficClass(tos);
+    }
+
+    @Override
+    public void setSoTimeout(int to) throws SocketException {
+        delegate.setSoTimeout(to);
+    }
+
+    @Override
+    public void setSendBufferSize(int size) throws SocketException {
+        delegate.setSendBufferSize(size);
+    }
+
+    @Override
+    public void setReceiveBufferSize(int size) throws SocketException {
+        delegate.setReceiveBufferSize(size);
+    }
+
+    @Override
+    public boolean getTcpNoDelay() throws SocketException {
+        return delegate.getTcpNoDelay();
+    }
+
+    @Override
+    public boolean getReuseAddress() throws SocketException {
+        return delegate.getReuseAddress();
+    }
+
+    @Override
+    public boolean getKeepAlive() throws SocketException {
+        return delegate.getKeepAlive();
+    }
+
+    @Override
+    public int getSoTimeout() throws SocketException {
+        return delegate.getSoTimeout();
+    }
+
+    @Override
+    public int getSoLinger() throws SocketException {
+        return delegate.getSoLinger();
+    }
+
+    @Override
+    public int getSendBufferSize() throws SocketException {
+        return delegate.getSendBufferSize();
+    }
+
+    @Override
+    public int getReceiveBufferSize() throws SocketException {
+        return delegate.getReceiveBufferSize();
+    }
+
+    @Override
+    public boolean isConnected() {
+        return delegate.isConnected();
+    }
+
+    @Override
+    public boolean isClosed() {
+        return delegate.isClosed();
+    }
+
+    @Override
+    public boolean isBound() {
+        return delegate.isBound();
+    }
+
+    @Override
+    public boolean isOutputShutdown() {
+        return delegate.isOutputShutdown();
+    }
+
+    @Override
+    public boolean isInputShutdown() {
+        return delegate.isInputShutdown();
+    }
+
+    @Override
+    public void shutdownInput() throws IOException {
+        delegate.shutdownInput();
+    }
+
+    @Override
+    public void shutdownOutput() throws IOException {
+        delegate.shutdownOutput();
+    }
+
+    @Override
+    public void setOOBInline(boolean oobinline) throws SocketException {
+        delegate.setOOBInline(oobinline);
+    }
+
+    @Override
+    public boolean getOOBInline() throws SocketException {
+        return delegate.getOOBInline();
+    }
+
+    @Override
+    public int getTrafficClass() throws SocketException {
+        return delegate.getTrafficClass();
+    }
+
+    @Override
+    public void sendUrgentData(int value) throws IOException {
+        delegate.sendUrgentData(value);
+    }
+
+    @Override
+    public SocketChannel getChannel() {
+        return delegate.getChannel();
+    }
+
+    @Override
+    public FileDescriptor getFileDescriptor$() {
+        return delegate.getFileDescriptor$();
+    }
+
+    @Override
+    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
+        delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
+    }
+
+    // SSLSocket methods.
+
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return delegate.getSupportedCipherSuites();
+    }
+
+    @Override
+    public String[] getEnabledCipherSuites() {
+        return delegate.getEnabledCipherSuites();
+    }
+
+    @Override
+    public void setEnabledCipherSuites(String[] suites) {
+        delegate.setEnabledCipherSuites(suites);
+    }
+
+    @Override
+    public String[] getSupportedProtocols() {
+        return delegate.getSupportedProtocols();
+    }
+    @Override
+    public String[] getEnabledProtocols() {
+        return delegate.getEnabledProtocols();
+    }
+
+    @Override
+    public void setEnabledProtocols(String[] protocols) {
+        delegate.setEnabledProtocols(protocols);
+    }
+
+    @Override
+    public SSLSession getSession() {
+        return delegate.getSession();
+    }
+
+    @Override
+    public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
+        delegate.addHandshakeCompletedListener(listener);
+    }
+
+    @Override
+    public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
+        delegate.removeHandshakeCompletedListener(listener);
+    }
+
+    @Override
+    public void startHandshake() throws IOException {
+        delegate.startHandshake();
+    }
+
+    @Override
+    public void setUseClientMode(boolean mode) {
+        delegate.setUseClientMode(mode);
+    }
+
+    @Override
+    public boolean getUseClientMode() {
+        return delegate.getUseClientMode();
+    }
+
+    @Override
+    public void setNeedClientAuth(boolean need) {
+        delegate.setNeedClientAuth(need);
+    }
+
+    @Override
+    public void setWantClientAuth(boolean want) {
+        delegate.setWantClientAuth(want);
+    }
+
+    @Override
+    public boolean getNeedClientAuth() {
+        return delegate.getNeedClientAuth();
+    }
+
+    @Override
+    public boolean getWantClientAuth() {
+        return delegate.getWantClientAuth();
+    }
+
+    @Override
+    public void setEnableSessionCreation(boolean flag) {
+        delegate.setEnableSessionCreation(flag);
+    }
+
+    @Override
+    public boolean getEnableSessionCreation() {
+        return delegate.getEnableSessionCreation();
+    }
+
+    @Override
+    public SSLParameters getSSLParameters() {
+        return delegate.getSSLParameters();
+    }
+
+    @Override
+    public void setSSLParameters(SSLParameters p) {
+        delegate.setSSLParameters(p);
+    }
+
+    // OpenSSLSocket methods.
+    @Override
+    public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
+            throws CertificateEncodingException, SSLException {
+        throw new RuntimeException("Shouldn't be here!");
+    }
+
+    @Override
+    public void handshakeCompleted() {
+        throw new RuntimeException("Shouldn't be here!");
+    }
+
+    @Override
+    public void verifyCertificateChain(byte[][] bytes, String authMethod)
+            throws CertificateException {
+        throw new RuntimeException("Shouldn't be here!");
+    }
+
+    @Override
+    public void setUseSessionTickets(boolean useSessionTickets) {
+        delegate.setUseSessionTickets(useSessionTickets);
+    }
+
+    @Override
+    public void setHostname(String hostname) {
+        delegate.setHostname(hostname);
+    }
+
+    @Override
+    public void setChannelIdEnabled(boolean enabled) {
+        delegate.setChannelIdEnabled(enabled);
+    }
+
+    @Override
+    public byte[] getChannelId() throws SSLException {
+        return delegate.getChannelId();
+    }
+
+    @Override
+    public void setChannelIdPrivateKey(PrivateKey privateKey) {
+        delegate.setChannelIdPrivateKey(privateKey);
+    }
+
+    @Override
+    public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+        delegate.setSoWriteTimeout(writeTimeoutMilliseconds);
+    }
+
+    @Override
+    public int getSoWriteTimeout() throws SocketException {
+        return delegate.getSoWriteTimeout();
+    }
+
+    @Override
+    public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
+        delegate.setHandshakeTimeout(handshakeTimeoutMilliseconds);
+    }
+
+    @Override
+    public byte[] getNpnSelectedProtocol() {
+        return delegate.getNpnSelectedProtocol();
+    }
+
+    @Override
+    public void setNpnProtocols(byte[] npnProtocols) {
+        delegate.setNpnProtocols(npnProtocols);
+    }
+
+    // These aren't in the Platform's OpenSSLSocketImpl but we have them to support duck typing.
+
+    public byte[] getAlpnSelectedProtocol() {
+        return delegate.getAlpnSelectedProtocol();
+    }
+
+    public void setAlpnProtocols(byte[] alpnProtocols) {
+        delegate.setAlpnProtocols(alpnProtocols);
+    }
+}
diff --git a/src/compat/java/org/conscrypt/Platform.java b/src/compat/java/org/conscrypt/Platform.java
index a1aa32d..a13de4a 100644
--- a/src/compat/java/org/conscrypt/Platform.java
+++ b/src/compat/java/org/conscrypt/Platform.java
@@ -32,6 +32,7 @@
 
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.X509TrustManager;
 
 /**
@@ -287,4 +288,16 @@
 
         return AddressUtils.isLiteralIpAddress(hostname);
     }
+
+    /**
+     * Wrap the SocketFactory with the platform wrapper if needed for compatability.
+     */
+    public static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
+        if (Build.VERSION.SDK_INT < 19) {
+            return new PreKitKatPlatformOpenSSLSocketAdapterFactory(factory);
+        } else if (Build.VERSION.SDK_INT < 22) {
+            return new KitKatPlatformOpenSSLSocketAdapterFactory(factory);
+        }
+        return factory;
+    }
 }
diff --git a/src/compat/java/org/conscrypt/PreKitKatPlatformOpenSSLSocketAdapterFactory.java b/src/compat/java/org/conscrypt/PreKitKatPlatformOpenSSLSocketAdapterFactory.java
new file mode 100644
index 0000000..1b22b6e
--- /dev/null
+++ b/src/compat/java/org/conscrypt/PreKitKatPlatformOpenSSLSocketAdapterFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 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.IOException;
+import java.net.Socket;
+
+/**
+ * {@link SSLSocketFactory} which creates unbundled conscrypt SSLSockets and wraps them into
+ * pre-KitKat platform SSLSockets.
+ */
+public class PreKitKatPlatformOpenSSLSocketAdapterFactory extends BaseOpenSSLSocketAdapterFactory {
+    public PreKitKatPlatformOpenSSLSocketAdapterFactory(OpenSSLSocketFactoryImpl delegate) {
+        super(delegate);
+    }
+
+    @Override
+    protected Socket wrap(OpenSSLSocketImpl socket) throws IOException {
+        return new PreKitKatPlatformOpenSSLSocketImplAdapter(socket);
+    }
+}
diff --git a/src/compat/java/org/conscrypt/PreKitKatPlatformOpenSSLSocketImplAdapter.java b/src/compat/java/org/conscrypt/PreKitKatPlatformOpenSSLSocketImplAdapter.java
new file mode 100644
index 0000000..35fe01b
--- /dev/null
+++ b/src/compat/java/org/conscrypt/PreKitKatPlatformOpenSSLSocketImplAdapter.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright 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.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.channels.SocketChannel;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+
+/**
+ * This class delegates all calls to an {@code org.conscrypt.OpenSSLSocketImpl}.
+ * This is to work around code that checks that the socket is an
+ * {@code org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl} before
+ * calling methods, such as setting SNI. This is only for Pre-Kitkat devices.
+ *
+ * It delegates all public methods in Socket, SSLSocket, and OpenSSLSocket from
+ * JB.
+ */
+public class PreKitKatPlatformOpenSSLSocketImplAdapter
+        extends org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl {
+
+
+    private final org.conscrypt.OpenSSLSocketImpl delegate;
+
+    public PreKitKatPlatformOpenSSLSocketImplAdapter(org.conscrypt.OpenSSLSocketImpl delegate)
+            throws IOException {
+        super(null);
+        this.delegate = delegate;
+    }
+
+    // Socket methods.
+
+    @Override
+    public void close() throws IOException {
+        delegate.close();
+    }
+
+    @Override
+    public InputStream getInputStream() throws IOException {
+        return delegate.getInputStream();
+    }
+
+    @Override
+    public int getLocalPort() {
+        return delegate.getLocalPort();
+    }
+
+    @Override
+    public OutputStream getOutputStream() throws IOException {
+        return delegate.getOutputStream();
+    }
+
+    @Override
+    public int getPort() {
+        return delegate.getPort();
+    }
+
+    @Override
+    public void connect(SocketAddress sockaddr, int timeout) throws IOException {
+        delegate.connect(sockaddr, timeout);
+    }
+
+    @Override
+    public void connect(SocketAddress sockaddr) throws IOException {
+        delegate.connect(sockaddr);
+    }
+
+    @Override
+    public void bind(SocketAddress sockaddr) throws IOException {
+        delegate.bind(sockaddr);
+    }
+
+    @Override
+    public SocketAddress getRemoteSocketAddress() {
+        return delegate.getRemoteSocketAddress();
+    }
+
+    @Override
+    public SocketAddress getLocalSocketAddress() {
+        return delegate.getLocalSocketAddress();
+    }
+
+    @Override
+    public InetAddress getLocalAddress() {
+        return delegate.getLocalAddress();
+    }
+
+    @Override
+    public InetAddress getInetAddress() {
+        return delegate.getInetAddress();
+    }
+
+    @Override
+    public String toString() {
+        return delegate.toString();
+    }
+
+    @Override
+    public void setSoLinger(boolean on, int linger) throws SocketException {
+        delegate.setSoLinger(on, linger);
+    }
+
+    @Override
+    public void setTcpNoDelay(boolean on) throws SocketException {
+        delegate.setTcpNoDelay(on);
+    }
+
+    @Override
+    public void setReuseAddress(boolean on) throws SocketException {
+        delegate.setReuseAddress(on);
+    }
+
+    @Override
+    public void setKeepAlive(boolean on) throws SocketException {
+        delegate.setKeepAlive(on);
+    }
+
+    @Override
+    public void setTrafficClass(int tos) throws SocketException {
+        delegate.setTrafficClass(tos);
+    }
+
+    @Override
+    public void setSoTimeout(int to) throws SocketException {
+        delegate.setSoTimeout(to);
+    }
+
+    @Override
+    public void setSendBufferSize(int size) throws SocketException {
+        delegate.setSendBufferSize(size);
+    }
+
+    @Override
+    public void setReceiveBufferSize(int size) throws SocketException {
+        delegate.setReceiveBufferSize(size);
+    }
+
+    @Override
+    public boolean getTcpNoDelay() throws SocketException {
+        return delegate.getTcpNoDelay();
+    }
+
+    @Override
+    public boolean getReuseAddress() throws SocketException {
+        return delegate.getReuseAddress();
+    }
+
+    @Override
+    public boolean getKeepAlive() throws SocketException {
+        return delegate.getKeepAlive();
+    }
+
+    @Override
+    public int getSoTimeout() throws SocketException {
+        return delegate.getSoTimeout();
+    }
+
+    @Override
+    public int getSoLinger() throws SocketException {
+        return delegate.getSoLinger();
+    }
+
+    @Override
+    public int getSendBufferSize() throws SocketException {
+        return delegate.getSendBufferSize();
+    }
+
+    @Override
+    public int getReceiveBufferSize() throws SocketException {
+        return delegate.getReceiveBufferSize();
+    }
+
+    @Override
+    public boolean isConnected() {
+        return delegate.isConnected();
+    }
+
+    @Override
+    public boolean isClosed() {
+        return delegate.isClosed();
+    }
+
+    @Override
+    public boolean isBound() {
+        return delegate.isBound();
+    }
+
+    @Override
+    public boolean isOutputShutdown() {
+        return delegate.isOutputShutdown();
+    }
+
+    @Override
+    public boolean isInputShutdown() {
+        return delegate.isInputShutdown();
+    }
+
+    @Override
+    public void shutdownInput() throws IOException {
+        delegate.shutdownInput();
+    }
+
+    @Override
+    public void shutdownOutput() throws IOException {
+        delegate.shutdownOutput();
+    }
+
+    @Override
+    public void setOOBInline(boolean oobinline) throws SocketException {
+        delegate.setOOBInline(oobinline);
+    }
+
+    @Override
+    public boolean getOOBInline() throws SocketException {
+        return delegate.getOOBInline();
+    }
+
+    @Override
+    public int getTrafficClass() throws SocketException {
+        return delegate.getTrafficClass();
+    }
+
+    @Override
+    public void sendUrgentData(int value) throws IOException {
+        delegate.sendUrgentData(value);
+    }
+
+    @Override
+    public SocketChannel getChannel() {
+        return delegate.getChannel();
+    }
+
+    @Override
+    public FileDescriptor getFileDescriptor$() {
+        return delegate.getFileDescriptor$();
+    }
+
+    @Override
+    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
+        delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
+    }
+
+    // SSLSocket methods.
+
+    @Override
+    public String[] getSupportedCipherSuites() {
+        return delegate.getSupportedCipherSuites();
+    }
+
+    @Override
+    public String[] getEnabledCipherSuites() {
+        return delegate.getEnabledCipherSuites();
+    }
+
+    @Override
+    public void setEnabledCipherSuites(String[] suites) {
+        delegate.setEnabledCipherSuites(suites);
+    }
+
+    @Override
+    public String[] getSupportedProtocols() {
+        return delegate.getSupportedProtocols();
+    }
+    @Override
+    public String[] getEnabledProtocols() {
+        return delegate.getEnabledProtocols();
+    }
+
+    @Override
+    public void setEnabledProtocols(String[] protocols) {
+        delegate.setEnabledProtocols(protocols);
+    }
+
+    @Override
+    public SSLSession getSession() {
+        return delegate.getSession();
+    }
+
+    @Override
+    public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
+        delegate.addHandshakeCompletedListener(listener);
+    }
+
+    @Override
+    public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
+        delegate.removeHandshakeCompletedListener(listener);
+    }
+
+    @Override
+    public void startHandshake() throws IOException {
+        delegate.startHandshake();
+    }
+
+    @Override
+    public void setUseClientMode(boolean mode) {
+        delegate.setUseClientMode(mode);
+    }
+
+    @Override
+    public boolean getUseClientMode() {
+        return delegate.getUseClientMode();
+    }
+
+    @Override
+    public void setNeedClientAuth(boolean need) {
+        delegate.setNeedClientAuth(need);
+    }
+
+    @Override
+    public void setWantClientAuth(boolean want) {
+        delegate.setWantClientAuth(want);
+    }
+
+    @Override
+    public boolean getNeedClientAuth() {
+        return delegate.getNeedClientAuth();
+    }
+
+    @Override
+    public boolean getWantClientAuth() {
+        return delegate.getWantClientAuth();
+    }
+
+    @Override
+    public void setEnableSessionCreation(boolean flag) {
+        delegate.setEnableSessionCreation(flag);
+    }
+
+    @Override
+    public boolean getEnableSessionCreation() {
+        return delegate.getEnableSessionCreation();
+    }
+
+    @Override
+    public SSLParameters getSSLParameters() {
+        return delegate.getSSLParameters();
+    }
+
+    @Override
+    public void setSSLParameters(SSLParameters p) {
+        delegate.setSSLParameters(p);
+    }
+
+    // OpenSSLSocket methods.
+    @Override
+    public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
+            throws CertificateEncodingException, SSLException {
+        throw new RuntimeException("Shouldn't be here!");
+    }
+
+    @Override
+    public void handshakeCompleted() {
+        throw new RuntimeException("Shouldn't be here!");
+    }
+
+    @Override
+    public void verifyCertificateChain(byte[][] bytes, String authMethod)
+            throws CertificateException {
+        throw new RuntimeException("Shouldn't be here!");
+    }
+
+    @Override
+    public void setUseSessionTickets(boolean useSessionTickets) {
+        delegate.setUseSessionTickets(useSessionTickets);
+    }
+
+    @Override
+    public void setHostname(String hostname) {
+        delegate.setHostname(hostname);
+    }
+
+    @Override
+    public void setChannelIdEnabled(boolean enabled) {
+        delegate.setChannelIdEnabled(enabled);
+    }
+
+    @Override
+    public byte[] getChannelId() throws SSLException {
+        return delegate.getChannelId();
+    }
+
+    @Override
+    public void setChannelIdPrivateKey(PrivateKey privateKey) {
+        delegate.setChannelIdPrivateKey(privateKey);
+    }
+
+    @Override
+    public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+        delegate.setSoWriteTimeout(writeTimeoutMilliseconds);
+    }
+
+    @Override
+    public int getSoWriteTimeout() throws SocketException {
+        return delegate.getSoWriteTimeout();
+    }
+
+    @Override
+    public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
+        delegate.setHandshakeTimeout(handshakeTimeoutMilliseconds);
+    }
+
+    @Override
+    public byte[] getNpnSelectedProtocol() {
+        return delegate.getNpnSelectedProtocol();
+    }
+
+    @Override
+    public void setNpnProtocols(byte[] npnProtocols) {
+        delegate.setNpnProtocols(npnProtocols);
+    }
+
+    // These aren't in the Platform's OpenSSLSocketImpl but we have them to support duck typing.
+
+    public byte[] getAlpnSelectedProtocol() {
+        return delegate.getAlpnSelectedProtocol();
+    }
+
+    public void setAlpnProtocols(byte[] alpnProtocols) {
+        delegate.setAlpnProtocols(alpnProtocols);
+    }
+}
diff --git a/src/main/java/org/conscrypt/OpenSSLContextImpl.java b/src/main/java/org/conscrypt/OpenSSLContextImpl.java
index a52122c..58fdc4a 100644
--- a/src/main/java/org/conscrypt/OpenSSLContextImpl.java
+++ b/src/main/java/org/conscrypt/OpenSSLContextImpl.java
@@ -106,7 +106,7 @@
         if (sslParameters == null) {
             throw new IllegalStateException("SSLContext is not initialized.");
         }
-        return new OpenSSLSocketFactoryImpl(sslParameters);
+        return Platform.wrapSocketFactoryIfNeeded(new OpenSSLSocketFactoryImpl(sslParameters));
     }
 
     @Override
diff --git a/src/platform/java/org/conscrypt/Platform.java b/src/platform/java/org/conscrypt/Platform.java
index 4d36ced..23f54b6 100644
--- a/src/platform/java/org/conscrypt/Platform.java
+++ b/src/platform/java/org/conscrypt/Platform.java
@@ -37,6 +37,7 @@
 import java.security.spec.ECParameterSpec;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.X509TrustManager;
 
 class Platform {
@@ -152,4 +153,12 @@
     public static boolean isLiteralIpAddress(String hostname) {
         return InetAddress.isNumeric(hostname);
     }
+
+    /**
+     * Wrap the SocketFactory with the platform wrapper if needed for compatability.
+     * For the platform-bundled library we never need to wrap.
+     */
+    public static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
+        return factory;
+    }
 }
diff --git a/src/stub/java/com/android/org/conscrypt/NativeCrypto.java b/src/stub/java/com/android/org/conscrypt/NativeCrypto.java
new file mode 100644
index 0000000..e1523c7
--- /dev/null
+++ b/src/stub/java/com/android/org/conscrypt/NativeCrypto.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 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 com.android.org.conscrypt;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import javax.net.ssl.SSLException;
+
+class NativeCrypto {
+    public interface SSLHandshakeCallbacks {
+        /**
+         * Verify that we trust the certificate chain is trusted.
+         *
+         * @param asn1DerEncodedCertificateChain A chain of ASN.1 DER encoded certificates
+         * @param authMethod auth algorithm name
+         *
+         * @throws CertificateException if the certificate is untrusted
+         */
+        public void verifyCertificateChain(byte[][] asn1DerEncodedCertificateChain, String authMethod)
+            throws CertificateException;
+        /**
+         * Called on an SSL client when the server requests (or
+         * requires a certificate). The client can respond by using
+         * SSL_use_certificate and SSL_use_PrivateKey to set a
+         * certificate if has an appropriate one available, similar to
+         * how the server provides its certificate.
+         *
+         * @param keyTypes key types supported by the server,
+         * convertible to strings with #keyType
+         * @param asn1DerEncodedX500Principals CAs known to the server
+         */
+        public void clientCertificateRequested(byte[] keyTypes,
+                                               byte[][] asn1DerEncodedX500Principals)
+            throws CertificateEncodingException, SSLException;
+        /**
+         * Called when SSL handshake is completed. Note that this can
+         * be after SSL_do_handshake returns when handshake cutthrough
+         * is enabled.
+         */
+        public void handshakeCompleted();
+    }
+}
diff --git a/src/stub/java/com/android/org/conscrypt/OpenSSLSocketImpl.java b/src/stub/java/com/android/org/conscrypt/OpenSSLSocketImpl.java
new file mode 100644
index 0000000..c46ec63
--- /dev/null
+++ b/src/stub/java/com/android/org/conscrypt/OpenSSLSocketImpl.java
@@ -0,0 +1,269 @@
+/*
+ * 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 com.android.org.conscrypt;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+
+
+/**
+ * Implementation of the class OpenSSLSocketImpl based on OpenSSL.
+ * <p>
+ * Extensions to SSLSocket include:
+ * <ul>
+ * <li>handshake timeout
+ * <li>session tickets
+ * <li>Server Name Indication
+ * </ul>
+ */
+public class OpenSSLSocketImpl
+        extends javax.net.ssl.SSLSocket
+        implements NativeCrypto.SSLHandshakeCallbacks {
+
+
+    protected OpenSSLSocketImpl(SSLParametersImpl sslParameters) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    protected OpenSSLSocketImpl(SSLParametersImpl sslParameters,
+                                String[] enabledProtocols,
+                                String[] enabledCipherSuites) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    protected OpenSSLSocketImpl(String host, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    protected OpenSSLSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+
+    protected OpenSSLSocketImpl(String host, int port,
+                                InetAddress clientAddress, int clientPort,
+                                SSLParametersImpl sslParameters) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    protected OpenSSLSocketImpl(InetAddress address, int port,
+                                InetAddress clientAddress, int clientPort,
+                                SSLParametersImpl sslParameters) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    protected OpenSSLSocketImpl(Socket socket, String host, int port,
+            boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public synchronized void startHandshake() throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / client_cert_cb
+    public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
+            throws CertificateEncodingException, SSLException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / info_callback
+    public void handshakeCompleted() {
+        throw new RuntimeException("Stub!");
+    }
+    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks
+    @Override public void verifyCertificateChain(byte[][] bytes, String authMethod)
+            throws CertificateException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public InputStream getInputStream() throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public OutputStream getOutputStream() throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+    @Override public SSLSession getSession() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void addHandshakeCompletedListener(
+            HandshakeCompletedListener listener) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void removeHandshakeCompletedListener(
+            HandshakeCompletedListener listener) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public boolean getEnableSessionCreation() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setEnableSessionCreation(boolean flag) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public String[] getSupportedCipherSuites() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public String[] getEnabledCipherSuites() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setEnabledCipherSuites(String[] suites) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public String[] getSupportedProtocols() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public String[] getEnabledProtocols() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setEnabledProtocols(String[] protocols) {
+        throw new RuntimeException("Stub!");
+    }
+    public void setUseSessionTickets(boolean useSessionTickets) {
+        throw new RuntimeException("Stub!");
+    }
+
+    public void setHostname(String hostname) {
+        throw new RuntimeException("Stub!");
+    }
+
+    public void setChannelIdEnabled(boolean enabled) {
+        throw new RuntimeException("Stub!");
+    }
+
+    public byte[] getChannelId() throws SSLException {
+        throw new RuntimeException("Stub!");
+    }
+    public void setChannelIdPrivateKey(PrivateKey privateKey) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public boolean getUseClientMode() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setUseClientMode(boolean mode) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public boolean getWantClientAuth() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public boolean getNeedClientAuth() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setNeedClientAuth(boolean need) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setWantClientAuth(boolean want) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void sendUrgentData(int data) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setOOBInline(boolean on) throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setSoTimeout(int readTimeoutMilliseconds) throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public int getSoTimeout() throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    /**
+     * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+     */
+    public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    /**
+     * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+     */
+    public int getSoWriteTimeout() throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    /**
+     * Set the handshake timeout on this socket.  This timeout is specified in
+     * milliseconds and will be used only during the handshake process.
+     */
+    public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void close() throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    public FileDescriptor getFileDescriptor$() {
+        throw new RuntimeException("Stub!");
+    }
+
+    /**
+     * Returns the protocol agreed upon by client and server, or null if no
+     * protocol was agreed upon.
+     */
+    public byte[] getNpnSelectedProtocol() {
+        throw new RuntimeException("Stub!");
+    }
+
+    /**
+     * Sets the list of protocols this peer is interested in. If null no
+     * protocols will be used.
+     *
+     * @param npnProtocols a non-empty array of protocol names. From
+     *     SSL_select_next_proto, "vector of 8-bit, length prefixed byte
+     *     strings. The length byte itself is not included in the length. A byte
+     *     string of length 0 is invalid. No byte string may be truncated.".
+     */
+    public void setNpnProtocols(byte[] npnProtocols) {
+        throw new RuntimeException("Stub!");
+    }
+}
diff --git a/src/stub/java/com/android/org/conscrypt/SSLParametersImpl.java b/src/stub/java/com/android/org/conscrypt/SSLParametersImpl.java
new file mode 100644
index 0000000..079f89f
--- /dev/null
+++ b/src/stub/java/com/android/org/conscrypt/SSLParametersImpl.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 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 com.android.org.conscrypt;
+
+class SSLParametersImpl {
+    public static SSLParametersImpl getDefault() {
+        throw new RuntimeException("Stub!");
+    }
+}
diff --git a/src/stub/java/org/apache/harmony/xnet/xnet/provider/jsse/NativeCrypto.java b/src/stub/java/org/apache/harmony/xnet/xnet/provider/jsse/NativeCrypto.java
new file mode 100644
index 0000000..b47f764
--- /dev/null
+++ b/src/stub/java/org/apache/harmony/xnet/xnet/provider/jsse/NativeCrypto.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 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.apache.harmony.xnet.provider.jsse;
+
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import javax.net.ssl.SSLException;
+
+class NativeCrypto {
+    public interface SSLHandshakeCallbacks {
+        /**
+         * Verify that we trust the certificate chain is trusted.
+         *
+         * @param asn1DerEncodedCertificateChain A chain of ASN.1 DER encoded certificates
+         * @param authMethod auth algorithm name
+         *
+         * @throws CertificateException if the certificate is untrusted
+         */
+        public void verifyCertificateChain(byte[][] asn1DerEncodedCertificateChain, String authMethod)
+            throws CertificateException;
+        /**
+         * Called on an SSL client when the server requests (or
+         * requires a certificate). The client can respond by using
+         * SSL_use_certificate and SSL_use_PrivateKey to set a
+         * certificate if has an appropriate one available, similar to
+         * how the server provides its certificate.
+         *
+         * @param keyTypes key types supported by the server,
+         * convertible to strings with #keyType
+         * @param asn1DerEncodedX500Principals CAs known to the server
+         */
+        public void clientCertificateRequested(byte[] keyTypes,
+                                               byte[][] asn1DerEncodedX500Principals)
+            throws CertificateEncodingException, SSLException;
+        /**
+         * Called when SSL handshake is completed. Note that this can
+         * be after SSL_do_handshake returns when handshake cutthrough
+         * is enabled.
+         */
+        public void handshakeCompleted();
+    }
+}
diff --git a/src/stub/java/org/apache/harmony/xnet/xnet/provider/jsse/OpenSSLSocketImpl.java b/src/stub/java/org/apache/harmony/xnet/xnet/provider/jsse/OpenSSLSocketImpl.java
new file mode 100644
index 0000000..6b3cd16
--- /dev/null
+++ b/src/stub/java/org/apache/harmony/xnet/xnet/provider/jsse/OpenSSLSocketImpl.java
@@ -0,0 +1,269 @@
+/*
+ * 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.apache.harmony.xnet.provider.jsse;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+
+
+/**
+ * Implementation of the class OpenSSLSocketImpl based on OpenSSL.
+ * <p>
+ * Extensions to SSLSocket include:
+ * <ul>
+ * <li>handshake timeout
+ * <li>session tickets
+ * <li>Server Name Indication
+ * </ul>
+ */
+public class OpenSSLSocketImpl
+        extends javax.net.ssl.SSLSocket
+        implements NativeCrypto.SSLHandshakeCallbacks {
+
+
+    protected OpenSSLSocketImpl(SSLParametersImpl sslParameters) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    protected OpenSSLSocketImpl(SSLParametersImpl sslParameters,
+                                String[] enabledProtocols,
+                                String[] enabledCipherSuites) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    protected OpenSSLSocketImpl(String host, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    protected OpenSSLSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters)
+            throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+
+    protected OpenSSLSocketImpl(String host, int port,
+                                InetAddress clientAddress, int clientPort,
+                                SSLParametersImpl sslParameters) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    protected OpenSSLSocketImpl(InetAddress address, int port,
+                                InetAddress clientAddress, int clientPort,
+                                SSLParametersImpl sslParameters) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    protected OpenSSLSocketImpl(Socket socket, String host, int port,
+            boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public synchronized void startHandshake() throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / client_cert_cb
+    public void clientCertificateRequested(byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals)
+            throws CertificateEncodingException, SSLException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks / info_callback
+    public void handshakeCompleted() {
+        throw new RuntimeException("Stub!");
+    }
+    @SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks
+    @Override public void verifyCertificateChain(byte[][] bytes, String authMethod)
+            throws CertificateException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public InputStream getInputStream() throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public OutputStream getOutputStream() throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+    @Override public SSLSession getSession() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void addHandshakeCompletedListener(
+            HandshakeCompletedListener listener) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void removeHandshakeCompletedListener(
+            HandshakeCompletedListener listener) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public boolean getEnableSessionCreation() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setEnableSessionCreation(boolean flag) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public String[] getSupportedCipherSuites() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public String[] getEnabledCipherSuites() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setEnabledCipherSuites(String[] suites) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public String[] getSupportedProtocols() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public String[] getEnabledProtocols() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setEnabledProtocols(String[] protocols) {
+        throw new RuntimeException("Stub!");
+    }
+    public void setUseSessionTickets(boolean useSessionTickets) {
+        throw new RuntimeException("Stub!");
+    }
+
+    public void setHostname(String hostname) {
+        throw new RuntimeException("Stub!");
+    }
+
+    public void setChannelIdEnabled(boolean enabled) {
+        throw new RuntimeException("Stub!");
+    }
+
+    public byte[] getChannelId() throws SSLException {
+        throw new RuntimeException("Stub!");
+    }
+    public void setChannelIdPrivateKey(PrivateKey privateKey) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public boolean getUseClientMode() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setUseClientMode(boolean mode) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public boolean getWantClientAuth() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public boolean getNeedClientAuth() {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setNeedClientAuth(boolean need) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setWantClientAuth(boolean want) {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void sendUrgentData(int data) throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setOOBInline(boolean on) throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void setSoTimeout(int readTimeoutMilliseconds) throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public int getSoTimeout() throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    /**
+     * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+     */
+    public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    /**
+     * Note write timeouts are not part of the javax.net.ssl.SSLSocket API
+     */
+    public int getSoWriteTimeout() throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    /**
+     * Set the handshake timeout on this socket.  This timeout is specified in
+     * milliseconds and will be used only during the handshake process.
+     */
+    public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
+        throw new RuntimeException("Stub!");
+    }
+
+    @Override public void close() throws IOException {
+        throw new RuntimeException("Stub!");
+    }
+
+    public FileDescriptor getFileDescriptor$() {
+        throw new RuntimeException("Stub!");
+    }
+
+    /**
+     * Returns the protocol agreed upon by client and server, or null if no
+     * protocol was agreed upon.
+     */
+    public byte[] getNpnSelectedProtocol() {
+        throw new RuntimeException("Stub!");
+    }
+
+    /**
+     * Sets the list of protocols this peer is interested in. If null no
+     * protocols will be used.
+     *
+     * @param npnProtocols a non-empty array of protocol names. From
+     *     SSL_select_next_proto, "vector of 8-bit, length prefixed byte
+     *     strings. The length byte itself is not included in the length. A byte
+     *     string of length 0 is invalid. No byte string may be truncated.".
+     */
+    public void setNpnProtocols(byte[] npnProtocols) {
+        throw new RuntimeException("Stub!");
+    }
+}
diff --git a/src/stub/java/org/apache/harmony/xnet/xnet/provider/jsse/SSLParametersImpl.java b/src/stub/java/org/apache/harmony/xnet/xnet/provider/jsse/SSLParametersImpl.java
new file mode 100644
index 0000000..2c8231c
--- /dev/null
+++ b/src/stub/java/org/apache/harmony/xnet/xnet/provider/jsse/SSLParametersImpl.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 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.apache.harmony.xnet.provider.jsse;
+
+class SSLParametersImpl {
+    public static SSLParametersImpl getDefault() {
+        throw new RuntimeException("Stub!");
+    }
+}