New "from scratch" Junit tests for javax.net.ssl

These new tests focusi on areas where I am making OpenSSL
implementation chages, where I found differences from the RI when
working on that code, and places where we were missing functionality
compared to the spec. They all work on the RI, many fail on Dalvik

	luni/src/test/java/javax/net/ssl/AllTests.java
	luni/src/test/java/javax/net/ssl/SSLContextTest.java
	luni/src/test/java/javax/net/ssl/SSLSessionContextTest.java
	luni/src/test/java/javax/net/ssl/SSLSessionTest.java
	luni/src/test/java/javax/net/ssl/SSLSocketFactoryTest.java
	luni/src/test/java/javax/net/ssl/SSLSocketTest.java

    Adding my javax.net.ssl.AllTests to the main list

	luni/src/test/java/tests/AllTests.java

    Adding Bouncy Castle provider /usr/share/java/bcprov.jar to host
    classpath for testing with --java-home /usr/lib/jvm/java-6-openjdk

	tools/runner/java/dalvik/runner/JavaVm.java

    Fix usage comment to reboot to bootloader
	run-core-tests

Change-Id: I7a7998fc7bedc7a00b1836517d043c6a27c8bd4d
diff --git a/libcore/luni/src/test/java/javax/net/ssl/AllTests.java b/libcore/luni/src/test/java/javax/net/ssl/AllTests.java
new file mode 100644
index 0000000..740b788
--- /dev/null
+++ b/libcore/luni/src/test/java/javax/net/ssl/AllTests.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.net.ssl;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AllTests {
+    public static final Test suite() {
+        TestSuite suite = new TestSuite();
+        suite.addTestSuite(SSLSocketFactoryTest.class);
+        suite.addTestSuite(SSLContextTest.class);
+        suite.addTestSuite(SSLSocketTest.class);
+        suite.addTestSuite(SSLSessionTest.class);
+        suite.addTestSuite(SSLSessionContextTest.class);
+        return suite;
+    }
+}
diff --git a/libcore/luni/src/test/java/javax/net/ssl/SSLContextTest.java b/libcore/luni/src/test/java/javax/net/ssl/SSLContextTest.java
new file mode 100644
index 0000000..39a59e3
--- /dev/null
+++ b/libcore/luni/src/test/java/javax/net/ssl/SSLContextTest.java
@@ -0,0 +1,361 @@
+/*
+ * 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 javax.net.ssl;
+
+import dalvik.annotation.KnownFailure;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Date;
+import java.util.Hashtable;
+import javax.net.ServerSocketFactory;
+import javax.net.SocketFactory;
+import junit.framework.TestCase;
+import org.bouncycastle.jce.X509Principal;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+
+public class SSLContextTest extends TestCase {
+
+    public static final boolean IS_RI = !"Dalvik Core Library".equals(System.getProperty("java.specification.name"));
+    public static final String PROVIDER_NAME = (IS_RI) ? "SunJSSE" : "HarmonyJSSE";
+
+    public void test_SSLContext_getInstance() throws Exception {
+        try {
+            SSLContext.getInstance(null);
+            fail();
+        } catch (NullPointerException e) {
+        }
+        assertNotNull(SSLContext.getInstance("SSL"));
+        assertNotNull(SSLContext.getInstance("SSLv3"));
+        assertNotNull(SSLContext.getInstance("TLS"));
+        assertNotNull(SSLContext.getInstance("TLSv1"));
+
+        assertNotSame(SSLContext.getInstance("TLS"),
+                      SSLContext.getInstance("TLS"));
+
+        try {
+            SSLContext.getInstance(null, (String) null);
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            SSLContext.getInstance(null, "");
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            SSLContext.getInstance("TLS", (String) null);
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            SSLContext.getInstance(null, PROVIDER_NAME);
+            fail();
+        } catch (NullPointerException e) {
+        }
+    }
+
+    public void test_SSLContext_getProtocol() throws Exception {
+        assertProtocolExistsForName("SSL");
+        assertProtocolExistsForName("TLS");
+    }
+
+    private void assertProtocolExistsForName(String protocolName) throws Exception {
+        String protocol = SSLContext.getInstance(protocolName).getProtocol();
+        assertNotNull(protocol);
+        assertEquals(protocolName, protocol);
+    }
+
+    public void test_SSLContext_getProvider() throws Exception {
+        Provider provider = SSLContext.getInstance("TLS").getProvider();
+        assertNotNull(provider);
+        assertEquals(PROVIDER_NAME, provider.getName());
+    }
+
+    public void test_SSLContext_init() throws Exception {
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(null, null, null);
+    }
+
+    public void test_SSLContext_getSocketFactory() throws Exception {
+        try {
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.getSocketFactory();
+            fail();
+        } catch (IllegalStateException e) {
+        }
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(null, null, null);
+        SocketFactory sf = sslContext.getSocketFactory();
+        assertNotNull(sf);
+        assertTrue(SSLSocketFactory.class.isAssignableFrom(sf.getClass()));
+    }
+
+    public void test_SSLContext_getServerSocketFactory() throws Exception {
+        try {
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.getServerSocketFactory();
+            fail();
+        } catch (IllegalStateException e) {
+        }
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(null, null, null);
+        ServerSocketFactory ssf = sslContext.getServerSocketFactory();
+        assertNotNull(ssf);
+        assertTrue(SSLServerSocketFactory.class.isAssignableFrom(ssf.getClass()));
+    }
+
+    public void test_SSLContext_createSSLEngine() throws Exception {
+        try {
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.createSSLEngine();
+            fail();
+        } catch (IllegalStateException e) {
+        }
+        try {
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.createSSLEngine(null, -1);
+            fail();
+        } catch (IllegalStateException e) {
+        }
+        {
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.init(null, null, null);
+            SSLEngine se = sslContext.createSSLEngine();
+            assertNotNull(se);
+        }
+        {
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+            sslContext.init(null, null, null);
+            SSLEngine se = sslContext.createSSLEngine(null, -1);
+            assertNotNull(se);
+        }
+    }
+
+    @KnownFailure("Should be able to ask SSLContext for SSLSessionContext's before called SSLContext.init")
+    public void test_SSLContext_getServerSessionContext() throws Exception {
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        SSLSessionContext sessionContext = sslContext.getServerSessionContext();
+        assertNotNull(sessionContext);
+
+        assertNotSame(SSLContext.getInstance("TLS").getServerSessionContext(),
+                      sessionContext);
+    }
+
+    @KnownFailure("Should be able to ask SSLContext for SSLSessionContext's before called SSLContext.init")
+    public void test_SSLContext_getClientSessionContext() throws Exception {
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        SSLSessionContext sessionContext = sslContext.getClientSessionContext();
+        assertNotNull(sessionContext);
+
+        assertNotSame(SSLContext.getInstance("TLS").getClientSessionContext(),
+                      sessionContext);
+    }
+
+    /**
+     * SSLContextTest.Helper is a convenience class for other tests that
+     * want a canned SSLContext and related state for testing so they
+     * don't have to duplicate the logic.
+     */
+    public static final class Helper {
+
+        static {
+            if (SSLContextTest.IS_RI) {
+                Security.addProvider(new BouncyCastleProvider());
+            }
+        }
+
+        public final KeyStore keyStore;
+        public final char[] keyStorePassword;
+        public final String publicAlias;
+        public final String privateAlias;
+        public final SSLContext sslContext;
+        public final SSLServerSocket serverSocket;
+        public final InetAddress host;
+        public final int port;
+
+        private Helper(final KeyStore keyStore,
+                       final char[] keyStorePassword,
+                       final String publicAlias,
+                       final String privateAlias,
+                       final SSLContext sslContext,
+                       final SSLServerSocket serverSocket,
+                       final InetAddress host,
+                       final int port) {
+            this.keyStore = keyStore;
+            this.keyStorePassword = keyStorePassword;
+            this.publicAlias = publicAlias;
+            this.privateAlias = privateAlias;
+            this.sslContext = sslContext;
+            this.serverSocket = serverSocket;
+            this.host = host;
+            this.port = port;
+        }
+
+        public static Helper create() {
+            try {
+                final char[] keyStorePassword = null;
+                final String publicAlias = "public";
+                final String privateAlias = "private";
+                return create(createKeyStore(keyStorePassword, publicAlias, privateAlias),
+                              null,
+                              publicAlias,
+                              privateAlias);
+            } catch (RuntimeException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public static Helper create(final KeyStore keyStore,
+                                    final char[] keyStorePassword,
+                                    final String publicAlias,
+                                    final String privateAlias) {
+            try {
+                final SSLContext sslContext = createSSLContext(keyStore, keyStorePassword);
+
+                final SSLServerSocket serverSocket = (SSLServerSocket)
+                    sslContext.getServerSocketFactory().createServerSocket(0);
+                final InetSocketAddress sa = (InetSocketAddress) serverSocket.getLocalSocketAddress();
+                final InetAddress host = sa.getAddress();
+                final int port = sa.getPort();
+
+                return new Helper(keyStore, keyStorePassword, publicAlias, privateAlias,
+                                  sslContext, serverSocket, host, port);
+            } catch (RuntimeException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        /**
+         * Create a BKS KeyStore containing an RSAPrivateKey with alias
+         * "private" and a X509Certificate based on the matching
+         * RSAPublicKey stored under the alias name publicAlias.
+         *
+         * The private key will have a certificate chain including the
+         * certificate stored under the alias name privateAlias. The
+         * certificate will be signed by the private key. The certificate
+         * Subject and Issuer Common-Name will be the local host's
+         * canonical hostname. The certificate will be valid for one day
+         * before and one day after the time of creation.
+         *
+         * The KeyStore is optionally password protected by the
+         * keyStorePassword argument, which can be null if a password is
+         * not desired.
+         *
+         * Based on:
+         * org.bouncycastle.jce.provider.test.SigTest
+         * org.bouncycastle.jce.provider.test.CertTest
+         */
+        public static KeyStore createKeyStore(final char[] keyStorePassword,
+                                              String publicAlias,
+                                              String privateAlias)
+                throws Exception {
+
+            // 1.) we make the keys
+            final int keysize = 1024;
+            final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+            kpg.initialize(keysize, new SecureRandom());
+            final KeyPair kp = kpg.generateKeyPair();
+            final RSAPrivateKey privateKey = (RSAPrivateKey)kp.getPrivate();
+            final RSAPublicKey publicKey  = (RSAPublicKey)kp.getPublic();
+
+            // 2.) use keys to make certficate
+
+            // note that there doesn't seem to be a standard way to make a
+            // certificate using java.* or javax.*. The CertificateFactory
+            // interface assumes you want to read in a stream of bytes a
+            // factory specific format. So here we use Bouncy Castle's
+            // X509V3CertificateGenerator and related classes.
+
+            final Hashtable attributes = new Hashtable();
+            attributes.put(X509Principal.CN, InetAddress.getLocalHost().getCanonicalHostName());
+            final X509Principal dn = new X509Principal(attributes);
+
+            final long millisPerDay = 24 * 60 * 60 * 1000;
+            final long now = System.currentTimeMillis();
+            final Date start = new Date(now - millisPerDay);
+            final Date end = new Date(now + millisPerDay);
+            final BigInteger serial = BigInteger.valueOf(1);
+
+            final X509V3CertificateGenerator x509cg = new X509V3CertificateGenerator();
+            x509cg.setSubjectDN(dn);
+            x509cg.setIssuerDN(dn);
+            x509cg.setNotBefore(start);
+            x509cg.setNotAfter(end);
+            x509cg.setPublicKey(publicKey);
+            x509cg.setSignatureAlgorithm("sha1WithRSAEncryption");
+            x509cg.setSerialNumber(serial);
+            final X509Certificate x509c = x509cg.generateX509Certificate(privateKey);
+            final X509Certificate[] x509cc = new X509Certificate[] { x509c };
+
+
+            // 3.) put certificate and private key to make a key store
+            final KeyStore ks = KeyStore.getInstance("BKS");
+            ks.load(null, null);
+            ks.setKeyEntry(privateAlias, privateKey, keyStorePassword, x509cc);
+            ks.setCertificateEntry(publicAlias, x509c);
+            return ks;
+        }
+
+        /**
+         * Create a SSLContext with a KeyManager using the private key and
+         * certificate chain from the given KeyStore and a TrustManager
+         * using the certificates authorities from the same KeyStore.
+         */
+        public static final SSLContext createSSLContext(final KeyStore keyStore, final char[] keyStorePassword)
+                throws Exception {
+            final String kmfa = KeyManagerFactory.getDefaultAlgorithm();
+            final KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfa);
+            kmf.init(keyStore, keyStorePassword);
+
+            final String tmfa = TrustManagerFactory.getDefaultAlgorithm();
+            final TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfa);
+            tmf.init(keyStore);
+
+            final SSLContext context = SSLContext.getInstance("TLS");
+            context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
+            return context;
+        }
+    }
+
+    public void test_SSLContextTest_Helper_create() {
+        Helper helper = Helper.create();
+        assertNotNull(helper);
+        assertNotNull(helper.keyStore);
+        assertNull(helper.keyStorePassword);
+        assertNotNull(helper.publicAlias);
+        assertNotNull(helper.privateAlias);
+        assertNotNull(helper.sslContext);
+        assertNotNull(helper.serverSocket);
+        assertNotNull(helper.host);
+        assertTrue(helper.port != 0);
+    }
+}
diff --git a/libcore/luni/src/test/java/javax/net/ssl/SSLSessionContextTest.java b/libcore/luni/src/test/java/javax/net/ssl/SSLSessionContextTest.java
new file mode 100644
index 0000000..38848da
--- /dev/null
+++ b/libcore/luni/src/test/java/javax/net/ssl/SSLSessionContextTest.java
@@ -0,0 +1,220 @@
+/*
+ * 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 javax.net.ssl;
+
+import dalvik.annotation.KnownFailure;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import junit.framework.TestCase;
+
+public class SSLSessionContextTest extends TestCase {
+
+    public static final void assertSSLSessionContextSize(int expected, SSLSessionContext s) {
+        assertEquals(expected, Collections.list(s.getIds()).size());
+    }
+
+    public void test_SSLSessionContext_getIds() {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        assertSSLSessionContextSize(0, c.sslContext.getClientSessionContext());
+        assertSSLSessionContextSize(0, c.sslContext.getServerSessionContext());
+
+        final SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+        assertSSLSessionContextSize(1, s.c.sslContext.getClientSessionContext());
+        assertSSLSessionContextSize(1, s.c.sslContext.getServerSessionContext());
+        final Enumeration clientIds = s.c.sslContext.getClientSessionContext().getIds();
+        final Enumeration serverIds = s.c.sslContext.getServerSessionContext().getIds();
+        final byte[] clientId = (byte[]) clientIds.nextElement();
+        final byte[] serverId = (byte[]) serverIds.nextElement();
+        assertEquals(32, clientId.length);
+        assertEquals(32, serverId.length);
+        assertEquals(clientId, serverId);
+    }
+
+    @KnownFailure("Should throw NullPointerException on getSession(null)")
+    public void test_SSLSessionContext_getSession() {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        try {
+            c.sslContext.getClientSessionContext().getSession(null);
+            fail();
+        } catch (NullPointerException e) {
+        }
+        assertNull(c.sslContext.getClientSessionContext().getSession(new byte[0]));
+        assertNull(c.sslContext.getClientSessionContext().getSession(new byte[1]));
+
+        final SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+        final SSLSessionContext client = s.c.sslContext.getClientSessionContext();
+        final SSLSessionContext server = s.c.sslContext.getServerSessionContext();
+        final byte[] clientId = (byte[]) client.getIds().nextElement();
+        final byte[] serverId = (byte[]) server.getIds().nextElement();
+        assertNotNull(client.getSession(clientId));
+        assertNotNull(server.getSession(serverId));
+        assertEquals(clientId, client.getSession(clientId).getId());
+        assertEquals(serverId, server.getSession(serverId).getId());
+    }
+
+    @KnownFailure("Should return 0 for unlimited, not 10 entries")
+    public void test_SSLSessionContext_getSessionCacheSize() {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        assertEquals(0, c.sslContext.getClientSessionContext().getSessionCacheSize());
+        assertEquals(0, c.sslContext.getServerSessionContext().getSessionCacheSize());
+
+        final SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+        assertEquals(0, s.c.sslContext.getClientSessionContext().getSessionCacheSize());
+        assertEquals(0, s.c.sslContext.getServerSessionContext().getSessionCacheSize());
+    }
+
+    @KnownFailure("Should return 0 for unlimited, not 10 entries")
+    public void test_SSLSessionContext_setSessionCacheSize_basic() {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        assertBasicSetSessionCacheSizeBehavior(c.sslContext.getClientSessionContext());
+        assertBasicSetSessionCacheSizeBehavior(c.sslContext.getServerSessionContext());
+    }
+
+    private static void assertBasicSetSessionCacheSizeBehavior(SSLSessionContext s) {
+        try {
+            s.setSessionCacheSize(-1);
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+        assertEquals(0, s.getSessionCacheSize());
+        s.setSessionCacheSize(1);
+        assertEquals(1, s.getSessionCacheSize());
+    }
+
+    @KnownFailure("Should return 0 for unlimited, not 10 entries")
+    public void test_SSLSessionContext_setSessionCacheSize_dynamic() {
+
+        final SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+        final SSLSessionContext client = s.c.sslContext.getClientSessionContext();
+        final SSLSessionContext server = s.c.sslContext.getServerSessionContext();
+        assertEquals(0, client.getSessionCacheSize());
+        assertEquals(0, server.getSessionCacheSize());
+        assertSSLSessionContextSize(1, client);
+        assertSSLSessionContextSize(1, server);
+
+        final LinkedList<String> uniqueCipherSuites
+            = new LinkedList(Arrays.asList(s.server.getEnabledCipherSuites()));
+        uniqueCipherSuites.remove(s.client.getSession().getCipherSuite());
+
+        // only use RSA cipher suites which will work with our TrustProvider
+        Iterator<String> i = uniqueCipherSuites.iterator();
+        while (i.hasNext()) {
+            String cipherSuite = i.next();
+            if (cipherSuite.startsWith("SSL_RSA_")) {
+                continue;
+            }
+            if (cipherSuite.startsWith("TLS_RSA_")) {
+                continue;
+            }
+            if (cipherSuite.startsWith("TLS_DHE_RSA_")) {
+                continue;
+            }
+            if (cipherSuite.startsWith("SSL_DHE_RSA_")) {
+                continue;
+            }
+            i.remove();
+        }
+
+        /*
+         * having more than 5 uniqueCipherSuites is a test
+         * requirement, not a requirement of the interface or
+         * implementation. It simply allows us to make sure that we
+         * will not get a cached session ID since we'll have to
+         * renegotiate a new session due to the new cipher suite
+         * requirement. even this test only really needs three if it
+         * reused the unique cipher suites every time it resets the
+         * session cache.
+         */
+        assertTrue(uniqueCipherSuites.size() > 5);
+
+        SSLSocketTest.Helper.connect_workaround(s.c,
+                                              new String[] { uniqueCipherSuites.remove() }); // 1
+        assertSSLSessionContextSize(2, client);
+        assertSSLSessionContextSize(2, server);
+        SSLSocketTest.Helper.connect_workaround(s.c,
+                                              new String[] { uniqueCipherSuites.remove() }); // 2
+        assertSSLSessionContextSize(3, client);
+        assertSSLSessionContextSize(3, server);
+
+        client.setSessionCacheSize(1);
+        server.setSessionCacheSize(1);
+        assertEquals(1, client.getSessionCacheSize());
+        assertEquals(1, server.getSessionCacheSize());
+        assertSSLSessionContextSize(1, client);
+        assertSSLSessionContextSize(1, server);
+        SSLSocketTest.Helper.connect_workaround(s.c,
+                                              new String[] { uniqueCipherSuites.remove() }); // 3
+        assertSSLSessionContextSize(1, client);
+        assertSSLSessionContextSize(1, server);
+
+        client.setSessionCacheSize(2);
+        server.setSessionCacheSize(2);
+        SSLSocketTest.Helper.connect_workaround(s.c,
+                                              new String[] { uniqueCipherSuites.remove() }); // 4
+        assertSSLSessionContextSize(2, client);
+        assertSSLSessionContextSize(2, server);
+        SSLSocketTest.Helper.connect_workaround(s.c,
+                                              new String[] { uniqueCipherSuites.remove() }); // 5
+        assertSSLSessionContextSize(2, client);
+        assertSSLSessionContextSize(2, server);
+    }
+
+    @KnownFailure("Should return 86400 seconds (1 day), not 0 for unlimited")
+    public void test_SSLSessionContext_getSessionTimeout() {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        assertEquals(86400, c.sslContext.getClientSessionContext().getSessionTimeout());
+        assertEquals(86400, c.sslContext.getServerSessionContext().getSessionTimeout());
+
+        final SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+        assertEquals(86400, s.c.sslContext.getClientSessionContext().getSessionTimeout());
+        assertEquals(86400, s.c.sslContext.getServerSessionContext().getSessionTimeout());
+    }
+
+    @KnownFailure("Should return 86400 seconds (1 day), not 0 for unlimited")
+    public void test_SSLSessionContext_setSessionTimeout() throws Exception {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        assertEquals(86400, c.sslContext.getClientSessionContext().getSessionTimeout());
+        assertEquals(86400, c.sslContext.getServerSessionContext().getSessionTimeout());
+        c.sslContext.getClientSessionContext().setSessionTimeout(0);
+        c.sslContext.getServerSessionContext().setSessionTimeout(0);
+        assertEquals(0, c.sslContext.getClientSessionContext().getSessionTimeout());
+        assertEquals(0, c.sslContext.getServerSessionContext().getSessionTimeout());
+
+        try {
+            c.sslContext.getClientSessionContext().setSessionTimeout(-1);
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            c.sslContext.getServerSessionContext().setSessionTimeout(-1);
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+
+        final SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+        assertSSLSessionContextSize(1, s.c.sslContext.getClientSessionContext());
+        assertSSLSessionContextSize(1, s.c.sslContext.getServerSessionContext());
+        Thread.sleep(1 * 1000);
+        s.c.sslContext.getClientSessionContext().setSessionTimeout(1);
+        s.c.sslContext.getServerSessionContext().setSessionTimeout(1);
+        assertSSLSessionContextSize(0, s.c.sslContext.getClientSessionContext());
+        assertSSLSessionContextSize(0, s.c.sslContext.getServerSessionContext());
+    }
+}
diff --git a/libcore/luni/src/test/java/javax/net/ssl/SSLSessionTest.java b/libcore/luni/src/test/java/javax/net/ssl/SSLSessionTest.java
new file mode 100644
index 0000000..bf0e4d6
--- /dev/null
+++ b/libcore/luni/src/test/java/javax/net/ssl/SSLSessionTest.java
@@ -0,0 +1,317 @@
+/*
+ * 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 javax.net.ssl;
+
+import dalvik.annotation.KnownFailure;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import junit.framework.TestCase;
+
+public class SSLSessionTest extends TestCase {
+
+    public static final class Helper {
+
+        /**
+         * An invalid session that is not connected
+         */
+        public final SSLSession invalid;
+
+        /**
+         * The server side of a connected session
+         */
+        public final SSLSession server;
+
+        /**
+         * The client side of a connected session
+         */
+        public final SSLSession client;
+
+        /**
+         * The associated SSLSocketTest.Helper that is the source of
+         * the client and server SSLSessions.
+         */
+        public final SSLSocketTest.Helper s;
+
+        private Helper(final SSLSession invalid,
+                       final SSLSession server,
+                       final SSLSession client,
+                       final SSLSocketTest.Helper s) {
+            this.invalid = invalid;
+            this.server = server;
+            this.client = client;
+            this.s = s;
+        }
+
+        public static final Helper create() {
+            try {
+                final SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+                final SSLSocket ssl = (SSLSocket) sf.createSocket();
+                final SSLSession invalid = ssl.getSession();
+                final SSLSocketTest.Helper s = SSLSocketTest.Helper.create_workaround();
+                return new Helper(invalid, s.server.getSession(), s.client.getSession(), s);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+
+    public void test_SSLSocket_Helper_create() {
+        final Helper s = Helper.create();
+        assertNotNull(s.invalid);
+        assertFalse(s.invalid.isValid());
+        assertTrue(s.server.isValid());
+        assertTrue(s.client.isValid());
+    }
+
+    public void test_SSLSession_getApplicationBufferSize() {
+        final Helper s = Helper.create();
+        assertTrue(s.invalid.getApplicationBufferSize() > 0);
+        assertTrue(s.server.getApplicationBufferSize() > 0);
+        assertTrue(s.client.getApplicationBufferSize() > 0);
+    }
+
+    @KnownFailure("Expected SSL_NULL_WITH_NULL_NULL but received TLS_NULL_WITH_NULL_NULL")
+    public void test_SSLSession_getCipherSuite() {
+        final Helper s = Helper.create();
+        assertNotNull(s.invalid.getCipherSuite());
+        assertEquals("SSL_NULL_WITH_NULL_NULL", s.invalid.getCipherSuite());
+        assertNotNull(s.server.getCipherSuite());
+        assertNotNull(s.client.getCipherSuite());
+        assertEquals(s.server.getCipherSuite(),
+                     s.client.getCipherSuite());
+    }
+
+    public void test_SSLSession_getCreationTime() {
+        final Helper s = Helper.create();
+        assertTrue(s.invalid.getCreationTime() > 0);
+        assertTrue(s.server.getCreationTime() > 0);
+        assertTrue(s.client.getCreationTime() > 0);
+        assertTrue(Math.abs(s.server.getCreationTime() - s.client.getCreationTime()) < 1 * 1000);
+    }
+
+    public void test_SSLSession_getId() {
+        final Helper s = Helper.create();
+        assertNotNull(s.invalid.getId());
+        assertNotNull(s.server.getId());
+        assertNotNull(s.client.getId());
+        assertEquals(0, s.invalid.getId().length);
+        assertEquals(32, s.server.getId().length);
+        assertEquals(32, s.client.getId().length);
+        assertEquals(s.server.getId(), s.client.getId());
+    }
+
+    public void test_SSLSession_getLastAccessedTime() {
+        final Helper s = Helper.create();
+        assertTrue(s.invalid.getLastAccessedTime() > 0);
+        assertTrue(s.server.getLastAccessedTime() > 0);
+        assertTrue(s.client.getLastAccessedTime() > 0);
+        assertTrue(Math.abs(s.server.getLastAccessedTime() -
+                            s.client.getLastAccessedTime()) < 1 * 1000);
+        assertTrue(s.server.getLastAccessedTime() >=
+                   s.server.getCreationTime());
+        assertTrue(s.client.getLastAccessedTime() >=
+                   s.client.getCreationTime());
+    }
+
+    public void test_SSLSession_getLocalCertificates() throws Exception {
+        final Helper s = Helper.create();
+        assertNull(s.invalid.getLocalCertificates());
+        assertNull(s.client.getLocalCertificates());
+        assertNotNull(s.server.getLocalCertificates());
+        assertEquals(1, s.server.getLocalCertificates().length);
+        assertEquals(s.s.c.keyStore.getCertificate(s.s.c.publicAlias),
+                     s.server.getLocalCertificates()[0]);
+    }
+
+    public void test_SSLSession_getLocalPrincipal() throws Exception {
+        final Helper s = Helper.create();
+        assertNull(s.invalid.getLocalPrincipal());
+        assertNull(s.client.getLocalPrincipal());
+        assertNotNull(s.server.getLocalPrincipal());
+        assertNotNull(s.server.getLocalPrincipal().getName());
+        final X509Certificate x509certificate = (X509Certificate)
+            s.s.c.keyStore.getCertificate(s.s.c.publicAlias);
+        assertEquals(x509certificate.getSubjectDN().getName(),
+                     s.server.getLocalPrincipal().getName());
+    }
+
+    public void test_SSLSession_getPacketBufferSize() {
+        final Helper s = Helper.create();
+        assertTrue(s.invalid.getPacketBufferSize() > 0);
+        assertTrue(s.server.getPacketBufferSize() > 0);
+        assertTrue(s.client.getPacketBufferSize() > 0);
+    }
+
+    public void test_SSLSession_getPeerCertificateChain() throws Exception {
+        final Helper s = Helper.create();
+        try {
+            s.invalid.getPeerCertificateChain();
+            fail();
+        } catch (SSLPeerUnverifiedException e) {
+        }
+        assertNotNull(s.client.getPeerCertificates());
+        assertEquals(1, s.client.getPeerCertificates().length);
+        assertEquals(s.s.c.keyStore.getCertificate(s.s.c.publicAlias),
+                     s.client.getPeerCertificates()[0]);
+        try {
+            assertNull(s.server.getPeerCertificates());
+            fail();
+        } catch (SSLPeerUnverifiedException e) {
+        }
+    }
+
+    public void test_SSLSession_getPeerCertificates() throws Exception {
+        final Helper s = Helper.create();
+        try {
+            s.invalid.getPeerCertificates();
+            fail();
+        } catch (SSLPeerUnverifiedException e) {
+        }
+        assertNotNull(s.client.getPeerCertificates());
+        assertEquals(1, s.client.getPeerCertificates().length);
+        assertEquals(s.s.c.keyStore.getCertificate(s.s.c.publicAlias),
+                     s.client.getPeerCertificates()[0]);
+        try {
+            s.server.getPeerCertificates();
+            fail();
+        } catch (SSLPeerUnverifiedException e) {
+        }
+    }
+
+    public void test_SSLSession_getPeerHost() {
+        final Helper s = Helper.create();
+        assertNull(s.invalid.getPeerHost());
+        assertNotNull(s.server.getPeerHost());
+        assertNotNull(s.client.getPeerHost());
+    }
+
+    public void test_SSLSession_getPeerPort() {
+        final Helper s = Helper.create();
+        assertEquals(-1, s.invalid.getPeerPort());
+        assertTrue(s.server.getPeerPort() > 0);
+        assertEquals(s.s.c.port, s.client.getPeerPort());
+    }
+
+    public void test_SSLSession_getPeerPrincipal() throws Exception {
+        final Helper s = Helper.create();
+        try {
+            s.invalid.getPeerPrincipal();
+            fail();
+        } catch (SSLPeerUnverifiedException e) {
+        }
+        try {
+            s.server.getPeerPrincipal();
+            fail();
+        } catch (SSLPeerUnverifiedException e) {
+        }
+        assertNotNull(s.client.getPeerPrincipal());
+        assertNotNull(s.client.getPeerPrincipal().getName());
+        final X509Certificate x509certificate = (X509Certificate)
+            s.s.c.keyStore.getCertificate(s.s.c.publicAlias);
+        assertEquals(x509certificate.getSubjectDN().getName(),
+                     s.client.getPeerPrincipal().getName());
+
+    }
+
+    public void test_SSLSession_getProtocol() {
+        final Helper s = Helper.create();
+        assertNotNull(s.invalid.getProtocol());
+        assertEquals("NONE", s.invalid.getProtocol());
+        assertNotNull(s.server.getProtocol());
+        assertNotNull(s.client.getProtocol());
+        assertEquals(s.server.getProtocol(),
+                     s.client.getProtocol());
+    }
+
+    public void test_SSLSession_getSessionContext() {
+        final Helper s = Helper.create();
+        assertNull(s.invalid.getSessionContext());
+        assertNotNull(s.server.getSessionContext());
+        assertNotNull(s.client.getSessionContext());
+        assertEquals(s.s.c.sslContext.getServerSessionContext(),
+                     s.server.getSessionContext());
+        assertEquals(s.s.c.sslContext.getClientSessionContext(),
+                     s.client.getSessionContext());
+        assertNotSame(s.server.getSessionContext(),
+                      s.client.getSessionContext());
+    }
+
+    public void test_SSLSession_getValue() {
+        final Helper s = Helper.create();
+        try {
+            s.invalid.getValue(null);
+        } catch (IllegalArgumentException e) {
+        }
+        assertNull(s.invalid.getValue("BOGUS"));
+    }
+
+    public void test_SSLSession_getValueNames() {
+        final Helper s = Helper.create();
+        assertNotNull(s.invalid.getValueNames());
+        assertEquals(0, s.invalid.getValueNames().length);
+    }
+
+    public void test_SSLSession_invalidate() {
+        final Helper s = Helper.create();
+        assertFalse(s.invalid.isValid());
+        s.invalid.invalidate();
+        assertFalse(s.invalid.isValid());
+        assertNull(s.invalid.getSessionContext());
+
+        assertTrue(s.server.isValid());
+        s.server.invalidate();
+        assertFalse(s.server.isValid());
+        assertNull(s.server.getSessionContext());
+
+        assertTrue(s.client.isValid());
+        s.client.invalidate();
+        assertFalse(s.client.isValid());
+        assertNull(s.client.getSessionContext());
+    }
+
+    public void test_SSLSession_isValid() {
+        final Helper s = Helper.create();
+        assertFalse(s.invalid.isValid());
+        assertTrue(s.server.isValid());
+        assertTrue(s.client.isValid());
+    }
+
+    public void test_SSLSession_putValue() {
+        final Helper s = Helper.create();
+        final String key = "KEY";
+        final String value = "VALUE";
+        assertNull(s.invalid.getValue(key));
+        assertEquals(0, s.invalid.getValueNames().length);
+        s.invalid.putValue(key, value);
+        assertSame(value, s.invalid.getValue(key));
+        assertEquals(1, s.invalid.getValueNames().length);
+        assertEquals(key, s.invalid.getValueNames()[0]);
+    }
+
+    public void test_SSLSession_removeValue() {
+        final Helper s = Helper.create();
+        final String key = "KEY";
+        final String value = "VALUE";
+        s.invalid.putValue(key, value);
+        assertEquals(1, s.invalid.getValueNames().length);
+        assertEquals(key, s.invalid.getValueNames()[0]);
+        s.invalid.removeValue(key);
+        assertNull(s.invalid.getValue(key));
+        assertEquals(0, s.invalid.getValueNames().length);
+    }
+}
diff --git a/libcore/luni/src/test/java/javax/net/ssl/SSLSocketFactoryTest.java b/libcore/luni/src/test/java/javax/net/ssl/SSLSocketFactoryTest.java
new file mode 100644
index 0000000..5ccae7f
--- /dev/null
+++ b/libcore/luni/src/test/java/javax/net/ssl/SSLSocketFactoryTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 javax.net.ssl;
+
+import dalvik.annotation.KnownFailure;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.ServerSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import javax.net.ServerSocketFactory;
+import javax.net.SocketFactory;
+import junit.framework.TestCase;
+
+public class SSLSocketFactoryTest extends TestCase {
+    public void test_SSLSocketFactory_getDefault() {
+        SocketFactory sf = SSLSocketFactory.getDefault();
+        assertNotNull(sf);
+        assertTrue(SSLSocketFactory.class.isAssignableFrom(sf.getClass()));
+    }
+
+    public void test_SSLSocketFactory_getDefaultCipherSuites() {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        String[] cs = sf.getDefaultCipherSuites();
+        assertNotNull(cs);
+        assertTrue(cs.length != 0);
+    }
+
+    public void test_SSLSocketFactory_getSupportedCipherSuites() {
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        String[] cs = sf.getSupportedCipherSuites();
+        assertNotNull(cs);
+        assertTrue(cs.length != 0);
+    }
+
+    @KnownFailure("Should not parse bogus port number -1 during createSocket")
+    public void test_SSLSocketFactory_createSocket() throws Exception {
+        try {
+            SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+            Socket s = sf.createSocket(null, null, -1, false);
+            fail();
+        } catch (NullPointerException e) {
+        }
+
+        try {
+            SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+            Socket ssl = sf.createSocket(new Socket(), null, -1, false);
+            fail();
+        } catch (SocketException e) {
+        }
+
+        ServerSocket ss = ServerSocketFactory.getDefault().createServerSocket(0);
+        InetSocketAddress sa = (InetSocketAddress) ss.getLocalSocketAddress();
+        InetAddress host = sa.getAddress();
+        int port = sa.getPort();
+        Socket s = new Socket(host, port);
+        SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        Socket ssl = sf.createSocket(s, null, -1, false);
+        assertNotNull(ssl);
+        assertTrue(SSLSocket.class.isAssignableFrom(ssl.getClass()));
+    }
+}
diff --git a/libcore/luni/src/test/java/javax/net/ssl/SSLSocketTest.java b/libcore/luni/src/test/java/javax/net/ssl/SSLSocketTest.java
new file mode 100644
index 0000000..e5ad54d
--- /dev/null
+++ b/libcore/luni/src/test/java/javax/net/ssl/SSLSocketTest.java
@@ -0,0 +1,686 @@
+/*
+ * 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 javax.net.ssl;
+
+import dalvik.annotation.KnownFailure;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.Principal;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import javax.net.ServerSocketFactory;
+import javax.net.SocketFactory;
+import junit.framework.TestCase;
+
+public class SSLSocketTest extends TestCase {
+
+    public void test_SSLSocket_getSupportedCipherSuites() throws Exception {
+        final SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        final SSLSocket ssl = (SSLSocket) sf.createSocket();
+        final String[] cs = ssl.getSupportedCipherSuites();
+        assertNotNull(cs);
+        assertTrue(cs.length != 0);
+    }
+
+    public void test_SSLSocket_getEnabledCipherSuites() throws Exception {
+        final SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        final SSLSocket ssl = (SSLSocket) sf.createSocket();
+        final String[] cs = ssl.getEnabledCipherSuites();
+        assertNotNull(cs);
+        assertTrue(cs.length != 0);
+    }
+
+    @KnownFailure("Should support disabling all cipher suites")
+    public void test_SSLSocket_setEnabledCipherSuites() throws Exception {
+        final SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        final SSLSocket ssl = (SSLSocket) sf.createSocket();
+
+        try {
+            ssl.setEnabledCipherSuites(null);
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            ssl.setEnabledCipherSuites(new String[1]);
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            ssl.setEnabledCipherSuites(new String[] { "Bogus" } );
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+
+        ssl.setEnabledCipherSuites(new String[0]);
+        ssl.setEnabledCipherSuites(ssl.getEnabledCipherSuites());
+        ssl.setEnabledCipherSuites(ssl.getSupportedCipherSuites());
+    }
+
+    public void test_SSLSocket_getSupportedProtocols() throws Exception {
+        final SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        final SSLSocket ssl = (SSLSocket) sf.createSocket();
+        final String[] p = ssl.getSupportedProtocols();
+        assertNotNull(p);
+        assertTrue(p.length != 0);
+    }
+
+    public void test_SSLSocket_getEnabledProtocols() throws Exception {
+        final SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        final SSLSocket ssl = (SSLSocket) sf.createSocket();
+        final String[] p = ssl.getEnabledProtocols();
+        assertNotNull(p);
+        assertTrue(p.length != 0);
+    }
+
+    @KnownFailure("Should thow IllegalArgumentException not NullPointerException on null enabled protocols argument")
+    public void test_SSLSocket_setEnabledProtocols() throws Exception {
+        final SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        final SSLSocket ssl = (SSLSocket) sf.createSocket();
+
+        try {
+            ssl.setEnabledProtocols(null);
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            ssl.setEnabledProtocols(new String[1]);
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+        try {
+            ssl.setEnabledProtocols(new String[] { "Bogus" } );
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+        ssl.setEnabledProtocols(new String[0]);
+        ssl.setEnabledProtocols(ssl.getEnabledProtocols());
+        ssl.setEnabledProtocols(ssl.getSupportedProtocols());
+    }
+
+    @KnownFailure("session of unconnected socket should not be valid")
+    public void test_SSLSocket_getSession() throws Exception {
+        final SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
+        final SSLSocket ssl = (SSLSocket) sf.createSocket();
+        final SSLSession session = ssl.getSession();
+        assertNotNull(session);
+        assertFalse(session.isValid());
+    }
+
+    @KnownFailure("Implementation should not start handshake in ServerSocket.accept")
+    public void test_SSLSocket_startHandshake() throws Exception {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+        if (!SSLContextTest.IS_RI) {
+            /*
+             * The following hangs in accept in the Dalvik
+             * implementation because accept is also incorrectly
+             * starting the handhake.
+            */
+            c.serverSocket.setSoTimeout(1 * 1000);
+            /*
+             * That workaround doesn't seem to work so...
+             *
+             * See test_SSLSocket_startHandshake_workaround for
+             * redundant version of this test that works around this
+             * issue.
+             */
+            fail();
+        }
+        final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+        final Thread thread = new Thread(new Runnable () {
+            public void run() {
+                try {
+                    server.startHandshake();
+                    assertNotNull(server.getSession());
+                    try {
+                        server.getSession().getPeerCertificates();
+                        fail();
+                    } catch (SSLPeerUnverifiedException e) {
+                    }
+                    Certificate[] localCertificates = server.getSession().getLocalCertificates();
+                    assertNotNull(localCertificates);
+                    assertEquals(1, localCertificates.length);
+                    assertNotNull(localCertificates[0]);
+                    assertNotNull(localCertificates[0].equals(c.keyStore.getCertificate(c.privateAlias)));
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        thread.start();
+        client.startHandshake();
+        assertNotNull(client.getSession());
+        assertNull(client.getSession().getLocalCertificates());
+        Certificate[] peerCertificates = client.getSession().getPeerCertificates();
+        assertNotNull(peerCertificates);
+        assertEquals(1, peerCertificates.length);
+        assertNotNull(peerCertificates[0]);
+        assertNotNull(peerCertificates[0].equals(c.keyStore.getCertificate(c.publicAlias)));
+        thread.join();
+    }
+
+    @KnownFailure("local certificates should be null as it should not have been requested by server")
+    public void test_SSLSocket_startHandshake_workaround() throws Exception {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        final Thread thread = new Thread(new Runnable () {
+            public void run() {
+                try {
+                    final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+                    server.startHandshake();
+                    assertNotNull(server.getSession());
+                    try {
+                        server.getSession().getPeerCertificates();
+                        fail();
+                    } catch (SSLPeerUnverifiedException e) {
+                    }
+                    Certificate[] localCertificates = server.getSession().getLocalCertificates();
+                    assertNotNull(localCertificates);
+                    assertEquals(1, localCertificates.length);
+                    assertNotNull(localCertificates[0]);
+                    assertNotNull(localCertificates[0].equals(c.keyStore.getCertificate(c.privateAlias)));
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        thread.start();
+        final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+        client.startHandshake();
+        assertNotNull(client.getSession());
+        assertNull(client.getSession().getLocalCertificates());
+        Certificate[] peerCertificates = client.getSession().getPeerCertificates();
+        assertNotNull(peerCertificates);
+        assertEquals(1, peerCertificates.length);
+        assertNotNull(peerCertificates[0]);
+        assertNotNull(peerCertificates[0].equals(c.keyStore.getCertificate(c.publicAlias)));
+        thread.join();
+    }
+
+    @KnownFailure("Should throw SSLException on server, not IOException on client")
+    public void test_SSLSocket_startHandshake_noKeyStore_workaround() throws Exception {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create(null, null, null, null);
+        final Thread thread = new Thread(new Runnable () {
+            public void run() {
+                try {
+                    c.serverSocket.accept();
+                    fail();
+                } catch (SSLException e) {
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        thread.start();
+        final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+        if (!SSLContextTest.IS_RI) {
+            client.startHandshake();
+        }
+        thread.join();
+    }
+
+    /**
+     * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround
+     */
+    @KnownFailure("local certificates should be null as it should not have been requested by server")
+    public void test_SSLSocket_HandshakeCompletedListener_workaround() throws Exception {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        final Thread thread = new Thread(new Runnable () {
+            public void run() {
+                try {
+                    final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+                    server.startHandshake();
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        thread.start();
+        final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+        client.addHandshakeCompletedListener(new HandshakeCompletedListener() {
+            public void handshakeCompleted(final HandshakeCompletedEvent event) {
+                try {
+                    SSLSession session = event.getSession();
+                    String cipherSuite = event.getCipherSuite();
+                    Certificate[] localCertificates = event.getLocalCertificates();
+                    Certificate[] peerCertificates = event.getPeerCertificates();
+                    javax.security.cert.X509Certificate[] peerCertificateChain = event.getPeerCertificateChain();
+                    Principal peerPrincipal = event.getPeerPrincipal();
+                    Principal localPrincipal = event.getLocalPrincipal();
+                    Socket socket = event.getSocket();
+
+                    if (false) {
+                        System.out.println("Session=" + session);
+                        System.out.println("CipherSuite=" + cipherSuite);
+                        System.out.println("LocalCertificates=" + localCertificates);
+                        System.out.println("PeerCertificates=" + peerCertificates);
+                        System.out.println("PeerCertificateChain=" + peerCertificateChain);
+                        System.out.println("PeerPrincipal=" + peerPrincipal);
+                        System.out.println("LocalPrincipal=" + localPrincipal);
+                        System.out.println("Socket=" + socket);
+                    }
+
+                    assertNotNull(session);
+                    byte[] id = session.getId();
+                    assertNotNull(id);
+                    assertEquals(32, id.length);
+                    assertNotNull(c.sslContext.getClientSessionContext().getSession(id));
+                    assertNotNull(c.sslContext.getServerSessionContext().getSession(id));
+
+                    assertNotNull(cipherSuite);
+                    assertTrue(Arrays.asList(client.getEnabledCipherSuites()).contains(cipherSuite));
+                    assertTrue(Arrays.asList(c.serverSocket.getEnabledCipherSuites()).contains(cipherSuite));
+
+                    final Enumeration e = c.keyStore.aliases();
+                    Certificate certificate = null;
+                    Key key = null;
+                    while (e.hasMoreElements()) {
+                        String alias = (String) e.nextElement();
+                        if (c.keyStore.isCertificateEntry(alias)) {
+                            assertNull(certificate);
+                            certificate = c.keyStore.getCertificate(alias);
+                        } else if (c.keyStore.isKeyEntry(alias)) {
+                            assertNull(key);
+                            key = c.keyStore.getKey(alias, c.keyStorePassword);
+                        }
+                        else {
+                            fail();
+                        }
+                    }
+                    assertNotNull(certificate);
+                    assertNotNull(key);
+
+                    assertTrue(X509Certificate.class.isAssignableFrom(certificate.getClass()));
+                    final X509Certificate x509certificate = (X509Certificate) certificate;
+
+                    assertNull(localCertificates);
+
+                    assertNotNull(peerCertificates);
+                    assertEquals(1, peerCertificates.length);
+                    assertNotNull(peerCertificates[0]);
+                    assertEquals(peerCertificates[0], x509certificate);
+
+                    assertNotNull(peerCertificateChain);
+                    assertEquals(1, peerCertificateChain.length);
+                    assertNotNull(peerCertificateChain[0]);
+                    assertEquals(x509certificate.getSubjectDN().getName(),
+                                 peerCertificateChain[0].getSubjectDN().getName());
+
+                    assertNotNull(peerPrincipal);
+                    assertEquals(x509certificate.getSubjectDN().getName(),
+                                 peerPrincipal.getName());
+
+                    assertNull(localPrincipal);
+
+                    assertNotNull(socket);
+                    assertSame(client, socket);
+
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        client.startHandshake();
+        thread.join();
+    }
+
+    /**
+     * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround.
+     * Technically this test shouldn't even need a second thread.
+     */
+    public void test_SSLSocket_getUseClientMode_workaround() throws Exception {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        final Thread thread = new Thread(new Runnable () {
+            public void run() {
+                try {
+                    final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+                    assertFalse(server.getUseClientMode());
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        thread.start();
+        final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+        if (!SSLContextTest.IS_RI) {
+            client.startHandshake();
+        }
+        assertTrue(client.getUseClientMode());
+        thread.join();
+    }
+
+    /**
+     * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround.
+     * Technically this test shouldn't even need a second thread.
+     */
+    @KnownFailure("This test relies on socket timeouts which are not working. It also fails because ServerSocket.accept is handshaking")
+    public void test_SSLSocket_setUseClientMode_workaround() throws Exception {
+        // client is client, server is server
+        test_SSLSocket_setUseClientMode_workaround(true, false);
+        // client is server, server is client
+        test_SSLSocket_setUseClientMode_workaround(true, false);
+        // both are client
+        try {
+            test_SSLSocket_setUseClientMode_workaround(true, true);
+            fail();
+        } catch (SSLProtocolException e) {
+        }
+
+        // both are server
+        try {
+            test_SSLSocket_setUseClientMode_workaround(false, false);
+            fail();
+        } catch (SocketTimeoutException e) {
+        }
+    }
+
+    private void test_SSLSocket_setUseClientMode_workaround(final boolean clientClientMode,
+                                                            final boolean serverClientMode)
+            throws Exception {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        final SSLProtocolException[] sslProtocolException = new SSLProtocolException[1];
+        final SocketTimeoutException[] socketTimeoutException = new SocketTimeoutException[1];
+        final Thread thread = new Thread(new Runnable () {
+            public void run() {
+                try {
+                    final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+                    if (!serverClientMode) {
+                        server.setSoTimeout(1 * 1000);
+                        if (!SSLContextTest.IS_RI) {
+                            /* as above setSoTimeout isn't working in dalvikvm */
+                            fail();
+                        }
+                    }
+                    server.setUseClientMode(serverClientMode);
+                    server.startHandshake();
+                } catch (SSLProtocolException e) {
+                    sslProtocolException[0] = e;
+                } catch (SocketTimeoutException e) {
+                    socketTimeoutException[0] = e;
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        thread.start();
+        final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+        if (!clientClientMode) {
+            client.setSoTimeout(1 * 1000);
+            if (!SSLContextTest.IS_RI) {
+                /* as above setSoTimeout isn't working in dalvikvm */
+                fail();
+            }
+        }
+        client.setUseClientMode(clientClientMode);
+        client.startHandshake();
+        thread.join();
+        if (sslProtocolException[0] != null) {
+            throw sslProtocolException[0];
+        }
+        if (socketTimeoutException[0] != null) {
+            throw socketTimeoutException[0];
+        }
+    }
+
+    /**
+     * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround
+     */
+    public void test_SSLSocket_clientAuth_workaround() throws Exception {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        final Thread thread = new Thread(new Runnable () {
+            public void run() {
+                try {
+                    final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+                    assertFalse(server.getWantClientAuth());
+                    assertFalse(server.getNeedClientAuth());
+
+                    // confirm turning one on by itself
+                    server.setWantClientAuth(true);
+                    assertTrue(server.getWantClientAuth());
+                    assertFalse(server.getNeedClientAuth());
+
+                    // confirm turning setting on toggles the other
+                    server.setNeedClientAuth(true);
+                    assertFalse(server.getWantClientAuth());
+                    assertTrue(server.getNeedClientAuth());
+
+                    // confirm toggling back
+                    server.setWantClientAuth(true);
+                    assertTrue(server.getWantClientAuth());
+                    assertFalse(server.getNeedClientAuth());
+
+                    server.startHandshake();
+
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        thread.start();
+        final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+        client.startHandshake();
+        assertNotNull(client.getSession().getLocalCertificates());
+        assertEquals(1, client.getSession().getLocalCertificates().length);
+        thread.join();
+    }
+
+    /**
+     * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround
+     * Technically this test shouldn't even need a second thread.
+     */
+    public void test_SSLSocket_getEnableSessionCreation_workaround() throws Exception {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        final Thread thread = new Thread(new Runnable () {
+            public void run() {
+                try {
+                    final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+                    assertTrue(server.getEnableSessionCreation());
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        thread.start();
+        final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+        assertTrue(client.getEnableSessionCreation());
+        if (!SSLContextTest.IS_RI) {
+            client.startHandshake();
+        }
+        thread.join();
+    }
+
+    /**
+     * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround
+     */
+    @KnownFailure("Server side session creation disabling does not work, should throw SSLException, not fail")
+    public void test_SSLSocket_setEnableSessionCreation_server_workaround() throws Exception {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        final Thread thread = new Thread(new Runnable () {
+            public void run() {
+                try {
+                    final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+                    server.setEnableSessionCreation(false);
+                    try {
+                        server.startHandshake();
+                        fail();
+                    } catch (SSLException e) {
+                    }
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        thread.start();
+        final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+        try {
+            client.startHandshake();
+            fail();
+        } catch (SSLException e) {
+        }
+        thread.join();
+    }
+
+    /**
+     * Marked workaround because it avoid accepting on main thread like test_SSLSocket_startHandshake_workaround
+     */
+    @KnownFailure("Should throw SSLException on server, not IOException")
+    public void test_SSLSocket_setEnableSessionCreation_client_workaround() throws Exception {
+        final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+        final Thread thread = new Thread(new Runnable () {
+            public void run() {
+                try {
+                    final SSLSocket server = (SSLSocket) c.serverSocket.accept();
+                    try {
+                        server.startHandshake();
+                        fail();
+                    } catch (SSLException e) {
+                    }
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        thread.start();
+        final SSLSocket client = (SSLSocket) c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+        client.setEnableSessionCreation(false);
+        try {
+            client.startHandshake();
+            fail();
+        } catch (SSLException e) {
+            if (!SSLContextTest.IS_RI) {
+                fail();
+            }
+        }
+        thread.join();
+    }
+
+    /**
+     * SSLSocketTest.Helper is a convenience class for other tests that
+     * want a pair of connected and handshaked client and server
+     * SSLSocketsfor testing so they don't have to duplicate the
+     * logic.
+     */
+    public static final class Helper {
+        public final SSLContextTest.Helper c;
+        public final SSLSocket server;
+        public final SSLSocket client;
+
+        private Helper (final SSLContextTest.Helper c,
+                        final SSLSocket server,
+                        final SSLSocket client) {
+            this.c = c;
+            this.server = server;
+            this.client = client;
+        }
+
+        /**
+         * based on test_SSLSocket_startHandshake_workaround, should
+         * be written to non-workaround form when possible
+         */
+        public static Helper create_workaround () {
+            final SSLContextTest.Helper c = SSLContextTest.Helper.create();
+            final SSLSocket[] sockets = connect_workaround(c, null);
+            return new Helper(c, sockets[0], sockets[1]);
+        }
+
+        /**
+         * Create a new connected server/client socket pair within a
+         * existing SSLContext. Optional clientCipherSuites allows
+         * forcing new SSLSession to test SSLSessionContext caching
+         */
+        public static SSLSocket[] connect_workaround (final SSLContextTest.Helper c,
+                                                      String[] clientCipherSuites) {
+            try {
+                final SSLSocket[] server = new SSLSocket[1];
+                final Thread thread = new Thread(new Runnable () {
+                    public void run() {
+                        try {
+                            server[0] = (SSLSocket) c.serverSocket.accept();
+                            server[0].startHandshake();
+                        } catch (RuntimeException e) {
+                            throw e;
+                        } catch (Exception e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                });
+                thread.start();
+                final SSLSocket client = (SSLSocket)
+                    c.sslContext.getSocketFactory().createSocket(c.host, c.port);
+                if (clientCipherSuites != null) {
+                    client.setEnabledCipherSuites(clientCipherSuites);
+                }
+                client.startHandshake();
+                thread.join();
+                return new SSLSocket[] { server[0], client };
+            } catch (RuntimeException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public void test_SSLSocketTest_Test_create() {
+        Helper test = Helper.create_workaround();
+        assertNotNull(test.c);
+        assertNotNull(test.server);
+        assertNotNull(test.client);
+        assertNotNull(test.server.isConnected());
+        assertNotNull(test.client.isConnected());
+        assertNotNull(test.server.getSession());
+        assertNotNull(test.client.getSession());
+    }
+}
diff --git a/libcore/luni/src/test/java/tests/AllTests.java b/libcore/luni/src/test/java/tests/AllTests.java
index 3110026..4b3a484 100644
--- a/libcore/luni/src/test/java/tests/AllTests.java
+++ b/libcore/luni/src/test/java/tests/AllTests.java
@@ -65,6 +65,7 @@
         suite.addTest(java.text.AllTests.suite());
         suite.addTest(java.util.AllTests.suite());
         suite.addTest(javax.xml.parsers.AllTests.suite());
+        suite.addTest(javax.net.ssl.AllTests.suite());
         suite.addTest(org.apache.harmony.luni.platform.AllTests.suite());
         suite.addTest(org.json.AllTests.suite());
         suite.addTest(tests.api.org.apache.harmony.kernel.dalvik.AllTests.suite());
diff --git a/libcore/run-core-tests b/libcore/run-core-tests
index 67b79ee..57120c5 100755
--- a/libcore/run-core-tests
+++ b/libcore/run-core-tests
@@ -16,7 +16,7 @@
 
 # To run these tests:
 # mmm -j14 dalvik snod
-# adb reboot && fastboot flashall
+# adb reboot bootloader && fastboot flashall
 # adb shell run-core-tests
 
 tmp=/data/core-tests.tmp
diff --git a/libcore/tools/runner/java/vogar/JavaVm.java b/libcore/tools/runner/java/vogar/JavaVm.java
index 9c4e175..dcb0969 100644
--- a/libcore/tools/runner/java/vogar/JavaVm.java
+++ b/libcore/tools/runner/java/vogar/JavaVm.java
@@ -53,6 +53,15 @@
         classpath.addAll(this.classpath);
         classpath.addAll(environment.runnerClassesDir());
         classpath.addAll(runnerClasspath);
+        /*
+         * For javax.net.ssl tests dependency on Bouncy Castle for
+         * creating a self-signed X509 certificate. Needs to be run
+         * with an openjdk, not a sunjdk, which expects a signed jar
+         * to authenticate security providers. For example:
+         *
+         * --java-home /usr/lib/jvm/java-6-openjdk
+         */
+        classpath.addAll(new File("/usr/share/java/bcprov.jar"));
         return classpath;
     }
 }